The Programmer's Guide to LDOS/TRSDOS Version 6 by Roy Soltoff, BSEE MISOSYS Alexandria, Virginia Copyright (c) 1983 MISOSYS All Rights Reserved ~First Edition - 1983~ ~Second Edition - 1984~ Reproduction in any manner, electronic, mechanical, magnetic, optical, chemical, manual or otherwise, without expressed written permission is prohibited. Disclaimer: While MISOSYS has taken every precaution in the preparation of this book, it assumes no responsibility for errors or omissions. Neither is any liability assumed for damages resulting from the use of the information contained herein. LDOS is a trademark of Logical Systems, Incorporated. TRSDOS is a trademark of Tandy Corporation. CP/M is a trademark of Digital Research Incorporated. IBM is a trademark of International Business Machines, Inc. ~MISOSYS, Inc.~ P. O. Box 239 Sterling, Virginia 22170-0239 This book is dedicated to my first daughter, Stacey Elizabeth, whose birth the eighth of June of 1983 provided me my proudest moment in life. There is no way that I can sufficiently thank my wife, Brenda, for nurturing and bringing forth this new human being - but I'll try. ~- iii -~ ~Preface~ Many thousands of users take it upon themselves to explore the workings of an operating system so as to gain a better understanding of application software interfacing. This has always been such a waste of programmer talent because the system's designers usually know the best interfacing procedures. A complex operating system has many ideosyncracies. Because of this, some procedures work much better than others to accomplish the same goal. An operating system in this day and age demands that precious talent not be wasted. LDOS Version 6 is a complex operating system. There should not be a void of information that the programmer needs to properly write his or her software. For the programmer, this book should fill that void. It is not intended as an assembly language learning tool nor is it intended as an expose' of "mysteries" concerning the internal workings of the operating system. This book conveys that information which is essential to the job of programming application software, utilities, device drivers and filters. It is very important for the programmer to keep PORTABILITY paramount in the thinking that goes along with program design. LDOS Version 6 was designed to provide portability for application software by incorporating standard protocols and conventions for all interfacing. Keep that in mind when you explore the contents of this book. Knowing full well that the microcomputer community inherently finds distasteful the prospect of reading documentation cover-to-cover prior to jumping in and getting their feet wet, this book includes an index. Then again, what kind of book omits an index? Feel free to access the information randomly, although I recommend that a sequential scanning is more suited to the learning process. The chapter contents have been designed to be self contained. Thus, you may find some small repetition of subject matter where it was felt that a term or concept may not have been carried over from an earlier chapter due to an indexed access of the subject matter. I have tried to be complete within the subjects discussed. As there are some proprietary items within the operating system, confidentiality precludes their appearance in this book. However, any work of this magnitude is bound to omit a detail. If you feel that a subject should have been included, please bring it to the publisher's attention. Remember that the desire to foster the development of portable software may mean that certain points may have been omitted to preclude the writing of non-portable machine specific software. Where you must write machine specific software, it is recommended that you obtain the manufacturer's hardware technical manual. The programming examples were coded with the PRO-CREATE assembler which is available from MISOSYS. References to SuperVisor Calls in the form @XXXX should have a corresponding EQU statement which defines the SVC number. For those individuals firmly entrenched in operating system exploration, I heartily recommend THE SOURCE, a three-volume set of books that provide the complete set of assembler source listings that constitute LDOS Version 6.2.0. THE SOURCE is available from Logical Systems, Inc. Lastly, the author is always open to suggestions for improving this book. Certainly if you uncover erroneous data, suggest that it be corrected in the next printing. I wish you successful programming. ~- iv -~ ~Table of Contents~ Chapter 1 - An Operating System Overview LDOS Version 6 - An Operating System Overview .................... 1 Chapter 2 - Device Input/Output Interfacing Device I/O in General ............................................ 11 The Device Control Block ......................................... 12 Accessing Device Control Blocks .................................. 14 Device Chain Illustrations ....................................... 14 Device Driver/Filter Template .................................... 22 @CTL Interfacing to Device Drivers ............................... 28 Chapter 3 - Disk Drive Input/Output Interfacing General Disk Drive Configurations ................................ 34 Drive Control Table .............................................. 37 Disk Controller Communications ................................... 44 Hard Disk Allocation Schemes ..................................... 50 Placement of Disk Drivers ........................................ 54 Chapter 4 - The DOS Directory Structure General Directory Conventions .................................... 57 The Granule Allocation Table ..................................... 59 The Hash Index Table ............................................. 64 The Directory Record Structure ................................... 68 Chapter 5 - Disk File Access and Control General File Structures .......................................... 75 Controlling Disk Files ........................................... 78 Accessing Disk Files ............................................. 86 The File Control Block ........................................... 91 Chapter 6 - Interfacing via SuperVisor Calls SuperVisor Call Linkage .......................................... 99 Program Entry and Exit conditions ............................... 100 SuperVisor Calls Listed Alphabetically .......................... 101 SuperVisor Calls Listed Numerically ............................. 103 SuperVisor Calls Listed by Function Group........................ 106 SuperVisor Call Details ......................................... 109 Appendix - Miscellaneous Subject Matter Boot Initialization ICNFG interfacing ........................... 143 BREAK, PAUSE, ENTER Interrupt Latch Handling .................... 145 Disk Load Module Format ......................................... 149 Error Message Dictionary ........................................ 154 Header Protocol of Memory Modules ............................... 160 Interrupt Task Processor Interfacing ............................ 162 Low Memory Details .............................................. 166 Memory Bank Switching ........................................... 169 Non-interrupt Background Task (KITSK) Interfacing ............... 174 System Disk Boot Track .......................................... 176 System Overlay Contents and Access .............................. 179 Using the System Parameter Scanner .............................. 182 Sample Filters [TRAP, SLASH0, BOLDFACE] ......................... 189 List of Figures~...................................................... 201 Index~................................................................ 203 ~- v -~ ~Operating System Overview~ LDOS VERSION 6 - AN OPERATING SYSTEM OVERVIEW ============================================= After spending a few hours at any computer show featuring micro- computers, it becomes obvious that most 8-bit machines look surprisingly similar. Each comes equipped more or less with the following features: CRT monitor, keyboard, one or more 5-1/4" or 8" floppy disk drives (usually 5-1/4" minifloppies), 64K-128K of RAM, and a processor card. With the industry seemingly adopting CP/M as an operating system pseudo-standard, the chip usually chosen is Zilog's Z-80 microprocessor. The design of these machines must be sufficiently straight forward. While each competing manufacturer attempts to make its machine more desirable by implementing greater reliability, flexible interfacing, more peripheral support, additional hardware features, attractive packaging, and lower cost, cognizance of the cost effectiveness of utilizing smarter software may just be the important ingredient sometimes overlooked. Alternative operating systems are available that bring a great deal of main-frame power to the microcomputer. One such system, LDOS Version 6 [or its licensed dialects such as TRSDOS 6], is a classic example of a truly powerful operating system designed for an eight bit microcomputer using the Z-80 processor chip. LDOS provides a single-user system with total device independence, dynamic file space allocation, extensive file management, job control language structures, a large library of utilities, plus the ability to easily interface to disk storage devices with capacities from 88 kilobyte minifloppies to multi-megabyte winchester disk drives. Error trapping and an English-like command structure help make LDOS a user-friendly but powerful operating system. The primary design obligation of LDOS is to ensure MEDIA COMPATIBILITY across all machines running the DOS (within the 5-1/4 or 8" size). This means that a user must be able to take a diskette and use it across all machines running LDOS - so long as the hardware permits that size diskette. To accomplish this, the DOS has a "standard" 5-1/4" structure - both single density and double density. It also has a "standard" 8" diskette structure. The structure goes beyond just the format and allocation schemes - it covers the entire directory makeup. The hardware architecture chosen for LDOS Version 6 is a Z-80 based microcomputer with a minimum of 64K RAM and 80 by 24 video screen size. The DOS includes a bank-switching SuperVisor Call that implements memory bank switching. The SVC permits switching a memory segment (usually the top 32K) with up to seven auxiliary 32K memory banks. It also supports the controlled transfer of execution to a location within the bank at the option of the user. The system maintains supervision of the resident bank to ensure that the standard bank (bank 0) is always resident during certain operations (disk I/O, character I/O, and interrupt task handling). The DOS is designed to operate starting from address zero (page 0 origin) and is 100% SuperVisor Call (SVC) accessed. System data items needed by application software are also available via SVCs. Figure 1-1 represents a block diagram of the operating system. Essentially, there are two levels of interaction to the system - command level and primitive level. At the command level, the operator enters a command which requests the execution of some function [perhaps the listing of a file, the displaying of a disk directory, the running of a BASIC program, ~1 - 1~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ or the compiling of a C language source file]. The command interpreter parses the user entry, determines whether the request is for a system function or user-supplied function, then arranges for the necessary system resources. Control is transferred to the module necessary to satisfy the request. The system passes parameter pointers to the module and expects a return code upon the module's completion. System resources and data quantities are requested via a SuperVisor Call (SVC) processor. An SVC is associated with all system primitives (i.e. get a character, put a character, open a file, add a task, rename a file, ...). Application software written in a low-level language (such as assembler) makes direct use of the SVC. Programs using a high-level language (i.e. BASIC, C, PASCAL, ...) need not bother with the SVC as system interfacing is accomplished within the language interpreter or compiler. The DOS supports up to eight logical disk packs or volumes logically numbered 0-7. Each floppy, be it one or two sided, is treated as a single volume. Hard disk drives (winchesters) may be treated as a single volume or partitioned into multiple volumes. A Drive Control Table (DCT) contains the parameters associated with each disk (number of cylinders, heads, and sectors per track for example) and also interfaces the disk driver software to the system. ~1 - 2~ ~Operating System Overview~ Character Input/Output devices (i.e. keyboard, video display, printer, RS-232 serial ports, ...) and their associated software driver routines are interfaced to the system via Device Control Blocks (DCB). I/O devices are identified by a two-character device name such as KI (keyboard input), DO (video output), PR (printer), and CL (communications line). Whenever a device is specified, it is denoted by an asterisk followed by the device name to form a complete "device specification". The reason for this will soon become evident. Additional devices can be defined to the system once an appropriate software driver is available. The device name selection is left up to the user. A collection of data stored on disk is termed a file and is denoted by a file specification. A complete file specification consists of five parts: a file name of up to eight characters, a file extension of up to three characters, a file password of up to eight characters, the logical drive specification, and optionally, in certain cases of Partitioned Data Sets (PDS), a member specification of up to eight characters. Whenever users institute a structured naming convention, most files are accessible via the file name reference only. The DOS will search all drives for a file if the drive specification is omitted from the file specification. Also, many system utilities and user applications can use default file extensions to separate files into classes. For example, PRO-CREATE, a popular assembler running under the DOS, will automatically use the file extension "/ASM" for its source files and "/CMD" for its object code generation thus alleviating the user of the necessity to enter the file extensions (it also helps to prevent inadvertantly overwriting one file with another). Similarly, LDOS makes extensive use of default file extensions such as "JCL" for all Job Control Language, "TXT" for ASCII listings, "FLT" for all device filters, etc. File specifications and device specifications are generally inter- changeable. Thus, wherever a file specification is needed, a device specification can usually be entered. This is one of the examples of device independence in the system. The protocol used in character I/O is identical across logical devices (i.e. *KI, *PR, *SO, ...) and disk files. Thus, character I/O is handled the same way regardless of the physical device identified in the Device/File Control Block (DCB/FCB) - be it physical keyboard, printer, or disk file. For example, the COPY utility is used primarily to copy a file from one disk to another as in: COPY ARTICLE/TXT:0 TO ARTICLE/TXT:1 which creates a duplicate on drive 1 of the file specified "ARTICLE/TXT" located on drive 0. In lieu of the file specifications, device specifications could equally be used as in the following: COPY *KI TO *PR which copies keyboard input directly to the printer. With ease, a keyboard can be added to a daisy-wheel printer turning it into a temporary typewriter. Perhaps a more useful illustration would be the convenience of directing program output to video display, printer, or a file depending on the device/file specification provided. The acquisition of disk file space is completely transparent to the user. This frees the user from worrying about sectors, tracks, cylinders, ~1 - 3~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ heads, and even disk drives in most cases. File space is obtained dynamically for any given file when space is required. Since directory accesses are dynamic (i.e. any time directory information requires updating, a disk access is made), users can change floppy diskettes in a disk drive after any open files on the disk have been closed with out having to "log" the action. Files do not have to occupy contiguous space on a disk but can exist in blocks of space called extents. Linkage maps exist in a file's directory which connect each extent. Access to a file is achieved by placing the file specification in a File Control Block (FCB), referencing a user disk file I/O buffer, and issuing the "OPEN" SVC. The provision of a separate file buffer for each file greatly adds to the system's flexibility. Directory information needed by the file access routines is then placed in the "open" FCB. Thereafter, SVC requests for file positioning, reading, and writing are available to access any record in the file. Fixed record lengths of from one to 256 bytes are available directly at the SVC level. Languages, such as BASIC, generally provide sequential files with variable record lengths. Although the functions supported are many, a minimum of the machine's RAM space is required by LDOS. This is achieved by having only frequently used routines resident in memory while others are brought in to an overlay region on an as-required basis. All of the functions identified in Figure 1-1, including the device and disk drivers (both floppy and hard), are contained in a 9K memory space which includes a 1.5K (1536 bytes) system overlay region. Another 3K region is used for the execution of system library commands but may be used by applications that do not request system library functions. Functionally, the DOS is divided into seven regions: system low core (LOWCORE), Input/Output driver region (IOR), resident system (SYSRES), System Overlay Region (SOR), Library Overlay Region (LOR), User Program Region (UPR), and high memory region (HIMEM). The UPR extends from X'3000' through HIGH$. Figure 1-2 illustrates these regions. The DOS normally does not use HIMEM; however, certain user-specified requests must be satisfied by use of high memory. For example, SPOOL filter and buffer space use high memory. KSM filter and data space use high memory. A pointer to the top of HIMEM is available via an SVC and programs must honor this HIGH$ pointer. The interrupt task scheduler listed in figure 1-2 under SYSRES schedules the execution of small background tasks at periodic intervals. The time intervals are determined primarily by a hardware generated interrupt to the Z-80 processor. A desirable minimum interrupt rate would be 40-60 Hz. This "clock" is software divided to produce "high", medium, and "low" level task control. The DOS provides for eight low level tasks, three medium level tasks, and one high level task. For example, with a 60Hz interrupt rate, one task can be performed at 16.7ms intervals, three discrete tasks can be processed at 33.3ms intervals while eight other tasks are processed at 267ms intervals. The types of tasks generally operating from such a scheduler would be software time of day routines, printer despooling routines, address trace functions, keyboard type ahead scanning, blinking cursor routines, or other processes that need to be examined at periodic intervals. As a specific example of how software can reduce hardware costs, briefly examine keyboard type-ahead. This feature is quite significant to a fast typist. Even slow operator entry can gain from type ahead by the ability to enter responses in anticipation of known queries. Even if the hardware does not provide an interrupt generating keyboard, the DOS implements a 64-128 (depending on release) character type ahead buffer via task polling which is ~1 - 4~ ~Operating System Overview~ adequate for all operators. ===================================================== | | | ~LOWCORE:~ X'0000' - @$SYS | | RST vectors, NMI vector, System flags, Date, | | Time, System FCB, DEBUG register save area, | | JCL FCB, Command FCB, SVC Table, DCB Table, | | System stack, Miscellaneous data, Command input | | buffer, Drive Control Table, Device I/O handler,| | Clock task, Memory management routines. | |---------------------------------------------------| | ~IOR:~ @$SYS - X'12FF' | | Keyboard, Video, Printer, and Disk drivers. | |---------------------------------------------------| | ~SYSRES:~ X'1300' - X'1DFF' | | File access routines, SVC processor, System | | overlay handler, System program loader, | | Interrupt Task Schedular, System buffer. | |---------------------------------------------------| | ~SOR:~ X'1E00' - X'23FF' | | Execution region for system overlays 2-5, 9-13, | | overlay disk file buffer. | |---------------------------------------------------| | ~LOR:~ X'2400' - X'25FF' & X'2600' - X'2FFF' | | Execution region for system library comands | | contained in libraries A, B, & C. | |---------------------------------------------------| | ~UPR:~ X'3000' - (HIGH$) | | Execution region for user transient programs | | (note: programs not accessing the system | | libraries can start at X'2600'. | |---------------------------------------------------| | ~HIMEM:~ (HIGH$)+1 - X'FFFF' | | Region for relocation of extended system and | | user static modules. | |---------------------------------------------------| | | | ~Figure 1-2: System Map~ | | | ===================================================== The task scheduler is also used by the despooling function of the printer spooler. The DOS spooler implements a combination of memory and disk buffers to temporarily hold the printer output. This output is despooled to the printer under the control of the task scheduler. The function, being transparent to the user, can continue the despooling even after the application generating the output is finished and another started. When the system contains 128K (or more) of RAM, the extra RAM can be set aside for the spooler's memory buffer. The primary function of any operating system is to provide the user with a facility for managing and accessing files stored on disk storage devices. Since the user must not be burdened with the physical details of the storage devices themselves, it is the operating system's responsibility to translate all file record access requests into specific drive, track, sector, and head ~1 - 5~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ parameters that pinpoint the storage location of each record. The DOS supports a wide range of disk storage capacities. Let's take a brief look at how a disk drive is organized Each track is formatted into a specific quantity of 256-byte sectors with a maximum capacity of 32 sectors per track. Sectors are grouped into blocks called "granules" which vary in size according to total track capacity. Whenever additional disk space is needed for a file, an additional granule is allocated. The granule thus becomes the minimum size storage unit. Where multiple headed drives are in use, the track numbers on a surface are duplicated on each surface with all similarly numbered tracks constituting a cylinder. Cylinder capacities also have an upper limit of 256 sectors per cylinder or eight granules per cylinder while the system supports a maximum of eight heads per drive. In order to evenly use the entire surface of a drive, files are uniformly distributed across each surface [note: LSI unfortunately has changed to a fixed allocation scheme effective with release 6.1]. That means the head has a tendency to be randomly located whenever a directory access is needed. Because of this, each disk drive's directory is placed on the cylinder closest to its midpoint which provides a tendency to minimize the average seek time for directory accesses. The directory, of course, contains information on each file stored on the drive as well as additional tables and codes pertinent to the drive. The first sector of the directory contains a granule allocation table (GAT). The GAT is bit mapped to each granule of space on the drive. Other fields in the GAT contain the PACK NAME, DATE of creation, pack PASSWORD, and data pertaining to the configuration of the drive. The system can support a capacity of 13 Megabytes of directly addressable storage on each of eight drives. Rigid disk drives of greater capacities can be supported by partitioning them into two or more logical drives. Also, where a physical parameter exceeds the upper limits, translation techniques can be used in software. Again, the flexibility of the system provided through intelligent software allows for easy interfacing. When a file is to be opened for access, the system needs to search the directory for its directory record. Search time is minimized by using a hashing technique to reduce the 11-character string formed from the file name and extension to a one-byte value. The hash code for each file is stored in a Hash Index Table (HIT) which is the second sector of the directory. Each position in this table corresponds to a specific directory entry record. The hash table, being a sector in length, can index a maximum of 256 directory records or files. The directory itself is sized according to disk capacity by being a maximum of one cylinder (up to 34 sectors). Thus, the larger the disk storage capacity, the larger its directory, and the greater the number of file names that can be stored. To open a file, therefore, the file name and extension are gathered from the specification and put through the hashing algorithm. The HIT sector is read and searched for a matching value. When a match is found, the directory sector containing the corresponding directory record is read. To guard against a different file name/ext hashing to the same value (which is called a collision), the 11-byte string is then checked for a match. If the correct record has not been retrieved, the HIT is examined further. ~1 - 6~ ~Operating System Overview~ The directory record contains information such as the date the file was last modified, its update and access password codes, its access level, other attributes such as whether it is a SYStem or PDS file and if a backup has been made, the relative number of the last sector in the file and the last byte within the last sector. The record also contains the physical storage in use by the file by pointing to the cylinder, relative starting granule, and number of contiguous granules for each extent linking up the file. When a file has more than four extents, additional directory records are used as required with forward and backward pointers linking each record. A feature considered important by many users is the flexibility of the file management utilities. These utilities include such functions as copying files from one drive to another, appending two files together, listing files with structured formatting, renaming files, removing files, obtaining disk directories, and making archival backups of your "favorite" files. All are popular functions with BACKUP being one of the most important in light of the tremendous capacity available when using large storage devices. Ever since small winchester drives started to appear interfaced to small microcomputers, the question of how to backup these devices loomed large. Although some installations consider streaming tape for backup (relatively expensive as an added cost) while others are incorporating video cassette recorder interfaces (assumes the availability of VCRs at the micro site or another added cost), by far the most popular method has been the use of floppy diskettes (least expensive and widely available). Floppies do have a serious drawback. When comparing the available capacities of a single floppy to a small winchester, it soon becomes obvious that a good handful of diskettes are required to backup the hard drive. A sophisticated backup utility can ease the frustration of archiving hard disk files. For one thing, with the availability of 80-track 2-headed minifloppies, over 700 Kilobytes can be stored on a single 5-1/4" diskette when recorded in double density. With 2-headed 8" drives, 1.2 Megabytes of storage exist on a floppy diskette. For another thing, the backup utility provides exceptional flexibility as can be evidenced by the following command examples: BACKUP :4 TO :2 will copy all files from logical drive 4 to logical drive 2. If both drives are floppies having the same physical configuration (i.e. both 40-track 2-headed with the same density), then the backup will automatically be performed track by track called "mirror image". BACKUP /TXT:3 TO :5 (OLD) will copy all files with a file extension of "TXT" from logical drive 3 to logical drive 5 but only if the file already exists on logical drive 5. The use of the "OLD" parameter permits organization of archival copies. BACKUP R$S/BAS:4 TO :2 (MOD,DATE="11/09/82-11/15/82") will make copies of all files from logical drive 4 with a filename starting with the character "R", the third character "S", with any character acceptable in all other file name character positions. Also, files must have ~1 - 7~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ been last modified between the dates of November 9, 1982 through November 15, 1982 inclusive in order to be included in the backup. In addition, the file must not have been backed up since it was last modified. These examples illustrate the extreme flexibility of managing archival copies of working files. When used in a hard drive environment, large capacity floppy diskettes can be used to store selected "classes" of files with working files backed up in a structured manor only if they have been modified. Daily "churning" of working files is minimal, thus a procedure that enables a backup only if a modification has been done to a working file within a class certainly lends itself to optimum file management techniques without the need for expensive backup hardware. For those cases where a single file exceeds the capacity of a single floppy, a separate utility provides diskette spanning capabilities for the backup. The command to obtain a directory display is used frequently in most machine environments. The DOS directory command listing is sorted by file name/ext. When the length of a listing exceeds the line capacity of the video display, paging is performed with a pause at each page. The listing provides data on the protection level, logical record length, file length (in kilobytes), date of last update, and whether a backup copy exists, for each file in the directory. A partial file specification can be requested to limit the listing to those files in the "class" similar to the BACKUP utility. Disk files are supported with two types of access - Record I/O and character I/O. Logical Records of from one to 256 bytes in length can be read or written using the @READ or @WRITE SVC requests. Record I/O can be random access (by position SVC requests prior to READ/WRITE) or sequential access using repetitive READs or WRITEs. Character I/O is accomplished by @GET and @PUT SVC requests and is essentially the same as record I/O with a Logical Record Length (LRL) equal to one. However, if GET and PUT are used to implement sequential access, then a file can be considered a character I/O device just like a printer, a serial port, or a video display device. A byte I/O request is therefore independent of the physical device "connected" to the control block which is requesting the I/O. This makes the system "device independent". Routing, filtering, and linking is 100% - devices may be routed to files and subsequently filtered and linked. A priority level hierarchy is established according to bit assignments in the DCB: file, NIL, route, link, and filter (file being the highest). Filters are assigned control blocks in the DCB table area which supports up to 31 entries. Each device driver and filter has its own entry. The establishment of a LINK also uses a DCB entry to maintain the pointers used for each device in the LINK. Several system library commands, such as the FILTER, LINK, RESET, ROUTE, and SET commands, are provided that are used to support device independence. An illustration of the use of these commands lends well to understanding the full power of device independence. For example, if a suitable software driver (with a filename of RS232/DVR) is available for a serial port (RS-232 channel), then a simple: SET *CL TO RS232 will establish the serial port as a device with "CL" as the device name. Now that such a device is available, the user can: ~1 - 8~ ~Operating System Overview~ LINK *KI TO *CL LINK *DO TO *CL and the micro is established as a "host" because the serial communications line has been linked to both the machine's keyboard and its video display - the primary input and output devices of the machine. Device I/O can also be massaged with transformation functions, called filters. For example, an EBCDIC to ASCII translation filter is available that when applied to the serial port by a simple: SET *XL TO XLATE USING EBCDIC FILTER *CL WITH XLATE the micro can be tied to an IBM mainframe which supports only EBCDIC ports. Want to implement a DVORAK keyboard? By simply filtering the *KI device with the DVORAK translation filter, the keyboard is reorganized - with NO hardware changes required. Many filters are available to format print output, trap specific character codes, perform upper/lower case conversions - the limits are boundless. That's flexibility! Now that you have a flavor of the capabilities of the DOS, this guide can be used to understand how to interface your programs. The bulk of LDOS Version 6 is machine independent. What this means to you as a programmer is that once you write an application to run under LDOS 6.x, it is portable to any machine running version 6. All you need do is utilize the standard interfacing procedures discussed in this programmers guide. Let the DOS do what an operating system is supposed to do - interface the application to the hardware. ~1 - 9~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ This page intentionally left blank ~1 - 10~ ~Device Input/Output Interfacing~ DEVICE I/O IN GENERAL ===================== Devices interface to the operating system through driver modules. Character-oriented devices (keyboards, video display tubes, printers, and serial terminals, to name but a few), have their drivers connected to the DOS by Device Control Block (DCB) tables [this is in contrast to disk-type devices which have drivers connected to the system through Drive Control Tables (DCT)]. The purpose of the DCB is to associate a device name with the device hardware itself. A device specification (abbreviated as "devspec") is formed by prefixing an asterisk to the device name. Programs may then reference the device via the device specification in order to identify a particular device for character I/O. There are three input/output functions that are associated with all character-oriented devices. The "GET" function obtains a character from the device. The "PUT" function sends a character to the device. The "CTL" function provides a means of communicating with the device driver and generally does not invoke input/output with the physical device itself. It is up to the device driver to ensure that the device is currently able to take the character in the case of PUT as well as detect the availability of a character in the case of GET and return the proper condition. Disk files may also be interfaced via character I/O as well as record I/O [file access via record I/O is discussed in chapter 5, DISK FILE ACCESS AND CONTROL]. A disk file's actual physical storage location on a disk drive is transparent to the user by referencing the file with its associated name (more properly termed its file specification or "filespec"). The operating system permits filespecs and devspecs to be used equivalently in most cases. Character I/O is thus independent of a device or file. The DOS permits the redirection of character I/O at the command level. Because of this, applications must expect character I/O to be associated with a disk file as well as a standard character-oriented device. The DOS provides a uniform protocol for I/O handshaking regardless of character device. There are three major operations associated with devices. One of these is "routing" which implements the support of I/O redirection. Another is linking which is used to connect two or more devices together. The third operation associated with devices uses filters to achieve filtering. Filters are program modules that can be logically placed between the Device Control Block associated with a device and the device driver connected to the DCB. This operation will form what is called a "device chain". More than one filter module may be placed in the DCB-to-driver chain. These filters bear a very close resemblance to device drivers. In fact, they also utilize the Device Control Block tables to associate their memory storage location with the name assigned to them when they are installed. This section will discuss the activities that take place between a Device Control Block and a device so that you will better understand the concepts of character I/O. In this manner, you will have no problem in writing device filters and drivers - at least as far as DOS interfacing goes. ~2 - 11~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ THE DEVICE CONTROL BLOCK ======================== The Device Control Block (DCB) is used to interface with various logical devices such as the keyboard, the video display, a printer, a communications line, or other device defined by your hardware implementation. The DCB is composed of eight bytes divided into four fields: TYPE, VECTOR, SYSDATA, and NAME. Figure 2-1 illustrates the DCB. The TYPE field is a one-byte field that describes the capabilities and current state of the DCB (state indicative of routed, linked, filtered, etc.). The VECTOR field is a two-byte field that initially is a pointer to the entry-point of the driver or filter module associated with the DCB. The SYSDATA field is a three-byte field that is used by the system to support linking and routing. The NAME field is a two-byte field that contains the name associated with the device. =============================================== | | | _______________________________________ | | | | | | | | | | | | | | | | | T Y P E |VECTOR| SYSDATA | NAME | | | |_|_|_|_|_|_|_|_|______|_________|______| | | 7 6 5 4 3 2 1 0 15 0 23 0 15 0 | | | | ~Figure 2-1: DCB Fields~ | | | =============================================== The DCB follows a strict format that defines the utilization of all four fields. The programmer need be concerned only with the TYPE and VECTOR fields. The system requires sole use of the SYSDATA field. It also maintains the NAME field thus usually necessitating no programmer intervention. The DCB format must be followed in all Device Control Blocks established by the user. The following information provides specifications for each field of the DCB. TYPE Field - --------------------- Bit 7 => This bit specifies that the Control Block is actually a File Control Block (FCB) with the file in an OPEN condition. Since there is a great deal of similarity between DCBs and FCBs, and devices may be routed to files, tracing a path through a device chain may reveal a "device" with this bit set, indicating a routing to a file. Bit 6 => This bit specifies that the DCB is associated with a FILTER module. The VECTOR field then contains the entry point of the filter. A filter initializer must set this bit when the module is assigned to the DCB. Bit 5 => This bit specifies that the DCB (say device AA) is linked to another device associated with a DCB (say device BB). The VECTOR field of AA will point to a dummy LINK DCB (say device LK) which was established by the system when the LINK library command was invoked. The VECTOR field of LK then will point to the original VECTOR contents of AA ~2 - 12~ ~Device Input/Output Interfacing~ while the SYSDATA field will contain a pointer to the BB DCB. A picture is said to be worth a thousand words. The device chain linkage will be illustrated later. Bit 4 => This bit specifies that the device defined by the DCB is routed to another character-oriented device or file. The VECTOR field will either point to a DCB if the route destination is a device or it will contain a pointer to the file's FCB field contained in the route module established by the system's ROUTE library command. Bit 3 => This bit specifies that the device defined by the DCB is a NIL device. Any output directed to the device will be discarded. Any input request will be satisfied with a ZERO return condition. Bit 2 => This bit specifies that the device defined by the DCB is capable of handling requests generated by the @CTL Super- Visor Call. Bit 1 => This bit specifies that the device defined by the DCB is capable of handling output requests which come from the @PUT SuperVisor Call. Bit 0 => This bit specifies that the device defined by the DCB is capable of handling requests for input which come from the @GET SuperVisor Call. VECTOR Field - ---------------------------- This field initially will contain the address of the driver routine that supports the device hardware associated with the DCB. In the case of programmer-installed drivers, the driver initialization code must load the driver's entry point into the VECTOR field of its respective DCB. Likewise, when a filter module is established (via the SET library command), its entry point is placed into the VECTOR field. Once established by either the system or the driver/module initialization code to point to the module's entry point, the VECTOR field is then maintained by the system to effect routing, linking, and filtering. SYSDATA Field - --------------------------- These three bytes are used by the system for routing and linking and are unavailable for any other purpose. NAME Field - -------------------------- Byte 6 of this field contains the first character and byte 7 the second character of the device specification name. The system uses the device name field as a reference in searching the Device Control Block tables. When a DCB is assigned by the system during a SET or ROUTE command, this device name field will be loaded by the system with the device specification name ppassed in the command invocation. Programs requesting a spare DCB via the @GTDCB ~2 - 13~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ SuperVisor Call (and a binary ZERO name), are responsible for loading this name field. If the device has been routed to a file and a search of the device chain shows a TYPE byte with bit-7 set, then the respective control block is an FCB. In this case, byte 6 of the field will contain the DRIVE number of the drive containing the file and byte 7 will contain the Directory Entry Code (DEC) of the file. ACCESSING DEVICE CONTROL BLOCKS =============================== The system maintains space in low memory for the storage of the Device Control Block records. There is space sufficient for 31 records. The first DCB will always be associated with the system device named *KI. Therefore, a pointer to the first block may be determined by using the @GTDCB SuperVisor Call as follows: LD DE,'IK' ;Load name in reverse order LD A,@GTDCB ;Identify the SVC RST 40 ;Invoke the SVC JP NZ,ERROR ;Transfer if not found Upon return from the SVC, register HL will contain a pointer to the DCB associated with *KI. An error will result only if the DCB name field was altered. Spare DCB records are filled with binary zeroes. Therefore, a spare DCB record may be located by loading register pair DE with a binary zero value prior to issuing the SVC. The DOS command "DEVICE (B=Y)" can be used to obtain a linkage map of all device chains. As can be observed from such a listing, all 31 control blocks are not in use. Additional devices are defined by using the SET library command. Any device assigned by the user to a spare control block, may be removed from the system after the device is RESET by using the "REMOVE devspec" command. The DOS defined devices are protected and cannot be removed. DEVICE CHAIN ILLUSTRATIONS ========================== Before we can illustrate the device chain, it is necessary to first reiterate the memory module header protocol as required by the system. It is essential that this protocol be used for all modules placed into protected memory so that the system can properly deal with module access and device I/O. Header Protocol --------------- Each module placed into protected memory will incorporate a facimile of the following code at the start of the module: ENTRY JR BEGIN ;Branch around linkage STUFHI DW $-$ ;To contain last byte used ~2 - 14~ ~Device Input/Output Interfacing~ DB MODBGN-ENTRY-5 ;Calculate length of 'NAME' DB 'MODNAME' ;Name of this module MODDCB DW $-$ ;To contain DCB pointer for module SPARE DW 0 ;Reserved by the DOS . . ;Any data area needed BEGIN EQU $ ;Followed by module code The appendix is another source of information concerning the header protocol. It is sufficient for the illustration of device chains to understand that the MODDCB will contain a pointer that points to the Device Control Block established for the module during the execution of the SET library command. This pointer is passed in register pair DE to the module's initialization code by SET. The programmer writing the module code adds a routine which loads this valuue into MODDCB. Sample DCB Structure -------------------- For the purpose of this illustration, let's imagine three active DCBs. The first DCB is associated with the printer driver and has device specification of "*PR" (its devspec). We have also installed a filter via the SET command that performs a backspace followed by the output of a slash when it detects an ASCII zero (0). This filter has a devspec of "*S0". Lastly, we have a filter that toggles a boldface mode for a printer. This filter has a devspec of "*BF". To avoid confusion in the illustration, the devspec will be used to reference the DCB and the module names PRINTER, SLASH0, and BOLDFACE will be used to identify the entry point of the driver or filter module. We can now show this arrangement of DCB contents and module MODDCB contents as follows: ============================================== | | | ~TYPE VECTOR NAME MODULE/MODDCB~ | | ---- -------- ---- ________________ | | | | | | 06 PRINTER PR | PRINTER/*PR | | | |________________| | | _______________ | | | | | | 47 SLASH0 S0 | SLASH0/*S0 | | | |________________| | | ________________ | | | | | | 47 BOLDFACE BF | BOLDFACE/*BF | | | |________________| | | | | ~Figure 2-2: Initial DCB Table | | | ============================================== Note that the DCBs in figure 2-2 associated with the filters have bit-6 of the TYPE byte set to indicate that they are filters. Also note that the MODDCB pointer points to the DCB which points to the module. Where a filter's MODDCB is pointing to the DCB of the filter, this is indicative of an ~2 - 15~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ inactive filter. Filtering --------- Filters are written (as you will later learn) to perform all I/O via the @CHNIO SuperVisor Call. This SVC uses the contents of MODDCB within the filter invoking the SVC. Thus, the filter I/O is independent of any address by being handled completely through the SVC. If you perform a system command such as: FILTER *PR USING *S0 the operating system will swap the first three bytes of the *PR DCB with the *S0 DCB. This arrangement will establish that shown in figure 2-3. ============================================== | | | ~TYPE VECTOR NAME MODULE/MODDCB~ | | ---- ------- ---- ________________ | | | | | | 47 SLASH0 PR | PRINTER/*PR | | | |________________| | | ________________ | | | | | | 06 PRINTER S0 | SLASH0/*S0 | | | |________________| | | ________________ | | | | | | 47 BOLDFACE BF | BOLDFACE/*BF | | | |________________| | | | | ~Figure 2-3: DCB Table Modified~ | | | ============================================== Let's follow what happens to an @PUT which references the *PR device. The system passes control to SLASH0 (which is pointed to by the *PR vector). This filter performs its character transformation, as required, and sends characters down the chain by picking up the pointer contained in its MODDCB (a pointer to the *S0 DCB) then issuing the @CHNIO SVC. The SVC handles the call by passing control to PRINTER which is the pointer now stored in the VECTOR field of *S0. If we now try to issue the command: FILTER *PR USING *S0 the system will prohibit it since the *S0 Device Control Block does not show up as a filter (bit-6 of the TYPE byte is reset!). However, if we filter *PR using the *BF device, we achieve the arrangement in figure 2-4 after the system swaps the first three bytes of *PR with the first three bytes of *BF. Examine the arrangement in figure 2-4 closely. Note that the contents of MODDCB for each module are exactly what they were initialized to. Even though the *PR device has been twice filtered, the module itself needs absolutely no ~2 - 16~ ~Device Input/Output Interfacing~ change whatsoever. An *PUT to the *PR device (say with an *PRT SVC) may be a little more complicated now, but functions perfectly well. The system first passes control to BOLDFACE (which is pointed to by the *PR vector). This filter performs its necessary device stream massaging and sends characters down the chain by picking up the pointer contained in its MODDCB (a pointer to the *BF DCB) then issuing the @CHNIO SVC. The SVC handles the call by passing control to SLASH0 which is the pointer now stored in the VECTOR field of *BF. The SLASH0 filter performs its character transformation, as required, and sends characters down the chain by picking up the pointer contained in its MODDCB (a pointer to the *S0 DCB) then issuing the @CHNIO SVC. The SVC handles the call by passing control to PRINTER which is the pointer now stored in the VECTOR field of *S0. Upon completion, a series of RET instructions pass the return code back through the modules making up the chain. ============================================== | | | ~TYPE VECTOR NAME MODULE/MODDCB~ | | ---- ------- ---- ________________ | | | | | | 47 BOLDFACE PR | PRINTER/*PR | | | |________________| | | ________________ | | | | | | 06 PRINTER S0 | SLASH0/*S0 | | | |________________| | | ________________ | | | | | | 47 SLASH0 BF | BOLDFACE/*BF | | | |________________| | | | | ~Figure 2-4: DCB Table Further Modified~ | | | ============================================== It is interesting to observe that the process of removing the filters from the device chain is exactly the same as the process to add them into the chain. We can unhook the filters by exchanging the first three bytes of the DCBs in the order of last-in first-out (LIFO). Thus if you exchange the *PR and *BF Device Control Block TYPE and VECTOR fields, you will obtain the arrangement previously shown in figure 2-3. The RESET library command does this for the entire chain. By now you should be able to notice that we could equally as well remove just the SLASH0 filter if we swap the bytes associated with the *BF and *S0 Device Control Blocks! All that is needed is a facility to do the following: 1. Identify what filter (by module name) is to be removed; 2. Locate the filter in memory via the @GTMOD SuperVisor Call; 3. Obtain the MODDCB pointer to its Device Control Block; 4. Scan through all DCBs to find the DCB pointing to the filter; 5. Then swap the three bytes. ~2 - 17~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ Routing ------- Routing conveys the facility of I/O redirection. This function allows programs to be independent of the physical device actually handling the I/O. By maintaining a constant reference within a program to a particular DCB, the physical I/O can be channeled to some other device completely transparent to the program. This is achieved through altering the connection between the DCB and its initial driver by reconnecting the DCB to some other driver. The operating system handles all of the functions of implementing the DCB alteration when the ROUTE library command is invoked. The "routed-to" device may be another DCB identified by a devspec or it could be a disk file identified by a filespec. Let's look at an example. If we, for instance, invoke the command: ROUTE *PR TO FILE/TXT:3 the DOS performs a two-stage process. First, it establishes a 32-byte File Control Block and 256-byte buffer for the FILE/TXT:3 disk file. It places this "data" into high memory prefixed with the header protocol. Second, it saves the "route-from" VECTOR and TYPE fields in the SYSDATA field of the DCB while it revises the VECTOR to point to the "routed-to" FCB. The TYPE field is also altered to show a ROUTE is in effect. The DCBs will now look like figure 2-5. =========================================================== | | | ~TYPE VECTOR SYSDATA NAME MODULE/MODDCB~ | | ---- -------- ---------- ---- ________________ | | | | | | 10 FCB-FILE PRINTER/06 PR | PRINTER/*PR | | | |________________| | | | | 80 31-bytes of FCB data FCB | | | | ~Figure 2-5: DCB Table After ROUTE~ | | | =========================================================== Let's now follow an output request to the *PR device. The DOS device I/O handler will recognize the ROUTE bit (bit-4 of the TYPE byte) and update the register linkage so that the FCB will be pointed to instead of the DCB. Noticing that the control block now indicates a disk file (bit-7 of the TYPE byte), the I/O handler will pass control to the character I/O file routines. The action taken by the operating system to reset a DCB that has been routed is to first close the file, if a filespec was the initial "route-to", then recover the original TYPE and VECTOR from the SYSDATA field. Filtering a Routed Device ------------------------- Let's suppose we have a text file that needs line feeds removed (it may be a CP/M file that uses CR-LF as the end-of-line protocol). We could write a ~2 - 18~ ~Device Input/Output Interfacing~ program to read the file and write out to another file all characters that are not a line feed. We could also use a trap filter that is handy. We want to be able to filter the file with this trap filter. Using the routing identical to that shown in figure 2-5, establish the trap filter and invoke it with: SET *LF USING TRAP (CHAR=10) FILTER *PR USING *LF Figure 2-6 will now reflect the DCB structure after this series of commands. It is now easy to LIST the source file with the (P,T=N) option. This will direct a copy of the file to the *PR device (while suppressing tab expansion). As can be observed from the figure, the device handler passes *PR I/O requests to the TRAP filter. After performing whatever filtering is necessary, the @CHNIO request will reference the *LF Device Control Block (which is pointed to by the MODDCB field). The device handler then notes that the ROUTE bit is set and continues to control the @PUT request as was done under figure 2-5. A simple "RESET *PR" upon completion will close the filtered FILE/TXT. =========================================================== | | | ~TYPE VECTOR SYSDATA NAME MODULE/MODDCB~ | | ---- -------- ----------- ---- ________________ | | | | | | 47 TRAP PRINTER/06 PR | PRINTER/*PR | | | |________________| | | | | 80 31-bytes of FCB data FCB | | ________________ | | | | | | 10 FCB-FILE LF | TRAP/*LF | | | |________________| | | | | ~Figure 2-6: Filtering a Route~ | | | =========================================================== Linking ------- Linking is handled by establishing a link Device Control Block storage area for each LINK command invoked. For example, if you "LINK *DO TO *PR", we can illustrate the DCB area as shown in figure 2-7. The *DO Device Control Block now vectors to the newly established *L0 DCB while the TYPE byte identifies the link. Notice that *L0 has both the VIDEO vectors and a pointer to the *PR DCB (we can conceptualize this as a two legged fork). The system's device handler recognizes that a link is in effect (from *DO's TYPE byte) whereupon it establishes a fork via the link DCB, *L0. It uses the third byte of L0's SYSDATA field to store the direction indicator. After a return from VIDEO without error, the device handler takes the other fork leg (to *PR). ~2 - 19~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ =========================================================== | | | ~TYPE VECTOR SYSDATA NAME MODULE/MODDCB~ | | ---- -------- --------- ---- ________________ | | | | | | 20 *L0 DO | VIDEO/*DO | | | |________________| | | ________________ | | | | | | 06 PRINTER PR | PRINTER/*PR | | | |________________| | | | | 07 VIDEO *PR L0 | | | | ~Figure 2-7: Linking Devices~ | | | =========================================================== The legs of the fork are entered based on the I/O direction and the return code from a leg. @PUT requests will be sent to the "left" leg of the fork. Providing no error is encountered, the "right" leg of the fork will be entered. The return code passed back to the caller will be either an error from the left leg, an error from the right leg, or a no-error condition. Requests for @GET, will be passed first to the left leg. Only if the left leg has no input available will the right leg be entered. @CTL requests are handled like @PUT. Linking can be applied to a devspec that has been filtered, routed, or linked. There is no restriction on combinations. Thus, you can link a device that is already linked and filtered and routed. Figure 2-8 depicts the result of linking a device that has already been routed. It is left up to the reader as an exercise to derive the series of commands that composed the associated DCBs/FCB as well as tracing through the device chain for I/O. =========================================================== | | | ~TYPE VECTOR SYSDATA NAME MODULE/MODDCB~ | | ---- -------- --------- ---- ________________ | | | | | | 20 *L1 DO | VIDEO/*DO | | | |________________| | | | | 80 31-bytes of FCB data FCB | | | | | | 10 FCB/FILE DD | | | | | | 07 VIDEO *DD L1 | | | | ~Figure 2-8: Linking a Routed Device~ | | | =========================================================== ~2 - 20~ ~Device Input/Output Interfacing~ Device Chain Hierarchy ---------------------- It is possible for the Device Control Block TYPE byte to have more than one bit set in the positions 3-7 (positions 0-2 usually have multiple bits set depending on the I/O supported by the driver). Because of this, the system must utilize a priority level to indicate what function is to be interpreted. The device I/O handler hierarchy is illustrated in figure 2-9. ==================================== | | | Bit-7: Disk File character I/O | | Bit-3: NIL device - no I/O | | Bit-4: ROUTE to DCB or FCB | | Bit-5: LINK to 2nd DCB | | Bit-6: FILTERed DCB or filter | | | | ~Figure 2-9: DCB Hierarchy~ | | | ==================================== Device Chain Summary -------------------- The preceding discussion should shed a great deal of light on the handling of device I/O by the operating system. You should also understand that in order to accomplish this device independence and flexible handling of character I/O, the programmer of device drivers and filters must adhere to a strict protocol of handshaking the modules with the operating system. The next section will explore device I/O looked at from the standpoint of the modules and drivers. Once you grasp these requirements, you will be in total control of filters and device drivers. ~2 - 21~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ DEVICE DRIVER/FILTER TEMPLATE ============================= The system contains command level procedures that provide easy access to device references so that modifications may be made to the way in which devices are treated by the system. All devices require some type of driving program (a device driver) that is used to handshake the device with the system and cater to the special features and requirements of the device hardware. Some drivers are already implemented within the operating system to handle standard devices. For instance, drivers for handshaking the keyboard, video display, parallel printer port, and RS-232 serial port are included with the system. Some devices are completely supported with the existing drivers in the total DOS environment. Other devices may need a little more support. The characteristics of a driver may be modified by the introduction of a FILTER. For instance, suppose your printer required a line feed upon receipt of a carriage return to advance the paper. The printer driver does not provide this function. Instead of writing a completely new printer driver, only a filter need be included to add that single function (the FORMS/FLT filter which incorporates this function is usually provided with the system). The DOS provides two commands to aid in interfacing drivers and filters. The SET command is used to define a new device, re-define an existing device, or install a filter module while assigning it a device name. FILTER is used to place the installed filter into an existing device chain. The SET command takes the device specification from the command line "SET *XY to filespec" and searches the Device Control Block tables for a matching device name. If the requested device is not defined in your configuration, SET establishes a Device Control Block for the new device. Control then passes to the DRIVER or FILTER with register pair DE containing the address of the Device Control Block record assigned to the "SET" device. Register pair HL points to the command line character separating the DRIVER/FILTER program filespec and optional parameters. This provides the module initialization routines with the opportunity of parsing a parameter string by using a parameter table and the @PARAM SuperVisor Call. SET provides a default file extension of /FLT since the function of adding filters to the system is the more usual case. The SET and FILTER commands are designed such that the DRIVER or FILTER program should first load into the User Program Region (starting at X'3000'). After parsing any options or parameters, the module initialization routine automatically relocates the resident module to high memory (or low memory if sufficient space is available - see the section on Placing Disk Drivers in chapter 3). HIGH$ (or the Driver Input/Output Region pointer) must be properly set after your module relocates. Samples of filters are provided in the Appendix which should demonstrate the technique of writing the relocating driver portion of your routine. The remaining sections in this chapter discuss the handshaking and initialization requirements necessary for device drivers and filters. ~2 - 22~ ~Device Input/Output Interfacing~ I/O Primitives -------------- Device independence has its roots in "character I/O". The term shall apply to any I/O passed through a device channel, one character or byte at a time. Three primitive routines are available at the assembly language level for byte I/O. Primitive is not used here to imply rudimentary but rather elementary. Just as the atom is considered a basic building block of molecules, these byte I/O primitives can be used to build larger routines. The three DOS SuperVisor Calls are designated @GET, @PUT, and @CTL. @GET is used to input a byte from a device or file. @PUT is used to output a byte to a device or file. @CTL is used to communicate with the driver routine servicing the device (the character file I/O routines ignore @CTL requests). Other SuperVisor Calls are available that perform byte I/O, such as @KBD (scan the *KI device and return the key code if a character is available), @DSP (send a character to the *DO device), and @PRT (send a character to the *PR device). These functions operate by first loading register pair DE with a pointer to a specific Device Control Block (DCB) assigned for use by the device, then issuing an @GET or @PUT SuperVisor Call for the respective input or output requests. When the DOS device handler passes control over to the device driver routine, the Z-80 flag conditions are unique for each different primitive. This provides a method that the drivers can use to establish what primitive was used to access the routine and thus distribute the I/O request to the proper driver or filter subroutine - according to the direction of the request - input, output, or control! Figure 2-10 illustrates the FLAG register conditions prevailing upon entry to a driver or filter. ================================== | | | C,NZ = @GET primitive | | Z,NC = @PUT primitive | | NZ,NC = @CTL primitive | | | | ~Figure 2-10: Flag Conventions~| | | ================================== Register B contains the I/O direction code (1 = GET, 2 = PUT, or 4 = CTL) while register C will contain the character code that was passed in an @PUT or @CTL SuperVisor Call. Register IX will point to the TYPE byte of the Device Control Block being referenced. Registers BC, DE, HL, and IX have been saved on the stack and thus are available for use. Remember that any given module may have been filtered or linked; therefore, do not expect the DCB address in IX to be a constant over time. If the module is a filter, it will be invoking the @CHNIO SuperVisor Call. Thus it will be important to save those registers that must stay unchanged prior to invoking @CHNIO. I/O Separation -------------- Now let's move on to the device driver linkage used to separate out the @GET, @PUT, and @CTL calls. Remember the FLAG register direction conditions shown in figure 2-10 that were set according to the primitive byte I/O ~2 - 23~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ routine that got us to the driver. These conditions provide the key to the separation process. Consider the following protocol for the driver or filter header. ENTRY JR BEGIN ;Branch around linkage STUFHI DW $-$ ;To contain last byte used DB MODDCB-BEGIN-5 ;Calculate length of 'NAME' DB 'MODNAME' ;Name of this module MODDCB DW $-$ ;To contain DCB pointer for module DW 0 ;Reserved by the DOS BEGIN EQU $ ;*=*=* ; Actual module code start ;*=*=* JR C,WASGET ;Go if @GET request JR Z,WASPUT ;Go if @PUT request JR WASCTL ;Was @CTL request At the entry of the driver, an absolute relative jump instruction executes which causes a branch around some data. Ignore, for a moment, the header data which is discussed in the appendix. At the label "BEGIN", a test is made on the CARRY FLAG. If the CARRY was set, then it must have been the result of an input request (@GET). Thus, an input request could be directed to that part of the module which handles character INPUT. If the request was not from the @GET primitive, the CARRY will not be set. The next test is if the ZERO FLAG is set. The ZERO condition prevailed when an @PUT primitive was the initial request. Thus the jump to WASPUT can transfer to that part of the module that deals specifically with character OUTPUT. If neither the ZERO nor CARRY flags are set, the routine falls through to the next instruction, a jump to WASCTL - that part of the module that would handle @CTL requests. Obviously,the module code that handles @CTL requests could be placed immediately after the first two tests thereby obviating the need for the "JR WASCTL". Some modules are written to assume that @CTL requests are to be handled exactly like @PUT requests although this is not recommended. The processing of @CTL requests is entirely up to the function of the driver and the author thereof with the exception that the author should not deviate from the functions identified in the @CTL INTERFACING section. When a device has been routed to a disk file, the DOS will ignore @CTL requests. That is, the @CTL codes will not be written to the disk file. The functions of @CTL requests are covered as a separate topic later in this chapter. Device Driver/Filter Return Codes --------------------------------- One last topic needs to be discussed relating to drivers - the subject of register handshaking conventions. On @GET requests, the character input should be placed in the accumulator. On output requests (either @PUT or @CTL), the character is obtained from register C. It is extremely important for drivers and filters to observe return codes. Specifically, if the request is @GET and no byte is available, the driver returns an NZ condition with the accumulator containing a zero (i.e. OR 1 : LD A,0 : RET). If a byte is available, the byte is placed in the accumulator and the Z-flag is set (i.e. ~2 - 24~ ~Device Input/Output Interfacing~ LD A,CHAR : CP A : RET). If there is an input error, the error code is returned in the accumulator and the Z-flag is reset (i.e. LD A,ERRNUM : OR A : RET). On output requests, the Z-flag is set if no output error occured. The accumulator may be loaded with the character that was output; however, applications invoking an @PUT cannot depend on the accumulator containing the output character on return from the SVC - the character will, however, still be contained in the C register! In the case of an output error, the accumulator must be loaded with the error code and the Z-flag reset as shown above. Filter Interfacing ------------------ A filter module is inserted between the DCB and driver routine (or between the DCB and the current filter when applied to a DCB already filtered). The application of insertion is performed by the DOS FILTER command once the filter module is resident and associated with a device name. The function of residing a filter module is a responsibility shared by the SET library command and the programmer's filter initialization routine. The usual linkage for a filter is to access the chained module by calling the @CHNIO SuperVisor Call with specific linkage data in registers IX and BC. Register IX is loaded with the filter's DCB pointer obtained from the memory header MODDCB pointer. Register B must contain the I/O direction code (1 = GET, 2 = PUT, 4 = CTL). This code is already in register B when the filter is entered. You can either keep register B undisturbed or load it with the direction code based on the primitive request. Also, output requests will expect the output character to be in register C. Filter Initialization --------------------- The DCB pointer obtained from MODDCB for the interfacing, is originally obtained from the operating system. It is passed in register DE by the SET command and is loaded into MODDCB by your filter initialization routine. The initialization routine also relocates the filter to high (or low) memory while adjusting any absolute address reference with a suitable relocation routine. The DOS takes care of loading the DCB's NAME field with the associated device name passed in the SET command. The filter initializer must attach itself to the DCB assigned by the SET command by loading the TYPE and VECTOR fields. The TYPE field is loaded with an ORing of the filter bit (bit-6) and any valid direction bits (bits 0-2). If the initialization front end transfers the DCB pointer from DE to IX and loads the filter's entry address into register pair HL, the following code could be used to establish the TYPE byte and vector for a filter which supports GET, PUT, and CTL: LD (IX),40H.OR.7 ;Init DCB type to LD (IX+1),L ; FILTER, G/P/C I/O, LD (IX+2),H ; & stuff vector One final point concerns a test that should be made by the filter initializer. The operating system permits the execution of any load module. A filter program is a load module. To guard against the execution of a filter program by inadvertantly entering its full file specification at DOS Ready, the system provides the programmer with an indicator that execution is under control of the SET command. When SET passes control to a filter program, it ~2 - 25~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ will set bit-3 of the CFLAG$ (the system request bit). Thus, by testing this bit upon entry to the program, an error exit can be taken if the system request bit is not set. An error message of the form: Must install via SET can be logged and the program aborted. The system automatically resets the system request bit upon regaining control at DOS Ready. A Partial Filter ---------------- A filter module can operate on input, output, control, or any combination based on the author's design. The memory header provides a region for user data storage conveniently indexed by the module. An illustration of a filter follows. The purpose of the filter is to add a line feed on output whenever a carriage return is to be sent. Although the filter requires no data storage, the technique for accessing data storage is shown. Pay close attention to the method of passing characters to the device chain (@CHNIO). ENTRY JR BEGIN ;Branch to start DW FLTEND-1 ;Last byte used by module DB 6,'SAMPLE' ;Name length and name MODDCB DW $-$ ;Ptr to DCB loaded by initialization DW 0 ;Reserved ;*=*=* ; Data storage area for your filter ;*=*=* DATA$ EQU $ DATA1 EQU $-DATA$ DB 0 ;Data storage DATA2 EQU $-DATA$ DB 0 ;Data storage ;*=*=* ; Start of filter ;*=*=* BEGIN JR Z,GOTPUT ;Go if @PUT ;*=*=* ; @GET and @CTL requests are chained to the next module ; attached to the device. This is accomplished by falling ; through to the @CHAINIO call. Note that the sample filter ; does not effect the B register, so the filter does not ; have to load it with the direction code. ;*=*=* FLTPUT PUSH IX ;Save our data pointer LD IX,(MODDCB) ;Grab the DCB vector RX01 EQU $-2 LD A,@CHNIO ; & chain to it RST 40 POP IX RET ;*=*=* ; Filter code ;*=*=* GOTPUT LD IX,DATA$ ;Base register is used to RX02 EQU $-2 ; index data as (IX+DATA1),... ~2 - 26~ ~Device Input/Output Interfacing~ ; LD A,C ;P/u char to test CP CR ;If not CR, put it JR NZ,FLTPUT CALL FLTPUT ; else put it RX03 EQU $-2 RET NZ ;Back on error LD C,LF ;Add line feed JR FLTPUT FLTEND EQU $ ;*=*=* ; Relocation table ;*=*=* RELTAB DW RX01,RX02,RX03 TABLEN EQU $-RELTAB/2 The relocation table, RELTAB, would be used by the filter initialization relocation routine. Complete filters are listed in the appendix. External Access of Module Data ------------------------------ It is sometimes necessary to access the data region of a resident module from outside the module. Perhaps a utility to alter the data is useful (for instance, the SETCOM command alters the data of the COM driver supplied with the system. The @GTMOD SuperVisor Call is used to obtain two pointers. One points to the entry point ot the module while the other points to the MODDCB field. If the data is located immediately following the reserved word in the module header, incrementing the MODDCB pointer by four will point it to the data area. The utility uses the module name assigned in the header to locate the module in memory. As an example, let's illustrate an update to DATA1 in the above filter. LD DE,FLTSTR$ ;Point to module name LD A,@GTMOD ;Identify the SVC RST 40 JR NZ,NOTRES ;Process "module noot resident" LD HL,4 ;Use pointer in DE to ADD HL,DE ; index past MODDCB & reserved LD A,(VALUE) ;P/u your new value LD (HL),A ; & stuff into resident module . . . FLTSTR$ DB 'SAMPLE',3 ;Search string ~2 - 27~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ @CTL INTERFACING TO DEVICE DRIVERS ================================== This section discusses the @CTL functions supported by the system supplied device drivers. @CTL functions are invoked by loading register pair DE with a pointer to the Device Control Block (DCB), loading the function code into register C, and issuing the @CTL SuperVisor Call. The DCB address can be located by either using the @GTDCB SVC or OPENing a File Control Block containing the device specification and using the FCB address. The DOS has assigned function codes for specific operations. Although these operations are not universal across all drivers, the designated function code should be used only for the operation assigned. Rarely will you find a driver that utilizes all of these codes. A driver that accepts a function code to perform an operation should provide a return code as if the request was @PUT. Where a driver does not wish to accept a specific code or codes, it should return a "no-error" result. Function codes in the range <0-31,255> are reserved by the operating system. Function codes in the range <32-254> are available for programmer use. The following operations are assigned function codes: CODE OPERATION ---- ---------------------------------------------------- 0 Return status of device (Z = available, NZ = not available). Where applicable, return an image of the status in the accumulator. 1 Request a or force an attention interrupt. 2 Execute any driver initialization code. 3 Reset any driver buffers and clear any pending I/O. 4 Interface a "wakeup" vector for interrupt driven drivers. Register IY should contain the execution transfer address to be passed control after the driver handles the interrupt. On return from the @CTL call, register IY will contain the previous "wakeup" vector. If a zero is passed in register IY, the "wakeup" vectoring will be disabled. 5 Reserved by the DOS. 6 Reserved by the DOS. 7 Reserved by the DOS. 8 Return the next character in the input buffer but do not empty it from the buffer. A return condition of A = 0 and NZ indicates no character is pending. A <> 0 and NZ indicates an error while Z indicates success while A contains the character. 9-31 These codes are reserved by the DOS. ~2 - 28~ ~Device Input/Output Interfacing~ The system-supplied drivers support some of these functions. The following sections cover what control functions are supported and suggests possible uses. The module name can be used with the @GTMOD SuperVisor Call to obtain the entry point of the driver. This is useful to obtain access to the data areas associated with each driver. Keyboard driver [system driver assigned to *KI] ------------------------------------------------- A function value of X'03' will clear the type-ahead buffer. This serves the same purpose as repeated calls to @KBD until no character is available. A function value of X'FF' will remain undocumented as its use is proprietary to Tandy Corporation and its function is not supported across all licensed versions of LDOS Version 6. All other function values are treated as @GET requests. The module name assigned to this driver is "$KI". Its data area includes the following: +0 - Contains the last character entered. +1 - Contains the repeat time check which is the system's timer value that when reached will result in a repeat of the last character if the keycode scanned has not changed. +2 - Contains the waiting time in timer units that must transpire before a character can initially be repeated. This value is altered by SETKI (W=dd). +3 - Contains the repeat rate in timer units. This value is altered by SETKI (R=dd). Video driver [system driver assigned to *DO] ---------------------------------------------- All @CTL requests are treated as if they were @PUT requests. The module name assigned to this driver is "$DO". Its data area includes the following: +0 - Bits 0-2 contain the number of video lines to protect against scrolling. Bit 3 denotes the action to be taken for character values in the range <192-255>. If set, the values are treated as displayable characters. If reset, the values are treated as space compression codes in excess 192 (i.e. 0-63). Bit 4 will denote the action to be taken for character values in the range <1-31>. If set, the value is interpreted as a displayable char- acter. If reset, the value is treated as a video function code as identified in your operating system user manual. Bits 5-7 are reserved by the DOS. +1 - Contains the low order address of the cursor. You must use the @VDCTL SuperVisor to reference the cursor by row,column. +2 - Contains the high order address of the cursor. You must use the @VDCTL SuperVisor to reference the cursor by row,column. ~2 - 29~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ +3 - Contains the character that is currently at the cursor position. +4 - Contains the character code defining the cursor. Printer driver [system driver assigned to *PR] ------------------------------------------------ The printer driver is transparent to all code values when requested by the @PUT SuperVisor Call. That means that all values from X'00' through X'FF' (0-255) can be sent to the printer. The printer driver accepts a function value of X'00' via the @CTL request to return the printer status. If the printer is available, the Z-flag will be set and the usual A register status image is an X'30'. If the Z-flag is reset, the accumulator will contain the four high-order bits of the parallel printer port (bits 4-7). The module name assigned to this driver is "$PR". There exists no data area within the printer driver. Forms Filter [non-resident system filter for forms control] ----------------------------------------------------------- If the FORMS filter is attached to the *PR device, then various codes are trapped and used by the filter according to user options as follows: X'0D' - Generates a carriage return and optionally a line feed (ADDLF). It will form feed as required. X'0A' - Is treated the same as X'0D'. X'0C' - Will form feed (via repeated line feeds if soft form feed). X'09' - Will advance to the next tab column. X'06' - Will set top-of-form by resetting the internal line counter to zero. Other character codes may be altered depending on the user translation option (XLATE). The FORMS filter's module name is "$FF". Its data area includes the following: +0 - Contains the maximum lines per page. +1 - Is used by the filter as a line counter. +2 - Contains the maximum number of lines to print prior to a FORM FEED operation. +3 - Is used by the filter as a character counter. +4 - Contains the character value that is to be translated. +5 - Contains the character value that <+4> is to become. ~2 - 30~ ~Device Input/Output Interfacing~ +6 - Contains the number of spaces to indent after an automatic NEWLINE is issued. +7 - Bit 0 specifies that a LINE FEED is to be added after each carriage RETURN. Bit 1 specifies the mode of FORM FEED - a 0 indicates SOFT (multiple line feeds) while a 1 indicates HARD (send X'0C' to the driver). +8 - Contains the maximum number of characters to print on a line prior to issuing an automatic NEWLINE. A value of zero indicates that no automatic NEWLINE is to be issued. +9 - Contains the column of the left hand margin. The filter will provide this count of spaces after a physical carriage RETURN. COM driver [non-resident system driver for the RS-232C] ------------------------------------------------------- This driver handles the interfacing between the RS-232C hardware and character I/O (usually the *CL device). An @CTL function value of X'00' will return an image of the RS-232 status register in the accumulator. The Z-flag will be set if the RS-232 is available for "sending" (i.e. transmit holding register empty and flag conditions matching as specified by the default protocol or that established by the user via SETCOM). A function value of X'01' will transmit a "modem break" until the next character is @PUT to the driver. A function value of X'02' will re-initialize the serial port hardware to the values last established by SETCOM. A function value of X'04' will enable/disable the WAKEUP feature. All other function values are ignored and the driver will return with register A containing a zero value and the Z-flag set. The WAKEUP feature deserves additional treatment since it can be quite useful for application software specializing in communications. The RS-232 hardware is usually equipped with the capability of generating a machine interrupt when any of three conditions prevail: transmit holding register empty, received character available, or an error condition has been detected (framing error, parity error, etc.). The COM driver makes use of the "received character available" interrupt to take control when a fully-formed character is in the receive holding register. The COM driver services the interrupt by reading the character and storing it in a one-character buffer. COM would then normally return from the interrupt while it awaits the next @GET request to take the character. An application can request that instead of returning from the interrupt, control is passed to the application for IMMEDIATE ATTENTION. It is important to note that this action would be occurring during interrupt handling and any processing by the application must be kept at a minimum before control is returned to COM via an RET instruction. If you use an @CTL function value of X'04', then register IY must contain the address of the handling routine in your application. Upon return from the @CTL request, register IY will contain the address of the previous WAKEUP vector. This should be restored to the COM driver when your application is finished with the WAKEUP feature. ~2 - 31~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ When control is passed to your WAKEUP vector upon detecting a "receive character available" interrupt, certain information is immediately available. Register A will contain an image of the serial port UART status register. The Z-flag will be set if a valid character is actually available. The character, if any, is in the C-register. Since system overhead takes a small amount of time in the @GET SuperVisor Call, you may only have to @GET the character via standard device interfacing. This will ensure that any filtering or linking in the *CL device chain will be honored. If, on the other hand, your application is attempting to transfer data at a very high rate (9600 baud or higher), you may need to bypass the @GET SuperVisor Call and use the character immediately available in the C-register. Note that this will ignore any device chain linkage. The module name of the COM driver is "$CL". Its data area includes the following: +0 - Contains the handshake mask established according to the default conventions (or those established via SETCOM). This mask is used by COM and needs no concern from the programmer. +1 - Contains the serial port control image (this image may turn out to be dependent on specific RS-232 hardware. Bit 7 => Parity [1 = EVEN; 0 = ODD] Bits 6 & 5 => Word length [00 = 5; 10 = 6; 01 = 7; 11 = 8] Bit 4 => Number of STOP bits [1 = 2 bits; 0 = 1 bit] Bit 3 => Parity enable/disable [1 = disable; 0 = enable] Bit 2 => Transmit data [1 = enable; 0 = BREAK] Bit 1 => Data Terminal Ready lead [0 = ON; 1 = OFF] Bit 0 => Request To Send lead [0 = ON; 1 = OFF] +2 - Contains the code for the baud rate. +3 - Flag to indicate KFLAG$ support [1 = ON; 0 = OFF] Effective with LDOS 6.2.0, this byte contains the BREAK character code, LOGBRK. If non-zero, then reception of that byte value from the communications line will cause the BREAK bit of the KFLAG$ to be set. If zero, no input character will be interpreted as a BREAK. +4 - One-character buffer flag [80H = no character; 0 = character] +5 - Storage for the one-character buffer. ~2 - 32~ ~Disk Drive Input/Output Interfacing~ GENERAL DISK DRIVE CONFIGURATION ================================ This chapter is designed to fully explain the purpose of the Disk Controller Communications SuperVisor Calls. It will also completely describe the fields constituting the Drive Control Table. We will cover the protocol linkage that interfaces the disk driver to the DOS. Finally, we will discuss some of the concepts that are associated with interfacing hard disk drives. There are two reasons for this chapter. On one hand, you may be interested in using the disk primitives to write disk-oriented utility programs. A good foundation in the functions of the controller primitives is essential. On the other hand, you may have the need to write a disk driver that supports a hard disk controller. In this case, it is essential to understand the requirements of the system for communicating with disk devices. Before we can begin these topics, we must gain a knowledge of the configuration of disk storage devices. The Disk Operating System incorporates the term "disk" because the operating system is associated with and directly supports disk drive storage devices. Although many users of small microcomputers may be used to systems with two or three disk drives, the Version 6 DOS supports up to eight disk storage devices. The most typical type of disk drive used in systems running Version 6 is the floppy disk drive. The hardware that interfaces the floppy disk drive to the computer is called a Floppy Disk Controller (FDC). The controller includes all of the electronics necessary to control and translate operating system commands into control pulses which the drive uses to perform mechanical actions (such as head stepping, drive select, head load, etc) and data transfer. The floppy disk drives are usually connected to the computer in a multiplexed arrangement. This means that all data and control signals share a common cabling. Where more than one disk drive is connected to the cable, a means of uniquely selecting one drive at a time must be provided. Over the years, a standard of drive selection has been developed that all floppy disk drives adhere to. This standard incorporates four separate drive select lines between the computer and all disk drives. These drive select lines are designated DS0, DS1, DS2, and DS3. Each disk drive is then jumpered to connect to only one of the drive select lines. Sometimes the drives connect to all of the lines while each plug on the cable severs all select lines but one - each cable plug a different select line. Thus, the computer hardware will, in general, support the handling of four floppy disk drives [some companies manufacture a multiplex device that uses the four drive selects as a binary number thus multiplexing up to 15 floppy drives]. Although the typical hardware configuration supports four floppy disk drives, the DOS has provisions for referencing eight distinct logical drives numbered 0-7. We use the term "logical" in case we have a single drive that is partitioned into multiple drives with each partition being referenced by a different drive number. The four extra positions are usually used with installations that connect hard disk drives in addition to the floppies. The DOS stresses device independence. Disk drives are treated no differently. In order to gain a high level of independence, the DOS uses a standardized set of SuperVisor Call functions we will term "Disk Controller Communications". These SVCs are primitive functions that should provide all of the activities needed to communicate I/O requests to the disk controller that's interfacing a disk drive. ~3 - 33~ ~The Programmers Guide to LDOS/TRSDOS Version 6~ The system also maintains a Drive Control Table (DCT) that stores the parameters associated with each of the eight logical drives. Disk drive parameters refer to how the total storage space on a drive is divided up into addressable units. Floppy disk drives use a removable flexible media which has one or two surfaces coated with a magnetic layer of particles. Hard disk drives use either fixed rigid platters or removable cartridges that contain rigid platters also containing magnetic layers of particles. Each platter of a hard drive contains two surfaces. Regardless of the disk drive type, the magnetic layer of particles on each surface is magnetized into concentric circles of storage areas called TRACKs. Each track is then divided into subareas called SECTORs. Each sector is uniquely identified by a pattern of information preceding each sector called an ID FIELD. The division of a surface into sectors may be envisioned as a pie cut up into equal sized pieces. The process of generating each of the tracks and sectors is termed the formatting process. The physical length of a sector will be greater on the outer tracks of the surface than the inner tracks of the surface (similar to the grooves of a phonograph record). Although the number of sectors per track may vary from one media type to another, the number of sectors in each track of the same media must always be a constant. The DOS assigns numbers to every sector, every track, and every surface. Surfaces are numbered consecutively by one starting from zero. Tracks are numbered consecutively by one starting from zero at the outermost portion of the disk giving the innermost track the highest number. A CYLINDER consists of the like-numbered tracks on all surfaces. For example, on a two-surface media, track zero of surface zero and track zero of surface one are grouped together into cylinder zero. Floppy disk drives use a read/write head that is positioned lateral to the disk surface. The head can step in towards the center of the disk and step out to the circumference of the disk while the disk rotates on its hub. The rotational speed is 300 rpm for 5-1/4" floppy disk drives and 360 rpm for 8" floppy disk drives. Hard disk drives rotate at speeds of 3600 rpm and higher. Because the physical lengths of the sector vary from the outer to the inner track, the bit density of each sector varies per track. Therefore, the amount of information stored in all sectors is dependent on the maximum bit density permitted in its shortest sized sector. Some manufacturers of computer systems are using a design which keeps the bit density per sector constant by use of a variable speed drive which maintains a constant linear velocity of the surface across the head regardless of the track position. This technique promotes a greater capacity for storage but requires a more precisely controlled drive. If such a drive control were utilized under this DOS, a suitable translation filter would be needed which would permit the DOS to think that each track still contained the same number of sectors. If we concern ourselves with a 5-1/4" double density floppy drive rot