PC System Programming PDF
PC System Programming PDF
19
[il~ II.
[-1[111
~11 1111
~ mI)~
System 1I1 ~
Programming
for developers ~
Michael Tischer
Published by
AbacuslliliiiiiUiil
This book is copyrighted. No part of this book may be reproduced, stored in a retrieval
system, or transmitted in any form or by any means, electronic, mechanical, photocopying,
recording or otherwise, without the prior written permission of Abacus or Data Becker,
GmbH.
Every effort has been made to ensure complete and accurate information concerning the
material presented in this book. However, Abacus can neither guarantee nor be held legally
responsible for any mistakes in printing or faulty instructions contained in this book. The
authors always appreciate receiving notice of any errors or misprints.
This book contains trade names and trademarks of many companies and products. Any
mention of these names or trademarks in this book are not intended to either convey
endorsement or other associations with this book.
PC-DOS, IBM PC, XT, AT, PS/2, OS/2 and PC-BASIC are trademarks or registered
trademarks of International Business Machines Corporation. Ventura Publisher is a
trademark or registered trademark of Xerox Corporation. GEM and CP/M are trademarks or
registered trademarks of Digital Research Corporation. Microsoft Works, Microsoft Quick
C, Microsoft Windows, MS-DOS, XENIX and GW-BASIC are trademarks or registered
trademarks of Microsoft Corporation. Lotus 1-2-3 is a trademark or registered trademark of
Lotus Development Corporation. dBASE is a registered trademark of Ashton-Tate, Inc.
Sidekick, Turbo C and Turbo Pascal are trademarks or registered trademarks of Borland
International. UNIX is a registered trademark of Bell Laboratories. Mickey Mouse is a
registered trademark of Walt Disney Corporation.
Library of congress Cataloging-in-publication Oata
Tischer, Michael, 1953
PC system programming for d'evelopers I Michael Tischer.
p. ClI.
"A Data Becker book ,'I
1. System programming (computer science) 2. Microcomputers-programming. I. Title
QA76.66.T57 1989 DDS.26S--dc20 85-183S0
ii
Table of Contents
1. Introduction ............................................................................................ 1
iii
Table of Contents PC System Programming
7.8 Accessing the Hard Disk from the BIOS .................................................... 323
7.9 Accessing the Serial Port from the BIOS ................................................... 330
7.11 Accessing the Keyboard from the BIOS ............................. ~ ....................... 358
iv
Abacus Table of Contents
7.13 Reading the Date and Time from the BIOS ................................................. 395
10.7 Accessing Video RAM from High Level Languages .................................... 554
H. Glossary of Terms.................................................................................903
v
Chapter 1
Introduction
A few years ago, my computer was a small home computer. When I became
interested in the IBM PC, I had to learn a lot of new things. I learned about MS
DOS and became familiar with 8088 assembly language. I soon reached a point
where I started developing commercial PC programs in partnership with my friend
Axel Sellemerten. All of this happened some time ago, but I still clearly
remember sitting at my desk, looking through dozens of PC books and technical
manuals, trying to find a critical piece of information.
These books and manuals were expensive and hard to find. Besides, none of them
covered all aspects of the PC. Some books tell you about PC hardware m: the
BIOS QI DOS. I could never find a book that dealt with the PC as a total system.
No single book was able to provide me with a complete system overview.
This book is the result of my experience with all of these references. The three
main areas of the PC (hardware, the BIOS and DOS) are combined in this book
from a software developer's point of view. This book was written to serve as an
instruction book as well as a reference manual. It is not, and was never intended to
be, a book for the beginner. The book assumes that you're familiar with MS-DOS
and are able to program in one of the four most popular PC programming
languages (machine language, BASIC, Pascal or C).
Organization
The book is divided into five parts. Part 1 (Chapters 1-5) gives an introduction to
the PC's internal components. Part 2 (Chapter 6) describes the Disk Operating
System (DOS) and Part 3 (Chapter 7) describes the Basic Input Output System
(BIOS). PC hardware that is not part of the central processor is discussed in Part 4
(Chapters 8-18). Part 5 (Chapter 19) describes the interaction between these
components and the keyboard. The book concludes with a large reference section
(Appendices) containing all functions of DOS and the BIOS, among other things.
To understand the content of this book, you must first know something about the
different number systems used in computers. Readers unfamiliar with the binary
1
1. Introduction PC System Programming
BASIC, Pascal and C programmers should read Chapters 2 and 3 and should work
through the subsections in Chapter 4 devoted to your preferred language. Chapter 5
is devoted exclusively to assembly language programming and may be skipped.
2
Chapter 2
While working with the PC, many users have wondered about its ability to solve
complex problems. Users often attribute these abilities to the software or operating
system. The fact is, hardware is as important as the software.
Microprocessor
The microprocessor is the brain of the PC. It understands a limited number of
machine language instructions and processes or executes programs in this machine
language. These instructions are very simple and can't be compared to commands
in high level languages such as BASIC, Pascal or C. Commands in these
languages must be translated into a large number of machine language instructions
that the PC's microprocessor can then execute. For example, displaying text with
the BASIC PRINT statement requires the equivalent of several hundred machine
language instructions.
The PC has its own family of microprocessor chips, all designed by the Intel
Corporation. The figure on the next page describes the Intel 80xx family tree.
Your PC may contain an 8086, an 8088 (used in the PClXn, an 80186, an 80286
(used in the An or even an 80386 microprocessor. The first generation of this
group (the 8086) was developed in 1978. The successors of the 8086 were different
from the original chip. The 8088 is actually a step backward since it has the same
internal structure and instructions of the 8086, but is slower than the 8086. The
reason is that the 8086 transfers 16 bits (2 bytes) between memory and the
microprocessor at one time. The 8088 is slower since it transfers only 8 bits (1
byte) at one time.
3
2. The PC's Brain PC System Programming
Multiprocessing
The three other microprocessors of this family are improved versions of the 8086.
The 80186 offers auxiliary functions. The 80286 has additional registers and
extended addressing capabilities. The 80286's biggest advantages over its
predecessors are its multiprocessing and virtual memory capabilities.
Multiprocessing allows several programs to execute at the same time, such as
compiling a program while using a word processor. This capability, which is
based on the fast switching between the individual programs, can also be
implemented through software (e.g., Microsoft Windows®), but working directly
through the processor is more efficient.
Virtual memory
Virtual memory means that a program appears to use much more memory than is
available in the computer's RAM. Portions of the programs or data which don't fit
into memory are temporarily stored on the mass storage device (floppy or hard
disk). The computer loads these sections into RAM as needed. The CPU and the
operating system share the task of virtual memory management. Earlier versions
of MS-DOS don't support the multiprocessing or virtual memory capabilities of
the 80286, so most AT computers aren't working to their full potential.
80486
8028~
6 8~?A •
t
Relative
power
74 75 76 77 78 79 80 81 8283 84 85 86 8788 89 90
Year
4
Abacus 2. The PC's Brain
The 80386 represents current state of the art technology. It has a more extensive
instruction set than the 80286, and offers additional memory protection features.
These processors are all upwardly compatible with software. This means that
machine language programs developed for the 8086 can be executed on the other
processors of this series. On the other hand, a program written for the 80386 may
not run correctly on the 80286 or the 8088, because instructions available on the
80386 may not be available in the earlier processors.
Throughout this book the PC processor is designated as the 8088, even though
your PC may use a different processor.
5
2. The PC's Brain PC System Programming
BH I Bt
CODE SEGMENT
CS
Cif9~LC( COUNT
SS STACK SEGMENT
Dif~~LD( DATA
01 DESTINATION INDEX
Program Counter
SI SOURCE INDEX
\ INSTRUCTION
IP POINTER
SP STACK POINTER \
BP BASER POINTER
Flag Register
_OIDIIITlslz~
15 11 10 9 8 7 6 4 2 0
8088 registers
All registers are 16 bits (2 bytes) in size. If all 16 bits of a register contain a I,
this is the largest number that can be represented within 16 bits. This number is
the decimal number 65535. Therefore, a register can contain any value from 0 to
65535.
Register groupings
As shown in the above figure, registers are divided into four groups: common
registers, segment registers, the program counter and the flag register. The different
register assignments are designed to duplicate the way in which a program
processes data-which is the basic task of a microprocessor.
The disk operating system and the routines stored in ROM use the common
registers a great deal, especially the AX, BX, ex and DX registers. The contents
of these registers tell DOS what tasks it should perfonn and which data to use for
execution.
6
Abacus 2.1 8088 Registers
These registers are affected mainly by mathematical (addition, subtraction, etc.) and
input/output instructions. They are assigned a special position within the registers
of the 8088 because they can be separated into two 8-bit (I-byte) registers. Each
common register may be thought to consist of three registers: a single 16-bit
register, or two smaller 8-bit registers.
AL
bit 15 bit 0
AX register
The registers have designators of H (high) and L (low). Thus the 16-bit AX
register may be divided into an 8-bit AH and an 8-bit AL register. The H and the L
register designators occur in such a way that the L register contains the lower 8
bits (bit 0 through 7) of the X register, and the H register the higher 8 bits (bits 8
through 15) of the X register. The AH register consists of bits 8-15 and the AL
register of bits 0-7 of the AX register. However, the three registers cannot be
considered independent of each other. For example, if bit 3 of the AH register is
changed, then the value of bit 11 of the AX register also changes automatically.
The values change in both the AH and the AX registers. The value of the AL
register remains constant since it is made of bits 0-7 of the AX register (bit 11 of
the AX register does not belong to it). This connection between the AX, the AH
and the AL register is also valid for all other common registers and can be
expressed mathematically.
You can determine the value of the X register from the values of the H and the L
registers, and vice versa. To calculate the value of the X register, multiply the
value of the H register by 256 and add the value of the L register.
Example: The value of the CH register is 10, the value of the CL register is
118. The value of the CX register results from CH*256+CL, which
is 10*256+ 118 = 2678.
Specifying register CH or CL, you can read or write an 8-bit data item from or to
any memory location. Specifying register CX, you can read or write a 16-bit data
item from or to a memory location.
7
2. The PC's Brain PC System Programming
Address register
The number of memory locations which a processor can access depends on the
width of the address register. Since every memory location is accessed by
specifying a unique number or address, the maximum value contained in the
address register determines the address space. Earlier microprocessors used a 16-bit
address register enabling access to addresses from 0 to 65535. This corresponds to
the 64K memory capacity of these processors.
To address one megabyte of memory the address register must be at least 20 bits
wide. At the time the 8088 was developed, it was impossible to use a 20-bit
address register, so the designers used an alternate way to achieve the 20-bit width:
the contents of two different 16-bit numbers are used to form the 20-bit address.
Segment register
One of the numbers is contained in a segment register. The 8088 has four segment
registers. The second number is contained in another register or in a memory
location. To form a 2O-bit number, the contents of the segment register are shifted
left by 4 bits (thereby multiplying the value by 16) and the second number is added
to the first
8
Abacus 22 Segment and Offset Addressing
Logical
address
1514 13 ... •.. 210 0ff
(16 bits
I I I I I I I I I I I I I I I I Iadd~:~s
Physical 191817 •..
- BIT
. .. 2 1 0
Combining the segment and offset addresses requires special notation to indicate a
memory location's address. This notation consists of the segment address in four
digit hexadecimal format, followed by a colon, and the offset address in four-digit
hexadecimal format. For example, a memory location with a segment address of
2000H and an offset address of AF3H would appear in this notation as 2000:0AF3.
Because of this notation, you can omit the H suffIx from hexadecimal numbers.
9
2. The PC's Brajn PC System Programming
::J
...
n
CI)
= 1104H
-
CI)
::J
CI)
Q.
Segment 3
CI)
3
Offset ...
o
'<
II)
Q.
...
Q.
CI)
til
Segment til
CI)
address = 1600H til
The 8088 has four segment registers, which have special roles in the execution of
an assembly language program. There are four registers to accommodate the basic
structure of any program. A program consists of a set of instructions (code). There
are also variables and data items that are processed by the program. A structured
program keeps the code and data separate from each other while they reside in
memory. Assigning code and data their own segments conveniently separates
them.
Each needs a segment address and a segment register. The CS (Code Segment)
register uses the IP (Instruction Pointer) register as the offset address. The CS then
determines the address at which the next assembly language instruction is located.
The IP is also called the Program Counter. When the processor executes the
current instruction, the IP register is automatically incremented to point to the
next assembly language instruction. This ensures the execution of instructions in
the correct order.
Like the CS register, the DS (Data Segment) register contains the segment address
of the data which the program accesses (writing or reading data to or from
10
Abacus 22 Segment and Offset Addressing
memory). The offset address is added to the content of the DS register and may be
contained in another register or may be contained as part of the current instruction.
The SS (Stack Segment) register specifies the starting address of the stack. The
stack acts as temporary storage space by some assembly language programs. It
allows fast storage and retrieval of data for various instructions. For example,
when the CALL instruction is executed, the processor places the return address on
the stack. The SS register and either the SP or BP registers form the address that is
pushed onto the stack.
The last segment register is the ES (Extra Segment) register. It is used by some
assembly language instructions to address more than 64K of data or to transfer data
between two different segments of memory.
CS:OOOO
SS:FFFF
SS:FFFF
SS:OOOO DS:FFFF
DS:FFFF SS:oooo
DS:OOOO
DS:OOOO
Non-overlapplng Overlapping
segments segments
As the figure above shows, two segment registers can specify areas of memory
which overlap, or are completely different from one another. In many cases, a
program doesn't require a full 64K segment for storing code or data. You can
conserve memory by overlapping the segments. For example, you can store data
immediately following the program code by setting the DS and CS registers
accordingly.
11
2. The PC's Brain PC System Programming
The flag register is of special importance. Various bits in this register indicate or
signal the special conditions which may occur during execution of an assembly
language instruction. For example, if an arithmetic operation results in a negative
number, the processor sets the S (sign) flag to 1 to indicate this change.
The C (carry) flag is set to 1 if the sum of two 8-bit numbers cannot be
represented as an 8-bit number.
As the figure above shows, the processor doesn't use all 16 bits of this register.
The unused bits normally contain the value O.
This ends our short trip into the PC's brain. If you didn't quite follow some of
these concepts, the sample application programs in the sections on the BIOS and
DOS functions should help you understand.
12
Abacus 2.3 The CPU Support Chips
These support chips communicate with and control external peripherals such as a
disk drive or the screen display.
Some of these support chips can be programmed using the assembly language
instructions IN and OUT. Since the programming of most support chips is very
complex, we recommend that you leave this up to DOS, unless you have a
complete understanding of the structure and operation of these chips.
The following sections define the most important support chips in the PC.
This chip gets its name from the acronym DMA which stands for Direct Memory
Access. This chip can directly write data to or read data from RAM. The DMA
controller performs disk input/output operations, moving data from RAM to disk
or from disk to RAM. This relieves the processor of this task and accelerates
program execution.
Interrupts are signals from individual components of the system to get the CPU's
attention and to initiate certain tasks. Several interrupts or requests for services
from different system components can be outstanding at one time. These requests
are initially handled by the interrupt controller, which passes them on to the CPU.
It assigns priority to every interrupt request according to its source and passes the
request with the highest priority to the CPU. The interrupt controller in the
PC/XT can process up to 8 interrupt requests at the same time. ATs require more
power, so they use two interconnected interrupt controllers which can process up
to 15 interrupt requests simultaneously.
This chip provides a link between the CPU and the peripherals such as the
keyboard or an audio speaker. However, it only operates as a mediator, addressed by
the CPU for unit access and transmission of certain signals. You cannot bypass
the PPI for direct communication between the CPU and peripherals.
13
2. The PC's Brain PC System Programming
2 .3 . 4 The Clock
If the microprocessor is the brain of the computer, then the clock could be
considered the heart of the computer. This heart beats several million times a
second (about 14.3 megaHertz) and paces the microprocessor and the other chips in
the system. Since almost none of the chips operate at such high frequencies, each
support chip modifies the clock frequency to its own requirements.
The timer chip can be used as a counter and timekeeper. This chip transmits
constant electrical pulses from one of its output pins. The frequency of these
pulses can be programmed as needed, and each output pin can have its own
frequency. Each output pin leads to another component. One line goes to the audio
speaker and another to the interrupt controller. The line to the interrupt controller
triggers interrupt 8 at every pulse, which advances the timer count.
Unlike the chips discussed up until now, the CRT (Cathode Ray Tube) controller
is separate from the main circuit board of the PC. You'll find this chip on the
video board which is mounted in one of the computer's expansion slots. Even
though there are many boards that differ widely in their capabilities (monochrome
display, color display, etc.), all video boards are based on the 6845 CRT controller.
It produces a display on the monitor connected to the computer. The controller has
several internal registers which control the output of the display.
The 8088, 80286 and the 80386 are not capable of performing floating point
arithmetic operations directly. There is a socket on the main circuit board of the
PC for adding a special math coprocessor. The PC/XT uses the 8087, the AT the
80287 and the new 80386 uses the 80387 coprocessor.
While floating point arithmetic can be performed using software routines, a math
coprocessor is up to 100 times faster. The 8087 and the 80287 can perform basic
14
Abacus 23 The CPU Support Chips
15
2. The PC's Brain PC System Programming
The support chips communicate with memory using a bus or path over which the
electronic signals travel.
Address bus
The address bus carries the number of the memory location to be accessed. The
signals on the bus represent a binary number whose value indicates the memory
location for access. Since only those memory locations represented on the address
bus can be accessed, the number which make up the bus lines determine the
number of addressable memory locations .
. The PC/XT has a 20-bit address bus and can address a maximum of 2'}J) (about 1
million) different memory locations. The AT has a 24-bit address bus and can
address more than 16 million memory locations.
Data bus
Once the bus knows the address of the memory location to be accessed, data can be
transferred between the individual chips and the memory location over the data bus.
The number of lines in this circuit determine how many bits are transferred to or
from memory simultaneously.
The PC/XT has 8 lines so it can transfer one byte at a time. However, since the
8088 is a 16-bit processor, 16-bit data must often be transferred. There aren't
enough lines to transfer 16-bit data, so the system divides a 16-bit data item into
two 8-bit numbers. These two 8-bit data bytes are transferred one after the other
along the bus.
The 8086 and 80286 processors can transfer 16 bits simultaneously over their 16
bit-wide data buses. This is one reason why the AT executes programs faster than
the 8088 processor. The 80386 processor can transfer 32 bits at a time.
Word storage
All members of the Intel 80xx processor family share the same method of storing
words (16-bit data) in memory. The lower numbered memory location contains
bits 0-7 (the low byte) and the higher numbered memory location contains bits 8
15 (the high byte). For example, if you store the word 3F87H starting at address
0000:0400, memory location 0000:0400 accepts the low byte 87H and memory
location 0000:0401 accepts the high byte 3FH.
16
1.) The processor doesn't care if a memory address is located in a RAM chip
or a ROM chip. The main difference between RAM and ROM lies in the
fact that you can't write or store new data into ROM (hence its name:
Read Only Memory).
J- RC ca.:r LC:tq~
RC cartr Ldoe
ac Lt~ona H JS RUM
I,; .eo RAM
1: : itio:lal ieo RAM
uo :0
1-: uo :0
,-' UO
110
0
:0
,-!
,-I
,-:
UO
UO
'n
:0
uo :0
,-; 110 :0
un :0
Memory allocation
The first 10 memory segments are reserved for the main RAM memory, limiting
maximum RAM to 640K. A computer's memory size may differ from one PC
manufacturer to another but has at least 64K installed in segment O. If you install
additional RAM, its first memory address must immediately follow the last
existing memory address, since no gaps may exist between individual RAM
memory segments. Memory segment 0 has a special role since it contains
important data and operating system routines.
Memory segment A follows the RAM memory. In this case, an EGA (Extended
Graphics Adapter) is installed. This board uses the memory for the screen display
in different graphic modes.
17
2. The PC's Brain PC System Programming
Segment F contains the actual BIOS routines, the system loader and the ROM
BASIC available on many computers.
18
Chapter 3
Introduction to Interrupts
This chapter presents a view of interrupts, which are vitally important to the
operation of the 8088 processor. An interrupt is a signal from a peripheral device
or a request from a program to perform a specific service. When an interrupt
occurs, the currently executing program is temporarily suspended and an interrupt
routine begins execution to handle the condition that caused the interrupt
Program interrupt
When a program is suspended, the processor saves the contents of the CS and IP
registers on the stack, and begins the interrupt routine. After the interrupt routine
has completed its task, it issues the IRET (Interrupt RETurn) instruction which
restores the contents of the CS and IP registers from the stack, thus resuming the
program.
The interrupt routine saves and restores contents of the other registers before
returning to the interrupted program.
19
3. Introduction to Interrupts PC System Programing
Each interrupt has an associated interrupt routine to handle the particular condition.
To organize the 256 interrupts, the starting address of the corresponding interrupt
routines are arranged in the interrupt vector table.
When an interrupt occurs, the processor automatically retrieves the starting address
of the interrupt routine from the interrupt vector table.
The starting address of each interrupt routine is specified in the table in terms of
the offset address and segment address. Both addresses are 16 bits (2 bytes) wide.
Therefore each table entry occupies 4 bytes. The tota1length of the table is 256*4
or 1024 bytes (IK).
Interrupt Purpose
0000:003FE
,...---
cs
... number:
Free
255
0000:003FC IP
~
OOOO:OOOE CS
OOOO:OOOC 3 Breakpoint
IP
OOOO:OOOA CS
0000:0008 IP
2 NMI
0000:0006 CS
1 Single-step
0000:0004 IP
0000:0002 CS
IP
o Division by 0
0000:0000
15 o
The table itself is located in memory from OH to 3FFH. Since the interrupt's
number is the same as the table entry for the corresponding interrupt routine, the
interrupt routine address for interrupt 0 is the zero table entry in locations OH-3H.
20
Abacus 3.1 The Structure of the Interrupt Vector Table
Memory locations 4H-7H contain the address for the interrupt routine for
interrupt I, etc. The last interrupt, interrupt 255, occupies the end of the table at
locations 3FCH-3FFH.
Advantages
An advantage of using the interrupt vector table is that it's easy to change an entry
in the table to the starting address of a user-written interrupt routine. This makes a
new interrupt routine available to any program which can invoke the routine
simply by executing the corresponding interrupt instruction.
The next section explains the different types of interrupts and how they are used in
the system.
21
3. Introduction to Interrupts PC System Programming
Interrupt types
Software interrupts make it possible to use many of the basic operating system
services from either the assembler (or machine language) level or from many of the
higher level languages which support interrupt processing.
A hardware device such as a disk drive or keyboard can trigger a hardware interrupt.
This is a simple and efficient mechanism for handling events which require
attention.
One example is the keyboard. When you press or release a key, interrupt 9 (the
keyboard interrupt) is signalled. The standard DOS interrupt routine responds by
placing the character value corresponding to the key that was pressed into the
22
Abacus 3.2 Interrupt Types
keyboard buffer following any value which may have been previously there. If the
keyboard buffer is full, the routine generates a short beep. As in any other
interrupt, the original program continues after the completion of the interrupt
routine.
Maskable interrupts
Non-maskable interrupts
In contrast, a non-maskable interrupt cannot be disabled by the STI instruction.
One example is interrupt 2. This interrupt indicates an error in the PC's memory.
It displays a message on the screen that one or more of the RAM chips is defective
and should be replaced.
The last interrupt type to be described is the internal hardware interrupt. The
processors on the main circuit board of the PC trigger this interrupt. One example
is interrupt 8 which is designated as a timer interrupt. The timer triggers this
interrupt at a rate of 12.8 times per second. It also disables the disk drive motor if
no disk access is in progress.
23
3. Introduction to Interrupts PC System Programming
24
Abacus 3.3 Interrupts at a Glance
General overview--interrupts
25
Chapter 4
A BASIC programmer can call an interrupt using a short assembly language pro
gram. You'll find an example of this in Section 4.1.
This chapter provides information on calling interrupts from Pascal, BASIC and
C. Each describes how interrupts can be called in the particular language and the
rules the programmer must observe. Each section concludes with a short
demonstration program.
Read through the section devoted to the language with which you feel most
comfortable. A comparison of the three sample programs could be interesting for
those of you who wish to compare the similarities and differences in the three
languages.
The programs are only examples. Experiment as much as you want-you won't
damage your computer if you change them a little.
27
4. Using Interrupts from High Level Languages PC System Programming
GW-BASIC does not have a function for calling interrupts. However, the CALL
command can be used to execute a machine language program. You can also use
the CALL command to pass certain parameters to the called program. The called
machine language program must be located in the 64K used by GW-BASIC for
program statements and variable storage. Because of this, the interpreter must be
told to reserve part of program memory for the machine language routine.
Otherwise the program or variables may overwrite the machine language routine,
causing a system crash. You can reserve memory directly when you call BASIC
from the operating system. Enter the name GWBASIC followed by the 1M:
parameter. After the colon, enter the highest memory location you want used by
BASIC. For example, since the sample program starts at memory location 60000,
start the GW-BASIC interpreter as follows:
gwbasic /m:60000
This reserves the required memory space. Now you can place the machine language
routine into memory by making it part of the current BASIC program and loading
it into memory using a suitable subroutine. The current BASIC program must
contain the following commands:
60000
60010
60020
•• initialize the routine for the interrupt call .
.***************.****************** •• *******.******.*.**********1
'
,* ____________________________________________________----_____ *1
60030 '. Input: none
60040 '. Output: IA is the Start address of the Interrupt routine *,
60050 1.********************************.*.*.******.****.*************'
60060 '
60070 IA=60000! 'Start address of the routine in the BASIC segment
60080 DEF SEG 'set BASIC segment
60090 RESTORE 60130
60100 FOR 1% = 0 TO 160 : READ X% : POKE IA+I%,X% : NEXT 'poke Routine
60110 RETURN 'back to caller
60120 '
60130 DATA B5, 139, 236, 30, 6,139, llB, 30,139, 4,232,140, 0,139, llB
60140 DATA 12,139, 60,139,118, 8,139, 4, 61,255,255,117, 2,140,216
60150 DATA 142,192,139,118, 28,138, 36,139,118, 26,138, 4,139,118, 24
60160 DATA 138, 60,139,118, 22,138, 28,139,118, 20,138, 44,139,118, 18
60170 DATA 138, 12,139,118, 16,138, 52,139,118, 14,138, 20,139,118, 10
60180 DATA 139, 52, 85,205, 33, 93, 86,156,139,118, 12,137, 60,139,118
60190 DATA 28,136, 36,139,118, 26,136, 4,139,118, 24,136, 60,139,118
60200 DATA 22,136, 28,139,118, 20,136, 44,139,118, 18,136, 12,139,118
60210 DATA 16,136, 52,139,118, 14,136, 20,139,118, 8,140,192,137, 4
60220 DATA 88,139,118, 6,137, 4, 88,139,118, 10,137, 4, 7, 31, 93
60230 DATA 202, 26, 0, 91, 46,136, 71, 66,233,108,255
The DATA statements contain the machine language routine which performs the
interrupt call. The routine is READ and then POKEd into memory. To start this
routine at another memory location, change the value in line 60070. Remember
28
Abacus 4.1 Inlerrupt Calls from BASIC
that the parameters used to start GW-BASIC must also be changed so that the
routine cannot be overwritten by the variables of the program.
To use the machine language routine to call an interrupt, this subroutine must of
course be called frrst. The frrst line of the user program should therefore be:
100 GOSUB 60000
The actual program which calls the interrupt function during its execution can be
stored between line numbers 100 and 60000. The following program line
demonstrates how this can be done:
200 CALL IA(INTNRt,AHt,ALt,BHt,BLt,CH',CL\,DH\,DLt,Dlt,SI\,ES\,FLAGSt)
The variables within parentheses are the variables passed to the assembly language
program. All variables must pass true integer variables and not constants. The
variable names mentioned above may be changed but their order must remain
unchanged. Within your program they can have other names.
The frrst variable in this example, called INTNR%, is the number of the interrupt
you want to call. Be careful to specify the exact interrupt number. Also, avoid
passing a variable which has not been initialized. Otherwise, you may call the
wrong interrupt, which could lead to a system crash. The variables following
INTNR % are copied into the processor registers of the same names. If a register is
not used by an interrupt routine, you can pass any integer variable in the
corresponding register variable. The value of the ES register is treated differently. If
the value of ES% is -1, the contents of the DS register is copied to the ES
register.
Following the completion of the interrupt call, the values are returned in the
designated register variables.
This technique works only with half registers (AH, AL, BH ... ). It may be
necessary to transform these half registers into a whole register. This can be done
as follows:
300 AX' - AH' * 256 + AL\
On the other hand, a whole register can be split into two half registers with the
following commands:
410 AHt - INT (AXt / 256)
420 AL\ - AX' AND 255
After calling interrupt functions, the carry flag in the flag register indicates if the
called functions were executed correctly. In a BASIC program, it may be necessary
to test the carry or zero flags. Since the content of the flag register is in the
variable FLAGS% after the interrupt call, the status of individual flags can be
inspected through this variable. This is possible with the following program
statements:
29
4. Using Interrupts from High Level Languages PC System Programming
Another problem with interrupt calling is passing variable addresses (e.g., character
string output). BASIC stores this set of characters as a string. To determine the
offset address of such a string (the segment address of all variables is constant), use
the VARPTR function. The LO and ill byte of the offset address can be determined
with the following two program lines:
300 LO=PEEK (VARPTR (STRING NAME) +1) 'LQ-Byte of the Offset address
310 HI=PEEK(VARPTR(STRING=NAME) +2) 'HI-Byte of the Offset address
Garbage collection
Remember to include an end marker ("$" or a CHR$(O» at the end of the string
(BIOS and DOS functions expect one of these).
Note: Before copying this subroutine and trying it, we have a small
suggestion. During your first attempts something will probably go
wrong. This is perfectly normal, and you can even expect the
computer to crash a couple of times. Save programs
frequently...especially ~ running the program. This way, you
won't have to type in the program again from the beginning.
Here is a short sample program which uses the subroutine described above to
display text on the screen with function 9 of interrupt 21H.
100 ,**** ••• **********.***********************************************,
110 '. I N T DOS B
120 '*---------------------------------------------------------------*,
130 '. Assignment outputs as an example of an Interrupt
140 '. a String through a DOS function on
.
150 the display screen
160 Author MICHAEL TISCHER
170 '. developed 07/30/87 ,
180 last Update 04/08/89
190 •••••••••••••••••••••••••••••••••••••••••••••••••••••• ************'
200 '
210 CLS : KEY OFF
220 PRINT"NOTE: This program can only be started if the GWBASIC was "
230 PRINT"started from the DOS level with the command"
235 PRINT"<GWBASIC /m:60000>."
240 PRINT: PRINT"If this is not the case, please input <s> for Stop."
250 PRINT"otherwise press any key ... ";
260 A$ - INKEY$ : IF A$ = "s" THEN END
270 IF A$ = "" THEN 260
280 PRINT
290 GOSUB 60000 'install function for interrupt call
30
Abacus 4.1 In.terrupt Calls from BASIC
How it works
The program is composed of separate parts. Lines 210-290 call the subroutine to
initialize the machine language function for the interrupt call. Then the individual
variables for the interrupt call are loaded. T$ accepts the string to be output.
CHR$(13) and CHR$(lO) print a blank line before the output of the actual text
This text ends with the "$" character because the 005 function which outputs the
string expects this character as an end marker (it will not display this character).
INR% and FKT% contain the interrupt number and the function number to be
called. Besides these two variables, the variables OFSLO% and OFSHI% contain
the offset address of 1'$.
The CALL command (line 350) calls the interrupt. The first variable passed is
INR% with the number of the interrupt to be called. Then follows FKT%, which
transfers to the AH register before the interrupt call and informs interrupt 21H of
the function number to be called. Several Z% variables follow. These act as
dummy variables for all registers which have no special significance to the
function which is called. The content of Z% is unimportant. The content of the
register into which it is copied is irrelevant for the called function. After the Z%
variables, which determine the contents of the AL, BH, BL, CH and CL registers,
follow the variables OFSHI% and OFSLO%, which set the offset address of the
string in the DX register. The remaining register contents are unimportant for the
function call and are fIlled with Z%.
31
4. Using Interrupts from High Level Languages PC System Programming
To permit the DOS function which is called to output the text, its offset and
segment address must be known. This address is expected in the DS register and
wHI be set automatically by GW-BASIC.
To conclude this section, here is the listing of the assembler program that we just
used to call an interrupt.
:***********************************************************************
;* BASINT.ASM: This routine offers the capability of
;* calling any interrupt from BASICA or
;* GWBASIC
;*
;**-------------------------------------------------------------------**
:* Call: *
;* CALL ADR(INTNR%,AH%,AL%,BH%, BL%,CH%,CLl,OH%,OL%,OI%, SI%,ES%,FLAGS%) *
:**-------------------------------------------------------------------**
;* On passing control to the machine language program BASIC
;* deposits the variables on the following positions of the stack
;* INTNR% SP+30 AH% SP+28 ALl SP+26 BH% SP+24
;* BLl SP+22 CH% SP+20 CLl - SP+l8 OH% SP+l6
;* OLl SP+l4 OH SP+l2 SH SP+lO ES% SP+8
;* FLAGS% SP+6
i**-------------------------------------------------------------------**
;* for ES the value -1 is passed, then ES is set to OS
i***************************************************** *************** •• !
code segment
assume cs:code,ds:code,es:code,ss:code
32
Abacus 4.1 Interrupt Calls from BASIC
push si ;Store SI
basint endp
i---------------------------------------------------------------------
set intnr proc near ;stores the interrupt number
pop bx
jmp ad 1 -
;---------------------------------------------------------------------
code ends
end
33
4. Using Interrupts from High Level Languages PC System Programmi.ng
Some brief notes on this program follow for those not familiar with the calling
and linking of assembly language programs in GW-BASIC: The program ftrst
pushes the base pointer on the stack since it will be reset by the next instruction.
During re-entry into GW-BASIC, the base pointer must have the value it had
during the call of the routine. Then the base pointer is set to the value of the stack
pointer for access to data on the stack. This is necessary for GW-BASIC to pass
the BASIC variables named in the CALL command to the stack. In the next step,
the DS and the ES registers are stored on the stack, because their content may
change during execution of the routine and must be preserved for return to GW
BASIC.
Now the routine can read in the variables and set the various processor registers. It
is important to note that the stack does not contain variable contents, but their
addresses relative to the DS register. Because of this, the address of the variable
must be loaded fIrst and then the relative value of this address.
Which addresses contain the addresses of the individual variables stored on the stack
can be determined from the header of the assembly language routine. First you
must determine the number of the interrupt to be called. This value must be treated
in a different manner than the other variables on the stack because it isn't passed in
one of the processor registers, but is a part of the INT instruction which calls the
interrupt. It is indicated as a byte following the code of the !NT instruction (CDH).
To set the interrupt number, the number to be passed must be stored following the
CDH code of the !NT instruction. This creates a small problem since this routine
can be POKEd by the BASIC program into any memory location. Because of this,
the address of the INT instruction depends on the current starting address of the
routine instead of remaining constant. The routine doesn't know where the !NT
instruction is located.
A small trick can be used to help here. The routine does not know where it is
stored, but the processor knows the location of the !NT instruction (it has to
know, otherwise it couldn't execute the routine). The subroutine SET_INTR is
called after the interrupt number is loaded into the AX. register. The processor, as
in any CALL instruction, stores the address where the program execution is to
continue on the stack, before calling any subroutine. This is the instruction which
precedes the label AD_I.
Subroutine SET_INTR gets the address of AD_I from the stack. While the address
of the INT instruction is still not known, the distance between AD_I and the !NT
instruction remain constant, the address of the INT instruction can be calculated
and the interrupt number can be stored following the instruction. The task ends and
the routine returns to the main program (to the label AD_I).
The rest of the routine consists of repeating instructions which determine the
contents of the different variables and pass them to the corresponding processor
34
Abacus 4.1 1111errupt Calls from BASIC
registers. The value for the ES register is given a special test: if it is equal to -I,
the value of the DS register is copied to the ES register.
After all registers are loaded, the interrupt is called and the contents of the
processor registers are transferred back to the corresponding BASIC variables. The
last step is to restore the contents of all registers which had been saved on the
stack. Finally control returns to GW-BASIC.
3S
INTR
Turbo Pascal uses the INTR procedure. Since this parameter can accept any value
between 0 and 255, all available interrupts can be called.
MSDOS
The InterruptNumber parameter needed by Turbo Pascal Version 3.0 isn't required
in this procedure since it always calls interrupt 21H, through which almost all
operating system functions can be called.
In both procedures, the parameter register is a record type which holds the contents
of the registers to be passed. These are copied into the registers before the interrupt
call.
The DOS unit contains the parameters for the type called Registers:
type Registers = record
case integer of
o : (AX, BX, CX, DX, BP, sr, D1, DS, ES, Flags: word);
1 : (AL, All, BL, BH, CL, CH, DL, DH : byte);
end;
Once the DOS unit has been included in a Turbo Pascal source code, the var
statement can be used to define the register variables under the name Regs:
var Regs : Registers;
Now Turbo Pascal can easily communicate with the following processor registers:
Regs.ax,
Regs.bx,
Regs .ex,
Regs.ah, etc.
You then pass the values to the registers through standard assignments. For
example:
Register.ax := 254;
36
Abacus 4.2 Interrupt Calls from Turbo Pascal
Unfortunately, the contents of the half registers AH, AL, BL, etc. can't be defined
this way. In this case, a trick can be used by defining the half registers as normal
integer or byte variables and then merging them together into a whole register.
In this statement, the AX register is assigned value composed of the sum of the
AH register multiplied by 256 (shifting a variable left by 8 places is equivalent to
multiplying it by 256) and the AL register.
begin
WholeRegister :- 10 + Hi shl 8;
end;
Before calling the interrupt, you must first specify the interrupt value in the
register. The contents of all other registers are unimportant here. If the called
interrupt returns values to the calling program through registers, they can be
examined by looking at the individual components of the variable register.
Sometimes individual flags pass information from the interrupt to the calling
program. In most cases, the Carry flag serves this purpose. If an error occurs
during the execution of an interrupt, the flag is set.
To test for a set flag, the following Pascal statements are used. They return lRUE
or FALSE as a result depending on whether the corresponding flag was set or not.
carry flag: (register. flags and 1)
zero flag: (register. flags and 64)
sign flag: (register. flags and 128)
Turbo Pascal uses a different format than DOS and BIOS for string storage,
especially for text buffers (mostly variables of type string).
37
4. Using Interrupts from High Level Languages PC Systtfm Programming
TURBO PASCAL
1 DOS
"$"
To convert a Turbo Pascal string into DOS or BIOS format, an end character
(ASCII code 0) or the dollar sign "$" (ASCII code 36) is appended. Which of these
two characters you should use for indicating the end of the string is described
during the discussions of individual interrupts. Regardless of which format you
use, the characters appear as in either of the following commands:
string := string+iO;
string := string+i36;
The address returned by the Ofs function ~ 1 must be passed to the interrupt,
otherwise the byte which indicates the length of the string is accepted by the
interrupt as its ftrst character.
Here is the sample program. Just like the example in Section 4.1, it displays text
on the screen using function 9 of interrupt 21H:
{******.**********************.****************************************}
{* INTDOS *1
{*-----------------------------~--------------------------------------*1
{*----------------------------------,----------------------------------* 1
{* developed : 07/30/87 *1
program INTDOSP;
38
Abacus 42 Interrupt Calls from Turbo Pascal
Uses Dos;
begin
The variable TEXT contains the text to be displayed. The sequence "#13#10"
places the ASCII code 13, followed by ASCII code 10, at the beginning and the
end of the text, creating a blank line before and after the text. The last character is
the "$" character which indicates the last character of text to DOS.
The number of the function being called (9) is copied to the AH register. Since
Turbo Pascal doesn't allow access to the AH register alone, the entire AX register
must be addressed. The value 0 is loaded into the AL register, but any other value
could be entered into this register since its content has no significance to the called
function. As a last step, before calling interrupt 21H using the MSDOS procedure,
the segment address of the string is placed in the DS register and the offset address
in the DX register.
39
4. Using Interrupts from High Level LangUQges PC System Programming
The C language is the language of choice for most developers. Since it was
originally designed for operating system development, C has provisions to include
machine language routines, which is a benefit within the scope of this book.
The standard libraries of both the Microsoft C and Borland Turbo C compilers have
a number of functions for calling interrupts.
int86x
intdos
intdosx
seqread
All functions and applicable data structures are declared in the OOS.R library me.
A program which wants to access one of these functions must therefore link the
me to the current program using the #include preprocessor command
The BYTEREGS and the WOROREGS structures are joined in the union REGS
which lets the programmer work selectively with either half or whole registers.
Using a variable of the type REGS (called register here for simplicity's sake) gives
us the following:
union REGS register;
AH: register.h.ah
AL: register.h.al
The carry flag is represented by the variable register.x.cflag. If this variable is equal
to 0, the carry flag remains unset. Any other value sets the carry flag.
40
Abacus 43 Interrupt Calls from C
The functions starting with the characters int all serve to call interrupts. The
SEGREAD function reads the current contents of the segment register.
The functions that call interrupts use different register variables for input to the
interrupt routine, and output from the interrupt routine. There is an advantage to
this method over returning information to the same register variable in that the
input information is not overwritten.
Since the individual functions pass only the address of the variable representing the
register and not the variable itself, it is possible to combine the input and output
registers into a single variable. In this case, the address of one variable is provided
for the variable representing the input and the output registers (this method is used
in the sample program at the end of this section).
Before calling the interrupt, the contents of the input variable are copied to the
corresponding processor registers. Following the interrupt call their contents
become the output variables.
All interrupt functions return the content of the AX register as a result code after
the interrupt call.
int86
int86x
The int86x function differs from the int86 function in that it requires an additional
argument of the SREGS type. Its contents are copied into the segment register
before calling the interrupt, but are not copied back following the call to the
interrupt routine.
41
4. Using Interrupts from High Level Languages PC System Programming
The intdos and the intdosx functions differ from the two functions described above,
in that the number of the interrupt to the call is not passed. As the names suggest,
they call DOS interrupt 21H through which most DOS functions can be accessed.
intdos
Only the addresses of the input and the output variables representing the processor
registers are passed to the intdos function:
intdos(InRegister, OutRegister);
intdosx
The intdosx function, like the int86x function, has an additional parameter for the
segment register. The function call is as follows:
intdosx(InRegister, OutRegister, SegRegister);
So far you've seen how to call an interrupt from C and how to set the registers.
You also have to determine the address of a variable.
In C, you can easily determine the address of a variable. To do this, use the address
operator &, which returns the offset address of any desired variable. Use the
SEGREAD function mentioned above to determine the segment address of a
variable. The address of a variable of the SREG type is passed to the function
(using the address operator &) into which the content of the segment register can
be copied.
If, for example, the address of the variable SegRegister is passed to the function
and the variable was previously defined by the command:
union SREG SegRegister;
Then the variable SegRegister.ds contains the segment address of the variable
SegRegister, after calling the SEGREAD function.
While C supports interrupt calls with numerous functions, the library of the
Microsoft C compiler library does not have a function to return the contents of a
memory location. Since such a function could be very valuable in some programs,
the assembler program below contains the PEEKB and POKEB functions for
inclusion in programs created with the Microsoft C compiler. PEEK returns the
contents of a memory location (one byte), while the POKE function writes a one
byte value into a memory location.
Note: If you use the Borland Turbo C compiler, you won't need to use this
program since the Turbo C library already contains the PEEK,
PEEKB, POKE and POKEB functions. Because of this, linking the
assembler program into the C example programs of this book is
42
Abacus 43 Interrupt Calis from C
If you are using the Microsoft C compiler, enter the following program with a text
editor and save it under the name PEPO.ASM. It can then be assembled with:
masm pepo;
BSS segment word public 'sss' ;this segment accepts all non
BSS ends ;initialized static variables
PeekB endp
43
4. Using Interrllpts from High Level LanglUlges PC System Programming
_PokeB endp
i---------------------------------------------------------------------
text ends ;End of the program segment
The example program below uses the two functions described above. This next
program examines the model identification number or code of the PC and displays
PC type on the screen using a ooS function:
/*******.***** ••• *****.************************.***********************/
1* I N T DOS *I
1*--------------------------------------------------------------------*/
1* Task an example of an interrupt call, outputs *1
1* a string through a DOS function on *I
1* the display screen *I
1*--------------------------------------------------------------------*1
1* Author MICHAEL TISCHER *I
1* developed : 08/30/87 *1
1* last update : 04/08/89 */
/*--------------------------------------------------------------------*/
1* (MICROSOFT C) */
1* Creation MSC INTDOSC *1
/* LINK INTDOSC PEPO; *1
1* Call INTDOSC *1
1*--------------------------------------------------------------------*1
1* (BCRIJ\ND TURBC C v2.0) *1
1* Creation through the RUN cO!lVTland in the menu •.. or... *1
1* tcc -K intdosc *1
1* Call intdosc *1
/.**** •• ******************** ••• ******************* ••• ************.*****/
void main()
44
Abacus 43 Interrupt Calls from C
I
intdos(&Register, &Register); /* Call DOS interrupt 21H */
I
The main function defines three CHAR pointers which point to the text for each
PC type. Each of them starts and ends with an "'n" character. This creates a blank
line before and after the text itself.
In the first instruction of the main program the AH register is loaded with the
DOS function number for string output on the screen. Then the model
identification byte is read from memory location FOOO:FFFE using the PEEKB
function. Depending on the value read, the offset address of the accompanying text
is transferred to the OX register where it is expected by the interrupt 21H function.
In addition to this offset address, the function also requires the segment address of
the text in the OS register. Since the compiler automatically sets this register, you
don't have to be concerned with the segment address. The last instruction of the
program calls the INTDOS function which in turn calls interrupt 21H with the
registers which were dermed earlier.
The file header states how it can be executed: If you are using the Microsoft C
computer, then it is important that you link the file with the previously assembled
PEPO program so that the new program contains the PEEKB and POKEB
functions. These can then be called from the C program.
45
Chapter 5
Unlike programmers using any of the higher level languages, the assembly
language programmer doesn't have to rely on complicated functions or procedures
to call an interrupt. The MOV instruction loads the input parameters into the
registers provided, and the !NT instruction calls the interrupt.
Certain interrupts, or the functions hidden behind these interrupts, are called
frequently in many programs. An example of this is interrupt 21H function 9,
which displays text on the screen. You call it by placing function number 9 in the
AH register and the offset address of the text you want displayed in the DX
register. This process looks like this in assembly language:
mov ah,9 ;load function number 9
mov dx,offset Text ;load offset address of text
int 21h ;call DOS interrupt 21h
Even if you call the function very frequently, it doesn't pay to write a subroutine
for it since the address of the text to be displayed must be passed. All that remains
is to load the value 9 into the AH register and to call the interrupt. You'll fmd the
three program lines described above included for every function call in a program in
this chapter.
47
5. Using Interrupts from Assembly Language PC System Programming
The first line declares the macro name (PRINT). In this case, the macro also has
one parameter (string). The assembly language instructions follow in successive
lines until the ENDM instruction terminates the macro.
int 21h
48
Abacus 5.2 A Sample Mocro
Data segment
Text db CR, LF, "This is how MACROS are used", CR, LF, TEND
Data ends
dw 64 dup (7)
stack ends
rnov ds,ax
49
5. Using Inlerrupts from Assembly Language PC System Programming
Program ends
end Start ;beqin with START
After you enter the source program, it can be assembled, linked and executed as
indicated in the header.
Most of the lines in this listing have nothing to do with the actual program but
are definitions and declarations for the assembler.
The macro and constants are defmed in the fIrst part of the program, which helps to
make the listing more understandable to the reader. The definition of the data
segment follows, where the string to be displayed is stored as a character string. It
is preceded and followed by a carriage return and a linefeed to display a blank line
before and after the actual text. The text ends with the character "$" (the DOS
function used for text display always looks for this as the last character in a
string).
Following the data segment is the stack segment, which controls the stack during
program execution. Since the program is not very large, the stack can be fairly
small. The last segment is the code segment which contains the program
instructions. It consists of only fIve commands: The first two instructions
initialize the program. They load the segment address of the data segment into the
DS register to provide access to the text in this segment Then the macro PRINT
is called, and the text is passed to it.
Note: You may fmd it useful to group together certain macros into a me or
library. When one of these macros will be used in a program, the
library may be linked or included with the assembly language code.
50
Chapter 6
The following chapter discusses the PC's operating system, which the PC loads
from floppy diskette or hard disk. It is commonly referred to as PC-DOS, MS
DOS or just DOS.
What is DOS?
Most users only know the user interface of DOS, with which you run programs,
format disks, etc. In the following sections, however, you'll view DOS from an
angle you may not have known existed.
Beneath the surface of DOS many processes takes place. DOS uses a large number
of different routines (called/unctions) to accomplish its tasks. These functions are
available to the user as well as to DOS. The main focus is on how these functions
can be used in practical applications.
The data structures which act as the connecting link between the different DOS
functions will also be examined in this chapter. These data structures make mass
storage devices such as floppy disks and a hard disk possible.
Finally, this chapter discusses each DOS function in detail, and includes a brief
look at DOS Version 4.0.
51
6. The Disk Operating System PC System Programming
In April 1980 the CP/M-86 operating system announced by Digital Research for
use on the 8086 processor was unavailable. A programmer named Tim Paterson
began developing a new operating system. This system is the ancestor of the
current MS-DOS.
At this time a lot of software was available for CP/M-80 systems. The
development of new software for an 8086 operating system would have required
enormous expenses and effort. Paterson's goal was to allow easy conversion of
existing software from CP/M-80 to the new operating system. He tried to include
the functions and the most important data structures of the CP/M-80 operating
system, while removing the weak points of CP/M-80. The fmished product was an
operating system that required only 6K of memory. Programs developed for CP/M
80 could also be converted with little effort to the 8086. The new system was
named 86-DOS.
Many changes have been made to DOS since 1981. Because these changes are of
great significance to the DOS programmer, this chapter contains a segment for
each major version of DOS. Each segment lists changes from preceding versions
with explanations. Many components of DOS are explained here, which will give
you some idea of the complexity of an operating system.
Version 1.0
This version represented a compromise for Microsoft They had relied heavily on
CP/M-80 and needed to transfer existing programs quickly and easily. This can be
seen in the fact that the file names (eight-character filename, three-character
extension) was identical with CP/M-80. Also, the designation of the disk drives
and the internal structure had many similarities to the successful 8-bit operating
system.
52
Abacus 6.1 A Short History of DOS
During this time many improvements and enhancements of the hardware occurred,
such as more RAM and faster disk drives. Microsoft decided to make DOS more
hardware independent by removing the association between physical file length and
logical file length.
In CP/M-80 every disk was divided into 128-byte units which could only be
accessed as a whole. This is why you couldn't access individual bytes on the disk
(this created a programming problem that shouldn't have existed in the frrst place).
DOS solved this problem by making the logical and physical data length
independent of one another. In addition, functions were implemented to permit
reading or writing of more than one data set of a file on a disk. Treating the input
and output devices like files achieved hardware independence. These input and
output devices were assigned their own names:
If you used one of these three names instead of a filename to access a file with a
DOS routine, then the computer addressed the corresponding device and not the
disk drive. This also permitted redirecting input and output from the keyboard or
screen to a file or other device.
Before this time, DOS only supported program files which loaded and executed
from a fixed location in memory. This proved to be impractical, and so Version
1.0 introduced a new program file type. This new file type had a file extension of
.EXE instead of .COM. An .EXE file could be stored and executed from almost
any memory location.
Two changes were made to the command processor, the part of the operating
system which accepts commands from the user and controls the execution of these
commands. The first change was to store the command processor in a separate file
named COMMAND.COM. This allowed programmers to develop a customized
command processor and link it to the system.
The second change was to divide the command processor into a resident and a
transient portion. This approach was taken because early PC systems contained
only a small amount of memory. The resident portion was written to be as small
as possible. Many DOS commands were stored on disk and loaded and run only
when required, hence the name transient. Examples of transient commands are
DISKCOPY and FORMAT.
A major innovation that took MS-DOS Version 1.0 beyond CP/M-80 was the
introduction of the FAT (file allocation table) on disk. Every entry in this table
corresponds to a data area of 512 bytes (called a sector) on the disk. The FAT
indicates whether the sector is allocated to a file or is still available.
S3
6. The Disk Operating System PC System Programming
The FAT has special significance in connection with the directory entry which
exists for every file type. Besides the filename and other information, it also
indicates the number of an entry in the FAT which corresponds with the fIrst
sector of a file on the disk. This FAT entry points to another FAT entry which
indicates the next sector which was allocated to the file. The other FAT entries on
a disk perform the same task.
The introduction of batch processing offers the user the option of placing several
DOS commands into one file. When you "run" this file (which has a fIle extension
of .BA1'), DOS executes the individual commands from this fIle as if you had
entered the commands from the keyboard, thus saving the user time in entering
frequently used groups of commands repeatedly.
The current date and time follows every filename. DOS includes this data to help
the user determine the last time a file was modified.
When IBM introduced a new PC in 1982 which used both sides of a disk for data
storage, Microsoft released DOS Version 1.1.
Version 2.0
IBM announced a new personal computer in March of 1983, called the PC XT,
which in addition to the floppy disk drive also had a hard disk (also called afued
disk). The enormous capacity of this hard disk (10 megabytes) allowed the user to
store several hundred files on one unit, but created some problems for the operating
system. The largest problem was that DOS could only handle one directory for
each storage unit. It would be nearly impossible for the hard disk user to maintain
hundreds of fIles in a single directory. Microsoft had two options to solve this
problem: They could either borrow an idea from the CP/M-SO operating system, or
from the UNIX operating system.
CP/M views a hard disk as several individual disk drives which share the total
storage on the hard disk, each with only one directory.
UNIX uses a hierarchical file system, in which every storage unit has a root
directory which can contain subdirectories as well as files. Every one of these
subdirectories can have subdirectories within them. This creates a directory tree
whose trunk is the root directory and whose branches are represented by the
individual subdirectories.
Microsoft chose the hierarchical file system, which has since become a popular
component of DOS. This· was another step away from CP/M-80 toward an
efficient 16-bit operating system. With the introduction of an hierarchical fIle
system some major changes had to be made in the area of fIle control by DOS.
Before this time, fIle access was conducted through afile control block or FCB.
54
Abacus 6.1 A Short History of DOS
This file control block had been introduced for compatibility with CP/M-80. The
FCB contained important information about the name, size and location of a file
on disk. This CP/M would not allow access to a file in another directory.
The DOS developers standardized file access through DOS functions. The access to
a file occurs exclusively through the file handles. A handle is a numerical value
passed to the program as soon as it opens a file through a DOS function. The
FCBs were not eliminated, but the programmer no longer came in contact with
them since DOS took over the control block manipulation.
Version 2.0 added the option of formatting the individual tracks of a disk with nine
sectors instead of eight. This increased the storage capacity of a single-sided disk
from 160K to 180K, and the capacity of a double-sided disk from 320K to 360K.
Version 3.0
Version 3.0, like Version 2.0, was developed for a new PC, the IBM PC AT. It
was released in August of 1984 and supported the 20 megabyte hard disk of the
ATs as well as the high capacity 1.2 megabyte floppy disk drive. Many changes
occurred in DOS's internal routines. They contributed to faster execution of certain
operations, but are transparent to the programmer.
Version 4.0
DOS 4.0 appeared on the market in August 1988. Before this, Microsoft released a
new multiprocessing operating system called OS/2. Before OS/2, multiprocessing
was unknown to MS-DOS.
The user can easily seethe changes to DOS 4.0 over earlier versions of DOS. In
place of the line-Oriented command line interpreter used by DOS versions 3.3 and
earlier, DOS 4.0 has a Shell allowing user-defined menus, easy selection of
applications, files and directories from both mouse and keyboard.
Most important are the unseen changes made to DOS, particularly in adapting the
operating system to the new hardware standards on the market. As the operating
system has grown in power, it has also grown in complexity and memory use. For
example, earlier versions of DOS were limited to "only" 640K of RAM and a 32
megabyte hard disk. However, DOS 4.0 handles the Expanded Memory System
(EMS) following the LIM standard, normal RAM capacity of up to 8 megabytes,
and hard disks up to 2 gigabytes (2048 megabytes) capacity.
55
6. The Disk Operating System PC System Programming
DOS-BIOS
Do not confuse the device drivers in this module with the installable device drivers.
The DOS-BIOS device drivers cannot be changed by the user.
DOS kernel
Command processor
Unlike the two modules described above, the command processor is contained in
the file named COMMAND. COM. It displays the "A">" or "C>" prompt on the
screen, accepts user input and controls input execution. Many users wrongly think
that the command processor is actually the operating system. In reality it is only a
special program which executes under DOS control.
S6
Abacus 6.2 Internal Structure of DOS
The transient portion contains code for displaying the (A» prompt, reading user
input from the keyboard and executing the input. The name of this module is
derived from the fact that the RAM memory where it is located is unprotected, and
can be overwritten under certain circumstances. When a program ends, control
returns to the resident portion of the command processor. It executes a checksum
program to determine whether the transient portion was overwritten by the applica
tion program. If so, the resident portion reloads the transient portion.
The initialization portion loads during the booting process and initializes DOS.
This part of the command processor will be examined in detail in the next chapter.
When its job ends, it is no longer needed and the RAM memory it occupies can be
overwritten by another program. The commands accepted by the transient portion
of the command processor can be divided into three groups: internal commands,
external commands and batch files.
Internal commands lie in the resident portion of the command processor. COPY,
RENAME and DIR are internal commands.
External commands must be loaded into memory from diskette or hard disk as
needed. FORMAT and CHKDSK are external commands.
After execution the command processor releases the memory used by these
programs. This memory can then be used for other purposes.
Batch files
A batch file is a text file containing a series of DOS commands. When a batch file
is started, a special interpreter in the transient portion of the command processor
executes the batch file commands. Execution of batch file commands is the same
as if the user entered them from the keyboard. An important batch file is the
AUTOEXEC.BAT file which executes immediately after DOS is flfSt loaded.
Like all commands of a batch file, these commands are checked for internal
commands, external commands or calls to other batch files. If the ftrst is true, the
57
6. The Disk Operating System PC System Programming
command executes immediately, since the code is already in memory (in the
transient part of the command processor). If it is an external command or another
batch file, the system searches the current directory for the command. If such a file
doesn't exist in this directory, all directories specified in the PATII command are
searched in sequence. During the search, only files with the .COM•.EXE or .BAT
extensions are examined.
Since the command processor cannot search for all three extensions at the same
time, it first searches for files with .COM extensions, then for .EXE files and
finally for .BAT files. If the search is unsuccessful, the screen displays an error
message and the system waits for new input.
58
Abacus 63 Booting DOS
First the PC checks for a disk in the floppy disk drive. If a disk exists in the
floppy disk drive, the PC checks the disk for the boot sector. If a disk is not in the
drive, the PC searches for a hard disk from which to boot DOS. If no hard disk
exists, the PC displays an error message asking the user to insert a system disk.
The ftrst sector on a bootable floppy disk or hard disk is called the boot sector. The
program in the boot sector is read into memory and executes. First it checks for
the presence of two files: IBMBIO.COM (sometimes called IO.SYS) and
IBMDOS.COM (sometimes called MSooS.SYS). A bootable floppy disk or hard
disk must contain these two files or an error message is displayed. Next these
program fIles are loaded into memory.
The program file IBMBIO.COM consists of two modules. The ftrst contains the
basic device drivers-keyboard, display and disk. The second contains the
initialization sequence for DOS. When the IBMBIO.COM program executes it
continues to initialize the system by moving the DOS kemal (loaded in the
IBMooS.COM program fIle) to the last available memory location.
The DOS kemal builds several important tables and data areas, and performs
initialization procedures for individual device drivers which were loaded with the
IBMBIO.COM program fIle.
Next, DOS searches the boot disk for a file named CONFIG.SYS. If found, the
commands contained in the me are executed. These commands add device drivers to
DOS, allocate disk buffers and file control blocks for DOS and initialize the
standard input and output devices.
Lastly the command processor COMMAND.COM (or other shell specifted in the
CONFIG.SYS fIle) is loaded and control is passed to it. The booting process ends
and the initialization routines remain as "garbage" data in memory until
overwritten by another program.
S9
This section describes the structure and functions of these last two program types.
One difference between COM and EXE program files is in the size limitation for
each type of program. A COM program cannot exceed 64K in size. An EXE
program can be as large as the memory capacity available to DOS.
In a COM program, the program code, data and stack are stored in one 64K
partition. All of the segment registers are set at the start of the program and remain
fixed for the duration of the program execution. They point to the start of the 64K
memory segment. The contents of the ES register may be changed however, since
it has no direct effect on program execution.
In an EXE program, the code, data and stack may be stored in different segments,
and depending on program size, may be distributed over severnl segments.
While a COM program file is stored on disk as an image copy of RAM memory,
an EXE program file is stored in a special format that will be described shortly.
EXEC
Both program types can be loaded and started using the DOS EXEC function. Any
user can access this, but the command processor uses it for executing external
commands. Before the EXEC function loads the program into memory, it reserves
the RAM memory to hold the program. At the beginning of this memory the
EXEC function stores a PSP (program segment prefIX) data structure. The program
is then loaded immediately following the PSP. The segment registers and the stack
are initialized and the program is given control. Later, when the program ends, the
memory is released based on the contents of the PSP.
60
Abacus 6.4 COM and EXE Programs
+OEH
+ 12H
Copy of interrupt
vector 23H
Copy of interrupt
vector 24H
(2 words)
(2 words)
1
+ 16H reserved (22 bytes)
+ 2CH Segment address of (1 word)
environment block
in command line
The PSP itself is always 256 bytes long and contains information important for
DOS and the program to be executed.
Memory location OOH of the PSP contains a DOS function call to terminate a
program. This function releases program memory and returns control to the
command processor or the calling program. Memory location OSH of the PSP
contains a DOS function call to interrupt 21H. Neither of these are used by DOS,
but are leftovers from the CP/M system.
Memory location 02H of the PSP contains the segment address to the end of the
program. Memory location OAH contains the previous contents of the program
termination interrupt vector. Memory location OEH contains the previous contents
of the <Ctrl><C> or <Ctrl><Break> interrupt vector. Memory location 12H
contains the previous contents of the critical error interrupt vector. For each of
these memory locations, the program changes one of the corresponding vectors
during execution; DOS can use the original vector in the event that it detects an
error.
Location 2CH contains the segment address of the environment block. The
environment block contains information such as the current search path and the
directory in which the COMMAND.COM command processor is located on disk.
61
6. The Disk Operating System PC System Programming
Memory locations 5CH through 6CH contain a file control block. This FCB is
not often used by DOS since it does not support hierarchical files (paths) and is
also left over from CP/M.
The string of parameters that are entered on the command line following the
program name is called the command tail. The command tail is copied to the
parameter buffer in the PSP beginning at memory location 81H and its length is
stored at memory location 80H. Any redirection parameters are eliminated from the
command tail as it is copied to the parameter buffer. The program can examine the
parameters in the parameter buffer to direct its execution.
The parameter buffer is also used by DOS as a disk transfer area (DTA) for
transmitting data between the disk drive and memory. Most OOS programs do not
use the DTA contained in the PSP because it is another leftover from CP/M.
SS:OOOO
DS:OOOO ES:OOOO
ES:OOOO DS:OOOO
PSP (256 BYTES) PSP (256 BYTES)
cs:oooo
CS:IP
Code
Code, data
and stack in (Address defined
one 64K segment by the END
CS:IP ...... command in an
assembler
SS:SP - Stack adjusts
to the direction
program)
COM program files are stored on disk as an image copy of memory. Because of
this, no further processing is required during loading. Therefore COM programs
load faster and start execution faster than EXE programs.
A COM program loads immediately following the PSP. Execution then begins at
the ftrst memory location following the PSP at offset lOOH. For this reason, a
COM program must begin with an executable instruction, even it if is only a
jump instruction to the actual start of the program.
62
Abacus 6.4 COM and EXE Programs
When control is turned over to the COM program, all segment registers point to
the beginning of the PSP. Because of this, the beginning of the COM program
(relative to the beginning of the PSP) is always at address lOOH. The stack pointer
points to the end of the 64K memory segment containing the COM program
(usually FFFEH). During every subroutine call within the COM program, the
stack is adjusted by 2 bytes in the direction towards the end of the program. The
programmer is responsible for preventing the stack from growing and overwriting
the program, which would cause it to crash.
There are several ways to end a COM program and return control to DOS or the
calling program:
If the program runs under DOS Version 1.0, it can be terminated by calling
interrupt 2lH function 0, or by calling interrupt 20H. It can also be terminated by
using the RET (RETurn) assembler instruction. When this instruction executes,
the program continues at the address which is at the top of the stack. Since the
EXEC function stored the value 0 at this location before turning control over to
the COM program, program execution continues at location CS:O (the start of the
PSP). Recall that this location contains the call for interrupt 20H which
terminates the program.
Programs that run on versions later than DOS Version 1.0, are terminated using
interrupt 2lH function 4CH. The terminating program can pass a numeric return
code to the calling program. For example, a value of 0 may indicate that the
program executed successfully, while a non-zero value indicates an error during
execution.
Next we'll talk about a few of the details that the assembly language programmer
will have to take care of in developing a COM program. Note that the high level
language programmer is usually insulated from these details by the compiler or
interpreter, so you may want to skip ahead.
A COM program is limited to a 64K size. The code and data for the program must
be contained within a single segment and addressed through NEAR procedures.
Therefore an assembly language program that is to become a COM program may
not contain any FAR procedures.
63
6. The Disk Operating System PC System Programming
Before calling a COM program, DOS reserves all available memory for the
program even though it normally uses only one 64K segment and indicates this by
setting memory location 2 in the PSP. Usually the program terminates and the
memory is made available to DOS again.
In other circumstances you may want to execute another program from this COM
program using the EXEC function. Again, since DOS thinks that memory is
unavailable, it won't allow the new program to run.
There are two approaches in doing this: release only the memory outside of the
64K COM segment or release memory outside of the 64K COM segment plus any
unused memory within the 64K COM segment. This creates more memory for
other programs, but relocates the stack outside the protected COM segment
memory, leaving it open to be overwritten by other programs. Because of this, the
stack must be relocated to the end of the code segment before releasing the
memory. The stack must have a certain limit in size (in most cases 512 bytes will
be more than enough).
The following sample program can serve as an example for developing a COM
program. A small (init) routine relocates the stack to the end of the code segment
after the start of the program and releases all remaining memory. Even when this
program loads another program, itremains resident. This routine can be useful to
applications, and can be part of any COM program.
itestcorn.asrn
code segment para 'CODE' ;Definition of CODE-segments
64
Abacus 6.4 COM and EXE Programs
First you must assemble the source program using the assembler. In the following
example, we are using the Microsoft assembler. Following assembly, you then
link the object code using the LINK program. When you execute the LINK
program, the following message appears:
Warning: no stack segment
You can disregard this message. If the program contains no errors, the LINK
program creates an EXE file. Since you want a COM program and not an EXE
program developed, you must run the EXE2BIN program as the last step. This
converts EXE programs into COM programs. Here are the steps for preparing an
assembly language program using the Microsoft assembler. The program to
assemble is named TESTCOM.ASM.
masrn testcom:
link testcom;
If all steps were carried out correctly, the program TESTCOM.COM can be
executed from DOS by simply typing TESTCOM.
6S
6. The Disk Operating System PC System Programming
EXE programs have an advantage over COM programs because they are not
limited to a maximum length of 64K for code, data and stack. The disadvantage of
this is the greater complexity of these files. This means that in addition to the
program itself, other information must be stored in an EXE file.
EXE programs contain separate segments for code, data and stack which can be
organized in any sequence. Unlike a COM program, an EXE program loads into
memory from disk and undergoes processing by the EXEC function and then
finally begins execution. This is necessary because of the limitations already
described for COM programs.
EXE programs aren't limited to loading at a fixed memory location, but to any
desired location in memory that's a multiple of 16. Since an EXE program can
have several segments, this requires the use of FAR machine language
instructions. For example, a main program can be in one segment and call a
subroutine in another segment. The segment address must be provided for this
FAR instruction in addition to the offset for the routine to be called. The problem
is that the segment address may be different for every execution of the program.
COM files avoid this problem since the program size is limited to 64K, which
makes the use of FAR commands unnecessary. EXE programs solve this problem
in a more complex way: the LINK program places a data structure at the beginning
of every EXE file which contains the addresses of all segments, among other
things. It contains the addresses of all memory locations in which the segment
address of a certain segment is stored during program execution.
If the EXEC function loads the EXE program, it knows the addresses where the
various segments should be loaded. It can therefore enter these values into the
memory locations at the beginning of the EXE file. Because of this, more time
elapses between the initial program call and when the program actually begins
execution than for a COM program. The EXE program also occupies more
memory than a COM program. The following illustration shows the structure of
the header for an EXE file.
66
Abacus 6.4 COM and EXE Programs
After the segment references within the EXE program have been resolved to the
current addresses, the EXEC function sets the DS and the ES segment register to
the beginning of the PSP which also precedes all EXE programs in memory.
Because of this, the EXE program can access the information contained in the
PSP, such as the address of the environment block and the parameters contained in
the command line (command tail). The stack address and the contents of the stack
pointer are stored in the EXE file header and accessed from there. This also applies
to the code segment address containing the first instructions of the program, and
the program counter. After the values have been assigned, the program execution
starts.
Of course, memory must be available for the EXE program. The EXE loader
determines the total program size based on the size of the individual segments of
the EXE program. Then it can allocate this amount of memory and some
additional memory immediately following the EXE program. The first two fields
of the EXE program file header contain the minimum and maximum size of
memory required in paragraphs (1-6 bytes).
First, the EXE loader tries to reserve the maximum number of paragraphs. If this
is not possible the loader tries to reserve the remaining memory which may be no
smaller than the minimum number of paragraphs. These fields are determined by
the compiler or assembler, llil1 the linker. The minimum is 0 and the maximum
67
6. The Disk Operating System PC System Programming
allowed is FFFFH. This last number is unrealistic in most cases (it adds up to 1
megabyte) but reserves the entire memory for the EXE program.
This brings us back to the same problem as in COM programs. EXE files make
poor resident programs, but an EXE program may need to call another program
during execution. This is possible only by fIrst releasing the additional reserved
memory. The following program below contains a routine which reduces the
reserved memory to a minimum.
The program uses separate code, data and stack segments. It can serve as a model
for other EXE programs that you can write.
; testexe.asm
i== stack ============================================_ac============-
68
Abacus 6.4 COM and EKE Programs
Here are the individual steps for preparing an EXE program from the assembly
language source named 1ES1EXE.ASM.
masm testexe;
link testexe;
If all these steps were executed correctly, the program TESTEXE.EXE can be
started from the DOS level by typing TESTEXE.
69
6. The Disk Operating System PC System Programming
The functions can be divided into two types: those carried over from the CP/M
operating system and those borrowed from the UNIX operating system. While the
two types of functions can be intermixed, we recommend that you use one type of
function throughout a program for the sake of consistency.
The handle functions perform file access as well as character input to or output
from a device. DOS recognizes the difference by examining the name assigned by
the handle. If the handle is a device name, it addresses the device; otherwise it
assumes that file access should occur. The device names are as follows:
Output and input go to and from the AUX, PRN and NUL devices. For the device
CON, output is sent to the screen and input is read from the keyboard.
When DOS passes control to a program, five handles are available for access to
individual devices. These handles have values from 0 to 4 and represent the
following devices:
70
Abacus 65 Character Input and Output from DOS
Before discussing these devices, here are some functions used to access any device.
Function 40H of interrupt 21H sends data to a device. The function number (40H)
is passed in the AH register and the handle is passed in the BX register. For
example, to display an error message, the value 2 indicates the handle for
displaying the error message (this device cannot be redirected, so handle 2 always
addresses the console). The number of characters to be in the error message is
passed in the ex register. The characters making up the message are stored
sequentially in memory whose segment address is stored in the DS register and
offset address in the DX register.
Following the call to the function, the carry flag signals any error. If there was no
error, the carry flag is reset and the AX register contains the number of characters
that were displayed. If the AX register contains the value 0, then there was no
more space available on the storage medium for the message. If the carry flag is
set, the error message was not sent and an error code is indicated in the AX
register. An error code of 5 indicates that the device was not available. An error
code of 6 indicates that the handle was not opened.
Function 3FH of interrupt 21H reads character data from a device and has many
similarities to the previous function. Both functions have identical register usage.
The function number is passed in the AX register and the handle in the BX
register. The number of characters read is passed in the ex register and the
memory address of the characters transferred are passed in the DS:DX register pair.
Following the call to the function, the carry flag also signals any error. Again, any
error code is passed in the AX register. Error codes 5 and 6 have the same meaning
as when using function 40H. If the carry flag is reset, then the function executed
successfully. The AX register then contains the number of characters read into the
buffer. A value of 0 in the AX register means that the data to be read should have
come from a file, but that this file contains no more data.
71
6. The Disk Operating System PC System Programming
The function number 3DH is passed in the AH registel'. The AL register contains 0
to enable reading from the device, 1 to enable writing to the device and 2 for both
reading and writing to the device. The name of the device is placed in memory
whose address is passed in the DS:DX register pair. So that the DOS can properly
identify the device name, the names must be specified in uppercase characters. The
last character of the string must be an end character (ASCII value 0).
Following the function calls the status is indicated by the carry flag. A reset flag
means that the device was opened successfully and the handle number is passed
back in the AX register. A set flag indicates an error and the AX register contains
any error code.
The handle is closed using function 3EH of interrupt 21H. The function number is
passed in the AH register and the handle number is passed in the BX register. The
carry flag again indicates the status of the function call. A set carry flag indicates
an error.
You can also close the predefined handles 0 through 4 using this function. But if
you close handle 0 (the standard input device) you'll no longer be able to accept
input from the keyboard
Keyboard
The keyboard can perform only read operations. The results of the read operations
depend on the mode in which the device was addressed. Here DOS differentiates
between raw and cooked. In the cooked mode OOS checks every character sent to a
device or received from a device to see if it is a special control character. If DOS
finds a special control character, it performs a certain action in response to the
character. In raw mode the individual characters are passed through unchecked and
unmanipulated. DOS normally operates the device in cooked mode for character
input and output. However, you can switch to raw mode within a program (see
below).
The difference between cooked and raw mode can be best explained by an example
of reading the keyboard. Assume that 30 characters are read from the keyboard in
cooked mode. As you enter the characters DOS allows you to edit the input using
several of the control keys. For example <Ctrl><C> and <Ctrl><Break> abort the
input. <Ctrl><S> temporarily halts the program until another key is pressed.
<Ctrl><P> directs subsequent data from the screen to the printer (until <Ctrl><P>
is pressed again). <Backspace> removes the last character from the DOS buffer. If
the <Enter> key is pressed, the first 30 characters (or all characters input up to
now if there are less than 30) are copied from the DOS buffer into the input buffer
of the program without the control characters.
In raw mode all characters entered (including control characters) are passed to the
calling program without requiring the user to press the <Enter> key. Mter exactly
72
Abacus 6.5 Character Input and Output from DOS
30 characters, control passes to the calling program, even if you pressed the
<Enter> key as the second character of the input.
Screen
To display characters on the screen, handle 1 is usually addressed as the standard
output device. Since this device can be redirected, output through this handle can
pass to devices other than the screen. On the other hand, you cannot redirect the
standard error output device (handle 2), so error messages that pass through this
handle always appears on the screen. This handle is recommended for character
display on the screen ~.
Printer
Unlike the keyboard and screen, printer output cannot be redirected-at least not
from the user level. An exception to this rule is redirecting output from a parallel
printer to a serial printer. Characters ready to print can be sent to a buffer before
they are sent to the printer. Handle 4 is used to address the standard printer. There
are three standard printer devices LPTl, LPT2 and LPT3. Device PRN is
synonymous with LPTI. When this handle is opened the device name is specified
as one of the three: LPTl, LPT2 or LPTI.
Serial interface
Much of the information that applies to the printer also applies to the serial
interface. For example, serial input and output cannot be redirected to another
device (e.g., from a serial printer to a parallel printer). The programmer can use the
predefined handle 3 for serial access, through which you can address the standard
serial interface (AUX).
Handle 3 is used to address the standard serial device. The two are names COMl
and COM2. A PC can have multiple serial interfaces. Only the first two (COMl
and COM2) are supported by DOS. Since the system doesn't know exactly which
interface to access during AUX device access, you should open a new handle for
access to the specific device.
Errors during read operations in DOS mode are returned to the serial interface in
cooked mode. The number returned to the AX register will not match the number
of characters actually read. We recommend that you operate the serial interface in
the raw mode, even if this mode ignores control characters such as <Ctrl><C> and
EOF (end-of-fIle).
73
6. The Disk OperaJing System PC System Programming
The DOS functions for input and output aren't based on the handle oriented
functions. If you use these functions you won't need to specify a handle, since
each function pertains to a specific device.
Below are the various input and output devices and the way in which these
functions work with them.
Keyboard
There are seven DOS functions for addressing the keyboard but they differ in many
ways. For example, they respond differently to the <Ctrl> <Break> key. Some
functions echo the characters on the screen; others don't.
You can use DOS functions OIH, 06H, 07H and OSH to read a single keyboard
character. The function number is passed in the AH register. Following the call,
the character is returned in the AL register.
For DOS function OIH, DOS waits for a keypress if the keyboard buffer is empty.
When this happens, the character is echoed on the screen. If the keyboard buffer is
not empty, a new character is fetched and returned to the calling program. DOS
function 06H can be used for both character input and output. To input a character
a value of FFH is loaded into the DL register. This function doesn't wait for a
character to be input but returns immediately to the calling program. If the zero
flag is set, a character was not read. If the zero flag is reset, a character was read and
returned in the AL register. The character is not echoed on the screen.
DOS functions 07H and 08H are used to read the keyboard similar to function 1.
Both either fetch a character from the keyboard buffer or wait for a character to be
entered at the keyboard. Neither echo the character to the screen. They differ in that
function OSH responds to <Ctrl><C> and function 07H does not.
By using function OBH, a program can determine whether one or more characters
are in the keyboard buffer before calling any functions that read characters. After
calling this function, the AL register contains 0 if the keyboard buffer is empty,
and FFH if the keyboard buffer is not empty.
DOS function OCH is used to clear the keyboard buffer. After it is cleared, the
function whose number was passed to function OCH in the AL registered is
automatically called.
DOS function OAH is used to read a string of characters. Again this function
number is passed in the AH register. In addition, the memory address of a buffer
for the character string is passed in the DS:DX register pair. This buffer is used to
hold the character string. The first byte of the buffer indicates the maximum
number of characters that may be contained in the buffer.
74
Abacus 65 Chmacter Inplll tmd Outplll from DOS
When this function is called, OOS reads up to the maximum number of characters
and stores them in the buffer starting at the third byte. It reads until either the
maximum number of characters is entered or the <Enter> key is pressed. The
actual number of characters is stored in the second byte of the buffer. Extended key
codes which occupy two bytes each in the buffer may be entered. The fIrst byte of
the pair (ASCII value 0) signifies that an extended key code follows. This means,
for example, that for a maximum buffer size of 10 bytes, only five extended
characters may be entered.
The following table illustrates how the various functions respond to <CtrlxC>
or <Ctrb<Break>, and provides a quick overview of the individual functions for
character input
Pet. Task <Ctrl><C> Echo
01H Character input yes I yes
06H direct character input ro ro
07H Character in~ut ro ro
OBH Character input yes ro
OAll Character string input Yf!s ro
OBH Read input-status yes no
OCH Reset input-buffer then input varies varies
Screen output
DOS function 02H outputs a single character to the screen or standard output
device. The character is passed to the DL register.
DOS function 09H is used for string output. Again, the function number is passed
in the AH register. The address of the string is passed in the DS:DX register pair.
The last character of the string is a dollar sign. In addition, the following control
codes are recognized.
Code Operation
7 "Bell", rinqs the bell on the PC
B "Backspace", erases the preceding character and moves the cursor
back by one character
10 ULine Feed 11 , (LF) moves the cursor one line down
13 "Carriage Return", (CR) moves the cursor to the beginning of the
current line
75
6. The Disk Operating System PC System Programming
Printer
DOS function 05H is used to output a single character to the printer. If the printer
is busy, this function waits until it is ready before returning control to the calling
program. During this time, it will respond to the <Ctrl><C> and <Ctrl><Break>
keys.
Serial interface
There are two DOS functions for communicating using serial interface-one for
input and one for output. Both functions respond to <Ctrl><C> and
<Ctrl><Break>, but they don't return the status of the serial interface, nor do they
recognize transmission errors.
DOS function 03H is used to input data from the serial interface. The character is
returned in the AL register. Since the data is not buffered, the data can overrun the
interface if the interface receives data faster than this function can handle it
DOS function 04H is used to output data over the serial interface. The character to
output is passed in the DL register. If the serial interface is not ready to accept the
data, this function waits until it is free.
Again, most programmers prefer to use the BIOS equivalent functions (see Section
7.9) to perform serial data transmission because of their more complete data
handling capabilities.
Demonstration programs
Earlier we mentioned that it was possible to switch a device from cooked mode to
raw mode and back. The BASIC, Pascal and C programs that follow show you
how to do this. They use the IOCTL functions which permit access to the DOS
device drivers (see Section 6.11.7 for details on this routine). These are routines
which serve as interfaces between the DOS input/output functions and the
hardware. The IOCTL functions in these programs tell the CON device driver
(responsible for the keyboard and the display) whether it should operate in the
cooked mode or in the raw mode.
To demonstrate how differently characters respond in the two modes, the programs
switch the CON driver into raw mode fIrst. Then this driver displays a sample
string several times. Unlike cooked mode, pressing <Ctrl><C> or <Ctrl><S> in
raw mode has no effect on stopping program execution or text display.
76
Abacus 65 Character Input and Output from DOS
After the program finishes displaying the sample string, the driver switches to the
cooked mode. The sample string is displayed again several times. When you press
<Ctrl><C> the program stops (Turbo Pascal version). For the BASIC and C
versions, you can press <Ctrl><C> to stop the program, or press <Ctrl><S> to
pause or continue the display.
Switching between the raw and the cooked mode does not take place directly
through a function. First the device attribute of the driver is determined. This
attribute contains certain information which identifies the driver and describes its
method of operation. One bit in this word indicates if the driver operates in raw or
cooked mode. The programs set or reset this bit, depending on the mode you want
running the driver.
200 '
220 PRINT"WARNING: This program can only be started if the GWBASIC was"
240 PRINT : PRINT"If this is not the case, please input <s> for Stop.·
340 PRINT"Because of this not even <CTRL> + <S> can stop the "
400 CLS
425 PRINT"input/output."
77
6. The Disk Operating System PC System Programming
51060 '
51070 GOSUB 53000 'Get device attribute of driver
51080 ATTRIB% = ATTRIB% AND 223 'Find COOKED-Bit
51090 GOSUB 54000 'Set device attribute of driver
51100 RETURN 'back to caller
51110 '
52000 .******* •• ****************** •• ***** •••• *****.*.*******.****.***.
52010 ,* change device driver to RAW mode
52020 ,*-------------------------------------------------------------.'
52030 ,* Input: HANDLE% ~ handle connected to the driver
78
Abacus 6.5 Character Input and Output from DOS
53070 '.
determined
53080 ,****** •• ******.***************** •• ****.******.****.*.**********,
53090 '
53100 FCT%-&H44 'Function number for IOCTL
53110 FCTl%-O 'Read Function number for IOCTL: Read device attribute
53120 INR%=&H21 'Call DOS-Interrupt 21H
53130 HANDHH = INT(HANDLE%/256) 'HI-byte of the handle
53140 HANDLO% - HANDLE% AND 255 'LO-byte of the handle
53150 CALL IA(INR%,FCT%,FCT1%,HANDHI%,HANDLO%,Z%,Z%,Z%,ATTRIB%,Z%,Z%,Z%,Z%)
53160 RETURN 'back to caller
53170 '
54000 1*********.*****************************************************,
54010 '1*. _____________________________________________________________
Set device attribute of a driver *'
54020
..'
54030 ' . Input : HANDLE% - handle connected to a driver *,
54040 ,* ATTRIB% - the attribute of the driver
54050 Output: none
54060 Info Z% used as Dummy-Variable ,
54070 1**************************************************.*.*.********,
54080
54090 FCT%=&H44 'Function number for IOCTL
54100 FCT1%-1 'Set function number for IOCTL: device attribute
54110 INR%-&H21 'Call DOS-Interrupt 21(h)
54120 HANDHI% - INT(HANDLE%/256) 'HI-byte of the handle
54130 HANDLO% - HANDLE% AND 255 'LO-byte of the handle
54140 ATHH - INT (ATTRIB%/256) 'HI-byte of the Attribute
54150 ATLO% - ATTRIB% AND 255 'LO-byte of the Attribute
54160 CALL IA(INR%,FCT%,FCTl%,HANDHI%,HANDLO%,Z%,Z%,ATHI%,ATLO%,Z%,Z%,Z%,Z%)
54170 RETURN 'back to caller
54180
60000 ,*******************************.*******************************,
60010 '. Initialize the Routine for Interrupt call *,
.*____________________________________________________----_____ *1
.'.'
60020
60030 '* Input : none
60040 '* Output: IA is the Start address of the Interrupt-Routine
60050 1******** •• ******************* •• * •••• *****.*****.* •••• **********'
60060
60070 IA-60000! 'Start address of the routine in the BASIC-Segment
60080 DEF SEG 'Set BASIC-Segment
60090 RESTORE 60130
60100 FOR 1% - 0 TO 160 READ X% POKE IA+I%,X% NEXT 'Poke Routine
60110 RETURN 'back to caller
60120 '
60130 DATA 85,139,236, 30, 6,139,118, 30,139, 4,232,140, 0,139,118
60140 DATA 12,139, 60,139,118, 8,139, 4, 61,255,255,117, 2,140,216
60150 DATA 142,192,139,118, 28,138, 36,139,118, 26,138, 4,139,118, 24
60160 DATA 138, 60,139,118, 22,138, 28,139,118, 20,138, 44,139,118, 18
60170 DATA 138, 12,139,118, 16,138, 52,139,118, 14,138, 20,139,118, 10
60180 DATA 139, 52, 85,205, 33, 93, 86,156,139,118, 12,137, 60,139,118
60190 DATA 28,136, 36,139,118, 26,136, 4,139,118, 24,136, 60,139,118
60200 DATA 22,136, 28,139,118, 20,136, 44,139,118, 18,136, 12,139,118
60210 DATA 16,136, 52,139,118, 14,136, 20,139,118, 8,140,192,137, 4
60220 DATA 88,139,118, 6,137, 4, 88,139,118, 10,137, 4, 7, 31, 93
60230 DATA 202, 26, 0, 91, 46,136, 71, 66,233,108,255
79
6. The Disk Operating System PC System Programming
program RAWCooKP;
begin
Regs .ah : ~ $44; ( Function number for IOCTL: Get Mode
Regs.bx := Handle;
MsDos ( Regs ); Call DOS-Interrupt 21H
GetMode Regs.dx ( Pass device attribute
end;
begin
Regs.ax $4401; ( Function number for IOCTL: Set Mode
Regs.bx Handle;
Regs.dx GetMode(Handle) and 255 or 32; ( new device attribute
MsDos ( Regs ); Call DOS-Interrupt 21H
end;
(*********************************************************************}
(* SETCOOKED Change a character driver into the COOKED-Mode *)
(* Input the handle passed must be connected with the *J
{* device addressed *1
(* Output none *)
{*********************************************************************}
80
Abacus 65 Character Input and Output from DOS
begin
Regs.ax .= $4401; 1 Function number for IOCTL: Set Mode
Regs.bx := Handle;
Regs.dx .= GetModelHandle) and 223; new device attribute
MsDosl Regs ); Call DOS-Interrupt 21H
end;
procedure TestOutput;
begin
Test := ·Test ..... ':
Regs.bx .- STANDARDOUT; { output on the Standard output device
begin
end:
writeln;
end:
{************************************* •• ******************************}
{* MAIN PROGRAM *}
{************************************.*************.*.********* •• *****}
begin
ClrScr; { Clear screen }
writeln('RAWCOOK (c) 1987 by Michael Tischer"13flO);
writeln('The Console driver is now in RAW-Mode. Control keys such as <Ctrl><C>');
writeln('are not recognized during output. Press a key to display a text on
'113110) ;
writeln{'the screen, and try stopping the display by pressing <Ctrl><C>');
Keys : = ReadKey; ( wait for key )
SetRaw(STANDARDIN); { Console driver in RAW mode }
TestOutput; Output Test-String 1000 times }
ClrScr; ( Clear Screen )
while KeyPressed do
Keys := ReadKey; ( Empty keyboard buffer
writeln{'The Console driver is now in COOKED mode. Control keys such as');
SetCooked(STANDARDIN);
end.
81
6. The Disk Operating System PC System Programming
C listing: RAWCOOK.C
/***************************.**********************.*.******** •••••• **/
1* RAW COO K *1
1*-------------------------------------------------------------------*1
1* Task provides two functions for *1
1* switching a character device driver into the RAW *1
1* or into the COOKED mode *1
1*-------------------------------------------------------------------*1
1* Author MICHAEL TISCHER *1
1* developed on : 08/16/87 *1
1* last Update : 04/08/89 *I
1*-------------------------------------------------------------------*1
1* (MICROSOFT C) *1
1* Creation MSC RAWCOOKC; *1
1* LINK RAWCOOKC; *1
1* Call RAWCOOKC *1
1*-------------------------------------------------------------------*1
1* (BORLAND TURBO C) *1
1* Creation : through command RUN in the menu *1
'*._----------_._._---._._--_._._---_._.... _-._._.-._-****************/
'include <dos.h> 1* include Header files *1
'include <stdio.h>
'include <conio.h>
int GetMode(Hand1e)
int SetRaw(Handle)
82
Abacus 65 Character Input and Olllput from DOS
int SetCooked(Handle)
Register.x.bx - Handle;
I
/**********************.*** ••• *****.***** •• ********.*.**********.*****/
/* TESTOUTPUT: outputs a Test-String 1000 times on the Standard */
/* output device */
/* Input none */
/* Output none */
/******** ••• ************************************* ••• **.*.* ••• *** ••• ***/
void TestOutput()
printf ("\n");
for (i - 0; i < 1000; i++) /* output 1000 times */
fputs(Test, stdout); /* Output String on the Standard output. */
printf ("\n");
I
/*******************.*.*.*.*******************.**********.************/
/** MAIN PROGRAM **/
/******** •••• *.*************** •• **** ••• ********* ••• *******************/
void main ()
(
printf("\nRAWCOOK (c) 1987 by Michael Tischer\n\n");
printf ("RAW Mode. \nDuring the following output control characters, \n");
printf("Try it.\n\n");
TestOutput () ;
83
6. The Disk Operating System PC System Programming
Operating systems such as DOS provide the programmer with functions for file
management. For example, DOS provides functions which return special file
information or functions to rename a file. One peculiarity of DOS is that these
functions exist in two forms because of the combined CP/M & UNIX
compatibility. For every UNIX compatible file function, there is also a CP/M
compatible file function.
FeB functions
The CP/M compatible functions are designated as FCB functions since they are
based on a data structure called the FCB (File Control Block). OOS uses this data
structure for information storage during file manipulation. The user must reserve
space for the FCB within this program. The FCB permits access to the FCB
functions which open, close, read from and write to files.
Since the FCB functions were developed for compatibility with CP/M's functions,
and since CP/M has no hierarchical file system, FCB functions do not support
paths. As a result, FCB functions can only access files which are in the current
directory.
It is easier for the programmer to access a file using the handle functions than to
access a file using the FCB functions. The handle functions do not require a
programmer to use a data structure for file access like the FCB functions do. In a
manner similar to the functions of the UNIX operating system, file access is
performed using a filename. The filename is passed as an ASCII string when the
file is opened or fIrst created. This must be performed before the fIrst write or read
operation to the file. In addition to the filename, it may contain a device
designator, a pathname and a file extension. The ASCII string ends with the end
84
Abacus 6.6 File Management in DOS
character (ASCII code 0). After the file is opened, a numeric value called the handle
is returned. Any further operations to this file are performed using this 16-bit
handle. For a subsequent read or write operation, the handle and not the filename is
passed to the appropriate function.
For each open file, DOS saves certain information pertaining to that file. If the
FCB functions are used, DOS saves the information in the FCB table within the
program's memory block. When the handle functions are used, the information is
stored in an area outside of the program's memory block in a table that is
maintained by the DOS. The number of open files is therefore limited by the
amount of available table space. The amount of table space set aside by DOS is
specified by the FILES parameter of the CONFIG.SYS file:
FILES = x
In DOS Version 3.0, this maximum is 255. If you change the maximum number
of files in the CONFIG.SYS file, the change will not go into effect until the next
time that DOS is booted.
FILES
While the FILES parameter specifies the maximum number of open files for the
entire operating system, DOS limits the number of open files to 20 per program.
Since five handles are assigned to standard devices such as the keyboard, monitor
and line printer, only 15 handles are available for the program. For example, if a
program opens three files, DOS assigns three available handles and reduces the
number of additional handles available by three. If this program calls another
program, the three files opened by the original program remain open. If the new
program opens additional files, the remaining number of handles available is
reduced even further.
In addition to the standard read and write functions, there is also a file positioning
function. This lets you specify an exact location within the file for the next data
access. Knowing both a record number and the length of each data record allows
you to specify the position to access a particular data record:
This function is not used during sequential file access since DOS sets the file
pointer during opening or creation of a file to the first byte within the file. Each
subsequent read or write operation moves the file pointer by the number of bytes
read towards the end of the me so that the next me access starts where the previous
onecnded.
The following table summarizes the handle functions. For a more detailed
description of these functions, see Appendix C.
85
6. The Disk Operating System PC System Programming
Here are a few general rules to follow when using these functionsl
Function 59H of DOS interrupt 21H returns very detailed infonnation concerning
errors which occur during disk operations. This function is available only in DOS
Versions 3.0 and higher.
As discussed earlier, DOS uses an FeB data structure for managing a file. The
programmer can use this data structure to obtain information about a fIle or change
infonnation about a file. For this reason we shall examine the structure of an FeB
before discussing the individual FeB functions.
The FeB is a 37-byte data structure which can be subdivided into different data
fields. The following figure illustrates these fields.
86
Abacus 6.6 File MQNJgement in DOS
RAM
1\ 0000(00
+ OOH Device name (1 byte)
+ 01H Filename (S bvtesl
+ 09H File mode (3 bytes)
+ OCH Current block number (1 word)
+ OEH Data record size (1 word)
+ 10H File size (2 words)
+ 14H Modification date (1 word)
+ 16H Modification time (1 word)
+ ISH Reserved (8 bytes)
+ 20H Current data record number(l byte)
+ 21H Data record number for (2 words)
random access
~otice that the name of the file is found beginning at offsets om through OBH of
the FCB. The byte at offset 0 is the device indicator, 0 is the current drive, I drive
A, 2 drive B, etc.
The filename which begins at offset I is an ASCII string. It may not contain a
pathname since it's limited to 8 characters. For this reason, the FCB functions can
access only files in the current directory. Filenames shorter than eight characters
are padded with spaces (ASCII code 32). The file extension, if any, occupies the
next three bytes of the FCB.
At offset OCH of the FCB is the current number of the block for sequential file
access. The two bytes at offset OEH are the record size. The four bytes at offset
lOH are the length of the file.
The date and time of the last modifications to the file are stored beginning at offset
14H of the FCB in encoded form.
87
6. The Disk Operating System PC System Programming
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 bit
I~~1~1~1~1~1~1~1~1~1~lrrl~ITI~I~1~I
~' ____ ~Ir- _____A'______ ~I ______-,A'____ ~I ______'
Hour Minute Seconds in
2-second
Increments (e.g.,
13 means 26)
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 obit
I I I I I I I I II II II I I I
\. A .A. J
I I I
Year (relative to 1980) Month Day of month
An eight· byte data area follows and is reserved for DOS (no user modifications
allowed). The use of this area varies from one version of DOS to another.
Following this reserved data area is the current record number which is used in
connection with the current block number to simulate CP/M operations.
Random files
The last data field of the FCB is used for a type of access in which the data within
the file may be retrieved or written in a non-sequential order. This field is four
bytes long. If a record is equal to or larger than 64 bytes, only the first three bytes
are used for indicating the current record number. All four bytes of this field are
used for records smaller than 64 bytes.
Extended FCB
Besides a standard FCB, DOS also supports the extended FCB. Unlike normal
FCBs, extended FCBs access files with special attributes, such as hidden files or
system files. Furthermore, they permit access to volume names and subdirectories
(this doesn't mean that you can access files in other directories besides the current
directory).
An extended FCB is similar to a standard FCB, but it's seven bytes larger. These
seven bytes are located at the beginning of the data structure. All subsequent fields
are therefore displaced by seven bytes.
88
Abacus 6.6 File Management in DOS
RAM
+ OOH FF (1 byte)
1\ OOT OO
+ 01H Reserved(O) (5 bytes)
+ 06H File attribute n byte)
+ 07H Device name (1 byte)
+ 08H Filename (8 bytes)
+ 10H File extension (3 bytes)
+ 13H Current block number (1 word)
+ ISH File record size (1 word)
+ 17H File size (2 words)
+ 1BH Modifications-date (1 word)
+ 1DH Modifications-time (1 word)
+ 1FH Reserved (8 bytes)
+ 27H Current data record number (1 byte)
+ 28H Data record number (2 words)
The ftrst byte of an extended FCB always contains the value 255 and identifies this
as an extended FCB. Since this address contains the device number in a normal
FCB and can therefore not contain the value 255, OOS can tell the difference
between a normal and an extended FCB. The next five bytes are reserved
exclusively for the use by OOS. They should not be changed. The seventh byte is
a fIle attribute byte. See Section 6.1.2 for the details of the file attribute byte.
Now that you're familiar with the FCB structures, the next section focuses on
using FCBs for accessing fIles.
Before accessing a fIle, an FCB must be built in the program's memory area. The
area can be reserved within the data segment of the program or by allocating
additional memory using another DOS function (see Section 6.9).
Although it is possible to write the data directly into the FCB, it is better to use
one of the appropriate OOS functions to do this.
For example, to set the filename in the FCB you can use OOS function 29H. The
function number is passed in the AH register. The address of the FCB is passed in
the ES:DI register pair. The address of the fIlename is passed in the DS:SI register
pair. The fIlename is an ASCII string terminated by the end character (ASCII code
0). The AL register contains flags for converting the fIlename and are discussed in
more detail in Appendix C.
Open FCB
After the FCB is properly formatted the fIle can be opened or created using a OOS
function. When this happens DOS stores information about that fIle in the PCB
89
6. The Disk Operating System PC System Programming
such as the file size, date and time of file creation, etc. At this point the FCB is
considered opened.
By default, the record length is set to 128 bytes when the FCB is opened. To
override this record length, store the desired record length at offset OEH of the FCB
after it is opened. Otherwise the default length will be used.
DTA
For record lengths greater than 128 bytes, the record buffer also known as the
DTA, or Disk Transfer Area must be moved to accommodate the longer record
size. Normally, DOS builds the DTA in the PSP (Program Segment Prefix).
Accessing the file using the default DTA for a record length greater than 128 bytes
would overwrite some of the other fields in the PSP.
The most convenient way to select a new DT A is to reserve the space in the
program's data segment. To change the address of the DTA use DOS function
lAH. The address of the new DTA is passed in the DS:DX register pair. DOS
assumes that you have set aside an area large enough to accommodate your largest
record length so you don't have to specify the new length.
File access
For sequential file access, processing begins at the first record in the file. DOS
maintains a record pointer in the FCB to keep track of the current record within the
file. Each time the file is accessed, DOS advances the pointer so that the second,
third, fourth, etc record is processed in order.
For random file access, the records can be processed in any order. The position of
each record relative to the beginning of the file determines its record number. This
record number is then passed to DOS to access a specific record. The last field of
the FCB is used to specify the record number to DOS.
It's also possible to change from sequential access mode to random access mode
and vice versa since processing depends on a specific DOS function to access the
file. In effect. there are two sets of independent functions. one for sequential access
and one for random functions.
Following is a list of all of the FCB functions of DOS interrupt 21H. A more
detailed description of the functions is found in Appendix C.
90
Abacus 6.6 File ManlJgemenl in DOS
Using the FCB functions, you can access several files, each with their own unique
FCB. To tell OOS which file is to be accessed, pass the address of the file's FCB
in the DS:DX register pair.
Most of the functions return an error code in the AL register or the value zero if
the function was successfully completed. For functions which open, close, create
or delete a file, a code of 255 is returned if an error occurs. The other functions
return specific error codes. More detailed information about these errors can be
determined by calling OOS function 59H but is available only in versions of DOS
V3.0 or later.
After the two groups of functions made available by OOS have been presented, the
advantages and disadvantages of the individual functions should be discussed
briefly. For those who want to convert a program from the CP/M or UNIX
operating systems into DOS, the choice will be easy, but for those who want to
develop a new program under OOS, this discussion can help in your deciding on
which set of functions to use.
Handles
There are two main advantages to using handle functions. The first is the
capability to access a file in any subdirectory of the disk. The second is that the
handle functions are not limited to the number of FCBs which can be stored in a
program's memory space.
There are a number of additional considerations. You can access the name of a disk
drive only by using an FCB. When the FCB is opened, you can easily determine
its file size and the date of the last modification. The handle functions
automatically provide an area large enough to accommodate the records in the file.
As you can see there are arguments for and against using either the FCB functions
or the handle functions. For future versions of DOS, the handle functions will play
a more important role and the importance of the FCB functions will diminish.
This is reason enough to use the handle functions for your new program
development
91
6. The Disk Operating System PC System Programming
There are two groups of DOS functions for working with directories. The first
group is used to manipulate the subdirectories and the second to search for mes on
the mass storage devices.
With DOS Version 2.0 came the introduction of subdirectories. A mass storage
device could be logically divided into smaller subdirectories which could in turn be
further subdivided. In effect this organization created a directory tree.
Main directory
START.BAT
= Directory, subdirectory
= File
Directory tree
In this directory tree, the names and numbers of subdirectories are not static.
Therefore there must be a way to add, change and delete entries on the tree. Other
functions must be available to set the current directory so that a complete
pathname is not required for all me accesses.
At the user level the MD, RD and CD commands can be used to make a directory,
remove a directory and change a current directory. Internally, these commands are
performed with functions 39H, 3AH and 3BH of DOS interrupt 21H.
The function number is passed in the AH register. The address of the path is passed
in the DS:DX register pair. The path is a string and may be a complete path
designation including a preceding drive letter followed by a colon (a device name)
and terminated by ASCII code O. If the device name is omitted, the current device
is the default.
92
Abacus 6.7 Accessing the DOS Directory
Following execution, the carry flag indicates the return code. If the carry flag is
reset (0), then execution was successful. If the carry flag is set, then an error
occurred and the error code is passed back in the AX register.
Function 39H creates or makes a new directory (Make Directory). The name for the
new directory is specified as the last element in the path. An error will be returned
by the functions if one or more of the directories specified in the path do not exist,
if the new directory name already exists or if the maximum number of ftles in the
root directory is exceeded.
Function OEH sets the default disk drive. Besides the function number in the AH
register, only the device code of the new current device must be passed in the DL
register. Code 0 stands for the device A, 1 for B, 2 for C, etc.
Directory specification
Before specifying the current directory using function 3BH, it is sometimes
necessary to find the current directory. DOS makes function 47H available to the
programmer for this purpose. Since it can return the path of the current directory
for any device, the device number must be passed to the function. If this is the
current device, the value 0 must be passed in the DL register. For all other devices,
the value 1 must be passed for drive A, 2 for B, 3 for C, etc.
Besides the device code, the function must also have the address of a 64-byte buffer
within the user program. The DS register contains the segment and the SI register
holds the offset address of this buffer. After the function call this buffer contains
the path designation of the current directory, terminated with the end character
(ASCII code 0). The path designation cannot be preceded by the device name or the
\ character. If the current directory is the root directory, the buffer contains only the
end character. If a device code unknown to DOS was passed during the function
call, the carry flag is set and the AX register contains the error code OFH.
Let's consider the functions for searching for one or more files in the current
directory on the current device. Again the parallel between handle and FCB
functions appears. Two function groups exist to search for files. The group of
FCB functions has the disadvantage that they limit the search to ftles in the current
directory of a certain device, while handle functions allow searching for files in any
directories of any devices. The term "handle" functions doesn't really fit these
functions since they are not addressed with a handle. This designation originated
with the introduction of subdirectories (and therefore the handle functions) in DOS
Version 2.0. Version 1.0 offered only the FCB functions.
93
6. The Disk Operating System PC System Programming
This method of file search uses functions llH and 12H. Using them you can
search for files with a fixed name or flIes with a mename extension. Function IlH
finds the first flIe in the current directory. Function 12H finds all other additional
files. The FCBs play a significant role since they mediate between the calling
program and the two functions. Let's see how we can search for mes in a directory:
First the program must reserve space for two FCBs. This is done either by
reserving memory in the data area of the program, or by requesting memory from
DOS using function 48H. The programmer can use either normal or extended
FCBs. Extended FCBs offer the advantage of being able to search for mes with
special attributes (system or hidden), volume names and subdirectories. The
flIename for which the search will be made is specified in one of the FCBs. DOS
places the name of the flIe(s) that it finds in the other FCB. To differentiate
between the two FCBs, they are designated with the names Search FCB and Found
FCB.
The address of the Found FCB must be passed to DOS using function lAH. The
Found FCB becomes the new data transmission area (DTA) when this function call
occurs. This area is important for these two functions as well as all other functions
which transfer data between computer and disks. For this reason function 2FH
should determine the address of the current DTA before activating the new DTA.
When the file search ends, the DTA can be restored to its original state using
function lAH.
After the DTA is set to the Found FCB, the next step is to place the name of the
file you are looking for into the'Search FCB. For a more general search, the
wildcards * and ? may be used. You can transfer the flIename directly or transfer it
using function 29H. If you want to search through all mes, use the filename *.*.
If an extended FCB is used, you may insert an additional value into the attribute
field of the Search FCB to limit the search to mes with certain attributes only (see
Section 6.12 for more information on the various attributes).
This concludes the preliminary work. The file search can begin with the current
directory. For this purpose, function IlH is called with the function number in the
AH register, the segment address of the Search FCB in DS and the offset address in
the DX register. If the system finds a flIe with the indicated name, the AL register
contains the value 0 after the function call. If the flIename wasn't found, the AL
register contains a value of 255. The found flIename and its attributes (if extended
FCBs are used) can be read from the Found FCB. For additional searches, function
12H (not function I1H) is called. Function 12H's register contents during call and
return are similar to function IlH. If it returns the value 255 in the AL register
during one of the calls, the Search has ended.
94
Abacus 6.7 Accessing the DOS Directory
Working with handle functions is easier than working with the FCB functions.
There are functions for searching for the first file (the 4EH function) and
subsequent files (the 4FH function). Both functions return the information to the
DTA. For this reason the DTA should be moved into an area accessible to the
current program before calling either of these functions. This area must have at
least 43 bytes available. As mentioned in connection with the FCB functions, the
DTA should be restored to its original address after the search ends.
During the call of the 4EH function, the function number is passed in the AH
register, the attribute in the CX register and the address of the file to be found in
the DS:DX register pair. The filename is a series of ASCII characters, terminated
with an end character (ASCII code 0). In addition to a device name, you may add a
complete path designation and the wildcard characters * and ? If a path is not
specified, DOS assumes that the search should be made in the current directory of
the indicated device. If a device is not specified, the search proceeds on the current
device. Mter the function call, the carry flag indicates whether a file was found. If
the file couldn't be found, the carry flag is set, and the AX register contains an
error code. An error code of 2H is returned if the indicated path does not exist If no
file could be found, an error code of 12H is returned. If the carry flag is reset, the
DTA contains the information about the file found. It has the following structure:
Address Contents Type
+OOH reserved for DOS 21 bytes
+15H Attribute of file found 1 byte
+16H Time of last modification 1 word
+18H Date of last modification 1 word
+1AH low word of file size 1 word
+lEH high word of file size 12 bytes
Function 4FH executes any further searches. The function number is passed in the
AH register, and no other parameters are required. The carry flag indicates if there
are additional fIles in the current directory to which the search may be applicable.
95
6. The Disk Operaling System PC System Programming
Demonstration programs
The three programs below read directory entries and display them on the screen
using one of the handle functions. You'll find the display more user friendly than
the DOS DIR command: the files appear in a window, and the filename display
stops as soon as the window is fIlled with filenames. This permits easy reading of
filenames. By pressing any key, the program displays any additional pages of
fIlenames.
All three programs are designed on the same basic principle: first the main
program determines the search path. It contains the names of the directories in
which the search should be made for the files, the names of the files and the device
where the directory is located. This name can contain wildcards (* and 7) to search
for several files at the same time. If the user does not indicate a search path, the
program defaults to the search path n*.*n. This displays all fIles in the current
directory of the current device, as well as the hidden attribute fIles.
After the program determines the search path, a routine coordinates the loading and
display of individual directory entries. First a routine creates the display window on
the screen for individual entry output. Then a search proceeds for the first entry
using DOS function 4EH. If an entry is found, the screen displays the entry.
Function 4FH searches for all subsequent entries and displays them in the window.
The bottom line of the display window moves up one line with each new line
displayed. Once the entire window fills with data. any further display of entries
stops until the user presses a key. After all entries in the selected directory have
been displayed, the number of fIles is displayed and the program ends.
220 PRINT"WARNING: This program can be run only if GWBASIC was started"
240 PRINT"If this is not the case, please enter <s> for Stop."
290 CLS
310 PRINT
330 PRINT"Example: If all files with the extension .BAT in the Root"
96
Abacus 6.7 Accessing the DOS Directory
97
6. The Disk Operating System PC System Programming
50620 '
51000 ,****.*************************************** ••••• **************'
51010 ,* Search for first entry in a Directory *,
51020 ,*-------------------------------------------------------------*,
51030 '* Input: DIR$ - Search path
51080 Z\ is a Dummy-Variable *,
52070 ,*
52090 ,**********************************************.****************'
52100 '
52110 FCT\ = &H4F 'Find function number for next entry
52120 INR\ = &H21 'Call DOS-Interrupt 21H
52130 CALL IA(INR\,FCT\,Z\,Z\,Z\,Z\,Z\,Z\,Z\,Z\,Z\,Z\,FLAGS\)
52140 FOUNDIT' = «FLAGS\ AND 1) - 0) 'test Carry-Flag
52150 RETURN 'back to calling program
52160 '
53000 ,******* ••• *****************************************************1
53010 ,* Output a Directory entry from the DTA to the display
53020 '* ____________________________________________________ ---------*1
53030 Input: OFFSET% first line of the Directory window *,
53040 ENTRY \ Number of entries in the Directory window
53050 DTAOFS\ Offset address of the DTA
53060 MONTH$ = contains the names of months
53070 Output: none *'
53080 ,***************************************************************1
53090 '
53100 DEF FNDTA(X) = PEEK(DTAOFS% + X)
53110 DEF SEG = DTASEG\ 'Set Segment address of the DTA
53120 LOCATE OFFSET'+ENTRY%+2,15 'Output in the last line of the window
53130 H = 30 'Offset address in DTA for file names
53140 WHILE FNDTA(I\) <> 0 'the END character terminates the name
53150 PRINT CHR$(FNDTA(I\)); 'output a character of the file name
53160 I\ = 1\ + 1 'next character
53170 WEND 'End of Loop
53180 LOCATE OFFSET'+ENTRY\+2,28 'Set Cursor to column 28
53190 PRINT USING ".If .. U"; FNDTA(26) + FNDTA(27) * 256! + FNDTA(28) *
4096! + FNDTA(29) * 65536!;
53200 DATE = FNDTA(24) + FNDTA(25) * 256 'Get Date
53210 LOCATE OFFSET%+ENTRY\+2,36 'Set Cursor to Column 36
53220 PRINT MONTH$[(INT(DATE / 32) AND 15) - 1]; 'Output name of month
53230 PRINT"/";:PRINT USING • .. ·;DATE AND 31; 'Output day of month
53240 PRINT USING "/ffU";INT(DATE / 512) + 1980; 'OUtput year
53250 LOCATE OFFSET'+ENTRY%+2,49 'Set Cursor to column 49
53260 FTIME = FNDTA(22) + FNDTA(23) * 256 'Get time
53270 PRINT USING "ff";INT(FTIME / 2048); 'Output hour
53280 PRINT " ..
....
,
98
AbaclU 6.7 Accessing the DOS Directory
54190 '
One problem in the BASIC version of the directory listing occurs during the
directory output. Functions 4EH and 4FH read the entry into the DTA. It would
make more sense to move the DTA to a variable within the program (an integer
array would be best) to make it easier for the routine which outputs the entry to
access the data. BASIC's garbage collection feature makes this difficult. The
integer array containing the DTA moves periodically in storage and the address of
the DTA, stored internally in DOS, no longer cOrresponds with the address of this
integer array.
For this reason, the DOS function 2FH determines the DTA address. As the entries
are displayed, this address accesses the DTA to determine the me information.
99
6. The Disk Operating System PC System Programming
program DIRP;
(! Turbo 4.0 owners should use the Registers type from the DOS unit.)
end;
Attribut byte;
Ztime integer;
Zdate integer;
Datgrlo integer;
Datgrhi integer;
end;
{*********************************************************************}
{* GETFIRST: read in the first Directory entry *1
{* Input none *J
{* Output true or false, depending if an entry was found */
(* *)
{* Info the entry is stored in Variable DIRBUF *1
{*********************************************************************}
begin
DateiName := DateiName + fO; { terminate filename with NUL
Register.ax := $4E shl 8; Function number for search of first
Register.cx := Attribute; ( Attribute, for which search is performed
Register.ds seg{DateiName); ( Segment address of filename
Register.dx := succ{ofs(DateiName»; (Offset address of filename
msdos (Dos. Registers (Register»; { Call DOS Interrupt 21H {Turbo 4.0/1
{NOTE:Turbo 3.0 users should change previous line to read msdos{Register);1
( defined in DOS unit.)
if (Register. flags and 11 = 0 { Test Carry-Flag 1
100
Abacus 6.7 Accessi"g the DOS Directory
begin
Register.ax :- S4F shl B; Function number for next search }
msdos{Dos.Registers{Register}}; { Call DOS Interrupt 21H V 4.0}
{NOTE: Turbo 3.0 users should change the previous}
{line to read msdos(Register};}
if (Register. flags and 1) - 0 ( Test Carry-Flag )
then Get Next '. true Equal to 0 : File found }
else GetNext :- false; otherwise no file found }
end;
(*** •• ***************.*.*******.************************************)
(* PRINTDATA: Output information on an entry *)
{* Input none *}
(* Output none *)
(* Info the information about the entry are taken by this *)
{* procedures from Variable DIRBUF *}
{*************** •• ******************************.*.***.******* ••• ***)
procedure PrintData;
DataLenght2 := DirBuf.Datgrlo;
gotoxy(21, ENTRY};
write (' ) , );
1 write ('Jan');
2 write ('Feb');
3 write ('Marl) ;
4 write ('Apr') ;
5 write ('May') ;
6 write ('Jun') ;
7 write ('Jul' );
8 write ('Aug') ;
9 write ('Sep') ;
10 write ('Oct') ;
11 write ('Nov') ;
12 write ('Dec')
101
6. The Disk Operating System PC System Programming
end:
write('/',DirBuf.Zdate and 31:2, 'j'); { determine day
write(DirBuf.Zdate shr 9 + 1980:4); ( determine year
gotoxy(34, ENTRY);
write('I', DirBuf.Ztime shr 11:2, ':'); determine hour
write(DirBuf.Ztime shr 5 and 63:3); determine minutes
gotoxy(44, ENTRY); evaluate file attribute
write (' I ') ; separator to preceding field
if (DirBuf.Attribut and 1)<>0 then write('X') ( Read-only?
else write(' ');
i f (DirBuf .Attribut and 2)<>0 then write('X') hidden?
else write(' ');
i f (DirBuf.Attribut and 4)<>0 then write('X') system?
else write (' ');
i f (DirBuf.Attribut and 8) <>0 then write('X') Volume-Label?
else write (' ');
i f (DirBuf.Attribut and 16)<>0 then write ('X') { Directory?
else write(' 'I;
write (' I'); ( right border of window frame )
end;
{****************************************************.****************}
1* SETDTA *'
1* Input
{* Output
set Address of DTA
: see above
: none *}
*'
(***********************•• ******************************** •• **********)
begin
Register.ax := $lA shl 8; { Set Function number for DTA
Register.ds := Segment; { Segment address into DS register
Register.dx := Offset; ( Offset address into DX register
msdos(Dos.Registers(Register»; ( Call DOS-Interrupt 21H )
INOTE: Turbo 3.0 users should change the previous}
Iline to read msdosIRegister);)
end:
{*********************************************************************}
{* BUILDSCREENDISPLAY: prepares the display for output of the *}
(* Directory *}
{* Input : none *}
(* Output : none *}
{**** •• ***************************************************************}
procedure BuildScreenDisplay;
begin
clrscr: 1 clear display
window (14, (20-ENTRY) shr 1+1,64, (20-ENTRY) shr 1 +5+ENTRY);
gotoxy(l,l); Cursor to left upper corner of window
write ('r T T TTl');
writel'l Filename I Size I Date Time IRHSVDI');
write('I------------lf------~-------------~-------+-----I');
for Counter .= 1 to ENTRY do
end:
{*********************************************************************}
{* DIR: controls the input and output of Directories *1
{* Input : none *)
102
Abacus 6.7 Accessing the DOS Directory
(* output : none *)
{*********.******* ••••• ***.*** ••• ****.********** •• ***.****************}
procedure Dir;
begin ( Yes
end:
PrintData; ( output data of entry
write (' I) ;
case NurnEntries of
end:
window (1, 1, 80, 25); set whole display as window I
end;
begin
Dir; { Load Directory and display }
103
6. The Disk Operating System PC System Programming
end.
In the above Pascal program and in the following C program, accessing the DTA
is much easier than in the BASIC version of the same program. RECORD or
STRUCT defines the structure of the directory entry into the DTA, and the
programs implement a variable of this type. OOS function lAH then transfers the
DTA to this variable. All the information in a directory entry can be easily
accessed. With Turbo Pascal, the display design is particularly easy. Turbo Pascal
also has a procedure to define any display area as a window. However, the C
language program uses the scroll function of the BIOS interrupt lOH to scroll the
directory window one line upward.
C listing: DIRC.C
/***************************************************** ****************j
1* D I R C *1
1*-------------------------------------------------------------------*1
1* Task : Displays all files in any Directory, *1
1* including Sub-Directories and volume names *1
1* on the screen. *1
1*-------------------------------------------------------------------*1
1* Author MICHAEL TISCHER *1
1* developed on : 08/15/87 *1
1* last Update : 04/08/89 *I
1*----------------_·_--------------------------------- ----------------* I
1* (MICROSOFT C) *1
1* Creation : MSC DIRC; *1
1* LINK DIRC; *1
1* Call : DIRC *I
1*-------------------------------------------------------------------*1
1* (BORLAND TURBO C) *1
1* Creation With the RUN command in the command line *1
1* Info Arguments can be passed to the program with *1
1* the OPTION/ARGS command in the command line *1
1* of TURBO C *1
1* or *1
1* *1
1* Creation : TCC DIRC *1
1* Call : DIRC *1
/*********************************************************************/
finclude <io.h>
'include <string.h>
104
Abacus 6.7 Accessing the DOS Directory
byte GETPAGEO
/****************** •• *************************************************/
1* SCROLLUP: moves a displa¥ area one or more lines *1
1* upward or erases it *1
1* Input see above *1
1* Output none *1
1* Info if 0 is passed as number, the display area *1
1* is filled with blanks *1
/*****************************************************.***************/
105
6. The Disk Operating System PC System Programming
/********** •• ****************************************************.****/
1* GETPOS Get the position of the Cursor in current display page *1
1* Input : none */
1* Output : see below *I
/*******************+.**************************** •• ** ****************1
/***********************************.*********************************/
/* WRITECHAR: writes a character with an attribute to the current */
/* cursor position on the current display page */
/* Input see below */
/* Output none */
j******************************************** •• ******* ****************/
/*********************************************************************/
/* WT writes a character string with constant color starting */
1* at a specified position on the current display page. */
/* Input see below */
/* Output none */
/* Info Text is a Pointer to a character Vector, which contains */
/* the text to be output and is terminated with a '\0' *1
/* character. *1
/*********************************************************************/
106
Abacus 6.7 Accessing the DOS Directory
void CIs ()
/** ••• ****** •••••• ******.*.*.* •• ****.* •••••• ** ••• ********* •• **** •• **.*,
1* BUILDSCREENDISPLAY: prepares the display for the output of the *1
1* Directory. *1
1* Input none *I
1* Output none *1
/ •• **.*** •••• ***** •••• ****** ••••••••••••• *** •• **** •••• *** ••• *** ••••• *./
void BuildScreenDisplay()
{
byte i; 1* Loop Counter *1
Cls(); 1* Clear Screen *1
WT (14, EZ, 'f-------r r ---"'lTr------"1Tr- ----4'"--10, NOF) ;
WT(14,EZ+l,"1 Filename I Size I Date I Time IRHSVD I ",NOF);
WT(14,i,"1 I I I I",NOF)·
WT (14, EZ+ENTRY+3, "L.-----.L---.L-----.. L-----.L--J·,
NOF);
107
6. The Disk Operating System PC System Programming
1-··_·---_··_··------------------_·_--_·_·_·_·_-_·_---.-.-._.-.---_._./
1* GETFIRST read the first Directory entry */
/* Input none */
f* Output TRUE, if entry was found, otherwise FALSE */
/* Info Entry is read into the DTA */
1-*------_·_·····_---------------_·_------*----------- -*_. __ .-._._._--/
byte GetFirst (Sname, Attribute)
j._--------*-----------------------_._------_._-_._._-----------_._._-/
/* SETDTA sets the DTA to a Variable in the Data Segment *f
/* Input : see below *f
/* Output : none */
1---·--·_--_·_-_·_------·_-_·_·_·· __ ·_·_---_····-·-·_· w.www.wwWWWWW_K_/
void SetDTA(Offset)
/*********************************************************************/
f* DIR controls the input and output of Directories *f
f* Input : see below *f
/* Output : none *f
/** •• **************************************************.*.************/
108
Abacus 6.7 Accessing the DOS Directory
(
int NumEntries, /* Total number of entries found */
Numwind; /* Number of entries 1n display */
struct DirStruct DirEntry; /* a Directory entry */
/* no entry found */
do
(
PrintData(&DirEntry, EZ+ENTRY+2); /* output entry */
if (++Numwind == ENTRY ) /* Window full 2 */
(
Nurnwind - 0; /* fill a window */
WT(14, EZ+4+ENTRY,
" Please press a key H, INV);
qetch (); /* wait for key */
WT(14, EZ+4+ENTRY,
",NRM) ;
switch (NurnEntries)
(
case 0 printf("no files found tt, ;
break:
break;
109
6. Tlu! Disk Operating System PC System Programming
Parent/child
The EXEC function is one of the many DOS functions which can be called with
interrupt 21H (function 4BH). Generally speaking, this function lets a parent
program (main program) call a child program (secondary program). The child
program is loaded from a mass storage device into memory and then executes. If
this child program doesn't become resident, the memory occupied by the child is
released following program execution. The child program can also call another
program which works with the parent program. This creates a type of program
chaining limited only by the amount of available RAM.
One example of the EXEC function is the command processor. Using the EXEC
function, the command processor executes user-specified programs and becomes the
parent program. Some programs (such as Microsoft Word®) permit the user to
execute DOS commands from the main program using this function.
The parent program can pass parameters to the child program in the command line
and can also pass parameters using the environment block. It can also transfer
information to the child program within the PSP. Since the child program, like all
executable programs, has a PSP preceding it, information can be entered into the
two FCBs within this PSP and made accessible to the child program.
Child program
After transferring control to the child program, it can access all files and devices
previously opened by the parent program (or one of the parent programs) with a
handle function. This allows the child program to read information from a file or
write information to a file whose handle is known (the child program doesn't need
to know the filename). This is only possible if the handle was passed by the parent
program in one of the three methods described, or if the child program refers to one
of the five handles which are always open. These file accesses affect the file
pointer. Since values are not reset, these file accesses become "visible" to the
parent program when control returns to the parent program.
After execution of the child program, control returns to the parent program and
execution continues. To pass information (e.g., an error that occurred during the
execution of the child program), the child program can pass a numeric value at the
end of its execution. This can be done using DOS function 4CH, which terminates
a program and returns a code to the parent program.
The communication between parent and child program functions only if both
programs agree on this return value. After control returns to the parent program, it
110
Abacus 6.8 Th£ EXEC Function
can detemline the code using function 4DH of interrupt 21H. To use function 4DH
only the function number is passed in the AH register. The code passed by the
child program is returned to the calling (parent) program in the AL register.
As mentioned previously, the EXEC function can only load the child program if
enough memory is available. While DOS can estimate the memory needed for
EXE programs fairly accurately, it cannot do the same for COM programs. For
COM programs DOS reserves all unused memory. Because of this, a COM
program cannot call another program with the EXEC function, since DOS reserves
no extra memory. The same is true for many EXE programs. If a call to a child
program is necessary, the required memory space must be released from the calling
program before calling the EXEC function (see Sections 6.4.1 and 6.4.2 for
explanations on how this is done).
EXEC
If the EXEC function is called, the various parameters are loaded into the registers
before calling interrupt 21H. Function number 4BH is passed in the AH register.
A value of 0 or 3 is passed in the AI.. register. A value of 0 indicates that the
EXEC function is to load and execute the program while a value of 3 indicates that
the program is loaded as an overlay (without executing it). The address of the name
of the program to be loaded or executed is passed in the DS:DX register pair. And
the address of the parameter block is passed in the ES:BX register pair.
The program name is specified as an ASCII string and ended with a null character
(ASCII code 0). The program name can include the device name and a complete
path description. Its last element is the program name which, besides the name
itself, must have the extension .COM or .EXE. If the device name or path
designation are omitted, the system searches for the program in the current
directory of the current device. Since the EXEC function cannot execute a batch
fIle directly, the program name passed cannot contain the extension .BAT.
Batch child
111
6. The Disk Operating System PC System Programming
processor with the Ic parameter offers the option of calling any other program, and
even internal DOS commands such as DIR.
Besides calling a program directly, it's possible to specify program names without
file extensions during a command processor call. The command processor searches
for an EXE file; then a COM file; and finally a BAT file. If none of these files
exist in the current directory, it searches all directories specified in the PATH
command. This chain of events is not followed during a direct program call
without the addition of the command processor.
The directory which contains the command processor should be specified. If not
specified, it will be loaded from the path indicated by the COMSPEC environment
string of the SET command.
Parameter blocks
Parameters can be passed to the command processor in the parameter block
following the program name. These parameters are identical to the parameters
entered from the keyboard when the program is called. How these parameters affect
the EXEC function will be seen shortly, but fmt take a look at the parameter
block's structure when the AL register contains the value O. This block's address is
passed to the EXEC function in the register pair ES:BX.
Field 1 indicates the segment address of the child program's environment block.
This block doesn't require an offset address since it always starts at a location
divisible by 16, and therefore its offset address is always to O.
Environment block
The command processor and other programs obtain information from the
environment block. The environment block is a series of ASCII character strings.
This infonnation can include paths for file searches. Each string has the following
syntax, tenninated by a null character (ASCII code 0):
Name - Parameter
The individual strings follow each other sequentially (i.e., the null character of one
string is immediately followed by the beginning character of the next string). The
environment block ends with a null character. Any environment block has a
maximum length of 32K.
112
Abacus 6.8 The EXEC FlU'lCtion
The environment block can be changed or modified by the user using the DOS
SET and PATH commands. Programs which remain resident after execution are
unaffected by any changes made to the environment block through these two DOS
commands once made resident
If the parent program wants to pass infonnation to the child program using the
environment block, it can either construct a new environment block or supplement
its own environment block with this infonnation. In the first case, the segment
address of the new environment block is specified in the fllSt field of the parameter
block. If the child program should have access to the environment block of the
parent program, specify a value of 0 in this field. Before blming over control to
the child program, the EXEC function stores the segment address of the
environment block in the memory location at address 2CH of the child program's
PSP.
If the child program is to use a new environment block, it should contain at least 3
strings which are normally part of the environment block of the parent program,
and are important to the command processor:
COMSPEC = Parameter
PATH - Parameter
PROMPT = Parameter
Fields 2 and 3 indicate the command parameters' address which is passed to the
PSP of the program starting at address 80H. They must have the same strucwre in
memory as expected by DOS in the PSP. The first byte indicates the number of
command characters minus 1, then follows the command characters as nonnal
ASCII codes. The command parameters terminate with a carriage return (ASCII
code 13) which is not included in the character count. The fllSt character in the
string should be a space for compatibility with COMMAND.COM.
To call a batch program (called DO.BAT) using the command processor, the
following command parameters must be specified as a string in memory:
DB 10," Ie OO.BAT",13
The EXEC function copies the command parameters in a controlled fashion into
the PSP of the program to be executed. It removes all parameters which would
redirect the input or output, since a redirection of the standard input/output can
only be performed by the parent program. The child program can still use
input/output redirection if the standard input/output handles have been redirected by
the parent program (see Section 6.10 for more detailed information and an example
of this process).
113
6. The Disk Operating System PC System Programming
Fields 6, 7, 10 and 11 indicate two FCBs installed in the PSP at address 5CH or
6CH. If this is not required, specify -1 (FFFFH) in these two fields. If program
execution requires it, enter the first two command parameters in the two FCBs
with DOS function 29H. Before passing control to the child program, the EXEC
function copies these two FCBs into the PSP of the child program.
Even though all registers and the parameter block now have the required values, the
EXEC function cannot be called yet. Since it destroys the contents of all registers
up to the CS and IP registers during execution,the contents of all registers must
be placed on the stack before it is invoked. Then the contents of the SS and SP
registers must be stored within the code segment. Only then can interrupt 21H
function 4BH be called to activate the EXEC function. After the EXEC function
ends, the carry flag signals if the function executed normally. Before program
execution can continue, the value of the SS and SP registers must be restored,
from the code segment. Then the contents of the other register can be restored
again from the stack.
The EXEC function serves a different purpose when a value of 3 appears in the AL
register. In this case, it loads a COM program or an EXE program into memory
without executing. After the target program is loaded, control immediately returns
to the calling program. In contrast with sub-function 0, the program loads to a
memory address indicated by the calling program instead of loading to any non
specific location. Since no parameters are passed to the loaded program, the
parameter block has a different structure during the call of sub-function 3 than
during the call of sub-function 0:
Field Byte Purpose
1 0-1 Segment address where overlay is loaded
2 2-3 Relocation factor
Before the function is called, the segment address to which the program should be
loaded is specified in the frrst field of the parameter block. If the calling program
doesn't have enough memory available for loading the external program, it should
request additional memory with one of the DOS memory management functions.
The loaded program loads directly to the segment address indicated with the offset
address 0 since no PSP precedes the program.
Relocation
The relocation factor adjusts the segment address of the called program. Since this
factor applies only to EXE programs (COM programs cannot have specific
segment assignments), the relocation factor for COM programs should always be
equal to O. The relocation factor for EXE programs should indicate the segment
address where the program will be loaded to confmn to the program's segment
assignments.
After the program is loaded, its routines are ready to be accessed. The routines of
the loaded program should always be treated as subroutines; and therefore, called
114
Abacus 6.8 TM EXEC FlUlCtion
with the machine language CALL instruction. It must always be a FAR type
instruction even though the loaded program may be located immediately following
the calling program, but can never have the same segment address. The offset
address for CALL is always l00H for a COM program, since execution always
starts immediately following the PSP at address l00H. This creates a problem.
Sub-function 3 prevents the PSP from loading. Therefore the code segment of the
COM program starts at address 0, not at the offset address l00H (relative to the
load segment). Since all jump instructions and accesses to data within the COM
program are relative to address l00H and not address 0, you cannot execute a FAR
CALL instruction with the address of the load segment as the segment address, and
address 0 as the offset address. The segment address for the FAR CALL must
indicate the address of the load segment minus 10H and the address l00H as the
offset address.
If the COM program specifically acts as an overlay for another program, entry
addresses other than address l00H are possible. In such a case, only the offset
address for the FAR CALL instruction changes. The segment address must remain
10H smaller than the address of the load segment.
The problem is different for EXE programs. If they are loaded for execution using
sub-function 0, the EXEC function sets the code segment and the instruction
pointer to the instruction which was declared as the first instruction in the
assembler source. This address, however, is unknown to the program which loaded
the EXE program as an overlay. This can easily be remedied by placing the frrst
executable instruction in the EXE program at the beginning of the EXE program.
This makes its offset address O. The EXE program source must not be in the
normal sequence with the stack frrst. In this case, the code segment must be the
frrst segment in the source to ensure that it begins the EXE program.
The FAR CALL uses the address of the load segment as the segment address, and
address 0 as the offset ad,dress.
While BASIC, Pascal and C have commands or procedures to call a program from
another program, assembly language routines must use DOS function 4BH. To
help you further understand this function, here is an example program.
The framework of the EXE program listed in Section 6.4.2 acts as the basis for
this program. The EXEPRG procedure performs the actual dirty work in this
program. It calls the new program using function 4BH. Two strings which contain
the name of the program to' be called and the necessary parameters are passed to it.
Both strings end with the null character (ASCII code 0). All variables required by
EXEPRG for execution can be found in the code segment. This offers the
advantage that the lines from the code segment only must be copied into one of the
application programs to use this routine. After calling EXEPRG, the carry flag
signals if an error occurred If true (carry flag=I), the AX register contains the error
115
6. The Disk Operating System PC System Programming
code as returned by the EXEC function of DOS. If the called program executed
correctly, the carry flag is reset (0) and the tennination code of the called progmm,
as returned by DOS function 4DH, is returned by the AX register.
Within this program, EXEPRG displays the current directory using the command
processor. The command processor defaults to the current directory of the current
device.
; •• ** ••• **.** •••••• ******.*** •••• ** ••••• ** •••••••••••• ··········**····i
;* EXEC *;
i*-------------------------------------------------------------------*;
;* Task Calls a program with the help of the *;
;* EXEC function of DOS. In this example *;
;* program the content of the current *;
;* Directory of the current device is displayed. *;
;*-------------------------------------------------------------------*;
116
Abacus 6.8 The EXEC Fu.nction
117
6. The Disk Operating System PC System Programming
pop si
pop di
pop dx
pop cx
pop bx
exeprg endp
118
Abacus 6.9 Memory Allocation from DOS
The second area is designated as the TPA (Transient Program Area). It contains
programs and their environment blocks for execution. The TPA starts after the end
of the operating system area. Depending on the memory requirements of the
individual programs, DOS assigns them different amounts of memory administered
through a special data block preceding every memory range and connected with the
data block of the next memory range. This also applies to memory not assigned to
a program.
This data block, called a memory control block (or MCB) is a 16-byte block
containing a variety of information. An MCB begins at one of the addresses
divisible by 16, and controls memory allocation. DOS looks for the segment
address of the allocated memory range, and is helped in this task through the MCB.
The following table shows the structure of an MCB in memory:
Address Contents Type
+OOH ID ("Z"-last MCB, "M"-another MCB follows) 1 byte
+OlH Segment address of correspondinq PSP 1 word
+03H Number of paragraphs in allocated range 1 word
+05H unused 11 bytes
+lOH Allocated memory ranqe x paragraphs
As the table above illustrates, the MCB contains three ftelds. The ftrst fteld
indicates whether any MCBs follow the current MCB under analysis. The letters
"M" (more MCBs to follow) and "Z" (last MCB) are the initials of one of the
creators of MS-DOS, Mark Zbikowski.
The second fteld speciftes the segment address of the corresponding program's PSP.
This only applies when memory allocation becomes a part of the environment of
the program being handled. in which case the PSP is indicated by the contents of
this field. In most cases, this fteld simply points to the memory range needed by
the program.
The third fteld of the MCB speciftes the size of the corresponding memory range in
paragraphs. Next follows the memory range itself. then any further MCBs after
that (provided that the frrst fteld contains an "M" ID letter). MCBs can be linked
together to create a group, as shown in the ftgure below:
119
6. The Dis/c Operating System PC System Programming
Memory allocation
If the DOS EXEC loader loads and executes a program, this function immediately
requests two data areas through another DOS function. The fU'St of these two areas
stores the environment block, while the second accepts the current program and the
program's PSP. The size of the area made available to a program is difficult to
estimate from the EXEC loader. This is even more difficult for COM programs
than for EXE programs since COM programs are copies of memory contents and
have no information preceding them. DOS therefore defaults to the maximum and
reserves the total available memory for a COM program.
This method worked well in the early days of DOS, but has created other
problems. While only one program could exist in memory at a time in the early
days of DOS, now it's common for one program to load and run a second program,
or even make one of the programs pennanently resident in memory. This can't be
done if no memory exists, as would be the case after loading a COM program.
This is why a COM program should always release the memory it no longer needs
after it starts (see Section 6.4.1 for details on how this happens).
A COM program can only load when a memory range large enough to
accommodate the COM program exists (Plus 256 bytes for the PSP and at least 2
bytes for the stack). The COM program ensures that enough memory is available.
Under the minimum conditions presented above, the program probably won't run
without errors, since few programs can operate with only a 2-byte stack.
EXE program fil~s have a set of information created by the linker. The EXEC
loader can determine the amount of memory required for code segment, data and
stack from this infonnation. The start of the EXE program itself contains
additional information about the amount of memory needed for the program. This
amount defines an upper and lower limit of the additional memory, rather than a
specific number of bytes. The EXEC loader tries to reserve the upper limit of
l20
Abacus 6.9 Memory Allocation from DOS
memory if it can. If this is not possible, the EXEC loader uses the lower limit or
reserves the remainder of memory. If the lower limit of memory cannot be
allocated, the loading process aborts and control returns to the program which
called the EXEC loader (in most cases, the command processor).
The same occurs after program execution when the EXEC loader releases the
reserved memory space for further use, unless prevented by function 31H of
interrupt 21H, called from the program.
Now that you know some of the theoretical aspects of DOS memory management,
here are descriptions of the most important of these DOS functions. Functions
48H, 49H and 4AH are all called through interrupt 21H. The function number is
passed in the AH register.
Function 48H allocates memory. The function number is passed in the AH register
and the number of paragraphs to be reserved (16 bytes per paragraph) is passed in
the BX register. If the requested number of paragraphs can be reserved, the function
returns with the carry flag clear. The AX register indicates the segment address of
the reserved memory. Therefore, it starts at address AX:OOOO. If the program
required more memory than was available, the carry flag is set following the call to
the function and the AX register contains an error code. The BX register contains
the maximum memory available in paragraphs.
Function 49H performs the reverse of function 48H. This function releases
memory previously reserved through function 48H. The segment address of the
memory area to be released is passed in the ES register. This segment address was
originally passed in the AX register when function 48H was called. Normally
function 49H should execute without errors and the carry flag should be reset after
the function call. If this is not the case, it could be caused by either a destroyed
data block (placed ahead of a memory area by DOS), or a segment address passed in
the ES register which doesn't match a reserved memory area.
A third function changes the size of memory area which had been previously
reserved. The memory area can be either enlarged or reduced by using function
4AH. The segment address of the area to be modified is passed in the ES register.
The BX register reserves the number of paragraphs (16-byte units) which the
memory area should contain. The register contents following the call to the
function are identical to those of function 48H.
121
6. TM Disk Operaling System PC System Programming
The assignment of this program is to go through the memory from MCB to MCB
and examine the allocated storage areas. In order to move to the next MCB each
time, it uses the third field within an MCB, which helps it point to the next
MCB. This sets up a loop which will run until the last MCB is discovered, which
will have the letter "z.. in its ID field.
But in order to move through the chain of MCBs, the address of the first link, that
is the first MCB, must be known. DOS lists this within an internal structure
called DIB (DOS Information Block), which is not normally accessible to
application programs, i.e. this represents an undocumented DOS feature (see also
Section 6.15). However, we can find out the address of this structure with the help
of function 52H, which will output the address to the ES:BX register pair when
called.
Curiously, this address points to the second field in the MCB rather than the fU'St
But since it's the first field that contains the address of the first MCB, the
information we're looking for is behind the pointer. Since the pointer on the first
MCB consists of an offset address and a segment address, it is four bytes long and
can therefore be found at the address ES:(BX-4). But be careful with the address
statement, because it makes it seem as though all you have to do is subtract 4
from the contents of the BX register in order to get the effective address of the
desired information in the ES:BX register pair. This will only be successful if the
offset address in the BX register is greater than or equal to 4. But if it is less than
4, the consequences are disastrous, because this leaves a negative number. There is
no such thing as a negative memory address. Let's use an example to make this
clear:
If the value 0 is returned to the BX register as the offset address of the DIB, the
subtraction would give the value OFFFCH. With arithmetic operations, this is
interpreted quite correctly as -4. However, during memory access, this will not
point to the address -4, but rather right to OFFFCH, and therefore to the end rather
than the beginning of the accompanying segment Of course, you won't fmd what
you're looking for there.
The program will help you here, first of all by decrementing the delivered segment
address by 1. This reduces the effective address, which you get by appending the
segment address and the offset address, by 16. Finally, by adding 12 to the offset
address, the effective address is reduced by only 4 and points to the desired memory
location. The address of the first MCB can then be taken from this memory
location without any problems.
The loop which runs through all MeBs and analyzes them begins with this
address. First, some status information on the MCB and the memory it controls is
given. This includes:
122
Abacus 6.9 Memory AllocaJ.ionfrom DOS
Next, the contents of the storage area that belongs to it are examined. We get its
address by incrementing the segment address of the MCB by 1. The first thing
we11 determine is whether we're df'.aling with an environment block in this storage
area. We'll know for sure if we find the string COMSPEC= at the beginning of the
area. This string starts every environment block. If this string is found, the
program proceeds as though this were indeed an environment block, and it lists the
individual environment strings. In front of these, it lists the name of the program
the environment block belongs to, which is located at the end of the environment
block for DOS version 3.0 and higher.
If the program also does not run into this, it can't tell if the memory range
contains program code, data, or whatever. In this case, the program will send the
first 80 bytes of the storage area to the screen as a hex and ASCII dump, in order
to give you a chance to figure it out for yourself.
After this, the user is prompted to strike any key. When the keystroke is received,
the next MCB is examined, and the program will finally end when the last MCB
has been handled.
The following programs in Pascal and C produce the MCB dump. A BASIC
version could not be implemented here because this program works its way
through the entire memory, and BASIC offers only the DEF, SEG and PEEK
commands for this purpose. The use of these commands is too awkward in this
case and would detract from the real task of the program.
Since both programs are very similar in terms of the logic, function calls, and
variables used, they are described together in the following section.
Both access memory with FAR pointers, since the storage areas to be addressed are
outside of their data segments. While Turbo Pascal automatically uses FAR
pointers, C requires selection of the appropriate memory configuration through
Compact, Huge, Large or with the help of Cast operations, each of which
explicitly defines the task with a FAR pointer. This program goes the latter way,
so that it may also be compiled in a memory configuration that works with NEAR
pointers by default (Tiny, Small, Medium).
123
6. TIu! Disk Operating System PC System Programming
Converting separately retrieved offset and segment addresses to one FAR pointer
presents a problem in both languages. This can be done in C with a macro, which
is already defined in the Include file DOS.H in Turbo C, but is missing in
Microsoft C. For this reason, the macro is defined within the C program, in case it
hasn't been previously defined. In Pascal, the address conversions happen with the
help of a small inline procedure, that enters both addresses directly into the
memory locations that form the pointer.
Beyond these brief remarks, the listings should be able to speak for themselves,
since they are fully documented.
Pascal listing: MEMP.PAS
1*********************************·******·**********··** •• *************}
{* MEMP *}
{*--------------------------------------------------------------------*}
{* Description : displays the memory blocks allocated by DOS. *}
{*--------------------------------------------------------------------*}
{* Author MICHAEL TISCHER *}
{* developed on : 08/22/1988 *}
{* last update : 08/22/1988 *}
1************·****_·*****************************···*·**********••• *.*.}
program MEMP;
1******************************·******·****··********·*****************}
{* GetDosVer: determines the DOS version *}
{* Input : none *}
{* Output: the DOS version number (30 for DOS 3.0, 33 for 3.3 etc.) *}
{*.**.*** ••• **•••••••••••••••••••• *•• *******••• *.**** •••• ********* •• ***}
begin
Regs.ah :~ $30; ( function no. for "Get Dos Version"
MsDos( Regs ); { call DOS interrupt $21
GetDosVer := Regs.al * 10 + Regs.ah; { get version number
end;
{*••• *** •••• *•• *••••• *••• *•••*.*.**•• ** •••••• *.*****.*** •••• *.***.** ••• }
(* MK_FP: creates a byte pointer out of the segment and offset *)
{* addresses passed. *}
{* Input - Seq - segment to which the point should point *}
{* - Ofs = offset address to which the pointer should point *}
{* Output the pointer *}
124
Abacus 6.9 Memory Allocationfrom DOS
begin
inline $8B / $46 / $08 / mov ax, [bp+8) (get segment address)
end;
begin
CvHStr :- 'xxxx'; { initialize the string
for Counter:-4 downto 1 do ( run through the 4 digits of the string
begin
end;
HexString := CvHStr; ( return the created string
end;
begin
Regs.ah := S52; { ftn. no.: get address of the DOS info block
MsDos ( Regs ); ( call DOS interrupt $21
(*-- ES: (BX-4) points to the first MCB, create pointer -------------*)
FirstMCB :- MCBPtr2( MK_FP( Regs.ES-1, Regs.BX+12 ) )A;
end;
125
6. The Disk Operating System PC System Programming
begin
HexStr[l] := chr( (HByte shr 4) + ord('O') ); first digit
if HexStr[l] > '9' then convert to letters?
HexStr[l] := chr( ord(HexStr[l]) + 7); ( yes
HexStr[2] :- chr( (HByte and 15) + ord('O') ); ( second digit
if HexStr[2] > 'g' then convert to letters?
HexStr[2] .- chr( ord(HexStr[2]) + 7); ( yes
end;
begin
HexStr :== IZ Z '; ( initialize the hex string
writeln;
write('DUMP I 01234567B9ABCDEF 00 01 02 03 04 05 06 07 OB');
writeln(' Og OA DB OC OD DE OF');
write ('-----+--------------------------------------------------------');
writeln('--------------------------');
Offset :- 0; ( start with the first byte in the block
while Num>O do ( run through the loop ANZ times
begin
procedure TraceMCB;
begin
DosVer := GetDosVer; get DOS version
Done :- false;
NrMCB :- 1; { the first MCB is number 1
CurMCB '- FirstMCB; get pointer to the first MCB
repeat { follow the MCB chain
126
Abacus 6.9 Memory Allocation from DOS
127
6. The Disk Operating System PC System Programming
begin
writeln('unidentifiable (program or data) ');
Dump ( MemPtr, 5); { dump the first 5x16 bytes)
end;
end;
{************************************************************.*****.***}
(** MAIN PROGRAM
{*******************••• **** ••• *.*********************** •••• ********•• **}
**'
begin
ClrScr; clear the screen
TraceMCB; run through the MeBs
end.
C listing: MEMC.C
/************* •••***************.***************** ••**********.********/
1* M E M C *1
1*--------------------------------------------------------------------*1
1* Description : Displays the memory blocks allocated by DOS *1
1*--------------------------------------------------------------------*1
1* Author MICHAEL TISCHER *1
1* developed on : 08/23/1988 *I
1* last update : 05/1211989 *1
1*--------------------------------------------------------------------*1
1* (MICROSOFT C, *I
1* creation : CL lAS IZp memc.c *1
1* call : MEMC *I
1*--------------------------------------------------------------------*1
1* (BORLAND TURBO CJ *1
1* creation : via the Compile-Make command *1
1* (no project file) *I
,********* •• ****************** ••• ********************* *****************1
finclude <dos.h>
'include <stdlib.h>
struct MCB {
byte
segadr psp;
'* 'M'
1* describes an MCB in memory *1
=a block follows, 'Z'
1* segment address of the PSP *1
= end */
128
Abacus 6.9 Memory Allocation from DOS
};
typedef struct MCB far *MCBPtr; '* FAR pointer to an MCB *'
MCBPtr first_mcb()
{
union REGS regs; '* stores the processor registers *'
struct SREGS sreqs; '* stores the seqrnent registers *'
regs.h.ah = Ox52; '* ftn. no.: get address of the DOS info block *'
intdosx( 'regs, ®s, &sregs ); '* call DOS interrupt Ox21 *'
}
printf ("\02X ., *lptr++);
printf ("\n"); '* move to the next line *'
/***********************************************************************
* Function : T R ACE MC B
**------------------------------=-------------------------------------**
129
6. The Disk Operating System PC System Programming
( 1* output a string *f
printf (" 00);
for ( ; *lptr ; ) 1* run through the string to a NUL character *1
printf ( "tc", * (lptr++) ); 1* output a character *1
printf ("\n") ; f* move to the next line *f
I
else 1* no envrionrnent *1
{
130
Abacus 6.9 Memory Allocation from DOS
printf("==-=-------===--=-----=======---==--==--");
while ( ! done ); /* repeat until the last MeB has been processed *j
)
/********************.***********************************.*.*.***** •• **/
/** MAIN PROGRAM **/
/*************************************************.******************.*/
void main ()
(
printf ("\nMEMC (c) 1988 by Michael Tischer\n\n");
131
6. The Disk Operating System PC System Programming
If the user calls a program from the OOS level, the < character redirects input and
the> character redirects output. In the following example, the input comes from
the file IN.TXT instead of the keyboard. The output is written to the file
OUT.TXT instead of the screen:
SORT
After the user enters the above command, DOS recognizes that a program named
SORT should be called. Then it encounters the expression <IN.TXT which
redirects the standard input. This occurs by assigning the handle 0 (standard input,
which formerly pointed to the keyboard) to the file IN.TXT. The expression
>OUT.TXT resets handle 1 to the OUT.TXT file instead of the screen. The affected
handle is first closed, and then the redirected file is opened.
132
Abacus 6.10 DOS Filters
Once the command processor finishes with the command line it calls the SORT
program using the EXEC function (DOS function 4BH). Since the program called
with the EXEC function has all the handles of the calling program available, the
SORT program can input/output characters to handles 0 and 1. Where the
characters originate is unimportant to the program.
After the SORT program completes its work, it returns control to the command
processor. The command processor resets the redirection and waits for further input
from the user.
Pipes
The filter principle as supported by DOS becomes especially powerful through
pipes. This expression comes from the idea of a pipeline used for transporting oil
or gas. DOS pipes have a similar function: they carry characters from one program
to another and permit the connection of various programs with each other.
When this happens, characters output from one program to the standard output
device can be read by another program from the standard input device. As in the
redirection of the standard input/output, the two programs do not notice the
pipelines. The difference between the two procedures is that under redirection of the
standard input/output devices, data can be redirected only to one device or file,
while the use of pipes allows data transfer to another program.
Combined filters
Pipes allow the user to connect multiple filters. The pipe character I is inserted
between the programs to be connected. An example should make this more
understandable: A text file named DEMO.TXT is sorted and then displayed on the
screen in page format. Even though this task appears to be very complicated at
first, it can be performed easily using two DOS filters: SORT and MORE. SORT
sorts the file and MORE displays the file on the screen in page format.
The question is, how can you tell the command processor to do these things? First
SORT is used. This filter is told to sort the file DEMO.TXT. The redirection of
standard input can be used as illustrated at the beginning of the chapter:
After the user enters this command, SORT sorts the file DEMO.TXT then
displays the file on the screen. This display would be much easier to read in page
format. Formatted output can be implemented by redirecting the output from
SORT to a file (for example TEMP.TXT) and displaying this fIle using the
MORE command. The following sequence of commands do this:
133
6. TIu! Disk Operating System PC System Programming
You can use a pipe to connect the SORT filter and the MORE filter, saving the
user typing time. The following command line sends the output from SORT
directly to MORE and immediately displays the sorted file in page format:
SORT <DEMO. TXT I MORE
Any number of filters can be connected using pipes. DOS always executes these
pipelined filters from left to right. It sends the output from the first program as
input to the second program, the second program's output as input to the third
program, etc. The last program can again force the redirection of the output with
the > character so that the final result of the whole program or filter chain travels
to a file or other device instead of the screen.
Note: DOS cannot send data from one filter directly to another because it
would have to execute both filters simultaneously, and the current
version of DOS doesn't have multiprocessing capabilities. Instead,
the following method is used. The input calls the first filter and
redirects its output to a pipe file. After the first filter ends its
processing, it calls the second filter but redirects its input to the pipe
file to read in the output from the first filter. This principle applies
to all filters. The pipe file is stored in the current working directory.
The word "dump" is a computer buzzword for a way to display the contents of a
file in ASCII characters and/or hexadecimal numbers. The DUMP programs below
performs this task as a filter. As the contents are displayed in ASCII format,
DUMP differentiates between normal ASCII characters (letters, numbers, etc.) and
control characters such as carriage return, linefeed, etc. These control characters are
displayed in mnemonic form (e.g., <CR> for carriage return and <LF> for
linefeed). This DUMP filter is fairly simple in structure, yet it can be very useful
to quickly examine a file's contents.
The structure of the DUMP program is typical for a filter. Since DUMP displays a
maximum of nine ASCII characters and/or hexadecimal codes per line, it asks for
nine characters by using the read function from the standard input device. If not
enough characters are available, it reads what characters are available. DUMP
places these characters in a buffer, then converts the characters into ASCII
characters and hex codes. This buffer will accept a comple.te line of 78 characters.
When the buffer processing finishes, the filter uses the handle to write to the
standard output device. This process is repeated until no more characters can be read
from the standard input device.
The following programs are written in Pascal, C and assembly language. Note that
there isn't a BASIC version. The reason is that BASIC, as an interpreted language,
is unsuitable for developing a filter which can be called from the DOS level. A
BASIC compiler would be required for this task.
134
Abacus 6.10 DOS Filters
program DUMP;
BS 8; ASCII-Code Backspace
(*-*--------------._._-_._.__.... ------_._.-----------****.**********)
(* SZ writes the name of a control character into a Buffer *)
(* Input see below *)
(* OUtput none *J
(* Info after the call of this procedure the pointer *)
(* which was passed, points behind the last character of *)
{* the control character name in the Dump-Buffer *}
{********************************************************************}
begin
Buffer [Pointer] :- '<'; ( leads control character
for Counter := 1 to length(Text) do ( transfer Text to Buffer
Buffer[Pointer + counter] := Text[Counter];
Buffer[Pointer + Counter + 1] := '>'; ( terminates control char
Pointer := Pointer + Counter + 2; { Pointer to next character
end;
procedure DoDump;
135
6. The Disk Operating System PC System Programming
if not(Endc) then
begin ( NO
for Counter :- 1 to 30 ( Fill buffer with blanks
do DumpBuf [Counter) := , ';
DumpBuf[31) '= '219; Place Separator between Hex and ASCII
NextA :- 32; ASCII-characters follow separator
for Counter 1 to Regs.ax do ( start processing characters
begin ( read in
HexChr :- ord(NewByte[Counter) shr 4 + 48; ( Hex top 4 bits
if (HexChr > 57) then HexChr :- HexChr + 7; ( convert char
DumpBuf[Counter * 3 - 2) := chr(HexChr); ( store in buffer
HexChr :- ord(NewByte[Counter) and 15 + 48; ( Hex bot. 4 bits
if (HexChr > 57) then HexChr := HexChr + 7; ( convert number
DumpBuf[Counter * 3 - 1J := chr(HexChr); ( store in buffer
case ord(NewByte[Counter) of ( test ASCII-Code
NUL SZ(DumpBuf, 'NUL', NextA); ( NUL-character
BEL SZ(DumpBuf, 'BEL', NextA); ( Bell character
BS SZ(DurnpBuf, 'BS' , NextA); ( Backspace
TAB SZ(DumpBuf, 'TAB', NextA); ( Tab
LF SZ(DurnpBuf, 'LF' , NextA); ( Linefeed
CR SZ(DurnpBuf, 'CR' , NextA); Carriage Return
EOF SZ (DurnpBuf, ' EOF', NextA); ( End of File
ESC SZ(DurnpBuf, 'ESC', NextA); ( Escape
else
begin ( normal character
DumpBuf[NextA] := NewByte[Counter); ( Store ASCII-character
NextA '= succ(NextA) ( Set pointer to next character
end
end;
end;
DurnpBuf[NextA] :- '219; ( Set End character
DumpBuf[NextA+1) :- chr(CR); Carriage-Return followed by Line
DumpBuf[NextA+2] :- chr(LF); ( feed to buffer end
Regs.ah '= $40; ( Function number for writing handle
Regs.bx :- 1; ( Standard output device is handle 1
Regs.cx '= NextA+2; ( Number of characters
Regs.ds := seg(DumpBuf); { Segment address of the buffer
Regs.dx '= ofs(DurnpBuf); ( Offset address of the buffer
MsDos ( Regs ); { Call DOS-Interrupt 21H
end;
until Endc; { repeat until no more characters are available
end;
t****************************************···*****··***.********** ••• *}
(* MAIN PROGRAM •)
{********** •• ***** •• ********* •• ***** ••• *.******** •• *********.********j
begin
DoDump; ( Output Dump )
end.
136
Abacus 6.10 DOS Filters
C listing: DUMPC.C
/********.* •• ******.*****.*************.****.***~***** ***************/
1* DUMPC *1
1*------------------------------------------------------------------*1
1* Task a Filter which reads in characters from the *1
1* Standard input and outputs them as *1
1* Hex and ASCII-Dump on *1
1* the Standard output device *1
1*------------------------------------------------------------------*1
1* Author MICHAEL TISCHER *1
1* developed on : 08/14/87 *1
1* last Update : 04/08/89 *1
1*------------------------------------------------------------------*1
1* (MICROSOFT C) *1
1* Creation MSC DUMPC; *1
1* LINK DUMPC; *1
1* Call DUMPC [<Input] [>Output] *1
1*------------------------------------------------------------------*1
1* (BORLAND TURBO C) *1
1* Creation : tcc dumpc *1
1* Call : DUMPC [<Input] [>Output] *1
/***************************************************** ***************j
j***************************************************** ***************/
1* GETSTDIN: reads a certain number of characters from the Standard *1
1* input device into a Buffer *1
1* Input see below *1
1* Output Number of characters read *1
1***************************************************** ***************/
1***************************************************** ***************/
1* STRAP Attach character to string *1
1 * Input : see below *1
1* Output : Pointer behind the last added character *1
/****************************** ••• **.***~*.*****.***** ***************/
137
60 The Disk Operating System PC System Programming
j**.**.****.**** ••• **** •• *******.*** ••• *** •••• *** ••••• ******** ••• ****/
/* DODUMP reads the characters in and outputs them as Dump */
1* Input : none *1
1* Output : none *I
,.**** ••• ************.***** ••• ********* •••• *** •••• **** •• *********** •• /
void DoDurnp ()
/********.*.* ••••••• *.*.*******.**** •• ***** ••• ********* ••• *** ••• *****,
/** MAIN PROGRAM **1
,.****.********.*** •••• ** •• ***** •••• *** ••••• *****.**** ••• *** •• ***** •• j
138
Abacus 6.10 DOS Filters
void main ()
org 100h
139
6. The Disk Operating System PC System Programming
140
Abacus 6.10 DOS Filters
141
6. The Disk Operating System PC System Programming
<Ctrl><Break>
In order to prevent this, DOS calls interrupt 23H. This interrupt is also known as
the <Ctrl><Break> interrupt. When a program is started, this interrupt points to a
routine which brings about the end of the program. But a program is free to select
a routine of its own, thus maintaining control of what occurs when the user
presses <Ctrl><Break>.
However, the interrupt routine doesn't execute immediately. The break flag
controls when the interrupt routine occurs. This flag can be set at the DOS prompt
using the BREAK (ON/OFF) command from DOS, or with the help of DOS
function 33H, sub-function 1. If the break flag is on, every time a function of
DOS interrupt 21H is called, the keyboard buffer will be checked to see if either
<Ctrl><Break> or <Ctrl><C> has been pressed. If the break flag is off, this check
will be made only when calling the DOS functions that access the standard input
and output devices.
If this test fmds the appropriate key combination, the processor registers are loaded
with the values contained in the DOS function to be executed. Only after this is
interrupt 23H called.
If a program directs this interrupt to a routine of its own, there are several ways to
react. For example, the program could open a window on the screen which asks if
the user would like to end the program. It can also decide for itself whether or not
the program should end.
Maintenance
If the program chooses to halt execution, some form of clean-up routine should
follow. A routine of this type closes all open files, resets any changed interrupt
pointers, and releases any allocated memory. After this, function 4CH can end the
program without returning control to the interrupt 23H caller.
142
Abacus 6.11 <Ctrl><.Break> and Critical EmJr Interrupts
Both ways of handling this situation will be demonstrated in an example at the end
of this section.
Critical error interrupt
Unlike the <Ctrl><Break> interrupt, the critical error interrupt call is rarely a
reaction to something the user does intentionally. It is usually a reaction to an
error that occurs when accessing an external device, such as a printer, disk drive, or
hard disk. While the user can correct the error in many cases (e.g., printer not
turned on), other errors can be caused by hardware failures that require repairs (e.g.,
read error while accessing hard disk).
To make allowances for the various kinds of errors, the critical error interrupt
(interrupt 24H) normally points to a DOS routine that displays the following or a
similar message on the screen and waits for input from the user:
(A)bort (R)etry (I)gnore (F)ail
This clears the screen of the program currently under execution. In addition, this
interrupt doesn't provide a "clean" program end. Like <Ctrl><Break>, the program
is in a situation where files are not properly closed, allocated memory is not
released, etc.
Installing an interrupt handler in a program to replace the DOS handler can help
here, too. DOS enlists the help of a processor register to pass this handler various
information when it is called. This helps the interrupt handler locate the source of
the error. Bit 7 in the AH register indicates either a floppy or hard disk access error
(bit 7 ofO, or some other error (bit 7 on). In addition, the BP:SI register pair
points to the head of the device driver that was being called when the error
appeared. A detailed error code is contained in the lower 8 bits of the DI register,
and the contents of the upper 8 bits are undefined. This returns the following error
codes:
143
6. The Disk Operating System PC System Programming
When called, the; critical error handler can respond by opening a window on the
screen that asks the user to decide to ignore the error, retry the access, or abort the
program. The latter option can only instruct the interrupt to call DOS functions
OlH to OCH. This means that the program ends abruptly, similar to pressing
<Ctrl><Break>. While it is true that calling other DOS functions within the
handler causes no errors in itself, the return to DOS causes a system crash. Such
handlers are also not allowed to end a program through the use of DOS function
4CH. Instead the handler must return to its caller with the help of the lRET
command. With that, DOS expects a code in the AL register that will show it how
to react to the error. It interprets the contents of the AL register as follows:
The last output code in the above list represents the most sensible reaction to an
error that can't be fixed by repeating the operation (as in the example where the
printer needs to be turned on). The receipt of this code invokes the normal ending
of the function call in which the error occurred. The function then sets the carry
flag to signal the error. While this makes a "critical" error and a "normal" error
indistinguishable to the program, it's possible to tell them apart by setting a flag
within the critical error handler.
*******.**.********************•••• **********.************************
CE HAN D
*--------------------------------=-----------------------------------*
Description : Forms the basic structure of an assembler
program, in which the DOS Ctrl-Break and *
Critical Error Interrupt are captured
*--------------------------------------------------------------------*
Author : MICHAEL TISCHER
developed on 9/5/1988
last update : 4/8/1989
144
Abacus 6.11 <Crrl> <Break> and Critical E"or Interrupts
;* call CE HAND *;
;* (please leave the disk drive open so that a *;
;* Critical Error occurs.) *;
;.**.***•••••• **••••••• ********** •••••• *****•••••**••• ····**···········i
i-- constants ---===~=-===================----==--==-=--=--=====--=====-
145
6. TIu! Disk Operating System PC System Programming
146
Abacus 6.11 <Ctrl> <Break> and Critical Error 1nterrupts
147
6. The Disk Operating System PC System Programming
In earlier operating systems, device drivers resided in the operating system code.
This meant that changes or upgrades of these routines to match new hardware were
very difficult, if not impossible. DOS Version 2.0 introduced a flexible concept of
device drivers. This makes it possible for the user to adapt even the most exotic
PC clone to DOS.
Custom drivers
Since communication between DOS and a device driver is based on relatively
simple function calls and data structures, the assembly language programmer can
develop a device driver to adapt DOS to any device. Unfortunately, device drivers
cannot be programmed in a higher level language.
When developing the code for a driver, the same rules are observed as for
developing a COM program (no direct segment access). The difference is that a
device driver starts at offset address OH, and not at lOOH. The end of this section
explains the assembly language implementation in detail.
Drivers
During the DOS boot process, the drivers NUL, CON, AUX, PRN and the drivers
for the disk drives and hard drive (if needed) are loaded and installed. They are
arranged sequentially in memory and connected to each other. If the user wants to
install his own driver, he has to inform DOS using the CONFIG.SYS file. This
text file contains the information which DOS requires for configuring the system.
Contents of the CONFIG.SYS file are read and evaluated during the boot process
after linking the standard drivers. If DOS finds the DEVICE= command, it knows
that a new driver should be included. The name of the driver and perhaps a device
and path designation are indicated after the equal sign.
ANSI.SYS
DEVICE=ANSI.SYS
148
Abacus 6.12 DOS Device Drivers
The new driver is added to the chain immediately following the NUL device driver
(the first driver in the chain). The ANSI.SYS driver replaces the default CON
driver. To ensure that all function calls for monitor or keyboard communication
operate through ANSI.SYS, the ANSI.SYS driver is placed first in the device
group, and the CON driver is moved farther down the chain of devices. Since the
operating system moves from link to link during the search, it finds the new CON
driver (ANSI.SYS) first and uses it. Therefore, the system ignores the old CON
driver as seen in the illustration below:
Before adding
new CON
5'
driver Q
CD
I»
en
:r
cc
3
CD
3
o
-<
!.
...CD
Q.
Z
en
Not all drivers can be replaced with new ones. The NUL driver is always the first
driver in the chain. If you add a new NUL driver, the system ignores the new driver
and continues accessing the original NUL driver. This also applies to the drivers
for floppy disk drives and hard drives. The reason for this is that disk drives have
drive specifiers instead of names such as CON (e.g., A:). A new disk drive can be
added to the system, but since DOS may assign it the name D:, it may not be
addressed by all programs which want to access device A:. This problem can be
avoided by redirecting all device accesses using ooS's ASSIGN command. You
can make the ASSIGN command part of the AUTOEXEC.BAT file. It executes
after adding drivers and executing the CONFIG.SYS file. To redirect all accesses
from drive A: (the first disk drive) to device D: (in this case, a new driver for a new
disk drive), the AUTOEXEC.BAT file must contain the following command
sequence:
149
6. The Disk Operating System PC System Programming
ASSIGN A=D
The drivers for mass storage devices and the drivers such as PRN are handled
differently. ooS has two kinds of device drivers:
Character device drivers communicate with the keyboard, screen, printer and other
hardware on a character by character (byte by byte) basis. Block device drivers can
transmit an entire series of characters during each function call (disks, hard disks,
etc.). The two driver groups differ somewhat through the ways each supports
different functions.
Let's start with character device drivers because their structure is simpler than block
device drivers. Character device drivers transmit one byte for every function call.
They communicate with devices such as the keyboard, display, printer and modem.
A device driver can service only one device. Therefore, individual drivers for
keyboard, display, printer, etc., exist in ooS after booting.
In cooked mode, the device driver reads characters from the device and performs a
test for certain control characters. OOS then passes the character to an internal
buffer. DOS also checks to determine whether any <Enter>, <Ctrl><P>,
<Ctrl><S> or <Ctrl><C> characters exist. If the system detects the <Enter>
character, it ignores any further input from the device driver, even if the specified
number of characters has not yet been read. Then the characters read are copied from
the internal buffer to the buffer of the calling program. If characters are output in
cooked mode, DOS tests for <Ctrl><C> or <Ctrl><Break>. If one of these
combinations is detected, the currently running program stops. Pressing
<Ctrl><S> temporarily stops the program until the user presses any other key.
<Ctrl><P> redirects the output from the screen to the printer (PRN). Pressing
<Ctrl><P> a second time redirects the output from the printer back to the screen.
Raw mode
In raw mode, the device driver reads all characters without testing. If a program
wants to read in 10 characters, it reads exactly 10 characters, even if the user
presses the <Enter> key as the second character of the string. Raw mode transmits
the characters direct to the calling program's buffer, instead of using an internal
DOS buffer. During character output, raw mode doesn't test for <Ctrl><C> or
<Ctrl><Break>.
150
Abacus 6.12 DOS Device Drivers
DOS function 44H of interrupt 21H defmes the mode of the character device driver
(see the end of this section for a detailed description of this interrupt).
A block device driver normally communicates with mass storage devices such as
floppy or hard disks, or high speed cassette tapes. For this reason, they
simultaneously transmit a number of characters which are designated as a block. In
some cases, a single call to a function transmits several blocks of data. The sizes
of these blocks can differ from one mass storage device to another, as well as
within one particular mass storage device.
Everyone of these devices must have a flle allocation table (FAn and a root
directory. Block device drivers make no distinction between cooked and raw modes.
They always read and write the exact number of blocks unless an error is detected.
Access
There are several ways to access a device driver. Character device drivers are
accessed using the normal FCB or handle functions by simply indicating the name
of a driver (e.g., CON: instead of a fllename). A block device driver is accessed
using the normal DOS functions (flle, directory, etc.) by using the drive designator
assigned by DOS during the boot process.
Even though the two types of device drivers differ in some important details, they
do have similar structures. Each has a device header, a strategy routine and an
interrupt routine (a different kind of interrupt from the ones you've read about up
until now).
151
6. The Disk Operating System PC System Programming
Device beader
The device header appears at the beginning of each device driver and contains
infonnation needed by OOS for implementing the driver.
The fIrst two fIelds are the link to the next driver (offset and segment address) in
the chain of device drivers. The memory locations required for these link fIelds
must be reserved by the programmer, but OOS fIlls in when the driver is installed
in the system. The next fIeld of the device header is the attribute word. The
attribute word describes the device driver and tells OOS, among other things, if it
is a block or character device driver.
\7000
RAM
OOH nff.... t- "nnr...... "f n .. "j- nriv.. r 11 worn)
+ 02H Seament address of next driver 11 word)
+ 04H Device attribute 11 word)
+ 06H
+ 08H
+OAH
Offset address of strateqv routine (1 word)
Offset address of interrupt routine (1 word)
Driver name from character driver (8 bytes)
or number of devices used by block driver
/F= "
'
152
Abacus 6.12 DOS Device Drivers
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 bit
1=Cl.IT9rlt sIa"ldard
I I I IXI IXIXIXIXIXIXIXI I I I I-+ output delAce
I 1=a.rrent sta1dErd
il>Ut deIAce
... 1=a.rrent
dockdeW:e
. 1=OJrrent
NULdeW:e
. 1=Medum
cha1ge
mcognized
1=OOn-1BM
~ bmat
(I:.b:k diver)
1~lJl1i
i1s1n.Ja3d
L...-. (cf1crac.U <:tiwr)
1=1OCTL
support
. 0=I:lI0ck driI.er
1=Ghaa::U ctiwr
Only bits 11 through 15 are used by a block driver. The IOCTL bit tells DOS if
this driver supports the IOCTL function of DOS. The end of this chapter and the
descriptions of functions 3 and 12 describe this function in greater detail. Bit 11
fIrst appears in DOS Version 3 and should be 0 in earlier versions. A block driver
indicates whether a medium change is recognized on the device supported (e.g., a
floppy disk drive). If the bit is set, the driver must support a few additional
functions.
The next two fields contain the offset address of the strategy routine and interrupt
routine. The last field contains the name of the device driver if it is a character
device driver. If the name is less than eight characters in length, blank spaces
(ASCII code 32) pad the remaining characters. If it is a block device driver, the fIrst
byte of this field contains the number of logical devices supported by the driver.
The remaining seven bytes of this fIeld remain unused and contain the value O.
Strategy routine
DOS calls the strategy routine first to initialize the driver, then repeatedly before
each subsequent I/O request from the driver's interrupt routine. The address of a data
153
6. The Disk Operating System PC System Programming
The request header, whose address is passed to the strategy routine, always contains
at least 13 bytes and contains information which tells the driver how to perfonn
the upcoming operation. Depending on the operations performed, further
information can be added to the end of the request header which differs depending on
the operation.
RAM
+ OOH Data block lenqth in bytes
+ 01H Device number in communication (1 mdl
(1 word) \ 0000:0000
+ 02H Command code (1 word)
+ OSH Reserved (8 bytes)
+ DDH Media descriptor (1 byte)
+ OEH Buffer offset address (1 word)
+
+
+
1DH
12H
14H
Buffer segment address
Number
Starting sector
(1
(1
(8
word)
word)
bytes) / "
DOS calls the interrupt routine immediately after calling the strategy routine. Its
first task is to save the processor registers that will have their contents changed by
the various functions of the driver to the stack. Then it obtains the command code
from field 3 of the request header and calls the appropriate command code routine.
After executing the routine, it fills in the status field of the request header and
restores the processor registers from the stack. As a last step it returns control to
the calling DOS function.
Status field
The value of the status field specifies whether the function executed without error,
or if an error occurred during execution. For this reason, every driver function must
set the DONE bit (bit 8) in the status field. This DONE bit must be set even if the
function is a dummy (non-perfonning) function.
154
Abacus 6.12 DOS Device Drivers
1=error
O=medium write
1=ready 1=unknown device
2=drive not ready
1=busy 3=unknown command
4=read (CRC-) error
5=parameter data block
has a false length
6=search error
7=unknown medium
8=sector not found
9=printer out of paper
10=write error
11 =read error
12=common error
Under DOS Version 2, any installable device driver must support 13 functions,
numbered from 0 to 12, even if their only action consists of setting the DONE
flag in the status word. DOS Versions 3 and 4 include four additional functions
which can be supported, but are not required. Some of these functions concern one
of the two driver types, while others apply to both driver types (e.g.,
initialization). Unused functions must at least set the DONE flag of the status
word. Let's look at the various functions in detail according to their function
numbers.
Request header
Every function described here receives its arguments from the request header (whose
address is passed by DOS to the strategy routine) and stores its "results" in the
request header. For this reason, the offset address to the arguments, relative to the
beginning of the request header, is passed to the specified function. These
arguments are later transferred to variables. Besides this offset address, a flag
indicates whether this information consists of a byte, word or PTR. The PfR data
type represents a pointer to a buffer and consists of two adjacent words. The fIrst
word is the offset address of the buffer. The second word is the segment address of
the buffer.
155
6. The Disk Operating System PC System Programming
OOS calls this function during the system boot procedure to initialize the device
driver. This function can involve hardware initialization, setting various internal
variables to their default values, or the redirection of interrupts. Since the entire
operating system has not been completely initialized at this point, the
initialization routine can only call functions 1 through OCH and 30H of OOS
interrupt 21H. These functions can be used to determine the OOS version number
and to display a driver identification message on the screen. Even if the newly
linked driver is a CON driver, the output to the display occurs through the old
CON driver, because there are no new drivers linked into the system after
completion of the initialization routine.
In this case, the device name is ANSI.SYS, which assigns the standard ANSI
escape sequences for screen control to the PC. The memory address passed to the
initialization routine points to the character following the equal sign (in this case,
the A of ANSI.SYS). This makes it possible to store additional information
following the name of the device driver. This information is ignored by OOS, but
can be read by other routines.
The initialization routine must retlD1l four parameters to the calling OOS function.
The first parameter is the status of the function, i.e., the indication of whether the
function has executed correctly. For a block device driver, the number of logical
devices supported must also be passed. This information could also be obtained
from the device driver's header, but is ignored by OOS.
156
Abacus 6.12 DOS Device Drivers
The next parameter that the device driver must pass to OOS is the highest memory
address which it occupies or uses. This lets DOS know where the next device
driver can be installed.
BPB
If the driver is a block device driver, the last argument passed must be the address
of an array which contains an entry for every logical device. This array contains the
addresses of BIOS parameter blocks (BPBs). The address is passed as two words,
the fIrst word contains the offset, and the second word contains the segment address
of the array. The fIrst two words within this table are the address for the fIrst
logical device supported. The next two words indicate the address for the second
logical device, etc. The BPB, described in detail in Section 6.12, is a data block
containing information which describes a logical device. If all or some of the
logical devices have the same format, all entries in the BPB address table can point
to a single BPB.
157
6. The Disk Operating System PC System Programming
This function is used only with a block device driver. A character device driver
should merely set the DONE flag of the status word and exit. This function is used
by DOS to determine whether the media (diskette) has changed. It is often used
when examining a disk directory. If the disk medium was not changed since the
last access, DOS still has this information in memory, otherwise DOS must reread
the information from the media which delays the execution of the current task.
In some cases, as with floppy disks, the answer to the question is fairly
complicated. For this reason DOS permits function 1 to answer not only with
"yes" and "no", but also with "don't know." In any case, the answer affects further
DOS activity.
If the media is unchanged, access to the media can take place immediately. If the
media was changed, however, DOS closes all internal buffers related to the current
logical device. This causes the loss of all data which should have been transmitted
to the media. Then it calls function 2 of the current device driver, loads the FAT
and the root directory. If the media check function answers with "don't know," the
additional steps taken by DOS depend on the status of the internal buffers related to
the current logical device. If these internal buffers are empty, DOS assumes that
the media was changed and acts as if function 1 answered "yes." If the buffers
contain data which should have been transmitted to the media, DOS assumes that
the media is intact and writes the data. If the media was indeed changed, the data
written to a changed media may damage the new diskette's file structure.
Since subsequent processing depends on the response from the media check
function, the driver should handle the response carefully. Before enabling the
mechanism used by the function to respond, the function examines the parameters
passed to it. If the driver supports several logical devices, the first parameter is the
158
Abacus 6.12 DOS Device Drivers
number of devices. Next is a media descriptor code. This code contains information
about the type of media last used in the current logical device. Only devices which
can handle several different formats can use this task. For example, AT disk drives
which can use both 360K and 1.2 megabyte diskette formats.
If the media check function determines that the medium in a device is non
removable (e.g., a fixed disk), it can always respond "not changed". If, on the other
hand the device media can be changed (e.g., a disk), the correct response can only
be determined by fairly complex procedures. If these procedures are not used, the
response should be "don't know".
For the sake of completeness, here are the three procedures which provide fairly
accurate results.
Since a device with changeable media has an opening and closing mechanism, the
function should check to determine whether the media was removed. However, it
cannot determine if the removed media is identical to the newly inserted medium.
If the media has a name, the function should read this name to determine whether
the media was changed. This procedure only makes sense if every media has a
unique name.
The disk drive procedure used by DOS hinges on the fact that changing medium
takes some time. DOS assumes that even a user that can move fast needs about
two seconds to remove a diskette from a drive and insert a new diskette in the same
drive. If two consecutive diskette accesses occur less than two seconds apart, DOS
assumes that no diskette change occurred.
A byte in the data block is used to indicate changes. The value -1 (FFH) means
"changed", 0 means "don't know" and 1 means "not changed".
If the media was changed, the device driver signals a media change (bit 11 in the
device attribute = 1), the address of a buffer must be passed to DOS Version 3 and
newer, which contains the volume name of the previous media. This name must
be stored there as an ASCII string and terminated with an end character (ASCII
code 0).
159
6. The Disk Operating System PC System Programming
This function is used only by block device drivers. A character device driver should
just set the DONE flag of the status word and exit. DOS calls this function when
the media check function determines that the media was changed. This function
returns a pointer to a new BPB for the media
As you can see by the layout of the calling parameters. the device number media
descriptor and a pointer to a buffer are passed to this function by DOS. If the
device is a standard format (bit 13 of the device attribute =0). then the buffer
contains the first sector of the FAT.
Returned~eters of function 2:
Offset 3Jwor<D J Status word
Offset 18 (pte) I Address of the BPB of addressed device
This function passes control information from the character or block device driver
to the application program. It can only be called through function 44H of interrupt
21H if the IOCTL bit in the device attribute word in the device driver header is set
Different parameters are passed to the function. depending on whether the driver is
a c.JaraCter or a block device driver.
A character device driver is passed the number of characters to be transferred and the
address of a buffer for the transfer of the data
A block device driver is passed the device number, the media descriptor byte, the
address of the buffer to be used for the data transfer, the pointer to the flTSt sector to
be read and the number of sectors to be read.
160
Abacus 6.12 DOS Device Drivers
Function 4: Read
This function reads data from the device to a buffer specified in the calling
parameter. Should an error occur reading the data. the error status must be set.
Additionally the function must report the number of sectors or bytes read
successfully. Simply reporting an error is not good enough.
This function is used by a character device driver to test for unread characters in the
input buffer. A block device should set the DONE flag of the status word and exit.
DOS tests for additional characters using this function. If more characters exist, the
busy bit must be cleared (set to 0) and the next character passed to DOS. The
character that is passed remains in the buffer so that a subsequent call to a read
161
6. The Disk Operating System PC System Programming
function will return this same character. If no additional characters exist, the busy
bit must be set (set to 1).
Function number 5
Returned...Jllll1!!!!eters of function 5:
If a character is waiting to be read from the input buffer, the busy bit is cleared (set
to 0). If a character is not in the input buffer, the busy bit is set (set to 1).
When a character is waiting to be read, the Input Status function (06H) resets the
status word busy bit to 0 and returns the character to DOS. The character is not
removed from the buffer and is therefore non-destructive. This function is
equivalent to a one-character look ahead.
Function number 6
Returned~ameters of function 6:
J
Offset 3 (word) Status word: Characters already in buffer =0; Read request to
l.J?l.!Ysical device = 1
This function clears the internal input buffers of a character device driver. Any
characters read but not yet passed to DOS are lost when this function is used. A
block device driver should set the DONE flag of the status word and exit
Function number 7
162
Abacus 6.12 DOS Device Drivers
Function 8: Write
This function transfers characters from a buffer to the current device. If an error
occurs during transmission, the status word is used to indicate this error. Both
block and character devices use this function.
The parameters used for this function depend on whether the driver is for a character
or block device. Both pass a buffer address from which a certain number of
characters should be transferred. A character device driver is passed the number of
bytes to be transferred in addition to this information.
A block driver is passed the number of sectors to transfer (not the number of
characters), the number of the device to be addressed, its media descriptor and the
address of the first sector on the medium.
Should an error occur writing the data, the error status must be set. Additionally
the function must report the number of sectors or bytes written successfully.
Simply reporting an error is not good enough.
This function is similar to function 8, but with the difference that the characters
written are reread and verified.
163
6. The Disk Operating System PC System Programming
This function indicates whether the last write operation to a character device is
completed or not A block device should set the DONE flag in the status word and
exit.
If the last write operation is complete then the busy bit of the status word is
cleared; otherwise the busy bit is set to 1.
Function number 10
I
Returned ~eter of function 10:
Offset 3 (word) Status word: The busy bit is 1 if the last character output
has not been completed
This function completely clears the output buffer even if it contains characters
waiting for output. A block device should set the DONE flag on the status word
and exit.
Function number 11
164
Abacus 6.12 DOS Device Drivers
This function passes control infonnation from the application program to the
character or block device driver. It can only be called through function 44H of
interrupt 21H provided the IOC1L bit in the device attribute word in the device
driver header is set. Different parameters are passed to the function, depending on
whether the driver is a character or a block device driver.
A character device driver is passed the number of characters to be written and the
address of the buffer from which these characters are transferred.
A block device driver is passed the device number (in case the driver services
logical devices), the media descriptor byte, the address of the buffer from which the
data is to be written, the number of the ftrst sector to be written and the number of
sectors to be written.
A character device driver returns the number of bytes written. A block device driver
returns the number of sectors written.
The following four functions are supported by OOS version 3.0 and higher.
This function can be used only if the OCR (Open/Close/RM) bit in the device
attribute word in the device driver header is set Its task differs, depending whether
it is a character or block driver.
A block driver uses this function every time a ftle is opened. This function
detennines how many open fIles exist on this device. Use this command carefully,
since programs which access FCB function calls tend not to close open ftles. This
problem can be avoided by assuming during every media change that no ftles
us
6. The Disk Operating System PC System Programming
remain open. For devices with non-changeable media (e.g., a hard disk) even this
procedure may not help.
Within a character driver, this function can send an initialization string to the
device before transmitting the data. This is an advantage when used for
communication with the printer. The initialization string should not be included in
the driver, but can be called, for example, with the IOCTL function of interrupt
21H, which calls function 12 of a driver to transmit it from an application
program to the driver. The function can also be useful because it can prevent two
processes (in a network or in multiprocessing) from both accessing the same
device.
For the devices CON, PRN and AUX, this function is not called since they are
always open.
IReturned :r I
Offset 3 (word
eter of function 13:
Status word
This function is the opposite of function 13. This function can only be addressed if
the OCR bit in the device attribute word of the device driver header is set Its task
differs, depending whether it is a character or block driver.
A block driver calls it after closing a file. This can be used to decrement a count of
open files. Once all files on a device are closed the driver should flush the buffers
on removable media devices, because it is likely that the user is about to remove
the media.
A character driver can use this function to send some closing control information
to a device after completing output. For a printer this could be a formfeed. As in
function 13, the string could be transmitted from an application program using the
IOC1L function.
166
Abacus 6.12 DOS Device Drivers
This function indicates if the media in a block device can be changed or not. This
function is used only if the OCR bit in the device attribute word of the device
driver is set. A character device driver should set the DONE flag in the status word
and exit.
If the media can be removed, the busy bit is cleared; otherwise it is set to 1.
I
Returned parameter of function 15:
Offset 3 (word) Status word: If the media can be removed, the busy bit must
contain the value 0
This function transfers data from a buffer to an output device until the device is
busy (i.e., can no longer accept more characters). As this function is supported by
character devices, a block device driver should set the DONE flag on the status
word and exit.
This function works particularly well with print spoolers, through which files can
be sent to a printer as a background activity while a program executes in the
foreground. It is possible that not all of the characters in the transfer request will
be sent to a device during this function call. This is usually not an error, it could
be the result of the device becoming busy. The function is passed the number of
characters to be transmitted as well as the buffer address. If the output device
indicates during transmission that it can no longer accept additional characters, it
indicates the number of characters successfully transferred and returns control to the
device driver.
167
6. The Disk Operating System PC System Programming
The clock driver is a character device driver whose only function is to pass the date
and time from DOS to an application. The clock driver can also have a different
name, since DOS identifies it by the fact that bit 2 in the device attribute word of
the device driver header is set to I, instead of by name. Bit 15 must also be set
since the clock driver is a character device driver. Functions 2AR to 2DH of DOS
interrupt 21H read the date and time and call the driver. A clock driver must
support only functions 4, 8 and 0 (initialization). During the call of function 4
(reading), the date and time pass from the driver to DOS. DOS can set a new date
and time with function 8. Both functions have the time and date passed in a buffer
of 6 bytes in length.
+ OSH
Hundredths of seconds
Seconds
(1 byte)
(1 byte) V r
The date format is unusual. Instead of passing the month, day and year separately,
DOS passes the number of days elapsed since January I, 1980 as a 16-bit number.
A fairly complex formula converts this number into normal date format, taking
leap years into account. The clock driver normally uses function 0 and 1 of the
BIOS interrupt IAH to read and set the time.
Clocks on AT models
AT and AT-compatible computers have a battery powered realtime clock.
Functions 0 and 1 of interrupt lAR use a software controlled time counter and not
the battery powered realtime clock. When the computer is rebooted, the date and
time previously set with driver function 8 is cleared. You can use the clock driver
to access the realtime clock using functions 2 and 5 of interrupt lAH instead of
function 0 and 1.
168
Abacus 6.12 DOS Device Drivers
Now that you have some familiarity with the functions of the different device
drivers, you can look toward developing your own personal device driver. Here are
the steps which take place before and after calling a device driver function.
A chain of events begins when a DOS function which handles input and output is
called using interrupt 21H. Calling one of these functions can in turn call a series
of other functions and corresponding read and write operations.
Open
One example of this is when the Open function 3DH is called to open a file in a
subdirectory. First of all, before it can be opened, DOS must find the file. This
may require the searching of a set of directories instead of just reading in the FAT.
During each access of interrupt 21H, DOS determines which of the available device
drivers should be used to read or write characters. When this happens, DOS sets
aside an area in memory to store the information required by the device driver.
For files, DOS must convert the number of records to be processed into logical
sector numbers. DOS then calls the strategy routine of the device driver, to which
it passes the address of the newly created data block (request header). Then the
interrupt routine of the driver is called, which stores all registers. It isolates the
function code of the requested function from the data block and starts to process the
function.
If the addressed driver is a character device driver, the function only has to send the
characters to the hardware or request the characters to be read.
Block devices
For a block device (e.g., a mass storage device such as a floppy or hard disk) the
logical sector number must be converted into a physical address before a read or
write access. The logical sector number is broken down into a head, track and
physical sector number.
Mter the read or write operation ends, the driver function must place a result code
in the status field of the request header to be returned to the calling DOS function.
Next the contents of all registers are restored and control is returned to the calling
DOS function, which, depending on the result of the driver function, sets or resets
the carry flag and places any error code into the AX register. The interrupt function
then returns control to the routine which called interrupt 21H.
169
6. The Disk Operating System PC System Programming
The IOCTL function itself is one of many functions addressable from DOS
interrupt 21H. Its function number is 44H. Three groups of sub-functions are
accessible:
Device configwation
Data transmission
Driver status
The number of the desired sub-function is passed to the IOCTL function in the AI..
register. After the function call, the carry flag indicates whether the function
executed correctly. A set carry flag indicates the occurrence of an error and the error
code can be found in the AX register.
Sub-functions 6 and 7 can determine the status of a character device driver. Sub
function 6 can determine if the device is able to receive data. Sub-function 7 can
determine if the device can send data. The handle of this device is passed in the BX
register.
If the device is ready, both functions 6 and 7 return the value FFH in the AI..
register.
Sub-function 2 reads control data from the character device driver. The handle is
passed in the BX register and the number of bytes to be read is passed in the CX
register. In addition, the DS:DX register pair contain the address of the buffer into
which the data will be read. If the carry flag is clear, then the function was
successful and the AX register contains the number of characters read. If the carry
flag is set, then there was an error and the AX register contains the error code.
170
Abacus 6.12 DOS Device Drivers
The return codes are the same as for sub-function 2. These two sub-functions are
used to pass information between the application program and the device driver.
Block device driver status
Sub-functions 4 and 5 have the same task as sub-functions 2 and 3. However, they
are used for block devices and not character devices. Instead of passing the handle in
register BX, you pass the drive code (O=A, I=B, etc.) in the BL register.
Sub-function 0 is used to get device information for a specified handle. The sub
function nwnber is passed in the AL register and the handle in the BX register. The
function returns the device information word in the DX register.
For block devices:
bit 15 = reserved
1 if raw mode
bit 4 reserved
Two final interrupts are sometimes used by block device drivers. These two
interrupts, 25H and 26H are used to read from and write to the disk drive. You can
use these interrupts, for example, to process disks that were formatted using a
"foreign" operating system.
171
6. The Disk Operating System PC System Programming
Major headaches in developing a device driver occur because of problems that arise
during the testing phases of a new driver. First, a device driver must load into a
memory location assigned to it by DOS, at an address unknown to the
programmer. Second, a newly developed eON driver can't be tested using the
DEBUG program, since DEBUG uses this driver for character input and output.
We recommend that after you write the actual driver, you write a short test
program that calls the individual functions in the same manner as DOS, but
without having the driver installed as part of DOS. The advantages to this are that
everything executes under user control, and the whole process can be corrected with
a debugger. In any case, a new device driver (especially a block device driver)
should only be linked into the system after it has been tested completely and has
been proven to be error-free.
Note: When working with a hard disk, prepare a floppy system diskette
before test booting the system from the hard disk with the new driver
installed for the first time. If a small bug should exist in the new
driver, and the initialization routine hangs up, the booting process
will not end and DOS will be out of control. In such a case, the only
remedy is to reset the system and boot with a DOS diskette in the
floppy drive. Once DOS loads, you can then access the hard disk and
remove the new driver.
This section contains a sample device driver for each of the three different types of
device drivers, to demonstrate the information you've read about so far.
The first program is a character driver which corresponds exactly to the format of a
normal console driver. The second program is a block device driver which creates a
160K RAM disk. The final program is a DOS clock driver to support an AT
computer realtime clock.
***********************************************.****** ****··*********i
CONDRV *;
*-------------------------------------------------------------------*;
Task : This program represents a nonnal Console *;
Driver (Keyboard and Display Monitor). It should *;
serve as a framework for a driver in the form of *i
an ANSI.SYS driver. *;
172
Abacus 6.12 DOS Device Drivers
i*-------------------------------------------------------------------*i
;* Author MICHAEL TISCHER *;
;* developed on 8.4.87 *;
,. * last Update 9.2l.B7 *.
:*-------------------------------------------------------------------*;
;* assembly MASM CONDRV; *;
,. * LINK CONDRV; *.,
;* EXE2BIN CONDRV CONDRV.SYS *;
;*-------------------------------------------------------------------*:
;* c..ll Copy into Root Directory, copy the comnand *;
;* DEVICE-CONDRV.SYS into the file CONFIG.SYS *;
, and then boot t he System. *;
i···**********************************************···· ****************:
code segment
assume cs:code,ds:code,es:code,ss:cocte
173
6. The Disk Operating System PC System Programming
strat endp
;---------------------------------------------------------------------
intr proc far ;Interrupt routine
intr endp
i---------------------------------------------------------------------
dummy proc near ;This routine does nothing
174
Abacus 6.12 DOS Device Drivers
durmny endp
;---------------------------------------------------------------------
no_sup proc near ;This routine called for all functions
;which should really not be called
mov aX,B003h ;Error: Command not recognized
ret ;back to caller
;---------------------------------------------------------------------
store c endp
;--------_._-----------------------------------------------------------
read proe near :read a certain number of characters
;from the keyboard to a buffer
175
6. The Disk Operating System PC System Programming
read endp
; ----------'-----------------------------------------------------------
read b endp
;---------------------------------------------------------------------
del in b proc near ierase input buffer
-
mov ah,1
;still characters in the buffer?
int 16h
;Call BIOS key board interrupt
je del e
;no character in the buffer --> END
write endp
;---------------------------------------------------------------------
init proc near ;Initialization routine
176
Abacus 6.12 DOS Device Drivers
;=====---===-==-======--=~=============--==---=-----===-==============
code ends
end
The header of this driver describes a character device driver which handles both the
standard input device (keyboard) and the standard output device (monitor). Mter
linking it into the system, setting the two bits in the device attribute calls this
driver on all function calls previously handled by the CON driver. Like any other
driver, this driver has a strategy routine and an interrupt routine. The former stores
the address of the datablock in the variable DB_PI'R.
The interrupt routine saves the contents of all registers which will be changed by it
on the stack and gets the routine number to be called from the data block. It then
checks whether CONDRY supports this function. If not, it jumps directly to the
end of the interrupt routine and sets the proper error code in the status field of the
request header which was passed to the routine. Then it restores the registers which
were saved on the stack and returns control to the calling DOS function.
For any of the functions that are supported by the device driver, the offset address
of a routine to handle a particular function is determined from the table labeled
FK.T_TAB. Notice that the routines named DUMMY and NO_SUP appear several
times. DUMMY is for all functions which apply only to block device drives and
therefore are not used in this driver. The DUMMY routine clears the AX register
and sets the BUSY bit in the status word. The NO_SUP routine handles any
functions which cannot be used since the drive attribute for CONDRY does not
support these functions.
The STORE_C routine can be accessed from the lower level routines in this driver.
Its purpose is to store a character in the internal keyboard buffer of the driver. The
driver really shouldn't have this buffer available since BIOS (whose functions are
used by the driver to read characters from the keyboard) also has such a buffer. The
problem is that the BIOS always returns two characters when pressing a key with
extended codes (cursor keys, function keys etc.). If the higher level functions of
DOS only ask for one character at a time from CONDRY, the second character
must not be lost. It should be stored in a buffer and delivered to DOS by the read
function on the next call. This is STORE_C's task.
Reading characters
The next routine is the READ function. It obtains the number of characters to be
read from the request header passed by DOS. If it is 0, the routine is terminated
immediately. If not, then a loop starts which executes once for every character read
It rrrst tests for characters still stored in the internal keyboard buffer. If so, a
character is passed to the buffer of the calling function. If no additional character
177
6. The Disk Operating System PC System Programming
exists in the keyboard buffer, function 0 of the BIOS keyboard interrupt 16H
inputs a character from the keyboard. This character is also passed to the internal
keyboard buffer. If it's an extended keycode, it is divided into two characters. The
next step removes a character from the internal keyboard buffer and passes the
character to the buffer of the calling function. The process repeats until all
characters requested have been passed to DOS. Then the routine ends.
The higher level DOS functions also call the function named READ_P. It tests
whether a character was entered from the keyboard. If not, it sets the BUSY bit in
the status field of the request header passed by DOS, and returns to the calling
function. If a character was entered without having been read, the driver reads this
character and passes it to the calling DOS function in the request header, and resets
the busy bit. The character remains in the keyboard buffer, and on a subsequent call
of the read function, it is again passed to DOS. To test the availability of a
character, the READ] function uses function 1 of the BIOS keyboard interrupt
16H.
The function DEL_IN_B also gets called by the higher level DOS functions.
DEL_IN_B deletes the contents of the keyboard buffer. It removes characters from
the buffer using function 0 of the BIOS keyboard interrupt until function I
indicates that no more characters are available. This ends the function and it returns
to the calling function after the busy bit is reset
Writing characters
WRITE takes the number of characters from a buffer passed by DOS and displays
the characters on the screen. This routine uses function OEH of the BIOS video
interrupt. Once all characters have been displayed, it sets the BUSY bit in the
status field and ends the function. This function also executes when the higher
level DOS functions call the Write and Verify functions.
Initialization
The last function, the initialization routine, is called first by DOS. Since
CONDRV does not initialize variables and hardware, the routine simply enters the
driver's ending address into the passed request header. The routine returns its own
starting address since it will never be called again, and is the end of the chain of
drivers.
In its current form the driver has little use, since it uses only those functions
already available to the CON driver of DOS. It would be more practical if an
enhanced driver like ANSI.SYS were developed, through which screen design could
be more tightly controlled. For example, it's possible that such a driver would
have complete windowing capability which could be accessed from any program,
in any programming language.
178
Abacus 6.12 DOS Device Drivers
assume cs:code,ds:code,es:code,ss:code
erst b equ this byte ;this is the first byte of the driver
db 1 ;a device is supported
fkt tab dw of fset init ; Funct ion 0: Ini t iali zat ion
dw offset med test ;Funct ion 1 : Media Test
dw offset get=bpb ; [unction 2: created BPS
dw offset read ; function 3: direct reading
dw offset read ;Function 4: Read
dw offset dummy ; Function 5: Read, remain in Buffer
dw offset dummy ;Function 6: Input-Status
dw offset dummy ; Funct ion 7: Erase Input-Buffer
dw offset write ; Funct ion 8: Write
dw offset write ; Function 9: Write & Verifi cation
dw offset dummy ;Function 10: Output -Stat us
dw offset dummy ;Function 11: Erase Output-Buffer
dw offset write ;Function 12: direct Write
dw offset dummy iFunctlon 13: Open (after DOS 3.0)
dw offset dummy ; Funct ion 14 : Close
179
6. The Disk Operating System PC System Programming
:-- the Boot routine not included since a System can not----
be booted from a RAM-Disk
strat endp
:---------------------------------------------------------------------
intr proc far ;Interrupt routine
180
Abacus 6.12 DOS Device Drivers
intr endp
i---------------------------------------------------------------------
init proc near ;Initialization routine
181
6. The Disk Operating System PC System Programming
init endp
:---------------------------------------------------------------------
dummy proc near ;This Routine does nothing
dummy endp
;---------------------------------------------------------------------
med_test proc near ;Media of RAM-Disk
;cannot be changed
:---------------------------------------------------------------------
get_bpb proc near ;Pass address of BPB to DOS
get_bpb endp
i---------------------------------------------------------------------
no_rem proc near ;Media of RAM-Disk cannot be changed
mov ax,20 ; Set busy-bit
ret ;back to caller
182
Abacus 6.12 DOS Device Drivers
no rem endp
;---------------------------------------------------------------------
write endp
j---------------------------------------------------------------------
read proc near
read endp
move endp
183
6. The Disk ()perating System PC System Programming
;---------------------------------------------------------------------
code ends
end
This driver is similar to the CONDRY driver. The biggest difference between the
two lies in the functions which each supports.
Note: The initialization routine INIT here is more comprehensive than the
CONDRY initialization routine, and remains in memory after the end
of execution even though it is no longer needed. You'll see why this
is so in the paragraph below entitled "The INIT routine" .
First, this routine fmds the DOS version number using function 30H. If the
version number equals or is greater than 3, the request header passed by DOS
contains the device designation of the RAM disk. The system reads the
designation, changes it to a character and places the character into the installation
message. DOS function 09H is used to display this message on the screen.
Next, the program computes the ending address of the RAM disk. Since the actual
data area of the RAM disk starts immediately after the last routine of this driver,
160K is added to the program's ending address. Further, the address of a variable
(BPB_P1R) containing the address of the BIOS parameter block is passed to DOS.
This variable describes the RAM disk's format. In this case, it tells DOS that the
RAM disk uses 512 bytes per sector. Each cluster is made up of one sector and
only one reserved sector (the boot sector) exists. In addition, only one FAT exists.
Additional information indicates that a maximum of 64 entries can be made in the
root directory and that the RAM disk has 320 sectors available (160K of memory).
The FAT occupies a single sector, and the media descriptor byte FEH designates a
diskette with one side and 40 tracks of 8 sectors each.
These parameters are then placed into the request header of DOS and the segment
address of the data area of the RAM disk is calculated (which the driver itself
requires, DOS does not need this information).
184
Abacus 6.12 DOS Device Drivers
with these data structures and would crash the system. This is why the
initialization routine is not at the end of the last routine of the driver, which would
place it at the beginning of the RAM disk's data area.
The boot sector occupies the complete first sector of the RAM disk, but only the
fIrst 15 words are copied into it since DOS only needs these. The name "boot
sector" is actually a misnomer here, since it's impossible to boot a system from a
RAM disk.
The second sector of the RAM disk contains the FAT. The fIrst two entries are the
media descriptor byte and 0 in the entries that follow. These zeros indicate
unoccupied clusters (an empty RAM disk).
The last data structure is the root directory. It contains no entries other than the
volume name.
Remaining routines
This concludes the work of the initialization routine and returns the system to the
calling function. The remaining driver routines are examined in order.
The DUMMY routine performs the same task as the routine of the same name in
the CONDRV driver.
The MED_TEST routine is found only in block device drivers. This routine
informs DOS whether or not the medium was changed.
The next routine, GET_BPB, simply passes the addresses of the variables which
contain the address of the BPB of the RAM disk to DOS, as the initialization
routine had already done.
NO_REM allows DOS to sense whether the medium (the RAM disk) can be
changed. You cannot change a RAM disk, so the program sets the BUSY bit in
the status fIeld.
The two most important functions of the driver perform read and write operations.
As in CONDRV, the program calls Write and Verify instead of the normal Write
function, since no data error can occur during RAM access. The routine itself does
very little; it loads the value 0 into the BP register and jumps to the MOVE
routine. The READ routine performs in a similar manner, except that it loads a 1
into the BP register.
MOVE itself is an elementary routine for moving data. The BP register signals
whether data is to move from the RAM disk to DOS or in the opposite direction.
The routine receives all other data (the DOS buffer's address, the number of the
sectors to be transferred and the fIrst sector to be transferred) from the data block
passed by DOS. See the comments in the MOVE routine for details of the
procedure.
185
6. The Disk Operating System PC System Programming
Changes
This RAM disk: can of course be enhanced. If you have enough unused memory,
you can extend the size of the RAM disk: to 360K. AT owners could make the
RAM disk: resident beyond the I megabyte boundary. In this case, the data transfer
between DOS and the RAM disk: would use function 87H of interrupt ISH.
The clock driver
This final sample driver directly accesses the battery powered clock of an AT
computer. It offers the advantage that when the two DOS commands DAlE and
TIME are used, the date and time are passed directly to the battery powered realtime
clock. Reading the date and time reads the information directly from the memory
locations of the realtime clock.
i**************************·*****···*********·***********************·i
;* ATCLK *;
i*------------