PCjs Machines

Home of the original IBM PC emulator for browsers.

Logo

MS-DOS Version 4.0 Programmer's Reference

The following document is from the Microsoft Programmer’s Library 1.3 CD-ROM.

Microsoft MS-DOS Programmer's Reference


════════════════════════════════════════════════════════════════════════════


Microsoft(R) MS-DOS(R) Programmer's Reference

Version 4.0


════════════════════════════════════════════════════════════════════════════


    Information in this document is subject to change without notice and does
    not represent a commitment on the part of Microsoft Corporation. The
    software described in this document is furnished under a license agreement
    or non-disclosure agreement. The software may be used or copied only in
    accordance with the terms of the agreement. It is against the law to copy
    the software on any medium except as specifically allowed in the license
    or non-disclosure agreement. No part of this manual may be reproduced or
    transmitted in any form or by any means, electronic or mechanical,
    including photocopying and recording, for any purpose without the express
    written permission of Microsoft.

    (C) Copyright Microsoft Corporation, 1988. All rights reserved.

    Simultaneously published in the United States and Canada.

    Printed in the United States of America.

    Microsoft, the Microsoft logo, MS-DOS, and XENIX are registered trademarks
    of Microsoft Corporation.

    IBM, IBM Personal Computer, IBM PC, and PC-DOS are registered trademarks
    of International Business Machines Corporation.

    INTEL is a registered trademark of Intel Corporation.

────────────────────────────────────────────────────────────────────────────
Contents

Chapter 1   System Calls
    1.1   Introduction
            1.1.1   System Calls That Have Been Superseded
            1.1.2   How to Use This Manual
    1.2   Standard Character Device I/O
    1.3   Memory Management
    1.4   Process Management
            1.4.1   Loading and Executing a Program
            1.4.2   Loading an Overlay
    1.5   File and Directory Management
            1.5.1   Handles
            1.5.2   File-Related Function Requests
            1.5.3   Device-Related Function Requests
            1.5.4   Directory-Related Function Requests
            1.5.5   File Attributes
    1.6   Microsoft Networks
    1.7   National Language Support
    1.8   Miscellaneous System-Management Functions
    1.9   Old System Calls
            1.9.1   File Control Block (FCB)
    1.10  Using the System Calls
            1.10.1  Issuing an Interrupt
            1.10.2  Calling a Function Request
            1.10.3  Using the Calls from a High-Level Language
            1.10.4  Treatment of Registers
            1.10.5  Handling Errors
            1.10.6  System Call Descriptions
    1.11  Interrupts
            1.11.1  Conditions upon Entry
            1.11.2  Requirements for an Interrupt 24H Handler
    1.12  Function Requests

Chapter 2   MS-DOS Device Drivers
    2.1   Introduction
    2.2   Format of a Device Driver
    2.3   How to Create a Device Driver
            2.3.1   Device Strategy Routine
            2.3.2   Device Interrupt Routine
    2.4   Installing Device Drivers
    2.5   Device Headers
            2.5.1   Pointer to Next Device Field
            2.5.2   Attribute Field
            2.5.3   Strategy and Interrupt Routines
            2.5.4   Name Field
    2.6   Request Header
            2.6.1   Length of Record
            2.6.2   Unit Code Field
            2.6.3   Command Code Field
            2.6.4   Status Field
    2.7   Device Driver Functions
            2.7.1   The Init Function
            2.7.2   The Media Check Function
            2.7.3   The Build BPB Function
            2.7.4   The Read or Write Function
            2.7.5   The Non-destructive Read, No Wait Function
            2.7.6   The Open or Close Function
            2.7.7   The Removable Media Function
            2.7.8   The Status Function
            2.7.9   The Flush Function
            2.7.10  The Generic IOCtl Function
            2.7.11  The Get/Set Logical Drive Map Function
    2.8   The Media Descriptor Byte
    2.9   Format of a Media Descriptor Table
    2.10  The CLOCK Device
    2.11  Anatomy of a Device Call
    2.12  Two Sample Device Drivers

Chapter 3   MS-DOS Technical Information
    3.1   Introduction
    3.2   MS-DOS Initialization
    3.3   The Command Processor
    3.4   MS-DOS Disk Allocation
    3.5   MS-DOS Disk Directory
    3.6   File Allocation Table (FAT)
            3.6.1   How to Use the FAT (12-Bit FAT Entries)
            3.6.2   How to Use the FAT (16-Bit FAT Entries)
    3.7   MS-DOS Standard Disk Formats

Chapter 4   MS-DOS Control Blocks and Work Areas
    4.1   Introduction
    4.2   Typical Contents of an MS-DOS Memory Map
    4.3   The MS-DOS Program Segment

Chapter 5   National Language Support
    5.1   Introduction
    5.2   National Language Support Calls
    5.3   Font Files
            5.3.1   Font File Structure

Chapter 6   .Exe File Structure and Loading
    6.1   Introduction
    6.2   Format of a File Header
    6.3   The Relocation Table

Chapter 7   Relocatable Object Module Formats
    7.1   Introduction
            7.1.1   Definition of Terms
    7.2   Module Identification and Attributes
            7.2.1   Segment Definition
            7.2.2   Addressing a Segment
            7.2.3   Symbol Definition
            7.2.4   Indices
    7.3   Conceptual Framework for Fixups
            7.3.1   Self-Relative Fixup
            7.3.2   Segment-Relative Fixup
    7.4   Record Sequence
    7.5   Introducing the Record Formats
            7.5.1   Sample Record Format (SAMREC)
            7.5.2   T-Module Header Record (THEADR)
            7.5.3   L-Module Header Record (LHEADR)
            7.5.4   List of Names Record (LNAMES)
            7.5.5   Segment Definition Record (SEGDEF)
            7.5.6   Group Definition Record (GRPDEF)
            7.5.7   Public Names Definition Record (PUBDEF)
            7.5.8   Communal Names Definition Record (COMDEF)
            7.5.9   Local Symbols Record (LOCSYM)
            7.5.10  External Names Definition Record (EXTDEF)
            7.5.11  Line Numbers Record (LINNUM)
            7.5.12  Logical Enumerated Data Record (LEDATA)
            7.5.13  Logical Iterated Data Record (LIDATA)
            7.5.14  Fixup Record (FIXUPP)
            7.5.15  Module End Record (MODEND)
            7.5.16  Comment Record (COMENT)
    7.6   Microsoft Type Representations for Communal Variables

Chapter 8   Programming Hints
    8.1   Introduction
    8.2   Interrupts
    8.3   System Calls
    8.4   Device Management
    8.5   Memory Management
    8.6   Process Management
    8.7   File and Directory Management
            8.7.1   Locking Files
    8.8   Miscellaneous

Figures
Figure 1.1  Example of the 8088 Registers
Figure 1.2  Sample Program with Common Skeleton
Figure 2.1  Sample Device Header
Figure 2.2  Attribute Word for Character Devices
Figure 2.3  Attribute Word for Block Devices
Figure 2.4  Request Header
Figure 2.5  Status Field
Figure 2.6  Format of a Boot Sector
Figure 2.7  Format of a Clock Device
Figure 4.1  Program Segment Prefix
Figure 5.1  Font File Structure
Figure 7.1  Location Types

Tables
Table 1.1   Standard-Character I/O Function Requests
Table 1.2   Memory Management Function Requests
Table 1.3   Process-Management Function Requests
Table 1.4   Predefined Device Handles
Table 1.5   File-Related Function Requests
Table 1.6   File-Sharing Function Requests
Table 1.7   Device-Related Function Requests
Table 1.8   Directory-Related Function Requests
Table 1.9   File Attributes
Table 1.10  Microsoft Networks Function Requests
Table 1.11  National-Language-Support Function Requests
Table 1.12  Miscellaneous System-Management Function Requests
Table 1.13  Old System Calls and Their Replacements
Table 1.14  Format of the File Control Block (FCB)
Table 1.15  Error Codes Returned in AX
Table 1.16  MS-DOS Interrupts, Numeric Order
Table 1.17  MS-DOS Interrupts, Alphabetic Order
Table 1.18  MS-DOS Function Requests, Numeric Order
Table 1.19  MS-DOS Function Requests, Alphabetic Order
Table 1.20  Bit Values for Function 29H
Table 1.21  Sharing Mode Bit Values
Table 1.22  Access Code Bit Values
Table 1.23  MS-DOS Data Bit Values
Table 1.24  Contents of the Parameter Block
Table 1.25  Contents of the Parameter Block
Table 1.26  DTA Values After Successful Find First File
Table 1.27  Allocation Strategy
Table 2.1   For Character Devices:
Table 2.2   For Block Devices:
Table 3.1   MS-DOS Standard Removable-Disk Formats
Table 3.2   MS-DOS Standard Removable-Disk Formats (High-Density)
Table 7.1   Object Module Record Formats
Table 7.2   Combination Attribute Example



────────────────────────────────────────────────────────────────────────────
Chapter 1  System Calls

    1.1   Introduction
            1.1.1   System Calls That Have Been Superseded
            1.1.2   How to Use This Manual
    1.2   Standard Character Device I/O
    1.3   Memory Management
    1.4   Process Management
            1.4.1   Loading and Executing a Program
            1.4.2   Loading an Overlay
    1.5   File and Directory Management
            1.5.1   Handles
            1.5.2   File-Related Function Requests
            1.5.3   Device-Related Function Requests
            1.5.4   Directory-Related Function Requests
            1.5.5   File Attributes
    1.6   Microsoft Networks
    1.7   National Language Support
    1.8   Miscellaneous System-Management Functions
    1.9   Old System Calls
            1.9.1   File Control Block (FCB)
    1.10  Using the System Calls
            1.10.1  Issuing an Interrupt
            1.10.2  Calling a Function Request
            1.10.3  Using the Calls from a High-Level Language
            1.10.4  Treatment of Registers
            1.10.5  Handling Errors
            1.10.6  System Call Descriptions
    1.11  Interrupts
            1.11.1  Conditions upon Entry
            1.11.2  Requirements for an Interrupt 24H Handler
    1.12  Function Requests


1.1  Introduction

    The routines that MS-DOS uses to manage system operation and resources can
    be called by any application program. Using these system calls makes it
    easier to write machine-independent programs and increases the likelihood
    that a program will be compatible with future versions of MS-DOS. MS-DOS
    system calls fall into several categories:

    ■  Standard character device I/O

    ■  Memory management

    ■  Process management

    ■  File and directory management

    ■  Microsoft Network calls

    ■  National Language Support calls

    ■  Miscellaneous system functions

    Applications invoke MS-DOS services by using software interrupts. The
    current range of interrupts used for MS-DOS is 20H-27H; 28H-40H are
    reserved. Interrupt 21H is the function request service; it provides
    access to a wide variety of MS-DOS services. In some cases, the full AX
    register is used to specify the requested function. Each interrupt or
    function request uses values in various registers to receive or return
    function-specific information.

1.1.1  System Calls That Have Been Superseded

    Many system calls introduced in versions of MS-DOS earlier than 2.0 have
    been superseded by function requests that are more efficient and easier to
    use. Although MS-DOS still includes these old system calls, they should
    not be used unless it is imperative that a program maintain
    backward-compatibility with versions of MS-DOS earlier than 2.0.

    A table of the pre-2.0 system calls and a description of the File Control
    Block (required by some of the old calls) appear in Section 1.8, "Old
    System Calls."

1.1.2  How to Use This Manual

    The first part of this chapter explains how DOS manages its resources──
    such as memory, files, and processes──and briefly describes the purpose of
    most of the system calls. The remainder of the chapter describes each
    interrupt and function request in detail.

    The system-call descriptions are in numeric order, interrupts followed by
    function requests. These descriptions include further detail on how MS-DOS
    manages its resources.

    Chapter 2 of this manual describes how to write an MS-DOS device driver.

    Chapters 3 and 4 contain more detailed information about MS-DOS,
    including how it manages disk space, uses control blocks, and loads and
    executes relocatable programs (files with an extension of .exe).

    Chapter 5 describes MS-DOS national language support.

    Chapter 6 describes .exe file structure and loading procedures (for
    versions of MS-DOS earlier than 2.0).

    Chapter 7 describes the Intel object module format.

    Chapter 8 gives some programming hints.


1.2  Standard Character Device I/O

    The standard-character function requests handle all input and output to
    and from character devices such as consoles, printers, and serial ports.
    If a program uses these function requests, its input and output can be
    redirected.

    Table 1.1 lists the MS-DOS function requests for managing
    standard-character input and output.

    Table 1.1
    Standard-Character I/O Function Requests
╓┌─┌─────────┌─────────────────────┌─────────────────────────────────────────╖
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    01H       Read Keyboard and    Gets a character from standard input and
                Echo                 echoes it to standard output

    02H       Display Character    Sends a character to standard output

    03H       Auxiliary Input      Gets a character from standard auxiliary

    04H       Auxiliary Output     Sends a character to standard auxiliary

    05H       Print Character      Sends a character to the standard printer
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    05H       Print Character      Sends a character to the standard printer

    06H       Direct Console I/O   Gets a character from standard input or
                                    sends a character to standard output

    07H       Direct Console Input Gets a character from standard input

    08H       Read Keyboard        Gets a character from standard input

    09H       Display String       Sends a string to standard output

    0AH       Buffered Keyboard    Gets a string from standard input
                Input

    0BH       Check Keyboard       Reports on the status of the standard
                Status               input buffer

    0CH       Flush Buffer, Read   Empties the standard input buffer and
                Keyboard             calls one of the other standard character
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
                Keyboard             calls one of the other standard character
    ──────────────────────────────────────────────────────────────────────────


    Although several of these standard-character I/O function requests seem to
    do the same thing, they are distinguished by whether they check for
    control characters or echo characters from standard input to standard
    output. The detailed descriptions later in this chapter point out the
    differences.


1.3  Memory Management

    MS-DOS keeps track of which areas of memory are allocated by writing a
    memory control block at the beginning of each. This control block
    specifies the size of the memory area; the name of the process, if any,
    that owns the memory area; and a pointer to the next area of memory. If
    the memory area is not owned, it is available.

    Table 1.2 lists the MS-DOS function requests for managing memory.

    Table 1.2
    Memory Management Function Requests
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    48H       Allocate Memory      Requests a block of memory

    49H       Free Allocated       Frees a block of memory previously
                Memory               allocated with 48H

    4AH       Set Block            Changes the size of an allocated memory
    ──────────────────────────────────────────────────────────────────────────

    When a process requests additional memory with Function 48H (Allocate
    Memory), MS-DOS searches for a block of available memory large enough to
    satisfy the request. If it finds such a block of memory, it changes the
    memory control block to show the owning process. If the block of memory is
    larger than the requested amount, MS-DOS changes the size field of the
    memory control block to the requested amount, writes a new memory control
    block at the beginning of the unneeded portion showing that it is
    available, and updates the pointers to add this memory to the chain of
    memory control blocks. MS-DOS then returns the segment address of the
    first byte of the allocated memory to the requesting process.

    When a process releases an allocated block of memory with Function 49H
    (Free Allocated Memory), MS-DOS changes the memory control block to show
    that it is available (not owned by any process).

    When a process uses Function 4AH (Set Block) to shrink an allocated block
    of memory, MS-DOS builds a memory control block for the memory being
    released and adds it to the chain of memory control blocks. When a process
    tries to use Function 4AH (Set Block) to expand an allocated block of
    memory, MS-DOS treats it as a request for additional memory rather than
    returning the segment address of the additional memory to the requesting
    process. However, MS-DOS simply chains the additional memory to the
    existing memory block. If MS-DOS can't find a block of available memory
    large enough to satisfy a request for additional memory made with either
    Function 48H (Allocate Memory) or Function 4AH (Set Block), MS-DOS
    returns an error code to the requesting process.

    When a program receives control, it should call Function 4AH (Set Block)
    to shrink its initial memory-allocation block (the block that begins with
    its Program Segment Prefix) to the minimum it requires. This frees
    unneeded memory and makes the best application design for portability to
    future multitasking environments.

    When a program exits, MS-DOS automatically frees its initial
    memory-allocation block before returning control to the calling program
    (command.com is usually the calling program for application programs). DOS
    frees any memory owned by the exiting process.

    Any program that changes memory that is not allocated to it will most
    likely destroy at least one memory-management control block. This causes a
    memory-allocation error the next time MS-DOS tries to use the chain of
    memory-control blocks; the only cure is to restart the system.


1.4  Process Management

    MS-DOS uses several function requests to load, execute, and terminate
    programs. Application programs can use these same function requests to
    manage other programs.

    Table 1.3 lists the MS-DOS function requests for managing processes.

    Table 1.3
    Process-Management Function Requests
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    31H       Keep Process         Terminates a process and returns control
                                    to the invoking process, but keeps the
                                    terminated process in memory

    4B00H,    Load and Execute     Loads and executes a program, or loads a
    4B03H     Program or Overlay   program overlay without executing it

    4CH       End Process          Returns control to the invoking process

    4DH       Get Return Code of   Returns a code passed by an exiting child
                Child Process        process

    62H       Get PSP              Returns the segment address of the current
    ──────────────────────────────────────────────────────────────────────────

    Function 4BH (Load and Execute Program or Overlay) has two subfunctions,
    4B00H (Load and Execute Program) and 4B03H (Load Overlay). The
    differences between them are described in the following sections.

1.4.1  Loading and Executing a Program

    When a program uses Function 4B00H (Load and Execute Program) to load and
    execute another program, MS-DOS allocates memory, writes a Program Segment
    Prefix (PSP) for the new program at offset 0 of the allocated memory,
    loads the new program, and passes control to it. When the invoked program
    exits, control returns to the calling program.

    Command.com uses Function 4B00H to load and execute command files.
    Application programs have the same degree of control over process
    management as does command.com.

    In addition to these common features, there are some differences in the
    way MS-DOS loads .com and .exe files.

    Loading a .Com Program

    When command.com loads and executes a .com program, it allocates all
    available memory to the application and sets the stack pointer 100H bytes
    from the end of available memory. A .com program should set up its own
    stack before shrinking its initial memory-allocation block with Function
    4AH (Set Block) because the default stack is in the memory to be
    released.

    If a newly loaded program is allocated all of memory──as a .com program
    is──or requests all of available memory by using Function 48H (Allocate
    Memory), MS-DOS allocates to it the memory occupied by the transient part
    of command.com. If the program changes this memory, MS-DOS must reload the
    transient portion of command.com before it can continue. If a program
    exits (via Function 31H, Keep Process) without releasing enough memory,
    the system halts and must be reset. To minimize this possibility, a .com
    program should use Function 4AH (Set Block) to shrink its initial
    allocation block before doing anything else. Also, before exiting, all
    programs must release all memory they allocate by using Function 48H
    (Allocate Memory).

    Loading a .Exe Program

    When command.com loads and executes a .exe program, it allocates the size
    of the program's memory image plus either the value in the MAX──ALLOC
    field (offset 0CH) of the file header (if that much memory is available)
    or the value in the MIN──ALLOC field (offset 0AH). The linker sets these
    fields. Before passing control to the .exe file, MS-DOS uses the
    relocation information in the file header to calculate the correct
    relocation addresses.

    For a more detailed description of how MS-DOS loads .com and .exe files,
    see Chapter 3, "MS-DOS Technical Information," and Chapter 4, "MS-DOS
    Control Blocks and Work Areas."

    Executing a Program from Within Another Program

    Since command.com builds pathnames, searches directory paths for
    executable files, and relocates .exe files, the simplest way to load and
    execute a program is to load and execute an additional copy of
    command.com, passing it a command line that includes the /C switch, which
    invokes the .com or .exe file. The description of Function 4B00H (Load
    and Execute Program) describes how to do this.

1.4.2  Loading an Overlay

    When a program uses Function 4B03H (Load Overlay) to load an overlay, it
    must pass MS-DOS the segment address at which the overlay is to be loaded.
    The program must call the overlay, which then returns directly to the
    calling program. The calling program is in complete control: MS-DOS does
    not write a PSP for the overlay or intervene in any other way.

    MS-DOS does not check to see if the calling program owns the memory where
    the overlay is to be loaded. If the calling program does not own the
    memory, loading the overlay will most likely destroy a memory-control
    block, causing an eventual memory-allocation error.

    Therefore, a program that loads an overlay must either allow room for the
    overlay when it calls Function 4AH (Set Block) to shrink its initial
    memory-allocation block, or shrink its initial memory-allocation block to
    the minimum and then use Function 48H (Allocate Memory) to allocate
    memory for the overlay.


1.5  File and Directory Management

    The MS-DOS hierarchical (multilevel) file system is similar to that of the
    XENIX operating system. For a description of the multilevel directory
    system and how to use it, see the MS-DOS User's Reference.

1.5.1  Handles

    To create or open a file, a program passes MS-DOS a pathname and the
    attribute to be assigned to the file. MS-DOS returns a 16-bit number,
    called a handle. For most subsequent actions, MS-DOS requires only this
    handle to identify the file.

    A handle can refer to either a file or a device. MS-DOS predefines five
    standard handles. These handles are always open, so you needn't open them
    before you use them. Table 1.4 lists these predefined handles.

    Table 1.4
    Predefined Device Handles
    Handle     Standard device      Comment
    ──────────────────────────────────────────────────────────────────────────
    0          Input                Can be redirected from command line
    1          Output               Can be redirected from command line
    2          Error
    3          Auxiliary
    4          Printer
    ──────────────────────────────────────────────────────────────────────────

    When MS-DOS creates or opens a file, it assigns the first available
    handle. Since a program can have 20 open handles, including the five
    predefined handles, it typically can open 15 extra files. By using
    Function 46H (Force Duplicate File Handle), MS-DOS can temporarily force
    any of the five predefined handles to refer to an alternate file or
    device. For more information about Function 46H, see its description
    later in this chapter.

1.5.2  File-Related Function Requests

    MS-DOS treats a file as a string of bytes; it assumes no record structure
    or access technique. An application program imposes whatever record
    structure it needs on this string of bytes. Reading from or writing to a
    file requires only pointing to the data buffer and specifying the number
    of bytes to read or write. Table 1.5 lists the MS-DOS function requests
    for managing files.

    Table 1.5
    File-Related Function Requests
╓┌─┌─────────┌─────────────────────┌─────────────────────────────────────────╖
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    3CH       Create Handle        Creates a file

    3DH       Open Handle          Opens a file

    3EH       Close Handle         Closes a file

    3FH       Read Handle          Reads from a file

    40H       Write Handle         Writes to a file

    42H       Move File Pointer    Sets the read/write pointer in a file

    45H       Duplicate File       Creates a new handle that refers to the
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    45H       Duplicate File       Creates a new handle that refers to the
                Handle               same file as an existing handle

    46H       Force Duplicate File Makes an existing handle refer to the same
                Handle               file as another existing handle

    5AH       Create Temporary     Creates a file with a unique name
                File

    5BH       Create New File      Attempts to create a file, but fails if a
                                    file with the same name exists

    67H       Set Handle Count     Increases or decreases the number of files
                                    a program can have open at one time

    68H       Commit File          Flushes buffered data for a file without
                                    closing it to ensure the disk image of
    ──────────────────────────────────────────────────────────────────────────

    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────


    File Sharing

    Version 3.1 of MS-DOS introduced file sharing, which let more than one
    process share access to a file. File sharing operates only after the share
    command has been executed to load file-sharing support. That is, you must
    use the share command to take advantage of file sharing.

    Table 1.6 lists the MS-DOS function requests for sharing files; if file
    sharing is not in effect, these function requests cannot be used. Function
    3DH (Open Handle) can operate in several modes. Here it is referred to in
    the file-sharing modes, which require file sharing to be in effect.
    (Compatibility mode is usable without file sharing being in effect.)

    Table 1.6
    File-Sharing Function Requests
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    3DH       Open Handle          Opens a file by using one of the
                                    file-sharing modes

    440BH     IOCtl Retry          (before Interrupt 24H is issued)
                                    Specifies how many times to retry an I/O
                                    operation that fails due to a file-sharing
                                    violation

    5C00H     Lock                 Locks a region of a file

    5C01H     Unlock               Unlocks a region of a file
    ──────────────────────────────────────────────────────────────────────────

1.5.3  Device-Related Function Requests

    I/O Control for devices is implemented with Function 44H (IOCtl), which
    includes several subfunctions necessary to perform device-related tasks.
    Some forms of the IOCtl function request require that the device driver be
    written to support the IOCtl interface. Table 1.7 lists the MS-DOS
    function requests for managing devices.

    Table 1.7
    Device-Related Function Requests
╓┌─┌──────────────┌─────────────────────────┌────────────────────────────────╖
    Code            Request                  Description
    ──────────────────────────────────────────────────────────────────────────
    4400H, 4401H  IOCtl Data               Gets or sets device description

    4402H, 4403H  IOCtl Character          Gets or sets character-device
                                            control data

    4404H, 4405H  IOCtl Block              Gets or sets block-device control
                                            data

    4406H, 4407H  IOCtl Status             Checks device input or output
                                            status

    4408H          IOCtl Is Changeable      Checks whether block device
                                            contains removable medium

    440CH          Generic IOCtl (for       Sets Generic IOCtl for handles
                    handles)                 and supports code pages for
    Code            Request                  Description
    ──────────────────────────────────────────────────────────────────────────
                    handles)                 and supports code pages for
                                            devices or returns DCBS
                                            information.

    440DH          Generic IOCtl (for       Sets Generic IOCtl for devices
                    devices)

    440EH, 440FH  Get/Set IOCtl Drive Map  Gets or sets logical drive map
    ──────────────────────────────────────────────────────────────────────────


    Some forms of the IOCtl function request can be used only with Microsoft
    Networks; these forms are listed in Section 1.6, "Microsoft Networks."

1.5.4  Directory-Related Function Requests

    A directory entry is a 32-byte record that includes the file's name,
    extension, date and time of last change, and size. An entry in a
    subdirectory is identical to an entry in the root directory. Directory
    entries are described in detail in Chapter 3.

    The root directory on a disk has room for a fixed number of entries: 64 on
    a standard single-sided disk, 112 on a standard double-sided disk. For
    hard disks, the number of directories depends on the DOS partition size. A
    subdirectory is simply a file with a unique attribute; there can be as
    many subdirectories on a disk as space allows. The depth of a directory
    structure, therefore, is limited only by the amount of storage on a disk
    and the maximum pathname length of 64 characters. Pre-2.0 disks appear to
    have only a root directory that contains files but no subdirectories.

    Table 1.8 lists the MS-DOS function requests for managing directories.

    Table 1.8
    Directory-Related Function Requests
╓┌─┌─────────┌─────────────────────┌─────────────────────────────────────────╖
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    39H       Create Directory     Creates a subdirectory

    3AH       Remove Directory     Deletes a subdirectory
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    3AH       Remove Directory     Deletes a subdirectory

    3BH       Change Current       Changes the current directory
                Directory

    41H       Delete Directory     Deletes a file
                Entry (Unlink)

    43H       Get/Set File         Retrieves or changes the attributes of a
                Attributes (Chmod)   file

    47H       Get Current          Returns current directory for a given
                Directory            drive

    4EH       Find First File      Searches a directory for the first entry
                                    that matches a filename

    4FH       Find Next File       Searches a directory for the next entry
                                    that matches a filename
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
                                    that matches a filename

    56H       Change Directory     Renames a file
                Entry

    57H       Get/Set Date/Time of Changes the time and date of last change
                File                 in a directory entry
    ──────────────────────────────────────────────────────────────────────────


1.5.5  File Attributes

    Table 1.9 describes the file attributes and how they are represented in
    the attribute byte of the directory entry (offset 0BH). The attributes can
    be inspected or changed with Function 43H (Get/Set File Attributes
    [Chmod]).

    Table 1.9
    File Attributes
    Code                     Description
    ──────────────────────────────────────────────────────────────────────────
    00H                      Normal; can be read or written without
                            restriction

    01H                      Read-only; cannot be opened for write; a file
                            with the same name cannot be created

    02H                      Hidden; not found by directory search

    04H                      System; not found by directory search

    08H                      VolumeID; only one file can have this attribute;
                            it must be in the root directory

    10H                      Subdirectory

    20H                      Archive; set whenever the file is changed, or
    ──────────────────────────────────────────────────────────────────────────

    The VolumeID (08H) and Subdirectory (10H) attributes cannot be changed
    with Function 43H (Get/Set File Attributes [Chmod]).


1.6  Microsoft Networks

    Microsoft Networks consists of a server and one or more workstations.
    MS-DOS maintains an assign list that keeps track of which workstation disk
    drives and devices have been redirected to the server. For a description
    of the operation and use of the network, see the Microsoft Networks
    Manager's Guide and Microsoft Networks User's Guide.

    Table 1.10 lists the MS-DOS function requests for managing a Microsoft
    Networks workstation.

    Table 1.10
    Microsoft Networks Function Requests
╓┌─┌─────────┌─────────────────────┌─────────────────────────────────────────╖
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    4409H     IOCtl Is Redirected  Checks whether a drive letter refers to a
                Block                local or redirected drive
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
                Block                local or redirected drive

    440AH     IOCtl Is Redirected  Checks whether a device name refers to a
                Handle               local or redirected device

    5E00H     Get Machine Name     Gets the network name of the workstation

    5E02H     Set Printer Setup    Defines a string of control characters to
                                    be added at the beginning of each file
                                    that is sent to a network printer

    5F02H     Get Assign-List      Gets an entry from the assign list, which
                Entry                shows the workstation drive letter or
                                    device name and the network name of the
                                    directory or device on the server to which
                                    the entry is reassigned

    5F03H     Make Network         Redirects a workstation drive or device to
                Connection           a server directory or device
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
                Connection           a server directory or device

    5F04H     Delete Network       Cancels the redirection of a workstation
                Connection           drive or device to a server directory or
    ──────────────────────────────────────────────────────────────────────────



1.7  National Language Support

    National language support of MS-DOS includes these major features:

    ■  Country-dependent information

    ■  Support for national keyboard layouts

    ■  Programming interfaces for national language support

    ■  Utility commands

    Country-dependent information is available on a per-country basis and
    includes the following:

    ■  Time, date, and currency

    ■  Lowercase-to-uppercase character-conversion tables

    ■  Collating sequence for character sorting

    ■  Valid single-byte characters used in filenames

    Keyboard support for different keyboard layouts is provided and
    selectable.

    Table 1.11 lists the MS-DOS national-language-support system calls that
    allow applications to use the country-dependent information just
    described.

    Table 1.11
    National-Language-Support Function Requests
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    65H       Get Extended Country Returns standard country information,
                Information          pointer to uppercase table, pointer to
                                    filename uppercasing table, or pointer to
                                    collating table.

    66H       Get/Set Global Code  Gets or sets the code page used by the
                Page                 kernel and all devices.
    ──────────────────────────────────────────────────────────────────────────


1.8  Miscellaneous System-Management Functions



    The remaining system calls manage other system functions and resources
    such as drives, addresses, and the clock. Table 1.12 lists the MS-DOS
    function requests for managing miscellaneous system resources and
    operations.

    Table 1.12
    Miscellaneous System-Management Function Requests
╓┌─┌─────────┌─────────────────────┌─────────────────────────────────────────╖
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    0DH       Reset Disk           Empties all file buffers

    0EH       Select Disk          Sets the default drive

    19H       Get Current Disk     Returns the default drive

    1AH       Set Disk Transfer    Establishes the disk I/O buffer
                Address

    1BH       Get Default Drive    Returns disk-format data
                Data
    1CH       Get Drive Data       Returns disk-format data

    25H       Set Interrupt Vector Sets interrupt-handler address

    29H       Parse File Name      Checks string for valid filename
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    29H       Parse File Name      Checks string for valid filename

    2AH       Get Date             Returns system date

    2BH       Set Date             Sets system date

    2CH       Get Time             Returns system time

    2DH       Set Time             Sets system time

    2EH       Set/Reset Verify     Turns disk verify on or off
                Flag

    2FH       Get Disk Transfer    Returns system-disk-I/O-buffer address
                Address

    30H       Get MS-DOS Version   Returns MS-DOS version number
                Number
    33H       CONTROL+C Check      Returns CONTROL+C check status
    Code       Request              Description
    ──────────────────────────────────────────────────────────────────────────
    33H       CONTROL+C Check      Returns CONTROL+C check status

    35H       Get Interrupt Vector Returns address of interrupt handler

    36H       Get Disk Free Space  Returns disk-space data

    38H       Get/Set Country Data Sets current country or retrieves country
                                    information

    54H       Get Verify State     Returns status of disk verify
    ──────────────────────────────────────────────────────────────────────────



1.9  Old System Calls

    Most of the superseded system calls deal with files. Table 1.13 lists
    these old calls and the function requests that have superseded them.

    Although MS-DOS still includes these old system calls, they should not be
    used unless a program must maintain backward-compatibility with earlier
    versions of MS-DOS.

    Table 1.13
    Old System Calls and Their Replacements
╓┌─┌──────────┌─────────────────────────┌──────────┌─────────────────────────╖
    Old System Call                       Has Been Superseded By
    Code        Function                 Code        Function
    ──────────────────────────────────────────────────────────────────────────
    00H        Terminate Program        4CH        End Process
    0FH        Open File                3DH        Open Handle
    10H        Close File               3EH        Close Handle
    11H        Search for First Entry   4EH        Find First File
    12H        Search for Next Entry    4FH        Find Next File
    13H        Delete File              41H        Delete Directory Entry
    14H        Sequential Read          3FH        Read Handle
    15H        Sequential Write         40H        Write Handle
    16H        Create File              3CH        Create Handle
                                        5AH        Create Temporary File
                                        5BH        Create New File
    Old System Call                       Has Been Superseded By
    Code        Function                 Code        Function
    ──────────────────────────────────────────────────────────────────────────
                                        5BH        Create New File
    17H        Rename File              56H        Change Directory Entry
    21H        Random Read              3FH        Read Handle
    22H        Random Write             40H        Write Handle
    23H        Get File Size            42H        Move File Pointer
    24H        Set Relative Record      42H        Move File Pointer
    26H        Create New PSP           4B00H,     Load and Execute Program
                                        4B03H      or Overlay
    27H        Random Block Read        42H        Move File Pointer
                                        3FH        Read Handle
    28H        Random Block Write       42H        Move File Pointer
                                        40H        Write Handle

    Code        Interrupt                Code        Function
    ──────────────────────────────────────────────────────────────────────────
    20H        Program Terminate        4CH        End Process
    27H        Terminate But Stay       31H        Keep Process
                Resident
    Old System Call                       Has Been Superseded By
    Code        Function                 Code        Function
    ──────────────────────────────────────────────────────────────────────────
                Resident
    ──────────────────────────────────────────────────────────────────────────


1.9.1  File Control Block (FCB)

    The old file-related function requests require that a program maintain a
    File Control Block (FCB) for each file; this control block contains such
    information as a file's name, size, record length, and pointer to current
    record. MS-DOS does most of this housekeeping for the newer,
    handle-oriented function requests.

    Some descriptions of the old function requests refer to unopened and
    opened FCBs. An unopened FCB contains only a drive specifier and filename.
    An opened FCB contains all fields filled by Function 0FH (Open File).

    The Program Segment Prefix (PSP) includes room for two FCBs at offsets 5CH
    and 6CH. For a description of how to use the PSP and FCB calls, see
    Chapter 4, "MS-DOS Control Blocks and Work Areas." Table 1.14 describes
    the FCB fields.

    Table 1.14
    Format of the File Control Block (FCB)
        Offset
    Hex            Dec            Bytes                 Field
    ──────────────────────────────────────────────────────────────────────────
    00H             0             1                     Drive Number
    01H             1             8                     Filename
    09H             9             3                     Extension
    0CH            12             2                     Current Block

    0EH            14             2                     Record Size
    10H            16             4                     File Size
    14H            20             2                     Date of Last Write
    16H            22             2                     Time of Last Write

    18H            24             8                     Reserved
    20H            32             1                     Current Record
    21H            33             4                     Relative Record
    ──────────────────────────────────────────────────────────────────────────

    Fields of the FCB

    Drive Number (offset 00H): Specifies the disk drive; 1 means drive A and 2
    means drive B. If you use the FCB to create or open a file, you can set
    this field to 0 to specify the default drive; Function 0FH (Open File)
    sets the field to the number of the default drive.

    Filename (offset 01H): Eight characters, left-aligned and padded (if
    necessary) with blanks. If you specify a reserved device name (such as
    PRN), do not put a colon at the end.

    Extension (offset 09H): Three characters, left-aligned and padded (if
    necessary) with blanks. This field can be all blanks (no extension).

    Current Block (offset 0CH): Points to the block (group of 128 records)
    that contains the current record. This field and the Current Record field
    (offset 1FH) make up the record pointer. This field is set to zero by the
    Open File system call.

    Record Size (offset 0EH): The size of a logical record, in bytes. Set to
    128 by the Open File system call. If the record size is not 128 bytes, you
    must set this field after opening the file.

    File Size (offset 10H): The size of the file, in bytes. The first word of
    this 4-byte field is the low-order part of the size.

    Date of Last Write (offset 14H): The date the file was created or last
    updated. The year, month, and day are mapped into two bytes as follows:

    Offset 15H          Offset 14H
    |Y|Y|Y|Y|Y|Y|Y|M|   |M|M|M|D|D|D|D|D|
    15              9   8    5 4        0

    Time of Last Write (offset 16H): The time the file was created or last
    updated. The hour, minutes, and seconds are mapped into two bytes as
    follows:

    Offset 17H          Offset 16H
    |H|H|H|H|H|M|M|M|   |M|M|M|S|S|S|S|S|
    15      11 10            5 4        0

    Reserved (offset 18H): These fields are reserved for use by MS-DOS.
    Current Record (offset 20H): Points to one of the 128 records in the
    current block. This field and the Current Block field (offset 0CH) make up
    the record pointer. The Open File system call does not initialize this
    field. You must set it before doing a sequential read or write to the
    file.

    Relative Record (offset 21H): Points to the currently selected record,
    counting from the beginning of the file (starting with 0). The Open File
    system call does not initialize this field. You must set it before doing a
    random read or write to the file. If the record size is less than 64
    bytes, both words of this field are used; if the record size is 64 bytes
    or more, only the first three bytes are used.

    ──────────────────────────────────────────────────────────────────────────
    Note
    If you use the FCB at offset 5CH of the Program Segment Prefix, the last
    byte of the Relative Record field is the first byte of the unformatted
    parameter area that starts at offset 80H. This is the default Disk
    Transfer Area.
    ──────────────────────────────────────────────────────────────────────────

    Extended FCB

    The Extended File Control Block is used to create or search for directory
    entries of files with special attributes. It adds the following 7-byte
    prefix to the FCB:

    Name                 Bytes      Offset
    ──────────────────────────────────────────────────────────────────────────
    Flag byte (FFH)      1          07H
    Reserved             5          06H
    Attribute byte       1          01H
    ──────────────────────────────────────────────────────────────────────────

    File attributes are described earlier in this chapter in Section 1.5.5,
    "File Attributes."

    ──────────────────────────────────────────────────────────────────────────
    Note
    You must remember to point to the beginning of the extended FCB if you
    are using Functions 0FH-16H with extended FCBs.
    ──────────────────────────────────────────────────────────────────────────


1.10  Using the System Calls

    The remainder of this chapter describes how to use the system calls in
    application programs, and it lists the calls in numeric and alphabetic
    order, describing each call in detail.

1.10.1  Issuing an Interrupt

    MS-DOS reserves Interrupts 28H through 3FH for its own use, and maintains
    the table of interrupt-handler addresses (the vector table) in locations
    80H-FCH. Also, in case you need to write your own routines for three
    particular MS-DOS interrupt handlers (Program Terminate, CONTROL+C, and
    Critical Error), this chapter includes descriptions of each. Function
    requests have superseded most of these interrupts.

    To issue an interrupt, move any required data into the registers and give
    the INT instruction with the number of the interrupt you want.

1.10.2  Calling a Function Request

    A function request is an MS-DOS routine for managing system resources. Use
    the following procedure to call a function request:

    1. Move any required data into the registers.

    2. Move the function number into AH.

    3. Move the action code, if required, into AL.

    4. Issue Interrupt 21H.

1.10.3  Using the Calls from a High-Level Language

    The system calls can be executed from any high-level language whose
    modules can be linked with assembly-language modules. In addition to this
    linking technique, you can:

    ■  Use the Interrupt 86 function of C to call a function request directly.

    ■  Use the DOSXQQ function of Pascal to call a function request directly.

    ■  Use the CALL statement or USER function to execute the required
        assembly-language code from the BASIC interpreter.

1.10.4  Treatment of Registers

    When MS-DOS takes control after a function request, it switches to an
    internal stack, and preserves any registers not used to return information
    (except AX). The calling program's stack must be large enough to
    accommodate the interrupt system──at least 128 bytes in addition to other
    needs.

1.10.5  Handling Errors

    Most of the function requests introduced with MD-DOS version 2.0 or later
    set the Carry flag if there is an error, identifying the specific error by
    returning a number in the AX register. Table 1.15 lists these error codes
    and their meanings.

    Table 1.15
    Error Codes Returned in AX
╓┌─┌──────────────┌──────────────────────────────────────────────────────────╖
    Code           Meaning
    ──────────────────────────────────────────────────────────────────────────
    1             Invalid function code
    2             File not found
    3             Path not found
    4             Too many open files (no open handles left)
    5             Access denied
    6             Invalid handle
    7             Memory control blocks destroyed
    8             Insufficient memory
    9             Invalid memory block address

    10             Invalid environment
    11             Invalid format
    12             Invalid access code
    13             Invalid data
    14             Reserved
    15             Invalid drive
    Code           Meaning
    ──────────────────────────────────────────────────────────────────────────
    15             Invalid drive
    16             Attempt to remove the current directory
    17             Not same device
    18             No more files
    19             Disk is write-protected

    20             Bad disk unit
    21             Drive not ready
    22             Invalid disk command
    23             CRC error
    24             Invalid length (disk operation)
    25             Seek error
    26             Not an MS-DOS disk
    27             Sector not found
    28             Out of paper
    29             Write fault

    30             Read Fault
    31             General failure
    Code           Meaning
    ──────────────────────────────────────────────────────────────────────────
    31             General failure
    32             Sharing violation
    33             Lock violation
    34             Wrong disk
    35             FCB unavailable
    36             Sharing buffer exceeded
    37             Reserved
    38             Handle end-of-file operation not completed
    39-49          Reserved

    50             Network request not supported
    51             Remote computer not listening
    52             Duplicate name on network
    53             Network name not found
    54             Network busy
    55             Network device no longer exists
    56             Net BIOS command limit exceeded
    57             Network adapter hardware error
    58             Incorrect response from network
    Code           Meaning
    ──────────────────────────────────────────────────────────────────────────
    58             Incorrect response from network
    59             Unexpected network error

    60             Incompatible remote adapter
    61             Print queue full
    62             Queue not full
    63             Not enough space for print file
    64             Network name was deleted
    65             Access denied
    66             Network device type incorrect
    67             Network name not found
    68             Network name limit exceeded
    69             Net BIOS session limit exceeded

    70             Temporarily paused
    71             Network request not accepted
    72             Print or disk redirection is paused
    73-79          Reserved

    Code           Meaning
    ──────────────────────────────────────────────────────────────────────────

    80             File Exists
    81             Reserved
    82             Cannot make
    83             Interrupt 24 failure
    84             Out of structures
    85             Already assigned
    86             Invalid password
    87             Invalid parameter
    88             Net write fault
    89             Function not supported by network

    90             Required system component not installed
    ──────────────────────────────────────────────────────────────────────────


    To handle error conditions, put the following statement immediately after
    calls that return errors:

    JC <error>

    <Error> represents the label of an error-handling routine that gets the
    specific error condition by checking the value in AX. This routine then
    takes appropriate action.

    Some of the older system calls return a value in a register that specifies
    whether the operation was successful. To handle such errors, check the
    error code and take the appropriate action.

    Extended Error Codes

    Versions of MS-DOS after 2.0 have added new error messages. Any programs
    that use the older system calls cannot use these new error messages. To
    avoid incompatibility, MS-DOS maps these new error codes to the old error
    code that most closely matches the new one.

    Function 59H (Get Extended Error) has been added so that these new calls
    can be used. It provides as much detail as possible about the most recent
    error code returned by MS-DOS. The description of Function 59H lists the
    new, more detailed error codes and shows how to use this function request.

1.10.6  System Call Descriptions

    Most system calls require that you move information into one or more
    registers before issuing the call that returns information in the
    registers. The description of each system call in this chapter includes
    the following:

    ■  A diagram of the 8088 registers that shows their contents before and
        after the system call

    ■  A more complete description of the register contents required before
        the system call

    ■  A description of the processing performed

    ■  A more complete description of the register contents after the system
        call

    ■  An example of how to use the system call

    The following figure is a sample illustration of the 8088 registers,
    showing how the information is presented. Shaded areas indicate that the
    register receives or returns information used by the call.

        ┌───────┬───────┐
    AX:│  AH   │  AL   │
        ├───────┼───────┤
    BX:│  BH   │  BL   │
        ├───────┼───────┤
    CX:│  CH   │  CL   │
        ├───────┼───────┤
    DX:│  DH   │  DL   │
        └───────┴───────┘
        ┌───────────────┐
        │       SP      │
        ├───────────────┤
        │       BP      │
        ├───────────────┤
        │       SI      │
        ├───────────────┤
        │       DI      │
        └───────────────┘
        ┌───────────────┐
        │       SP      │
        ├───────┬───────┤
        │FLAGS H│FLAGS L│
        └───────┴───────┘

        ┌───────────────┐
        │       CS      │
        ├───────────────┤
        │       DS      │
        ├───────────────┤
        │       SS      │
        ├───────────────┤
        │       ES      │
        └───────────────┘

    Figure 1.1  Example of the 8088 Registers

    Sample Programs

    The sample programs show only data declarations and the code that you need
    to use the system calls. Unless stated otherwise, each example assumes a
    common program skeleton that defines the segments and returns control to
    MS-DOS. Each sample program is intended to be executed as a .com file.
    Figure 1.2 shows a complete sample program. The unshaded portion shows
    what appears in this chapter; the shaded portions are the common skeleton.


    ------------------------------------------------------------
    code       segment
                assume  cs:code,ds:code,es:nothing,ss:nothing
                org     100H
    start:     jmp    begin
    ;
    filename   db     "b:\textfile.asc",0
    buffer     db      129 dup (?)
    handle     dw      ?
    ;
    begin:     open_handle filename,0    ; Open the file
                jc      error_open        ; Routine not shown
                mov     handle,ax         ; Save handle
    read_line: read_handle handle,buffer,128 ; Read 128 bytes
                jc      error_read        ; Routine not shown
                cmp     ax,0              ; End of file?
                je      return            ; Yes, go home
                mov     bx,ax             ; No, AX bytes read
                mov     buffer[bx],"$"    ; To terminate string
                display buffer            ; See Function 09H
                jmp     read_line         ; Get next 128 bytes

    return:    end_process 0             ; Return to MS-DOS
    last_inst:                           ; To mark next byte
    ;
    code       ends
                end     start
    ------------------------------------------------------------

    Figure 1.2  Sample Program with Common Skeleton

    A macro has been defined for each system call to allow the examples to be
    more complete programs, rather than isolated uses of the system calls.
    These macros, plus some general-purpose ones, are used in the sample
    programs. For instance, the sample program in the preceding figure
    includes four such macros: open_handle, read_handle, display, and
    end_process. All the macro definitions are listed at the end of this
    chapter.

    The macros assume the environment for a .com program as described in
    Chapter 4; in particular, they assume that all the segment registers
    contain the same value. To conserve space, the macros generally leave
    error checking to the main code and do not protect registers. This keeps
    the macros short, yet useful. You may find that such macros are a
    convenient way to include system calls in your assembly-language programs.

    Error Handling in Sample Programs

    Whenever a system call returns an error code, the sample program shows a
    test for the error condition and a jump to an error routine. To conserve
    space, the error routines themselves aren't shown. Some error routines
    might simply display a message and continue processing. For more serious
    errors, the routine might display a message and end the program
    (performing any required housekeeping, such as closing files).

    Tables 1.16 through 1.19 list the Interrupts and Function Requests in
    numeric and alphabetic order.

    Table 1.16
    MS-DOS Interrupts, Numeric Order
    Interrupt                            Description
    ──────────────────────────────────────────────────────────────────────────
    20H                                 Program Terminate
    21H                                 Function Request
    22H                                 Terminate Process Exit Address
    23H                                 CONTROL+C Handler Address
    24H                                 Critical-Error-Handler Address
    25H                                 Absolute Disk Read
    26H                                 Absolute Disk Write
    27H                                 Terminate But Stay Resident
    28H-3FH                              Reserved
    ──────────────────────────────────────────────────────────────────────────

    Table 1.17
    MS-DOS Interrupts, Alphabetic Order
    Description                          Interrupt
    ──────────────────────────────────────────────────────────────────────────
    Absolute Disk Read                   25H
    Absolute Disk Write                  26H
    CONTROL+C Handler Address            23H
    Critical-Error-Handler Address       24H
    Function Request                     21H
    Program Terminate                    20H
    Reserved                             28H-3FH
    Terminate Process Exit Address       22H
    Terminate But Stay Resident          27H
    ──────────────────────────────────────────────────────────────────────────

    Table 1.18
    MS-DOS Function Requests, Numeric Order
╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
    Function           Description
    ──────────────────────────────────────────────────────────────────────────
    00H               Terminate Program
    01H               Read Keyboard And Echo
    02H               Display Character
    03H               Auxiliary Input
    Function           Description
    ──────────────────────────────────────────────────────────────────────────
    03H               Auxiliary Input
    04H               Auxiliary Output
    05H               Print Character
    06H               Direct Console I/O
    07H               Direct Console Input
    08H               Read Keyboard
    09H               Display String
    0AH               Buffered Keyboard Input
    0BH               Check Keyboard Status
    0CH               Flush Buffer, Read Keyboard
    0DH               Reset Disk
    0EH               Select Disk
    0FH               Open File
    10H               Close File
    11H               Search For First Entry
    12H               Search For Next Entry
    13H               Delete File
    14H               Sequential Read
    15H               Sequential Write
    Function           Description
    ──────────────────────────────────────────────────────────────────────────
    15H               Sequential Write
    16H               Create File
    17H               Rename File
    18H                Reserved
    19H               Get Current Disk
    1AH               Set Disk Transfer Address
    1BH               Get Default Drive Data
    1CH               Get Drive Data
    1DH-20H            Reserved
    21H               Random Read
    22H               Random Write
    23H               Get File Size
    24H               Set Relative Record
    25H               Set Interrupt Vector
    26H               Create New PSP
    27H               Random Block Read
    28H               Random Block Write
    29H               Parse File Name
    2AH               Get Date
    Function           Description
    ──────────────────────────────────────────────────────────────────────────
    2AH               Get Date
    2BH               Set Date
    2CH               Get Time
    2DH               Set Time
    2EH               Set/Reset Verify Flag
    2FH               Get Disk Transfer Address
    30H               Get MS-DOS Version Number
    31H               Keep Process
    32H                Reserved
    33H               CONTROL+C Check
    34H                Reserved
    35H               Get Interrupt Vector
    36H               Get Disk Free Space
    37H                Reserved
    38H               Get/Set Country Data
    39H               Create Directory
    3AH               Remove Directory
    3BH               Change Current Directory
    3CH               Create Handle
    Function           Description
    ──────────────────────────────────────────────────────────────────────────
    3CH               Create Handle
    3DH               Open Handle
    3EH               Close Handle
    3FH               Read Handle
    40H               Write Handle
    41H               Delete Directory Entry (Unlink)
    42H               Move File Pointer
    43H               Get/Set File Attributes (Chmod)
    4400H, 4401H     IOCtl Data
    4402H, 4403H     IOCtl Character
    4404H, 4405H     IOCtl Block
    4406H, 4407H     IOCtl Status
    4408H             IOCtl Is Changeable
    4409H             IOCtl Is Redirected Block
    440AH             IOCtl Is Redirected Handle
    440BH             IOCtl Retry
    440CH             Generic IOCtl (for code page functions)
    440DH             Generic IOCtl (for devices)
    440EH             Get IOCtl Drive Map
    Function           Description
    ──────────────────────────────────────────────────────────────────────────
    440EH             Get IOCtl Drive Map
    440FH             Set IOCtl Drive Map
    45H               Duplicate File Handle
    46H               Force Duplicate File Handle
    47H               Get Current Directory
    48H               Allocate Memory
    49H               Free Allocated Memory
    4AH               Set Block
    4B00H, 4B03H     Load and Execute Program or Overlay
    4CH               End Process
    4DH               Get Return Code of Child Process
    4EH               Find First File
    4FH               Find Next File
    50H-53H            Reserved
    54H               Get Verify State
    55H                Reserved
    56H               Change Directory Entry
    57H               Get/Set Date/Time of File
    58H               Get/Set Allocation Strategy
    Function           Description
    ──────────────────────────────────────────────────────────────────────────
    58H               Get/Set Allocation Strategy
    59H               Get Extended Error
    5AH               Create Temporary File
    5BH               Create New File
    5C00H, 5C01H     Lock/Unlock Files
    5DH                Reserved
    5E00H             Get Machine Name
    5E02H             Set Printer Setup
    5E03H             Get Printer Setup
    5F02H             Get Assign-List Entry
    5F03H             Make Network Connection
    5F04H             Delete Network Connection
    60H-61H            Reserved
    62H               Get PSP
    63H,64H            Reserved
    65H               Get Extended Country Information
    66H               Get/Set Global Code Page
    67H               Set Handle Count
    68H               Commit File
    Function           Description
    ──────────────────────────────────────────────────────────────────────────
    68H               Commit File
    69H-6BH            Reserved
    6CH               Extended Open/Create
    6DH-7FH            Reserved
    ──────────────────────────────────────────────────────────────────────────


    Table 1.19
    MS-DOS Function Requests, Alphabetic Order
╓┌─┌───────────────────────────────────────────────────────┌─────────────────╖
    Description                                             Function
    ──────────────────────────────────────────────────────────────────────────
    Allocate Memory                                         48H
    Auxiliary Input                                         03H
    Auxiliary Output                                        04H
    Buffered Keyboard Input                                 0AH
    Change Current Directory                                3BH
    Change Directory Entry                                  56H
    Check Keyboard Status                                   0BH
    Description                                             Function
    ──────────────────────────────────────────────────────────────────────────
    Check Keyboard Status                                   0BH
    Close File                                              10H
    Close Handle                                            3EH
    Commit FIle                                             68H
    CONTROL+C Check                                         33H
    Create Directory                                        39H
    Create File                                             16H
    Create Handle                                           3CH
    Create New File                                         5BH
    Create New PSP                                          26H
    Create Temporary File                                   5AH
    Delete Directory Entry (Unlink)                         41H
    Delete File                                             13H
    Delete Network Connection                               5F04H
    Direct Console I/O                                      06H
    Direct Console Input                                    07H
    Display Character                                       02H
    Display String                                          09H
    Duplicate File Handle                                   45H
    Description                                             Function
    ──────────────────────────────────────────────────────────────────────────
    Duplicate File Handle                                   45H
    End Process                                             4CH
    Extended Open/Create                                    6CH
    Find First File                                         4EH
    Find Next File                                          4FH
    Flush Buffer, Read Keyboard                             0CH
    Force Duplicate File Handle                             46H
    Free Allocated Memory                                   49H
    Generic IOCtl (for devices)                             440DH
    Generic IOCtl (for code page functions)                 440CH
    Get Assign-List Entry                                   5F02H
    Get Current Directory                                   47H
    Get Current Disk                                        19H
    Get Date                                                2AH
    Get Default Drive Data                                  1BH
    Get Disk Free Space                                     36H
    Get Disk Transfer Address                               2FH
    Get Drive Data                                          1CH
    Get Extended Country Information                        65H
    Description                                             Function
    ──────────────────────────────────────────────────────────────────────────
    Get Extended Country Information                        65H
    Get Extended Error                                      59H
    Get File Size                                           23H
    Get Interrupt Vector                                    35H
    Get IOCtl Drive Map                                     440EH
    Get Machine Name                                        5E00H
    Get MS-DOS Version Number                               30H
    Get PSP                                                 62H
    Get Printer Setup                                       5E03H
    Get Return Code of Child Process                        4DH
    Get Time                                                2CH
    Get Verify State                                        54H
    Get/Set Allocation Strategy                             58H
    Get/Set Country Data                                    38H
    Get/Set Date/Time Of File                               57H
    Get/Set File Attributes (Chmod)                         43H
    Get/Set Global Code Page                                66H
    IOCtl Block                                             4404H, 4405H
    IOCtl Character                                         4402H, 4403H
    Description                                             Function
    ──────────────────────────────────────────────────────────────────────────
    IOCtl Character                                         4402H, 4403H
    IOCtl Data                                              4400H, 4401H
    IOCtl Is Changeable                                     4408H
    IOCtl Is Redirected Block                               4409H
    IOCtl Is Redirected Handle                              440AH
    IOCtl Retry                                             440BH
    IOCtl Status                                            4406H, 4407H
    Keep Process                                            31H
    Load and Execute Program                                4B00H
    Load Overlay                                            4B03H
    Lock/Unlock Files                                       5C00H, 5C01H
    Make Network Connection                                 5F03H
    Move File Pointer                                       42H
    Open File                                               0FH
    Open Handle                                             3DH
    Parse File Name                                         29H
    Print Character                                         05H
    Random Block Read                                       27H
    Random Block Write                                      28H
    Description                                             Function
    ──────────────────────────────────────────────────────────────────────────
    Random Block Write                                      28H
    Random Read                                             21H
    Random Write                                            22H
    Read Handle                                             3FH
    Read Keyboard                                           08H
    Read Keyboard And Echo                                  01H
    Remove Directory                                        3AH
    Rename File                                             17H
    Reserved                                                18H
    Reserved                                                1DH-20H
    Reserved                                                32H
    Reserved                                                34H
    Reserved                                                37H
    Reserved                                                50H-53H
    Reserved                                                55H
    Reserved                                                5DH
    Reserved                                                60H-61H
    Reserved                                                63H, 64H
    Reserved                                                69H-6BH
    Description                                             Function
    ──────────────────────────────────────────────────────────────────────────
    Reserved                                                69H-6BH
    Reserved                                                6DH-7FH
    Reset Disk                                              0DH
    Search for First Entry                                  11H
    Search for Next Entry                                   12H
    Select Disk                                             0EH
    Sequential Read                                         14H
    Sequential Write                                        15H
    Set Block                                               4AH
    Set Date                                                2BH
    Set Disk Transfer Address                               1AH
    Set Handle Count                                        67H
    Set Interrupt Vector                                    25H
    Set IOCtl Drive Map                                     440FH
    Set Printer Setup                                       5E02H
    Set Relative Record                                     24H
    Set Time                                                2DH
    Set/Reset Verify Flag                                   2EH
    Terminate Program                                       00H
    Description                                             Function
    ──────────────────────────────────────────────────────────────────────────
    Terminate Program                                       00H
    Write Handle                                            40H
    ──────────────────────────────────────────────────────────────────────────


    A detailed description of each system call follows. These calls are listed
    in numeric order, interrupts first, followed by function requests.

    ──────────────────────────────────────────────────────────────────────────
    Note
    Unless stated otherwise, in the system call descriptions──both text and
    code──all numbers are in hexadecimal.
    ──────────────────────────────────────────────────────────────────────────


1.11  Interrupts

    The following pages describe Interrupts 20H-27H.


────────────────────────────────────────────────────────────────────────────
Program Terminate (Interrupt 20H)
────────────────────────────────────────────────────────────────────────────

    Call:

    CS
    Segment address of Program Segment Prefix

    Return:

    None


    Comments:

    Interrupt 20H terminates the current process and returns control to its
    parent process. It also closes all open file handles and clears the disk
    cache. When this interrupt is issued, CS must contain the segment address
    of the Program Segment Prefix.

    Interrupt 20H is provided only for compatibility with MS-DOS versions
    prior to 2.0. New programs should use Function 4CH (End Process), which
    permits returning a completion code to the parent process and does not
    require CS to contain the segment address of the Program Segment Prefix.

    The following exit addresses are restored from the Program Segment Prefix:

    Offset             Exit Address
    ──────────────────────────────────────────────────────────────────────────
    0AH                Program terminate
    0EH                CONTROL+C
    12H                Critical error
    ──────────────────────────────────────────────────────────────────────────

    All file buffers are flushed to disk.

    ──────────────────────────────────────────────────────────────────────────
    Note
    You should close all files that have changed in length before issuing
    this interrupt. If you do not close a changed file, its length may not
    be recorded correctly in the directory. See Functions 10H and 3EH for
    a description of the Close File system calls. If sharing is loaded, you
    should remove all locks before using Interrupt 20H. See Function 5C00H
    (Lock) for more information.
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    terminate  macro
                int 20H
                endm

    Example:

    The following program displays a message and returns to MS-DOS.

    message db "displayed by INT20H example". 0DH, 0AH, "$"
    ;
    begin:  display message  ; see Function 09H
            terminate        ; THIS INTERRUPT
    code    ends
            end     start


────────────────────────────────────────────────────────────────────────────
Function Request (Interrupt 21H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH
    Function number

    Other registers
    As specified in individual function

    Return:

    None


    Comments:

    As specified in individual function. Interrupt 21H causes MS-DOS to carry
    out the function request whose number is in AH. See Section 1.12,
    "Function Requests," for a description of the MS-DOS functions.

    Example:

    To call the Get Time function:

    mov   ah,2CH           ; Get Time is Function 2CH
    int   21H              ; MS-DOS function request


────────────────────────────────────────────────────────────────────────────
Terminate Process Exit Address (Interrupt 22H)
────────────────────────────────────────────────────────────────────────────

    This interrupt may be issued only by MS-DOS; user programs must never
    issue it. If you must write your own terminate interrupt handler, use
    Function 35H (Get Interrupt Vector) to get the address of the standard
    routine, save the address, then use Function 25H (Set Interrupt Vector)
    to change the Interrupt 22H entry in the vector table so that it points to
    your routine.

    When a program terminates, MS-DOS transfers control to the routine that
    starts at the address in the Interrupt 22H entry in the vector table. When
    MS-DOS creates a program segment, it copies this address into the Program
    Segment Prefix, starting at offset 0AH.


────────────────────────────────────────────────────────────────────────────
CONTROL+C Handler Address (Interrupt 23H)
────────────────────────────────────────────────────────────────────────────

    When you type CONTROL+C or CONTROL+BREAK (on IBM-compatibles), MS-DOS
    transfers control as soon as possible to the routine that starts at the
    address in the Interrupt 23H entry in the vector table. When MS-DOS
    creates a program segment, it copies the address currently in the
    interrupt table into the Program Segment Prefix, starting at offset 0EH.

    This interrupt may be issued only by MS-DOS; user programs must never
    issue it. If you must write your own CONTROL+C interrupt handler, use
    Function 35H (Get Interrupt Vector) to get the address of the standard
    routine, save the address, then use Function 25H (Set Interrupt Vector)
    to change the Interrupt 23H entry in the vector table to point to your
    routine.

    If the CONTROL+C routine preserves all registers, it can end with an IRET
    instruction (return from interrupt) to continue program execution. If a
    user-written interrupt program returns with a long return, the program
    uses the carry flag to determine whether or not the program will abort. If
    the carry flag is set, it will abort; otherwise, execution will continue
    as with a return by IRET.

    If a user-written CONTROL+BREAK routine interrupts function calls 09H,
    0AH, or buffered I/O, and if it continues execution with an IRET, then I/O
    continues from the start of the line. MS-DOS always outputs a CONTROL+C to
    the screen when it issues an Interrupt 23H. There is no way to turn this
    off.

    When the interrupt occurs, all registers are set to the value they had
    when the original call to MS-DOS was made. There are no restrictions on
    what a CONTROL+C handler can do──including calling MS-DOS functions──as
    long as the program restores the registers.

    If a CONTROL+C interrupts Function 09H or 0AH (Display String or Buffered
    Keyboard Input), the three-byte sequence 03H-0DH-0AH (usually displayed as
    C followed by a carriage-return) is sent to the display and the function
    resumes at the beginning of the next line.

    Suppose a program uses Function 4BH (Load and Execute Program or Overlay)
    to create a second Program Segment Prefix and execute a second program,
    which then changes the CONTROL+C address in the vector table. MS-DOS
    restores this CONTROL+C vector to its original value before returning
    control to the calling program.


────────────────────────────────────────────────────────────────────────────
critical-error-handler address (Interrupt 24H)
────────────────────────────────────────────────────────────────────────────

    If a critical error occurs during execution of an I/O function
    request (this often means a fatal disk error), MS-DOS transfers control to
    the routine at the address in the Interrupt 24H entry in the vector table.
    When MS-DOS creates a program segment, it copies this address into the
    Program Segment Prefix, starting at offset 12H.

    This interrupt may be issued only by MS-DOS; user programs must never
    issue it. If you must write your own critical-error interrupt handler, use
    Function 35H (Get Interrupt Vector) to get the address of the standard
    routine, save the address, then use Function 25H (Set Interrupt Vector)
    to change the Interrupt 24H entry in the vector table to point to your
    routine.

    MS-DOS does not issue Interrupt 24H if a failure occurs during execution
    of Interrupt 25H (Absolute Disk Read) or Interrupt 26H (Absolute Disk
    Write). A command.com error routine handles these errors. This routine
    retries the disk operation, then gives you the choice of aborting the
    operation, retrying it, allowing the system call to fail and the
    application process to continue, or ignoring the error.

    The following topics describe the requirements of an Interrupt 24H
    routine, including the error codes, registers, and stack.

1.11.1  Conditions upon Entry

    After retrying an I/O error five times, MS-DOS issues Interrupt 24H,
    unless a File Allocation Table (FAT) or directory sector is involved. In
    those cases, DOS performs three retries. The interrupt handler receives
    control with interrupts disabled. AX and DI contain error codes, and BP
    contains the offset (to the segment address in SI) of a Device Header
    control block that describes the device on which the error occurred.

1.11.2  Requirements for an Interrupt 24H Handler

    To issue the "Abort, Retry, Fail or Ignore" prompt to a user, a
    user-written critical-error handler should first push the flags and
    execute a FAR call to the address of the standard Interrupt 24H handler
    (the user program that changed the Interrupt 24H vector also should have
    saved this address). After a user responds to the prompt, MS-DOS returns
    control to the user-written routine.

    ──────────────────────────────────────────────────────────────────────────
    Note
    There are source applications which will have trouble handling critical
    errors, since this changes the stack frame.
    ──────────────────────────────────────────────────────────────────────────

    The error handler can then do its processing. However, before it does
    anything else, it must preserve BX, CX, DX, DS, ES, SS, and SP. Also, the
    error handler may use only function calls 01H-0CH (inclusive) and 59H (if
    it uses any others, the error handler destroys the MS-DOS stack and leaves
    MS-DOS in an unstable state). The contents of the Device Header should not
    be changed.

    It is recommended that the Interrupt 24H routine fail critical errors and
    let the application test for an extended error code when the Interrupt
    21H routine returns.

    User Stack

    This call uses the user stack that contains the following (starting with
    the top of the stack):

    IP     MS-DOS registers from issuing Interrupt 24H
    CS
    FLAGS

    AX     User registers at time of original
    BX     INT 21H
    CX
    DX
    SI
    DI
    BP
    DS
    ES

    IP     From the original INT 21H
    CS     from the user to MS-DOS
    FLAGS

    The registers are set such that if the user-written error handler issues
    an IRET, MS-DOS responds according to the value in AL:

    AL                 Action
    ──────────────────────────────────────────────────────────────────────────
    0                  Ignore the error.
    1                  Retry the operation.
    2                  Abort the program by issuing Interrupt 23H.
    3                  Fail the system call that is in progress.
    ──────────────────────────────────────────────────────────────────────────

    Notice that the ignore option may cause unexpected results, such as
    causing MS-DOS to behave as if an operation had completed successfully.

    Disk Error Code in AX

    If bit 7 of AH is 0, the error occurred on a disk drive. AL contains the
    failing drive (0=A, 1=B, etc.). Bit 0 of AH specifies whether the error
    occurred during a read or write operation (0=read, 1=write), and bits 1
    and 2 of AH identify the area of the disk where the error occurred:

    Bits 1-2                             Location of error
    ──────────────────────────────────────────────────────────────────────────
    00                                   MS-DOS area
    01                                   File Allocation Table
    10                                   Directory
    11                                   Data area
    ──────────────────────────────────────────────────────────────────────────

    Bits 3-5 of AH specify valid responses to the error prompt:

    Bit        Value                Response
    ──────────────────────────────────────────────────────────────────────────
    3          0                    Fail not allowed
                1                    Fail allowed
    4          0                    Retry not allowed
                1                    Retry allowed
    5          0                    Ignore not allowed
                1                    Ignore allowed
    ──────────────────────────────────────────────────────────────────────────

    If you specify Retry but it isn't allowed, MS-DOS changes it to Fail. If
    you specify Ignore but it isn't allowed, MS-DOS changes it to Fail. If you
    specify Fail but it isn't allowed, MS-DOS changes it to Abort. The Abort
    response is always allowed.

    Other Device Error Code in AX

    If bit 7 of AH is 1, either the memory image of the File Allocation Table
    (FAT) is bad or an error occurred on a character device. The device header
    pointed to by BP:SI contains a WORD of attribute bits that identify the
    type of device and, therefore, the type of error.

    The word of attribute bits is at offset 04H of the Device Header. Bit 15
    specifies the type of device (0=block, 1=character).

    If bit 15 is 0 (block device), the error was a bad memory image of the
    FAT.

    If bit 15 is 1 (character device), the error was on a character device. DI
    contains the error code, the contents of AL are undefined, and bits 0-3 of
    the attribute word have the following meaning:

    Bit                Meaning if Set
    ──────────────────────────────────────────────────────────────────────────
    0                  Current standard input
    1                  Current standard output
    2                  Current null device
    3                  Current clock device
    ──────────────────────────────────────────────────────────────────────────

    See Chapter 2, "MS-DOS Device Drivers," for a complete description of the
    Device Header control block.

    Error Code in DI

    The high byte of DI is undefined. The low byte contains the following
    error codes:

    Error code         Description
    ──────────────────────────────────────────────────────────────────────────
    0                  Attempt to write on write-protected disk
    1                  Unknown unit
    2                  Drive not ready
    3                  Unknown command
    4                  CRC error in data
    5                  Bad drive request structure length
    6                  Seek error
    7                  Unknown media type
    8                  Sector not found
    9                  Printer out of paper
    A                  Write fault
    B                  Read fault
    C                  General failure
    ──────────────────────────────────────────────────────────────────────────

    A user-written Interrupt 24H handler can use Function 59H (Get Extended
    Error) to get detailed information about the error that caused the
    interrupt to be issued.


────────────────────────────────────────────────────────────────────────────
Absolute Disk Read (Interrupt 25H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AL
    Drive number
    DS:BX
    Disk Transfer Address or
    Far pointer to parameter packet if CX = -1
    CX
    Number of sectors or -1
    DX
    Beginning relative sector

    Return:

    AL
    Error code if CF=1
    FLAGS
    CF        = 0 if successful
            = 1 if not successful
    AX
        0207 if attempt made to access partition larger
        than 32 megabytes with MS-DOS 3.3 or earlier.


    Comments:

    The registers must contain the following:

    Register           Contents
    ──────────────────────────────────────────────────────────────────────────
    AL                 Drive number (0=A, 1=B, etc.)

    DS:BX              Offset of Disk Transfer Address or far pointer to
                        parameter packet if CX = -1

    CX                 Number of sectors to read or -1

    DX                 Beginning relative sector
    ──────────────────────────────────────────────────────────────────────────

    ──────────────────────────────────────────────────────────────────────────
    Warning
    Avoid using this function unless absolutely necessary. Instead, you
    should access files through normal MS-DOS function requests. There is no
    guarantee of upward compatibility for the Absolute Disk I/O in future
    releases of MS-DOS.
    ──────────────────────────────────────────────────────────────────────────

    If CX = -1, DS:BX is the far pointer to the parameter packet. The
    parameter packet API provides access to partitions larger than 32
    megabytes. The parameter packet contains:

            Starting sector number  dd        ?
            Number of sectors       dw        ?
            Transfer address        dd        ?


    Interrupt 25H transfers control to the device driver and reads from the
    disk to the Disk-Transfer Address the number of sectors specified in CX.
    The interrupt has the same requirements as and processes identically to
    Interrupt 26H (Absolute Disk Write), except that it reads data rather
    than writes it. Also, since this interrupt does not check your input
    parameters too closely, make sure they are reasonable. If you use
    unreasonable parameters, you may get strange results or cause your system
    to crash.

    ──────────────────────────────────────────────────────────────────────────
    Note
    This call destroys all registers except the segment registers. So before
    issuing the interrupt, save any registers that your program uses.
    ──────────────────────────────────────────────────────────────────────────

    The system pushes the flags at the time of the call; they are still there
    upon return. To prevent uncontrolled growth, be sure to pop the stack upon
    return.

    If the disk operation is successful, the Carry Flag (CF) is 0. If the disk
    operation is not successful, CF is 1 and AL contains the MS-DOS error code
    (see Interrupt 24H earlier in this section for the codes and their
    meanings).

    Macro Definition:

    abs_disk_read  macro  disk,buffer,num_sectors,first_sector
                    mov    al,disk
                    mov    bx,offset buffer
                    mov    cx,num_sectors
                    mov    dx,first_sector
                    int    25H
                    popf
                    endm

    Example:

    The following program copies the contents of a single-sided disk in drive
    A to the disk in drive B.

    prompt     db   "Source in A, target in B",0DH,0AH
                db   "Any key to start. $"
    first      dw    0
    buffer     db    60 dup (512 dup (?))  ; 60 sectors
    ;
    begin:     display prompt         ; see Function 09H
                read_kbd               ; see Function 08H
                mov     cx,6           ; copy 6 groups of
                                        ; 60 sectors
    copy:      push    cx             ; save the loop counter
                abs_disk_read  0,buffer,60,first  ; THIS INTERRUPT
                abs_disk_write 1,buffer,60,first  ; see INT 26H
                add  first,60          ; do the next 60 sectors
                pop  cx                ; restore the loop counter
                loop copy

    The following code is an example of reading the absolute sector with
    Interrupt 25H.

            mov  al, 'C'-'A'       ; select drive C:
            mov  cx,-1             ; request new absolute format
            mov  bx,code            ; set ds:bx up to point to parameter
            mov  ds,bx              ; packet with sector number, length
            mov  bx,offset par_pck  ; and transfer address information
            int  25H                ; do operation
            jc   error_on_read      ; check for errors

    par_pck:
            dd   12345H             ; absolute sector number to read=12345H
            dw   2                  ; number of sectors to read
            dd   buffer             ; transfer address

    buffer    db   1024 dup (0)       ; target buffer for read


────────────────────────────────────────────────────────────────────────────
Absolute Disk Write (Interrupt 26H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AL
    Drive number
    DS:BX
    Disk Transfer Address or
    Far pointer to parameter packet if CX = -1
    CX
    Number of sectors or -1
    DX
    Beginning relative sector

    Return:

    AL
    Error code if CF = 1
    FLAGS
    CF        = 0 if successful
            = 1 if not successful


    Comments:

    ──────────────────────────────────────────────────────────────────────────
    Warning
    Avoid using this function unless absolutely necessary. Instead, you
    should access files through normal MS-DOS function requests. There is no
    guarantee of upward compatibility for the Absolute Disk I/O in future
    releases of MS-DOS.
    ──────────────────────────────────────────────────────────────────────────

    The registers must contain the following:

    Register           Contents
    ──────────────────────────────────────────────────────────────────────────
    AL                 Drive number (0=A, 1=B, etc.)

    DS:BX              Offset of Disk Transfer Address or far pointer to
                        parameter packet if CX = -1

    CX                 Number of sectors to write or -1

    DX                 Beginning relative sector
    ──────────────────────────────────────────────────────────────────────────

    This interrupt transfers control to MS-DOS. The number of sectors
    specified in CX is written from the Disk Transfer Address to the disk. Its
    requirements and processing are identical to Interrupt 25H (Absolute Disk
    Read), except data is written to the disk rather than read from it. Also,
    since Interrupt 26H does not check your input parameters too closely, make
    sure they are reasonable. If you use unreasonable parameters, you may get
    strange results or cause your system to crash.

    If CX = -1, DS:BX is the far pointer to the parameter packet. The
    parameter packet contains:

            Starting sector number        dd        ?
            Number of sectors        dw        ?
            Transfer address        dd        ?


    ──────────────────────────────────────────────────────────────────────────
    Note
    This call destroys all registers except the segment registers. So before
    issuing the interrupt, be sure to save any registers your program uses.
    ──────────────────────────────────────────────────────────────────────────

    The system pushes the flags at the time of the call; they are still there
    upon return. To prevent uncontrolled growth, be sure to pop the stack upon
    return.

    If the disk operation is successful, the Carry Flag (CF) is 0. If the disk
    operation is not successful, CF is 1 and AL contains the MS-DOS error code
    (see Interrupt 24H for the codes and their meanings).

    Macro Definition:

    abs_disk_write  macro  disk,buffer,num_sectors,first_sector
                    mov    al,disk
                    mov    bx,offset buffer
                    mov    cx,num_sectors
                    mov    dx,first_sector
                    int    26H
                    popf
                    endm

    Example:

    The following program copies the contents of a single-sided disk in drive
    A to the disk in drive B, verifying each write. It uses a buffer of 32
    kilobytes.

    off        equ   0
    on         equ   1
    ;
    prompt     db   "Source in A, target in B",0DH,0AH
                db   "Any key to start. $"
    first      dw    0
    buffer     db    60 dup (512 dup (?))  ; 60 sectors
    ;
    begin:     display prompt       ; see Function 09H
                read_kbd             ; see Function 08H
                verify  on           ; see Function 2EH
                mov     cx,6         ; copy 6 groups of 60 sectors
    copy:      push    cx           ; save the loop counter
                abs_disk_read  0,buffer,60,first  ; see INT 25H
                abs_disk_write 1,buffer,60,first  ; THIS INTERRUPT
                add  first,60        ; do the next 60 sectors
                pop  cx              ; restore the loop counter
                loop copy
                verify  off          ; see Function 2EH

    The following code is an example of writing the absolute sector with
    Interrupt 26H.

            mov   al,'C'-'A'        ; select drive C:
            mov   cx,-1             ; request new absolute format
            mov   bx,code            ; set ds:bx up to point to parameter
            mov   ds,bx              ; packet with sector number, length
            mov   bx,offset par_pck  ; and transfer address information
            int   26H                ; do operation
            jc    error_on_write     ; check for errors

    par_pck:
            dd     12345H             ; absolute sector number to write=12345H
            dw     2                  ; number of sectors to write
            dd     buffer             ; transfer address

    buffer  db     1024 dup (0)       ; target buffer for write


────────────────────────────────────────────────────────────────────────────
Terminate But Stay Resident (Interrupt 27H)
────────────────────────────────────────────────────────────────────────────

    Call:

    CS:DX
    Pointer to first byte following
    last byte of code.

    Return:

    None


    Comments:

    This interrupt is provided only for compatibility with MS-DOS versions
    prior to 2.0. Unless your resident program must be compatible with MS-DOS
    versions before 2.0, you should use Function 31H (Keep Process) to
    install it. Function 31H lets programs larger than 64K remain resident
    and allows return information to be passed.

    However, Interrupt 27H, which is often used to install device-specific
    interrupt handlers, forces programs that are up to 64K to remain resident
    after they terminate.

    DX must contain the offset (from the segment address in CS) of the first
    byte that follows the last byte of code in the program. When Interrupt 27H
    is executed, the program terminates and control returns to MS-DOS, but the
    program is not overlaid by other programs. Files left open are not closed.
    When the interrupt is called, CS must contain the segment address of the
    Program Segment Prefix (the value of DS and ES when execution started).
    .Exe programs that are loaded into high memory must not use this
    interrupt. Similarly, since it restores the Interrupt 22H, 23H, and
    24H vectors, you should not use Interrupt 27H to install new CONTROL+C or
    critical-error handlers.

    Macro Definition:

    stay_resident  macro last_instruc
                    mov   dx,offset last_instruc
                    inc   dx
                    int   27H
                    endm

    Example:

    Since the most common use of Interrupt 27H is to install a
    machine-specific routine, there is no general example that applies. The
    macro definition, however, shows the calling syntax.


1.12  Function Requests

    The following pages describe function calls 00H-68H.


────────────────────────────────────────────────────────────────────────────
Terminate Program (Function 00H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 00H
    CS
    Segment address of
    Program Segment Prefix

    Return:

    None


    Comments:

    Function 00H performs the same function as Interrupt 20H. It terminates
    the current process and returns control to its parent process. It also
    closes all open file handles and clears the disk cache. When this
    interrupt is issued, CS must contain the segment address of the Program
    Segment Prefix.

    The CS register must contain the segment address of the Program Segment
    Prefix before you call this interrupt.

    The following exit addresses are restored from the specified offsets in
    the Program Segment Prefix:

    Offset             Exit Address
    ──────────────────────────────────────────────────────────────────────────
    0AH                Program terminate
    0EH                CONTROL+C
    12H                Critical error
    ──────────────────────────────────────────────────────────────────────────

    All file buffers are flushed to disk.

    ──────────────────────────────────────────────────────────────────────────
    Warning
    Close all files that have changed in length before calling this
    function. If you do not close a changed file, its length is not
    correctly recorded in the directory. See Function 10H for a description
    of the Close File system call.
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    terminate_program  macro
                        xor    ah,ah
                        int    21H
                        endm

    Example:

    The following program displays a message and returns to MS-DOS. It uses
    only the opening portion of the sample program skeleton shown in Figure
    1.2.

    message db "Displayed by FUNC00H example", 0DH,0AH,"$"
    ;
    begin:  display message    ; see Function 09H
            terminate_program  ; THIS FUNCTION
    code    ends
            end    start


────────────────────────────────────────────────────────────────────────────
Read Keyboard and Echo (Function 01H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 01H

    Return:

    AL
    Character typed


    Comments:

    Function 01H waits for a character to be read from standard input, then
    echoes the character to standard output and returns it in AL. If the
    character is CONTROL+C, it executes Interrupt 23H.

    Macro Definition:

    read_kbd_and_echo  macro
                        mov  ah, 01H
                        int  21H
                        endm

    Example:

    The following program displays and prints characters as you type them. If
    you press the ENTER key, the program sends a linefeed/carriage-return
    sequence to both the display and the printer.

    begin:     read_kbd_and_echo         ; THIS FUNCTION
                print_char   al           ; see Function 05H
                cmp          al,0DH       ; is it a CR?
                jne          begin        ; no, print it
                print_char   0AH          ; see Function 05H
                display_char 0AH          ; see Function 02H
                jmp          begin        ; get another character


────────────────────────────────────────────────────────────────────────────
Display Character (Function 02H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 02H
    DL
    Character to be displayed

    Return:

    None


    Comments:

    Function 02H sends the character in DL to standard output. If you press
    CONTROL+C, it issues Interrupt 23H.

    Macro Definition:

    display_char macro   character
                mov     dl,character
                mov     ah,02H
                int     21H
                endm

    Example:

    The following program converts lowercase characters to uppercase before
    displaying them.

    begin:     read_kbd                 ; see Function 08H
                cmp     al,"a"
                jl      uppercase        ; don't convert
                cmp     al,"z"
                jg      uppercase        ; don't convert
                sub     al,20H           ; convert to ASCII code
                                        ; for uppercase
    uppercase: display_char al          ; THIS FUNCTION
                jmp     begin:           ; get another character


────────────────────────────────────────────────────────────────────────────
Auxiliary Input (Function 03H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 03H

    Return:

    AL
    Character from auxiliary device


    Comments:

    Function 03H waits for a character from standard auxiliary devices (AUX,
    COM1, COM2, COM3, COM4), then returns the character in AL. This system
    call does not return a status or error code.

    If you press CONTROL+C, it issues Interrupt 23H.

    Macro Definition:

    aux_input  macro
                mov  ah,03H
                int  21H
                endm

    Example:

    The following program prints characters as soon as it receives them from
    the auxiliary device. It stops printing when it receives an end-of-file
    character (ASCII 26, or CONTROL+Z).

    begin:     aux_input        ; THIS FUNCTION
                cmp   al,1AH     ; end of file?
                je    return     ; yes, all done
                print_char  al   ; see Function 05H
                jmp   begin      ; get another character


────────────────────────────────────────────────────────────────────────────
Auxiliary Output (Function 04H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 04H
    DL
    Character for auxiliary device

    Return:

    None


    Comments:

    Function 04H sends the character in DL to standard auxiliary. This system
    call does not return a status or error code.

    If you press CONTROL+C, it issues Interrupt 23H.

    Macro Definition:

    aux_output  macro  character
                mov  dl,character
                mov  ah,04H
                int  21H
                endm

    Example:

    The following program gets a series of strings of up to 80 bytes from the
    keyboard and sends each string to the auxiliary device. It stops when you
    type a null string (carriage-return only).

    string   db    81 dup(?) ; see Function 0AH
    ;
    begin:   get_string  80,string       ; see Function 0AH
            cmp  string[1],0            ; null string?
            je   return                 ; yes, all done
            mov  cx, word ptr string[1] ; get string length
            mov  bx,0                   ; set index to 0
    send_it: aux_output string[bx+2]     ; THIS FUNCTION
            inc  bx                     ; bump index
            loop  send_it               ; send another character
            jmp  begin                  ; get another string


────────────────────────────────────────────────────────────────────────────
Print Character (Function 05H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 05H
    DL
    Character for printer

    Return:

    None


    Comments:

    Function 05H sends the character in DL to the standard printer. If you
    press CONTROL+C, it issues Interrupt 23H. This function does not return a
    status or error code.

    Macro Definition:

    print_char macro character
                mov   dl,character
                mov   ah,05H
                int   21H
                endm

    Example:

    The following program prints a walking test pattern on the printer. It
    stops if you press CONTROL+C.

    line_num    db    0
    ;
    begin:      mov   cx,60        ; print 60 lines
    start_line: mov   bl,33        ; first printable ASCII
                                    ; character (!)
                add   bl,line_num  ; to offset one character
                push  cx           ; save number-of-lines counter
                mov   cx,80        ; loop counter for line
    print_it:   print_char bl      ; THIS FUNCTION
                inc   bl           ; move to next ASCII character
                cmp   bl,126       ; last printable ASCII
                                    ; character (~)
                jl    no_reset     ; not there yet
                mov   bl,33        ; start over with (!)
    no_reset:   loop  print_it     ; print another character
                print_char 0DH     ; carriage return
                print_char 0AH     ; linefeed
                inc   line_num     ; to offset 1st char. of line
                pop   cx           ; restore #-of-lines counter
                loop  start_line   ; print another line


────────────────────────────────────────────────────────────────────────────
Direct Console I/O (Function 06H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 06H
    DL
    See text

    Return:

    AL
    If DL = FFH  before call,
    then zero flag not set means AL
    has character from standard input.
    Zero flag set means there was not
    a character to get, and AL = 0.


    Comments:

    The action of Function 06H depends on the value in DL when the function is
    called:

    Value in DL        Action
    ──────────────────────────────────────────────────────────────────────────
    FFH                If a character has been read from standard input, it is
                        returned in AL and the zero flag is cleared (0); if a
                        character has not been read, the zero flag is set (1).

    Not FFH            The character in DL is sent to standard output.
    ──────────────────────────────────────────────────────────────────────────

    This function does not check for CONTROL+C.

    Macro Definition:

    dir_console_io  macro switch
                    mov  dl,switch
                    mov  ah,06H
                    int  21H
                    endm

    Example:

    The following program sets the system clock to 0 and displays the time
    continuously. When you type any character, the display freezes; when you
    type any character again, the clock is reset to 0 and the display starts
    again.

    time      db  "00:00:00.00",0DH,0AH,"$"  ; see Function 09H
    ;                                      ; for explanation of $
    ;
    begin:        set_time  0,0,0,0       ; see Function 2DH
    read_clock:   get_time                ; see Function 2CH
                CONVERT  ch,time        ; see end of chapter
                CONVERT  cl,time[3]     ; see end of chapter
                CONVERT  dh,time[6]     ; see end of chapter
                CONVERT  dl,time[9]     ; see end of chapter
                display  time           ; see Function 09H
                dir_console_io  FFH     ; THIS FUNCTION
                cmp      al,0           ; character typed?
                jne      stop           ; yes, stop timer
                jmp      read_clock     ; no, keep timer
                                        ; running
    stop:         read_kbd                ; see Function 08H
                jmp      begin          ; start over


────────────────────────────────────────────────────────────────────────────
Direct Console Input (Function 07H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 07H

    Return:

    AL
    Character from keyboard


    Comments:

    Function 07H waits for a character to be read from standard input, then
    returns it in AL. This function does not echo the character or check for
    CONTROL+C. (For a keyboard input function that echoes or checks for
    CONTROL+C, see Function 01H or 08H.)

    Macro Definition:

    dir_console_input  macro
                        mov  ah,07H
                        int  21H
                        endm

    Example:

    The following program prompts for a password (eight characters maximum)
    and places the characters into a string without echoing them.

    password  db  8 dup(?)
    prompt    db "Password: $"       ; see Function 09H for
                                    ; explanation of $
    begin:    display prompt         ; see Function 09H
            mov  cx,8              ; maximum length of password
            xor  bx,bx             ; so BL can be used as index
    get_pass: dir_console_input      ; THIS FUNCTION
            cmp  al,0DH            ; was it a carriage return?
            je   return            ; yes, all done
            mov  password[bx],al   ; no, put character in string
            inc  bx                ; bump index
            loop get_pass          ; get another character


────────────────────────────────────────────────────────────────────────────
Read Keyboard (Function 08H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 08H

    Return:

    AL
    Character from keyboard


    Comments:

    Function 08H waits for a character to be read from standard input, then
    returns it in AL. If you press CONTROL+C, it issues Interrupt 23H. This
    function does not echo the character. (For a keyboard input function that
    echoes the character or checks for CONTROL+C, see Function 01H.)

    Macro Definition:

    read_kbd  macro
            mov   ah,08H
            int   21H
            endm

    Example:

    The following program prompts for a password (eight characters maximum)
    and places the characters into a string without echoing them.

    password  db  8 dup(?)
    prompt    db "Password: $"      ; see Function 09H
                                    ; for explanation of $
    begin:    display prompt        ; see Function 09H
            mov  cx,8             ; maximum length of password
            xor  bx,bx            ; BL can be an index
    get_pass: read_kbd              ; THIS FUNCTION
            cmp  al,0DH           ; was it a carriage return?
            je   return           ; yes, all done
            mov  password[bx],al  ; no, put char. in string
            inc  bx               ; bump index
            loop get_pass         ; get another character


────────────────────────────────────────────────────────────────────────────
Display String (Function 09H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 09H
    DS:DX
    Pointer to string to be displayed

    Return:

    None


    Comments:

    Function 09H sends to standard output a string that ends with "$" (the $
    is not displayed).

    The DX register must contain the offset (from the segment address in DS)
    of the string.

    Macro Definition:

    display  macro string
            mov   dx,offset string
            mov   ah,09H
            int   21H
            endm

    Example:

    The following program displays the hexadecimal code of the key that is
    typed.

    table    db     "0123456789ABCDEF"
    result   db     " - 00H",0DH,0AH,"$"  ; see text for
                                        ; explanation of $
    begin:   read_kbd_and_echo            ; see Function 01H
            xor     ah,ah                ; clear upper byte
            convert ax,16,result[3] ; see end of chapter
            display result               ; THIS FUNCTION
            jmp     begin                ; do it again


────────────────────────────────────────────────────────────────────────────
Buffered Keyboard Input (Function 0AH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 0AH
    DS:DX
    Pointer to input buffer

    Return:

    None


    Comments:

    Function 0AH gets a string from standard input. DX must contain the offset
    (from the segment address in DS) of an input buffer of the following form:

    Byte               Contents
    ──────────────────────────────────────────────────────────────────────────
    1                  Maximum number of characters in buffer, including the
                        carriage return (you must set this value).

    2                  Actual number of characters typed, not counting the
                        carriage return (the function sets this value).

    3-n                Buffer; must be at least as long as the number in byte
    ──────────────────────────────────────────────────────────────────────────

    Characters are read from standard input and placed in the buffer beginning
    at the third byte until an ENTER character (ASCII 0DH) is read. If the
    buffer fills to one less than the maximum, additional characters read are
    ignored and ASCII 07H (Bel) is sent to standard output until an ENTER
    character is read. If you type the string at the console, it can be edited
    as it is being entered. If you press CONTROL+C, it issues Interrupt 23H.

    MS-DOS sets the second byte of the buffer to the number of characters read
    (not counting the carriage return).

    Macro Definition:

    get_string  macro  limit,string
                mov    dx,offset string
                mov    string,limit
                mov    ah,0AH
                int    21H
                endm

    Example:

    The following program gets a 16-byte (maximum) string from the keyboard
    and fills a 24-line by 80-character screen with it.

    buffer           label byte
    max_length       db    ?                ; maximum length
    chars_entered    db    ?                ; number of chars.
    string           db    17 dup (?)       ; 16 chars + CR
    strings_per_line dw    0                ; how many strings
                                            ; fit on line
    crlf             db    0DH,0AH
    ;
    begin:           get_string 17,buffer   ; THIS FUNCTION
                    xor  bx,bx             ; so byte can be
                                            ; used as index
                    mov  bl,chars_entered  ; get string length
                    mov  buffer[bx+2],"$"  ; see Function 09H
                    mov  al,50H            ; columns per line
                    cbw
                    div  chars_entered     ; times string fits
                                            ; on line
                    xor  ah,ah             ; clear remainder
                    mov  strings_per_line,ax ; save col. counter
                    mov  cx,24             ; row counter
    display_screen:  push cx                ; save it
                    mov  cx,strings_per_line ; get col. counter
    display_line:    display  string        ; see Function 09H
                    loop display_line
                    display crlf           ; see Function 09H
                    pop  cx                ; get line counter
                    loop display_screen    ; display 1 more line


────────────────────────────────────────────────────────────────────────────
Check Keyboard Status (Function 0BH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 0BH

    Return:

    AL
    00H        = no characters in type-ahead buffer
    FFH        = characters in type-ahead buffer


    Comments:

    Function 0BH checks whether characters are available from standard input
    (if standard input has not been redirected, it checks the type-ahead
    buffer). If characters are available, AL returns FFH; if not, AL returns
    0. If CONTROL+C is in the buffer, it issues Interrupt 23H.

    Macro Definition:

    check_kbd_status  macro
                    mov   ah,0BH
                    int   21H
                    endm

    Example:

    The following program displays the time continuously until you press any
    key:

    time        db       "00:00:00.00",0DH,0AH,"$"
                .
                .
    begin:      get_time                  ; see Function 2CH
                byte_to_dec ch,time       ; see end of chapter
                byte_to_dec cl,time[3]    ; see end of chapter
                byte_to_dec dh,time[6]    ; see end of chapter
                byte_to_dec dl,time[9]    ; see end of chapter
                display time              ; see Function 09H
                check_kbd_status          ; THIS FUNCTION
                cmp     al,0FFH           ; has a key been typed?
                je      return            ; yes, go home
                jmp     begin             ; no, keep displaying
                                        ; time


────────────────────────────────────────────────────────────────────────────
Flush Buffer, Read Keyboard (Function 0CH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 0CH
    AL
    1, 6, 7, 8, or 0AH = the
    corresponding function
    is called.
    Any other value = no
    further processing.

    Return:

    AL
    00H = Type-ahead buffer
    was flushed; no other
    processing performed.


    Comments:

    Function 0CH empties the standard input buffer (if standard input has not
    been redirected, Function 0CH empties the type-ahead buffer). Further
    processing depends on the value in AL when the function is called.

    AL                 Action
    ──────────────────────────────────────────────────────────────────────────
    1,6,7,8, or 0AH    The corresponding MS-DOS function is executed.
    Any other value    No further processing; AL returns 0.
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    flush_and_read_kbd  macro switch
                        mov   al,switch
                        mov   ah,0CH
                        int   21H
                        endm

    Example:

    The following program both displays and prints characters as you type
    them. If you press the ENTER key, the program sends a
    carriage-return/linefeed sequence to both the display and the printer.

    begin:     flush_and_read_kbd 1      ; THIS FUNCTION
                print_char   al           ; see Function 05H
                cmp          al,0DH       ; is it a carriage return?
                jne          begin        ; no, print it
                print_char   0AH          ; see Function 05H
                display_char 0AH          ; see Function 02H
                jmp          begin        ; get another character


────────────────────────────────────────────────────────────────────────────
Reset Disk (Function 0DH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 0DH

    Return:

    None


    Comments:

    Function 0DH flushes all file buffers to ensure that the internal buffer
    cache matches the disks in the drives. It writes out buffers that have
    been modified and marks all buffers in the internal cache as free. This
    function request is normally used to force a known state of the system;
    CONTROL+C interrupt handlers should call this function.

    This function does not update directory entries; you must close changed
    files to update their directory entries (see Function 10H, Close File).

    Macro Definition:

    reset_disk macro
                mov   ah,0DH
                int   21H
                endm

    Example:

    The following program flushes all file buffers and selects disk A.

    begin:  reset_disk
            select_disk "A"


────────────────────────────────────────────────────────────────────────────
Select Disk (Function 0EH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 0EH
    DL
    Logical drive number
    (0 = A, 1 = B, etc.)

    Return:

    AL
    Number of logical drives


    Comments:

    Function 0EH selects the drive specified in DL (0=A, 1=B, etc.) as the
    current logical drive. AL returns the number of logical drives.

    ──────────────────────────────────────────────────────────────────────────
    Note
    For future compatibility, treat the value returned in AL with care. For
    example, if AL returns 5, it is not safe to assume that drives A, B, C,
    D, and E are all valid drive designators.
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    select_disk macro disk
                mov   dl,disk[-64]
                mov   ah,0EH
                int   21H
                endm

    Example:

    The following program toggles between drive A and drive B to select the
    current drive (in a two-drive system).

    begin:    current_disk       ; see Function 19H
            cmp    al,00H      ; drive A: selected?
            je     select_b    ; yes, select B
            select_disk "A"    ; THIS FUNCTION
            jmp    return
    select_b: select_disk "B"    ; THIS FUNCTION


────────────────────────────────────────────────────────────────────────────
Open File (Function 0FH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 0FH
    DS:DX
    Pointer to unopened FCB

    Return:

    AL
    00H        = Directory entry found
    FFH        = No directory entry found


    Comments:

    Function 0FH opens a file. DX must contain the offset (from the segment
    address in DS) of an unopened File Control Block (FCB). This call searches
    the disk directory for the named file.

    If the call finds a directory entry for the file, AL returns 0 and the FCB
    is filled as follows:

    ■  If the drive code was 0 (current drive), it is changed to the actual
        drive used (1=A, 2=B, etc.). This lets you change the current drive
        without interfering with subsequent operations on this file.

    ■  Current Block (offset 0CH) is set to 0.

    ■  Record Size (offset 0EH) is set to the system default of 128.

    ■  File Size (offset 10FH), Date of Last Write (offset 14H), and Time of
        Last Write (offset 16H) are set from the directory entry.

    Before performing a sequential disk operation on the file, you must set
    the Current Record field (offset 20H). Before performing a random disk
    operation on the file, you must set the Relative Record field (offset
    21H). If the default record size (128 bytes) is not correct, set it to the
    correct length.

    If the call doesn't find a directory entry for the file, or if the file
    has the hidden or system attribute, AL returns FFH.

    Macro Definition:

    open  macro fcb
        mov   dx,offset fcb
        mov   ah,0FH
        int   21H
        endm

    Example:

    The following program prints a file named textfile.asc that is on the disk
    in drive B. If a partial record is in the buffer at end-of-file, the
    routine that prints the partial record prints characters until it
    encounters an end-of-file mark (ASCII 26, or CONTROL+Z).

    fcb            db    2,"TEXTFILEASC"
                    db    26 dup (?)
    buffer         db    128 dup (?)
    ;
    begin:         set_dta  buffer       ; see Function 1AH
                    open  fcb             ; THIS FUNCTION
    read_line:     read_seq  fcb         ; see Function 14H
                    cmp   al,02H          ; end of file?
                    je    all_done        ; yes, go home
                    cmp   al,00H          ; more to come?
                    jg    check_more      ; no, check for partial
                                        ; record
                    mov   cx,80H          ; yes, print the buffer
                    xor   si,si           ; set index to 0
    print_it:      print_char buffer[si] ; see Function 05H
                    inc   si              ; bump index
                    loop  print_it        ; print next character
                    jmp   read_line       ; read another record
    check_more:    cmp   al,03H          ; part. record to print?
                    jne   all_done        ; no
                    mov   cx,80H          ; yes, print it
                    xor   si,si           ; set index to 0
    find_eof:      cmp   buffer[si],26   ; end-of-file mark?
                    je    all_done        ; yes
                    print_char buffer[si] ; see Function 05H
                    inc   si              ; bump index to next
                                        ; character
                    loop  find_eof
    all_done:      close fcb             ; see Function 10H


────────────────────────────────────────────────────────────────────────────
Close File (Function 10H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 10H
    DS:DX
    Pointer to opened FCB

    Return:

    AL
    00H        = Directory entry found
    FFH        = No directory entry found


    Comments:

    Function 10H closes a file. DX must contain the offset (to the segment
    address in DS) of an opened FCB. This call searches the disk directory for
    the file named in the FCB. If it finds a directory entry for the file, it
    compares the location of the file with the corresponding entries in the
    FCB. The call then updates the directory entry, if necessary, to match the
    FCB, and AL returns 0.

    After you change a file, you must call this function to update the
    directory entry. You should close any FCB (even one for a file that has
    not been changed) when you no longer need access to a file.

    If this call doesn't find a directory entry for the file, AL returns FFH.

    Macro Definition:

    close  macro fcb
            mov   dx,offset fcb
            mov   ah,10H
            int   21H
            endm

    Example:

    The following program checks the first byte of the file named mod1.bas in
    drive B to see if it is FFH and, if it is, prints a message.

    message        db   "Not saved in ASCII format",0DH,0AH,"$"
    fcb            db    2,"MOD1    BAS"
                    db    26  dup (?)
    buffer         db    128 dup (?)
    ;
    begin:         set_dta  buffer       ; see Function 1AH
                    open  fcb             ; see Function 0FH
                    read_seq  fcb         ; see Function 14H

                    cmp   buffer,0FFH     ; is first byte FFH?
                    jne   all_done        ; no
                    display  message      ; see Function 09H
    all_done:      close fcb             ; THIS FUNCTION


────────────────────────────────────────────────────────────────────────────
Search for First Entry (Function 11H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 11H
    DS:DX
    Pointer to unopened FCB

    Return:

    AL
    00H        = Directory entry found
    FFH        = No directory entry found


    Comments:

    Function 11H searches the disk directory for the first matching filename.
    DX must contain the offset (from the segment address in DS) of an unopened
    FCB. The filename in the FCB can include wildcard characters. To search
    for hidden or system files, DX must point to the first byte of an extended
    FCB prefix.

    If this call does not find a directory entry for the filename in the FCB,
    AL returns FFH.

    But if the call does find a directory entry for the filename in the FCB,
    AL returns 0 and the call creates an unopened FCB of the same type (normal
    or extended) at the Disk Transfer Address as follows:

    1. If the search FCB was normal, the first byte at the Disk Transfer
        Address is set to the drive number used in the search (1=A, 2=B, etc.)
        and the next 32 bytes contain the directory entry.

    2. If the search FCB was extended, the first byte at the Disk Transfer
        Address is set to FFH, the next 5 bytes are set to 00H, and the
        following byte is set to the value of the attribute byte in the search
        FCB. The remaining 33 bytes are the same as the result of the normal
        FCB (drive number and 32 bytes of directory entry).

    If you use Function 12H (Search for Next Entry) to continue searching for
    matching filenames, you must not alter or open the original FCB at DS:DX.

    The attribute field is the last byte of the extended FCB fields that
    precede the FCB (see Extended FCB in Section 1.9.1). If the attribute
    field is zero, Function 11H searches only normal file entries. It does not
    search directory entries for hidden files, system files, volume label, and
    subdirectories.

    If the attribute field is hidden file, system file, or subdirectory entry
    (02H, 04H, or 10H), or any combination of those values, this call also
    searches all normal file entries. To search all directory entries except
    the volume label, set the attribute byte to 16H (hidden file and system
    file and subdirectory entry).

    If the attribute field is Volume ID (08H), the call searches only the
    volume label entry.

    Macro Definition:

    search_first  macro fcb
                mov   dx,offset fcb
                mov   ah,11H
                int   21H
                endm

    Example:

    The following program verifies the existence of a file named report.asm on
    the disk in drive B.

    yes        db   "FILE EXISTS.$"
    no         db   "FILE DOES NOT EXIST.$"
    crlf       db    0DH,0AH,"$"
    fcb        db    2,"REPORT  *ASM"
                db    26 dup (?)
    buffer     db    128 dup (?)
    ;
    begin:     set_dta  buffer           ; see Function 1AH
                search_first fcb          ; THIS FUNCTION
                cmp      al,0FFH          ; directory entry found?
                je       not_there        ; no
                display  yes              ; see Function 09H
                jmp      continue
    not_there: display  no               ; see Function 09H
    continue:  display  crlf             ; see Function 09H


────────────────────────────────────────────────────────────────────────────
Search for Next Entry (Function 12H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 12H
    DS:DX
    Pointer to unopened FCB

    Return:

    AL
    00H        = Directory entry found
    FFH        = No directory entry found


    Comments:

    After you use Function 11H (Search for First Entry), you can use Function
    12H to find any additional directory entries that match a filename
    (containing wildcard characters). Function 12H searches the disk directory
    for the next matching name. DX must contain the offset (from the segment
    address in DS) of an FCB specified in a previous call to Function 11H. To
    search for hidden or system files, DX must point to the first byte of an
    extended FCB prefix──one that includes the appropriate attribute value.

    If the call does not find a directory entry for the filename in the FCB,
    AL returns FFH.

    But if the call does find a directory entry for the filename in the FCB,
    AL returns 0 and the call creates an unopened FCB of the same type (normal
    or extended) at the Disk Transfer Address (see Function 11H for a
    description of how the unopened FCB is formed).

    Macro Definition:

    search_next  macro fcb
                mov   dx,offset fcb
                mov   ah,12H
                int   21H
                endm

    Example:

    The following program displays the number of files on the disk in drive B.


    message     db   "No files",0DH,0AH,"$"
    files       db    0
    fcb         db    2,"???????????"
                db    26  dup (?)
    buffer      db    128 dup (?)
    ;
    begin:      set_dta  buffer         ; see Function 1AH
                search_first  fcb       ; see Function 11H
                cmp  al,0FFH            ; directory entry found?
                je   all_done           ; no, no files on disk
                inc  files              ; yes, increment file
                                        ; counter
    search_dir: search_next  fcb        ; THIS FUNCTION
                cmp  al,0FFH            ; directory entry found?
                je   done               ; no
                inc  files              ; yes, increment file
                                        ; counter
                jmp  search_dir         ; check again
    done:       convert files,10,message ; see end of chapter
    all_done:   display  message        ; see Function 09H


────────────────────────────────────────────────────────────────────────────
Delete File (Function 13H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 13H
    DS:DX
    Pointer to unopened FCB

    Return:

    AL
    00H        = Directory entry found
    FFH        = No directory entry found


    Comments:

    Function 13H deletes a file. DX must contain the offset (from the segment
    address in DS) of an unopened FCB. This call searches the directory for a
    matching filename. The filename in the FCB can contain wildcard
    characters.

    If the call does not find a matching directory entry, AL returns FFH.

    But if the call does find a matching directory entry, AL returns 0 and the
    call deletes the entry from the directory. If the filename contains a
    wildcard character, the call will delete all files that match.

    Do not delete open files.

    Macro Definition:

    delete  macro  fcb
            mov    dx,offset fcb
            mov    ah,13H
            int    21H
            endm

    Example:

    The following program deletes each file on the disk in drive B that was
    last written before December 31, 1982.

    year           dw    1982
    month          db    12
    day            db    31
    files          db    0
    message        db   "No files deleted.",0DH,0AH,"$"
    fcb            db    2,"???????????"
                    db    26 dup  (?)
    buffer         db    128 dup (?)
    ;
    begin:         set_dta  buffer      ; see Function 1AH
                    search_first fcb     ; see Function 11H
                    cmp   al,0FFH        ; directory entry found?
                    jne   compare        ; yes
                    jmp   all_done       ; no, no files on disk
    compare:       convert_date buffer  ; see end of chapter
                    cmp  cx,year         ; next several lines
                    jg   next            ; check date in directory
                    cmp  dl,month        ; entry against date
                    jg   next            ; above & check next file
                    cmp  dh,day          ; if date in directory
                    jge  next            ; entry isn't earlier.
                    delete  buffer       ; THIS FUNCTION
                    inc  files           ; bump deleted-files
                                        ; counter
    next:          search_next fcb      ; see Function 12H
                    cmp  al,00H          ; directory entry found?
                    je   compare         ; yes, check date
                    cmp  files,0         ; any files deleted?
                    je   all_done        ; no, display No files
                                        ; message.
                    convert files,10,message ; see end of chapter
    all_done:      display message      ; see Function 09H


────────────────────────────────────────────────────────────────────────────
Sequential Read (Function 14H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 14H
    DS:DX
    Pointer to opened FCB

    Return:

    AL
    00H = Read completed successfully
    01H = EOF
    02H = DTA too small
    03H = EOF, partial record


    Comments:

    Function 14H reads a record from a specified file. DX must contain the
    offset (from the segment address in DS) of an opened FCB. This call loads
    the record pointed to by the Current Block field (offset 0CH) and Current
    Record (offset 20H) field at the Disk Transfer Address, then increments
    the Current Block and Current Record fields.

    The length of the record is taken from the Record Size field (offset 0EH)
    of the FCB.

    AL returns a code that describes the processing:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    0                  Read completed successfully

    1                  End-of-file; no data in the record

    2                  Not enough room at the Disk Transfer Address to read
                        one record; read canceled

    3                  End-of-file; a partial record was read and padded to
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    read_seq  macro fcb
            mov   dx,offset fcb
            mov   ah,14H
            int   21H
            endm

    Example:

    The following program displays a file named textfile.asc that is on the
    disk in drive B; its function is similar to the MS-DOS type command. If a
    partial record is in the buffer at end-of-file, the routine that displays
    the partial record displays characters until it encounters an end-of-file
    mark (ASCII 26, or CONTROL+Z).

    fcb            db    2,"TEXTFILEASC"
                    db    26  dup (?)
    buffer         db    128 dup (?),"$"
    ;
    begin:         set_dta  buffer    ; see Function 1AH
                    open  fcb          ; see Function 0FH
    read_line:     read_seq  fcb      ; THIS FUNCTION
                    cmp   al,02H       ; DTA too small?
                    je    all_done     ; yes
                    cmp   al,00H       ; end-of-file?
                    jg    check_more   ; yes
                    display  buffer    ; see Function 09H
                    jmp   read_line    ; get another record
    check_more:    cmp   al,03H       ; partial record in buffer?
                    jne   all_done     ; no, go home
                    xor   si,si        ; set index to 0
    find_eof:      cmp   buffer[si],26 ; is character EOF?
                    je    all_done     ; yes, no more to display
                    display_char buffer[si]  ; see Function 02H
                    inc   si           ; bump index
                    jmp   find_eof     ; check next character
    all_done:      close fcb          ; see Function 10H


────────────────────────────────────────────────────────────────────────────
Sequential Write (Function 15H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 15H
    DS:DX
    Pointer to opened FCB

    Return:

    AL
    00H = Write completed successfully
    01H = Disk full
    02H = DTA too small


    Comments:

    Function 15H writes a record to a specified file. DX must contain the
    offset (from the segment address in DS) of an opened FCB. This call writes
    the record pointed to by the Current Block field (offset 0CH) and Current
    Record field (offset 20H) at the Disk Transfer Address, then increments
    the Current Block and Current Record fields.

    The record size is taken from the value of the Record Size field (offset
    0EH) of the FCB. If the record size is less than a sector, the call writes
    the data at the Disk Transfer Address to an MS-DOS buffer; MS-DOS writes
    the buffer to disk when it contains a full sector of data, when the file
    is closed, or when Function 0DH (Reset Disk) is issued.

    AL returns a code that describes the processing:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    0                  Write completed successfully

    1                  Disk full; write canceled

    2                  Not enough room at the Disk Transfer Address to write
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    write_seq  macro fcb
                mov   dx,offset fcb
                mov   ah,15H
                int   21H
                endm


    Example:

    The following program creates a file named dir.tmp on the disk in drive B
    containing the disk number (0=A, 1=B, etc.) and filename from each
    directory entry on the disk.

    record_size    equ   0EH           ; offset of Record Size
    ;                                   field in FCB
    fcb1           db    2,"DIR     TMP"
                    db    26 dup (?)
    fcb2           db    2,"???????????"
                    db    26 dup (?)
    buffer         db    128 dup (?)
    ;
    begin:      set_dta      buffer     ; see Function 1AH
                search_first fcb2       ; see Function 11H
                cmp          al,0FFH    ; directory entry found?
                je           all_done   ; no, no files on disk
                create       fcb1       ; see Function 16H
                mov          fcb1[record_size],12
                                        ; set record size to 12
    write_it:   write_seq    fcb1       ; THIS FUNCTION
                cmp          al,0       ; write successful?
                jne          all_done   ; no, go home
                search_next  fcb2       ; see Function 12H
                cmp          al,FFH     ; directory entry found?
                je           all_done   ; no, go home
                jmp          write_it   ; yes, write the record
    all_done:   close        fcb1       ; see Function 10H


────────────────────────────────────────────────────────────────────────────
Create File (Function 16H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 16H
    DS:DX
    Pointer to unopened FCB

    Return:

    AL
    00H        = Empty directory found
    FFH        = No empty directory available


    Comments:

    Function 16H creates a file. DX must contain the offset (from the segment
    address in DS) of an unopened FCB. MS-DOS searches the directory for an
    entry that matches the specified filename or, if there is no matching
    entry, an empty entry.

    If MS-DOS finds a matching entry, it opens the file and sets the length to
    zero (in other words, if you try to create a file that already exists,
    MS-DOS erases it and creates a new, empty file). If MS-DOS doesn't find a
    matching entry but does find an empty directory entry, it opens the file
    and sets its length to zero. In either case, the call creates the file,
    and AL returns 0. If MS-DOS doesn't find a matching entry and there is no
    empty entry, the call doesn't create the file, and AL returns FFH.

    You can assign an attribute to the file by using an extended FCB with the
    attribute byte set to the appropriate value (see "Extended FCB" in Section
    1.9.1).

    Macro Definition:

    create  macro fcb
            mov   dx,offset fcb
            mov   ah,16H
            int   21H
            endm

    Example:

    The following program creates a file named dir.tmp on the disk in drive B
    containing the disk number (0 = A, 1 = B, etc.) and filename from each
    directory entry on the disk.

    record_size    equ   0EH           ; offset of Record Size
    ;                                   field of FCB
    fcb1           db    2,"DIR     TMP"
                    db    26  dup (?)
    fcb2           db    2,"???????????"
                    db    26  dup (?)
    buffer         db    128 dup (?)
    ;
    begin:         set_dta   buffer     ; see Function 1AH
                    search_first  fcb2   ; see Function 11H
                    cmp       al,0FFH    ; directory entry found?
                    je        all_done   ; no, no files on disk
                    create    fcb1       ; THIS FUNCTION
                    mov       fcb1[record_size],12
                                        ; set record size to 12
    write_it:      write_seq fcb1       ; see Function 15H
                    cmp       al,0       ; write successful
                    jne       all_done   ; no, go home
                    search_next  fcb2    ; see Function 12H
                    cmp       al,FFH     ; directory entry found?
                    je        all_done   ; no, go home
                    jmp       write_it   ; yes, write the record
    all_done:      close     fcb1       ; see Function 10H


────────────────────────────────────────────────────────────────────────────
Rename File (Function 17H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 17H
    DS:DX
    Pointer to modified FCB in the following
    nonstandard format:


        Byte(s)           Contents
        ───────────────────────────────────────────────────────────────────────
        00H               Drive number
        01H-08H           Old filename (padded with blanks, if necessary)
        09H-0BH           Old file extension (padded with blanks, if necessary)
        0CH-10H           Zeroed out
        11H-18H           New filename (padded with blanks, if necessary)
        19H-1BH           New file extension (padded with blanks, if necessary)
        11CH-24H          Zeroed out
        ───────────────────────────────────────────────────────────────────────

    Return:

    AL
    00H        =        Directory entry found
    FFH        =        No directory entry found
                    or destination already exists


    Comments:

    Function 17H changes the name of an existing file. DX must contain the
    offset (from the segment address in DS) of an FCB with the drive number
    and filename filled in, followed by a second filename at offset 11H. DOS
    searches the disk directory for an entry that matches the first filename.
    This filename can contain wildcard characters.

    If MS-DOS finds a matching directory entry and there is no directory entry
    that matches the second filename, it changes the filename in the directory
    entry to match the second filename in the modified FCB. AL then returns
    zero. If the second filename does contain a wildcard character, this call
    does not change the corresponding characters in the filename of the
    directory entry. You cannot use this function request to rename a hidden
    file, a system file, or a subdirectory. If MS-DOS does not find a matching
    directory entry or if it finds an entry for the second filename, AL
    returns FFH.

    Macro Definition:

    rename macro fcb,newname
            mov   dx,offset fcb
            mov   ah,17H
            int   21H
            endm

    Example:

    The following program prompts for the name of a file and a new name; it
    then renames the file.

    fcb               db    37 dup (?)
    prompt1           db   "Filename: $"
    prompt2           db   "New name: $"
    reply             db    15 dup(?)
    crlf              db    0DH,0AH,"$"
    ;
    begin:            display  prompt1       ; see Function 09H
                    get_string  15,reply   ; see Function 0AH
                    display  crlf          ; see Function 09H
                    parse    reply[2],fcb  ; see Function 29H
                    display  prompt2       ; see Function 09H
                    get_string  15,reply   ; see Function 0AH
                    display  crlf          ; see Function 09H
                    parse    reply[2],fcb[16]
                                            ; see Function 29H
                    rename   fcb           ; THIS FUNCTION


────────────────────────────────────────────────────────────────────────────
Get Current Disk (Function 19H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 19H

    Return:

    AL
    Currently selected drive
    (0 = A, 1 = B, etc.)


    Comments:

    Function 19H returns the current drive in AL (0=A, 1=B, etc.).

    Macro Definition:

    current_disk  macro
                mov   ah,19H
                int   21H
                endm

    Example:

    The following program displays the default drive in a two-drive system.

    message    db  "Current disk is $"
    crlf       db   0DH,OAH,"$"
    ;
    begin:     display  message         ; see Function 09H
                current_disk             ; THIS FUNCTION
                cmp      al,00H          ; is it disk A?
                jne      disk_b          ; no, it's disk B:
                display_char "A"         ; see Function 02H
                jmp      all_done
    disk_b:    display_char "B"         ; see Function 02H
    all_done:  display  crlf            ; see Function 09H


────────────────────────────────────────────────────────────────────────────
Set Disk Transfer Address (Function 1AH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 1AH
    DS:DX
    Disk Transfer Address

    Return:

    None


    Comments:

    Function 1AH sets the Disk Transfer Address. DX must contain the offset
    (from the segment address in DS) of the Disk Transfer Address. Disk
    transfers cannot wrap around from the end of the segment to the beginning,
    nor can they overflow into another segment.

    If you do not set the Disk Transfer Address, MS-DOS defaults to offset 80H
    in the Program Segment Prefix. You can check the current Disk Transfer
    Address with Function 2FH (Get Disk Transfer Address).

    Macro Definition:

    set_dta  macro  buffer
            mov    dx,offset buffer
            mov    ah,1AH
            int    21H
            endm

    Example:

    The following program prompts for a letter, converts it to its alphabetic
    sequence (A=1, B=2, etc.), then reads and displays the corresponding
    record from a file named alphabet.dat that is on the disk in drive B. The
    file contains 26 records, each 28 bytes long.

    record_size     equ   0EH      ; offset of Record Size
                                    ; field of FCB
    relative_record equ   21H      ; offset of Relative Record
    ;                               field of FCB
    fcb           db    2,"ALPHABETDAT"
                db    26  dup (?)
    buffer        db    28 dup(?),"$"
    prompt        db   "Enter letter: $"
    crlf          db    0DH,0AH,"$"
    ;
    begin:        set_dta  buffer       ; THIS FUNCTION
                open     fcb          ; see Function 0FH
                mov      fcb[record_size],28 ; set record size
    get_char:     display  prompt       ; see Function 09H
                read_kbd_and_echo     ; see Function 01H
                cmp      al,0DH       ; just a CR?
                je       all_done     ; yes, go home
                sub      al,41H       ; convert ASCII
                                        ; code to record #
                mov      fcb[relative_record],al
                                        ; set relative record
                display  crlf         ; see Function 09H
                read_ran fcb          ; see Function 21H
                display  buffer       ; see Function 09H
                display  crlf         ; see Function 09H
                jmp      get_char     ; get another character
    all_done:     close    fcb          ; see Function 10H


────────────────────────────────────────────────────────────────────────────
Get Default Drive Data (Function 1BH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 1BH

    Return:

    AL
        Sectors per cluster
    CX
        Bytes per sector
    DX
        Clusters per drive
    DS:BX
        Pointer to FAT ID byte


    Comments:

    Function 1BH retrieves data about the disk in the default drive. The data
    returns in the following registers:

    Register           Contents
    ──────────────────────────────────────────────────────────────────────────
    AL                 Number of sectors in a cluster (allocation unit)
    CX                 Number of bytes in a sector
    DX                 Number of clusters on the disk
    ──────────────────────────────────────────────────────────────────────────

    BX returns the offset (to the segment address in DS) of the first byte of
    the File Allocation Table (FAT), which identifies the type of disk in the
    drive:

    Value       Type of Drive
    ──────────────────────────────────────────────────────────────────────────
    FF          Double-sided disk, 8 sectors per track, 40 tracks per side
    FE          Single-sided disk, 8 sectors per track, 40 tracks per side
    FD          Double-sided disk, 9 sectors per track, 40 tracks per side
    FC          Single-sided disk, 9 sectors per track, 40 tracks per side
    F9          Double-sided disk, 15 sectors per track, 40 tracks per side
    F9          Double-sided disk, 9 sectors per track, 80 tracks per side
    F8          Fixed disk
    ──────────────────────────────────────────────────────────────────────────

    This call is similar to Function 36H (Get Disk Free Space), except that
    it returns the address of the FAT ID byte in BX instead of the number of
    available clusters. It is also similar to Function 1CH (Get Drive Data),
    except that it returns data on the disk in the default drive instead of on
    the disk in a specified drive. For a description of how MS-DOS stores data
    on a disk, including a description of the File Allocation Table, see
    Chapter 3, "MS-DOS Technical Information."

    ──────────────────────────────────────────────────────────────────────────
    Warning
    The FAT ID byte is no longer adequate to identify the type of drive
    being used. See Chapter 2, "MS-DOS Device Drivers," for more details.
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    def_drive_data  macro
                    push   ds
                    mov    ah,1BH
                    int    21H
                    mov    al,byte ptr[bx]
                    pop    ds
                    endm

    Example:

    The following program displays a message that tells whether the default
    drive is a floppy disk or a hard disk drive.

    stdout      equ         1
    ;
    msg         db          "Default drive is "
    dskt        db          "disk."
    fixed       db          "fixed."
    crlf        db          ODH,OAH
    ;
    begin:      write_handle stdout,msg,17      ; display message
                jc           write_error        ; routine not shown
                def_drive_data                  ; THIS FUNCTION
                cmp          byte ptr [bx],0F8H ; check FAT ID byte
                jne          disk           ; it's a disk
                write_handle stdout,fixed,6     ; see Function 40H
                jc           write_error        ; see Function 40H
                jmp short    all_done           ; clean up & go home
    disk:   write_handle stdout,dskt,9      ; see Function 40H
    all_done:   write_handle stdout,crlf,2      ; see Function 40H
                jc           write_error        ; routine not shown


────────────────────────────────────────────────────────────────────────────
Get Drive Data (Function 1CH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 1CH
    DL
        Drive (0=default, 1=A, etc.)


    Return:

    AL
        0FFH if drive number is invalid;
        otherwise, sectors per cluster
    CX
        Bytes per sector
    DX
        Clusters per drive
    DS:BX
        Pointer to FAT ID byte




    Comments:

    Function 1CH retrieves data about the disk in the specified drive. DL must
    contain the drive number (0=default, 1=A, etc.). The data returns in the
    following registers:

    Register           Contents
    ──────────────────────────────────────────────────────────────────────────
    AL                 Number of sectors in a cluster (allocation unit)
    CX                 Number of bytes in a sector
    DX                 Number of clusters on the disk
    ──────────────────────────────────────────────────────────────────────────

    BX returns the offset (to the segment address in DS) of the first byte of
    the File Allocation Table (FAT), which identifies the type of disk in the
    drive:

    Value       Type of Drive
    ──────────────────────────────────────────────────────────────────────────
    FF          Double-sided disk, 8 sectors per track, 40 tracks per side
    FE          Single-sided disk, 8 sectors per track, 40 tracks per side
    FD          Double-sided disk, 9 sectors per track, 40 tracks per side
    FC          Single-sided disk, 9 sectors per track, 40 tracks per side
    F9          Double-sided disk, 15 sectors per track, 40 tracks per side
    F9          Double-sided disk, 9 sectors per track, 80 tracks per side
    F8          Fixed disk
    ──────────────────────────────────────────────────────────────────────────

    If the drive number in DL is invalid, AL returns 0FFH.

    ──────────────────────────────────────────────────────────────────────────
    Warning
    The FAT ID byte is no longer adequate to identify the type of drive
    being used. See Chapter 2, "MS-DOS Device Drivers," for more details.
    ──────────────────────────────────────────────────────────────────────────

    This call is similar to Function 36H (Get Disk Free Space), except that
    it returns the address of the FAT ID byte in BX instead of the number of
    available clusters. It is also similar to Function 1BH (Get Default Drive
    Data), except that it returns data on the disk in the drive specified in
    DL instead of the disk in the default drive. For a description of how
    MS-DOS stores data on a disk, including a description of the File
    Allocation Table, see Chapter 3, "MS-DOS Technical Information."

    Macro Definition:

    drive_data  macro  drive
                push   ds
                mov    dl,drive
                mov    ah,1BH
                int    21H
                mov    al, byte ptr[bx]
                pop    ds
                endm

    Example:

    The following program displays a message that tells whether drive B is a
    floppy disk or a hard disk drive.

    stdout      equ          1
    :
    msg         db           "Drive B is "
    dskt        db           "disk."
    fixed       db           "fixed."
    crlf        db           ODH,OAH
    ;
    begin:      write_handle stdout,msg,11      ; display message
                jc           write_error        ; routine not shown
                drive_data   2                  ; THIS FUNCTION
                cmp          byte ptr [bx],0F8H ; check FAT ID byte
                jne          disk           ; it's a disk
                write_handle stdout,fixed,6     ; see Function 40H
                jc           write_error        ; routine not shown
                jmp          all_done           ; clean up & go home
    disk:   write_handle stdout,dskt,9      ; see Function 40H
    all_done:   write_handle stdout,crlf,2      ; see Function 40H
                jc           write_error        ; routine not shown


────────────────────────────────────────────────────────────────────────────
Random Read (Function 21H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 21H
    DS:DX
    Pointer to opened FCB

    Return:

    AL
    0 = Read completed successfully
    1 = End of file, record empty
    2 = DTA too small
    3 = End of file, partial record


    Comments:

    Function 21H reads (into the Disk Transfer Address) the record pointed to
    by the Relative Record field (offset 21H) of the FCB. DX must contain the
    offset (from the segment address in DS) of an opened FCB. The Current
    Block field (offset 0CH) and Current Record field (offset 20H) are set to
    agree with the Relative Record field (offset 21H). The record is then
    loaded at the Disk Transfer Address. The record length is taken from the
    Record Size field (offset 0EH) of the FCB.

    AL returns a code that describes the processing:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    0                  Read completed successfully

    1                  End-of-file; no data in the record

    2                  Not enough room at the Disk Transfer Address to read
                        one record; read canceled

    3                  End-of-file; a partial record was read and padded to
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    read_ran  macro  fcb
            mov    dx,offset fcb
            mov    ah,21H
            int    21H
            endm

    Example:

    The following program prompts for a letter, converts it to its alphabetic
    sequence (A = 1, B = 2, etc.), then reads and displays the corresponding
    record from a file named alphabet.dat that is on the disk in drive B. The
    file contains 26 records, each 28 bytes long.

    record_size     equ   0EH      ; offset of Record Size
                                    ; field of FCB
    relative_record equ   21H      ; offset of Relative Record
    ;                               field of FCB
    fcb           db    2,"ALPHABETDAT"
                db    26  dup (?)
    buffer        db    28 dup(?),"$"
    prompt        db   "Enter letter: $"
    crlf          db    0DH,0AH,"$"
    ;
    begin:        set_dta  buffer            ; see Function 1AH
                open     fcb               ; see Function 0FH
                mov      fcb[record_size],28 ; set record size
    get_char:     display  prompt            ; see Function 09H
                read_kbd_and_echo          ; see Function 01H
                cmp      al,0DH            ; just a CR?
                je       all_done          ; yes, go home
                sub      al,41H            ; convert ASCII code
                                            ; to record #
                mov      fcb[relative_record],al ; set relative
                                            ; record
                display  crlf              ; see Function 09H
                read_ran fcb               ; THIS FUNCTION
                display  buffer            ; see Function 09H
                display  crlf              ; see Function 09H
                jmp      get_char          ; get another char.
    all_done:     close    fcb               ; see Function 10H


────────────────────────────────────────────────────────────────────────────
Random Write (Function 22H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 22H
    DS:DX
    Pointer to opened FCB

    Return:

    AL
    00H = Write completed successfully
    01H = Disk full
    02H = DTA too small


    Comments:

    Function 22H writes (from the Disk Transfer Address) the record pointed to
    by the Relative Record field (offset 21H) of the FCB. DX must contain the
    offset from the segment address in DS of an opened FCB. The Current Block
    (offset 0CH) and Current Record (offset 20H) fields are set to agree with
    the Relative Record field (offset 21H). This record is then written from
    the Disk Transfer Address.

    The record length is taken from the Record Size field (offset 0EH) of the
    FCB. If the record size is less than a sector, the data at the Disk
    Transfer Address is written to a buffer. The buffer is written to a disk
    when it contains a full sector of data, or when a program closes the file,
    or when it issues Function 0DH (Reset Disk).

    AL returns a code that describes the processing:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    0                  Write completed successfully

    1                  Disk is full

    2                  Not enough room at the Disk Transfer Address to write
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    write_ran  macro  fcb
                mov    dx,offset fcb
                mov    ah,22H
                int    21H
                endm

    Example:

    The following program prompts for a letter, converts it to its alphabetic
    sequence (A = 1, B = 2, etc.), then reads and displays the corresponding
    record from a file named alphabet.dat that is on the disk in drive B.
    After displaying the record, it prompts you to enter a changed record. If
    you type a new record, it is written to the file, but if you just press
    the ENTER key, the record is not replaced. The file contains 26 records,
    each 28 bytes long.

    record_size     equ   0EH      ; offset of Record Size
                                    ; field of FCB
    relative_record equ   21H      ; offset of Relative Record
    ;                               field of FCB
    fcb        db    2,"ALPHABETDAT"
                db    26  dup (?)
    buffer     db    28 dup(?),0DH,0AH,"$"
    prompt1    db   "Enter letter: $"
    prompt2    db   "New record (ENTER for no change): $"
    crlf       db    0DH,0AH,"$"
    reply      db    28 dup (32)
    blanks     db    26 dup (32)
    ;

    begin:     set_dta  buffer            ; see Function 1AH
                open     fcb               ; see Function 0FH
                mov      fcb[record_size],28 ; set record size
    get_char:  display  prompt1           ; see Function 09H
                read_kbd_and_echo          ; see Function 01H
                cmp      al,0DH            ; just a carriage return?
                je       all_done          ; yes, go home
                sub      al,41H            ; convert ASCII
                                        ; code to record #
                mov      fcb[relative_record],al
                                        ; set relative record
                display  crlf              ; see Function 09H
                read_ran fcb               ; THIS FUNCTION
                display  buffer            ; see Function 09H
                display  crlf              ; see Function 09H
                display  prompt2           ; see Function 09H
                get_string 27,reply        ; see Function 0AH
                display  crlf              ; see Function 09H
                cmp      reply[1],0        ; was anything typed
                                        ; besides CR?
                je       get_char          ; no
                                        ; get another char.
                xor      bx,bx             ; to load a byte
                mov      bl,reply[1]       ; use reply length as
                                        ; counter
                move_string blanks,buffer,26 ; see chapter end
                move_string reply[2],buffer,bx ; see chapter end
                write_ran fcb              ; THIS FUNCTION
                jmp      get_char          ; get another character
    all_done:  close    fcb               ; see Function 10H


────────────────────────────────────────────────────────────────────────────
Get File Size (Function 23H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 23H
    DS:DX
    Pointer to unopened FCB

    Return:

    AL
    00H = Directory entry found
    FFH = No directory entry found


    Comments:

    Function 23H returns the size of a specified file. DX must contain the
    offset (from the segment address in DS) of an unopened FCB.

    If there is a directory entry that matches the specified file, MS-DOS
    divides the File Size field (offset 10H) of the directory entry by the
    Record Size field (offset 0EH) of the FCB, puts the result in the Relative
    Record field (offset 21H) of the FCB, and returns 0 in AL.

    You must set the Record Size field of the FCB to the correct value before
    calling this function. If the Record Size field is not an even divisor of
    the File Size field, the value set in the Relative Record field is rounded
    up, yielding a value larger than the actual number of records.

    If this call does not find a matching directory, AL returns FFH.

    Macro Definition:

    file_size  macro  fcb
                mov    dx,offset fcb
                mov    ah,23H
                int    21H
                endm

    Example:

    The following program prompts for the name of a file, opens the file to
    fill in the Record Size field of the FCB, issues a File Size system call,
    and displays the record length and number of records.

    fcb           db      37 dup  (?)
    prompt        db     "File name: $"
    msg1          db     "Record length:     ",0DH,0AH,"$"
    msg2          db     "Records:      ",0DH,0AH,"$"
    crlf          db      0DH,0AH,"$"
    reply         db      17 dup(?)
    ;
    begin:        display prompt            ; see Function 09H
                get_string 17,reply       ; see Function 0AH
                cmp     reply[1],0        ; just a CR?
                jne     get_length        ; no, keep going
                jmp     all_done          ; yes, go home
    get_length:   display crlf              ; see Function 09H
                parse   reply[2],fcb      ; see Function 29H
                open    fcb               ; see Function 0FH
                file_size fcb             ; THIS FUNCTION
                mov     ax,word ptr fcb[33] ; get record length
                convert ax,10,msg2[9]     ; see end of chapter
                mov     ax,word ptr fcb[14] ;  get record number
                convert ax,10,msg1[15]    ; see end of chapter
                display msg1              ; see Function 09H
                display msg2              ; see Function 09H
    all_done:     close   fcb               ; see Function 10H


────────────────────────────────────────────────────────────────────────────
Set Relative Record (Function 24H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 24H
    DS:DX
    Pointer to opened FCB

    Return:

    None
    AL=00H
    Relative Record field is modified in FCB.


    Comments:

    Function 24H sets the Relative Record field (offset 21H) to the file
    address specified by the Current Block field (offset 0CH) and Current
    Record field (offset 20H). DX must contain the offset (from the segment
    address in DS) of an opened FCB. You use this call to set the file pointer
    before a Random Read or Write (Functions 21H, 22H, 27H, or 28H).

    Macro Definition:

    set_relative_record  macro  fcb
                        mov    dx,offset fcb
                        mov    ah,24H
                        int    21H
                        endm

    Example:

    The following program copies a file using the Random Block Read and Random
    Block Write system calls. It speeds the copy by setting the record length
    equal to the file size and the record count to 1, and by using a buffer of
    32 kilobytes. It positions the file pointer by setting the Current Record
    field (offset 20H) to 1 and using Function 24H (Set Relative Record) to
    make the Relative Record field (offset 21H) point to the same record that
    the combination of the Current Block field (offset 0CH) and Current Record
    field (offset 20H) points to.

    current_record  equ   20H        ; offset of Current Record
                                    ; field of FCB
    fil_size        equ   10H        ; offset of File Size
    ;                                 field of FCB
    fcb       db      37 dup (?)
    filename  db      17 dup(?)
    prompt1   db     "File to copy: $"  ; see Function 09H for
    prompt2   db     "Name of copy: $"  ; explanation of $
    crlf      db      0DH,0AH,"$"
    file_length  dw   ?
    buffer    db      32767 dup(?)
    ;
    begin:    set_dta  buffer              ; see Function 1AH
            display  prompt1             ; see Function 09H
            get_string  15,filename      ; see Function 0AH
            display  crlf                ; see Function 09H
            parse    filename[2],fcb     ; see Function 29H
            open     fcb                 ; see Function 0FH
            mov      fcb[current_record],0 ; set Current Record
                                            ; field
            set_relative_record  fcb     ; THIS FUNCTION
            mov      ax,word ptr fcb[fil_size] ; get file size
            mov      file_length,ax      ; save it for
                                            ; ran_block_write
            ran_block_read  fcb,1,ax     ; see Function 27H
            display  prompt2             ; see Function 09H
            get_string  15,filename      ; see Function 0AH
            display  crlf                ; see Function 09H
            parse    filename[2],fcb     ; see Function 29H
            create   fcb                 ; see Function 16H
            mov      fcb[current_record],0 ; set Current Record
                                            ; field
            set_relative_record  fcb     ; THIS FUNCTION
            mov      ax,file_length      ; get original file
            ran_block_write  fcb,1,ax    ; see Function 28H
            close    fcb                 ; see Function 10H


────────────────────────────────────────────────────────────────────────────
Set Interrupt Vector (Function 25H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 25H
    AL
    Interrupt number
    DS:DX
    Pointer to interrupt-handling
    routine

    Return:

    None


    Comments:

    Function 25H sets the address in the interrupt vector table for the
    specified interrupt.

    AL must contain the number of the interrupt. DX must contain the offset
    (to the segment address in DS) of the interrupt-handling routine.

    To avoid compatibility problems, programs should never set an interrupt
    vector directly and should never use Interrupt 25H to read directly from
    memory. To get a vector, use Function 35H (Get Interrupt Vector), and to
    set a vector, use Function 25H, unless your program must be compatible
    with MS-DOS versions earlier than 2.0.

    Macro Definition:

    set_vector  macro  interrupt,handler_start
                mov    al,interrupt
                mov    dx,offset handler_start
                mov    ah,25H
                endm

    Example:

    Because interrupts tend to be machine-specific, no example is shown.


────────────────────────────────────────────────────────────────────────────
Create New PSP (Function 26H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 26H
    DX
    Segment address of new PSP

    Return:

    None


    Comments:

    This function request has been superseded. Use Function 4B00H or 4B03H
    (Load and Execute Program or Overlay) to run a child process, unless your
    program must be compatible with MS-DOS versions earlier than 2.0.

    Function 26H creates a new Program Segment Prefix. DX must contain the
    segment address where the new PSP is to be created.

    Macro Definition:

    create_psp  macro  seg_addr
                mov    dx,seg_addr
                mov    ah,26H
                endm

    Example:

    Because Functions 4B00H (Load and Execute Program) and 4B03H (Load
    Overlay) have superseded this function request, no example is shown.


────────────────────────────────────────────────────────────────────────────
Random Block Read (Function 27H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 27H
    DS:DX
    Pointer to opened FCB
    CX
    Number of blocks to read

    Return:

    AL
    0 = Read completed successfully
    1 = End of file, empty record
    2 = DTA too small
    3 = End of file, partial record
    CX
    Number of blocks read


    Comments:

    Function 27H reads one or more records from a specified file to the Disk
    Transfer Address. DX must contain the offset (to the segment address in
    DS) of an opened FCB. CX must contain the number of records to read.
    Reading starts at the record specified by the Relative Record field
    (offset 21H); you must set this field with Function 24H (Set Relative
    Record) before calling this function.

    DOS calculates the number of bytes to read by multiplying the value in CX
    by the Record Size field (offset 0EH) of the FCB.

    CX returns the number of records read. The Current Block field (offset
    0CH), Current Record field (offset 20H), and Relative Record field (offset
    21H) are set to address the next record.

    If you call this function with CX=0, no records are read. AL returns a
    code that describes the processing:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    0                  Read completed successfully

    1                  End-of-file; no data in the record

    2                  Not enough room at the Disk Transfer Address to read
                        one record; read canceled

    3                  End-of-file; a partial record was read and padded to
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    ran_block_read  macro  fcb,count,rec_size
                    mov    dx,offset fcb
                    mov    cx,count
                    mov    word ptr fcb[14],rec_size
                    mov    ah,27H
                    int    21H
                    endm

    Example:

    The following program copies a file by using Function 27H (Random Block
    Read). This program speeds the copy process by specifying a record count
    of 1 and a record length equal to the file size, and by using a buffer of
    32 kilobytes; the file is read as a single record (compare to the sample
    program for Function 28H that specifies a record length of 1 and a record
    count equal to the file size).

    current_record  equ  20H  ; offset of Current Record field
    fil_size        equ  10H  ; offset of File Size field
    ;
    fcb       db      37 dup (?)
    filename  db      17 dup(?)
    prompt1   db     "File to copy: $"   ; see Function 09H for
    prompt2   db     "Name of copy: $"   ; explanation of $
    crlf      db      0DH,0AH,"$"
    file_length  dw   ?
    buffer    db      32767 dup(?)
    ;



    begin:    set_dta    buffer           ; see Function 1AH
            display    prompt1          ; see Function 09H
            get_string 15,filename      ; see Function 0AH
            display    crlf             ; see Function 09H
            parse      filename[2],fcb  ; see Function 29H
            open       fcb              ; see Function 0FH
            mov        fcb[current_record],0  ; set Current
                                        ; Record field
            set_relative_record fcb     ; see Function 24H
            mov        ax, word ptr fcb[fil_size]
                                        ; get file size
            mov        file_length,ax   ; save it
            ran_block_read  fcb,1,ax    ; THIS FUNCTION
            display    prompt2          ; see Function 09H
            get_string 15,filename      ; see Function 0AH
            display    crlf             ; see Function 09H
            parse      filename[2],fcb  ; see Function 29H
            create     fcb              ; see Function 16H
            mov        fcb[current_record],0; set current
                                        ; Record field
            set_relative_record fcb     ; see Function 24H
            ran_block_write  fcb,1,ax   ; see Function 28H
            close      fcb              ; see Function 10H


────────────────────────────────────────────────────────────────────────────
Random Block Write (Function 28H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 28H
    DS:DX
    Pointer to opened FCB
    CX
    Number of blocks to write
    (0 = set File Size field)

    Return:

    AL
    00H = Write completed successfully
    01H = Disk full
    02H = End of segment
    CX
    Number of blocks written


    Comments:

    Function 28H writes one or more records to a specified file from the Disk
    Transfer Address. DX must contain the offset (to the segment address in
    DS) of an opened FCB; CX must contain either the number of records to
    write or 0.

    If CX is not 0, the specified number of records is written to the file,
    starting at the record specified in the Relative Record field (offset 21H)
    of the FCB. If CX is 0, no records are written, but MS-DOS sets the File
    Size field (offset 10H) of the directory entry to the value in the
    Relative Record field (offset 21H) of the FCB. To satisfy this new file
    size, disk allocation units are allocated or released, as required.

    MS-DOS calculates the number of bytes to write by multiplying the value in
    CX by the Record Size field (offset 0EH) of the FCB. CX returns the number
    of records written; the Current Block field (offset 0CH), Current Record
    field (offset 20H), and Relative Record (offset 21H) field are set to
    address the next record. AL returns a code that describes the processing:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    0                  Write completed successfully

    1                  Disk full. No records written

    2                  Not enough room at the Disk Transfer Address to write
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    ran_block_write  macro  fcb,count,rec_size
                    mov    dx,offset fcb
                    mov    cx,count
                    mov    word ptr fcb[14],rec_size
                    mov    ah,28H
                    int    21H
                    endm

    Example:

    The following program copies a file using Function 27H (Random Block
    Read) and Function 28H (Random Block Write). This program speeds the copy
    process by specifying a record count equal to the file size and a record
    length of 1, and by using a buffer of 32 kilobytes. The file is copied
    quickly with one disk access each to read and write (compare to the sample
    program of Function 27H, which specifies a record count of 1 and a record
    length equal to file size).

    current_record  equ  20H   ; offset of Current Record field
    fil_size        equ  10H   ; offset of File Size field
    ;
    fcb       db      37 dup (?)
    filename  db      17 dup(?)
    prompt1   db     "File to copy: $"   ; see Function 09H for
    prompt2   db     "Name of copy: $"   ; explanation of $
    crlf      db      0DH,0AH,"$"
    num_recs  dw      ?
    buffer    db      32767 dup(?)
    ;



    begin:    set_dta    buffer      ; see Function 1AH
            display    prompt1     ; see Function 09H
            get_string 15,filename ; see Function 0AH
            display    crlf        ; see Function 09H
            parse      filename[2],fcb  ; see Function 29H
            open       fcb              ; see Function 0FH
            mov        fcb[current_record],0; set Current
                                        Record field
            set_relative_record fcb     ; see Function 24H
            mov        ax, word ptr fcb[fil_size]
                                        ; get file size
            mov        num_recs,ax      ; save it
            ran_block_read  fcb,num_recs,1  ; THIS FUNCTION
            display    prompt2          ; see Function 09H
            get_string 15,filename      ; see Function 0AH
            display    crlf             ; see Function 09H
            parse      filename[2],fcb  ; see Function 29H
            create     fcb              ; see Function 16H
            mov        fcb[current_record],0  ; set Current
                                        ; Record field
            set_relative_record fcb     ; see Function 24H
            ran_block_write fcb,num_recs,1 ; see Function 28H
            close      fcb              ; see Function 10H


────────────────────────────────────────────────────────────────────────────
Parse File Name (Function 29H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 29H
    AL
    Controls parsing (see text)
    DS:SI
    Pointer to string to parse
    ES:DI
    Pointer to buffer for unopened FCB

    Return:

    AL
    00H        = No wildcard characters
    01H        = Wildcard characters used
    FFH        = Drive letter invalid
    DS:SI
    Pointer to first byte past
    string that was parsed
    ES:DI
    Pointer to unopened FCB


    Comments:

    Function 29H parses a string for a filename of the form
    drive:filename.extension. SI must contain the offset (to the segment
    address in DS) of the string to parse; DI must contain the offset (to the
    segment address in ES) of an area of memory large enough to hold an
    unopened FCB. If the string contains a valid filename, this call creates a
    corresponding unopened FCB at ES:DI.

    AL controls the parsing. Bits 4-7 must be 0; bits 0-3 have the following
    meaning:

    Table 1.20
    Bit Values for Function 29H
╓┌─┌───────────┌────────────┌────────────────────────────────────────────────╖
    Bit         Value        Meaning
    ──────────────────────────────────────────────────────────────────────────
    0           0            Stops parsing if a file separator is encountered

                1            Ignores leading separators

    1           0            Sets the drive number in the FCB to 0 (current
                            drive) if the string does not contain a drive
                            number

                1            Leaves the drive number in the FCB unchanged if
                            the string does not contain a drive number

    2           0            Sets the filename in the FCB to eight blanks if
                            the string does not contain a filename

                1            Leaves the filename in the FCB unchanged if the
                            string does not contain a filename

    3           0            Sets the extension in the FCB to three blanks if
                            the string does not contain an extension
    Bit         Value        Meaning
    ──────────────────────────────────────────────────────────────────────────
                            the string does not contain an extension

                1            Leaves the extension in the FCB unchanged if the
    ──────────────────────────────────────────────────────────────────────────


    If the string contains a filename or extension that includes an asterisk
    (*), all remaining characters in the name or extension are set to question
    marks (?).

    Filename Separators:



    :  ;  .  ,  =  +  /  "  [  ]  \  <  >  |  space  tab

    Filename terminators include all the filename separators, plus any control
    character. A filename cannot contain a filename terminator, since, if the
    call encounters one, parsing stops.

    If the string contains a valid filename:

    ■  AL returns 1 if the filename or extension contains a wildcard character
        (* or ?); AL returns 0 if neither the filename nor extension contains a
        wildcard character.

    ■  DS:SI points to the first character following the parsed string.

    ■  ES:DI points to the first byte of the unopened FCB.

    If the drive letter is invalid, AL returns FFH. If the string does not
    contain a valid filename, ES:DI+1 points to a blank.

    Macro Definition:

    parse macro string,fcb
        mov   si,offset string
        mov   di,offset fcb
        push  es
        push  ds
        pop   es
        mov   al,0FH        ; bits 0-3 on
        mov   ah,29H
        int   21H
        pop   es
        endm

    Example:

    The following program verifies the existence of the file named in reply to
    the prompt.

    fcb           db      37 dup (?)
    prompt        db     "Filename: $"
    reply         db      17 dup(?)
    yes           db     "File exists",0DH,0AH,"$"
    no            db     "File does not exist",0DH,0AH,"$"
                crlf    db 0DH,0AH,"$"
    ;
    begin:        display    prompt        ; see Function 09H
                get_string 15,reply      ; see Function 0AH
                parse      reply[2],fcb  ; THIS FUNCTION
                display    crlf          ; see Function 09H
                search_first  fcb        ; see Function 11H
                cmp        al,0FFH       ; dir. entry found?
                je         not_there     ; no
                display    yes           ; see Function 09H
                jmp        return
    not_there:    display    no


────────────────────────────────────────────────────────────────────────────
Get Date (Function 2AH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 2AH

    Return:

    CX
    Year (1980-2099)
    DH
    Month (1-12)
    DL
    Day (1-31)
    AL
    Day of week (0=Sun., 6=Sat.)


    Comments:

    Function 2AH returns the current date set in the operating system as
    binary numbers in CX and DX:

    Register           Contents
    ──────────────────────────────────────────────────────────────────────────
    CX                 Year (1980-2099)
    DH                 Month (1=January, 2=February, etc.)
    DL                 Day of month (1-31)
    AL                 Day of week (0=Sunday, 1=Monday, etc.)
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    get_date  macro
            mov   ah,2AH
            int   21H
            endm

    Example:

    The following program gets the date, increments the day, increments the
    month or year, if necessary, and sets the new date.

    month      db      31,28,31,30,31,30,31,31,30,31,30,31
    ;
    begin:     get_date            ; THIS FUNCTION
                inc    dl           ; increment day
                xor    bx,bx        ; so BL can be used as index
                mov    bl,dh        ; move month to index register
                dec    bx           ; month table starts with 0
                cmp    dl,month[bx] ; past end of month?
                jle    month_ok     ; no, set new date
                mov    dl,1         ; yes, set day to 1
                inc    dh           ; and increment month
                cmp    dh,12        ; past end of year?
                jle    month_ok     ; no, set new date
                mov    dh,1         ; yes, set month to 1
                inc    cx           ; increment year
    month_ok:  set_date cx,dh,dl   ; see Function 2AH


────────────────────────────────────────────────────────────────────────────
Set Date (Function 2BH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 2BH
    CX
    Year (1980-2099)
    DH
    Month (1-12)
    DL
    Day (1-31)

    Return:

    AL
    00H        = Date was valid
    FFH        = Date was invalid


    Comments:

    Function 2BH sets the date in the operating system (and in the CMOS clock,
    if one exists). Registers CX and DX must contain a valid date in binary:

    Register           Contents
    ──────────────────────────────────────────────────────────────────────────
    CX                 Year (1980-2099)
    DH                 Month (1=January, 2=February, etc.)
    DL                 Day of month (1-31)
    ──────────────────────────────────────────────────────────────────────────

    If the date is valid, the call sets it and AL returns 0. If the date is
    not valid, the function aborts and AL returns FFH.

    Macro Definition:

    set_date  macro  year,month,day
            mov    cx,year
            mov    dh,month
            mov    dl,day
            mov    ah,2BH
            int    21H
            endm

    Example:

    The following program gets the date, increments the day, increments the
    month or year, if necessary, and sets the new date.

    month      db      31,28,31,30,31,30,31,31,30,31,30,31
    ;
    begin:     get_date            ; see Function 2AH
                inc    dl           ; increment day
                xor    bx,bx        ; so BL can be used as index
                mov    bl,dh        ; move month to index register
                dec    bx           ; month table starts with 0
                cmp    dl,month[bx] ; past end of month?
                jle    month_ok     ; no, set the new date
                mov    dl,1         ; yes, set day to 1
                inc    dh           ; and increment month
                cmp    dh,12        ; past end of year?
                jle    month_ok     ; no, set the new date
                mov    dh,1         ; yes, set the month to 1
                inc    cx           ; increment year
    month_ok:  set_date cx,dh,dl   ; THIS FUNCTION


────────────────────────────────────────────────────────────────────────────
Get Time (Function 2CH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 2CH

    Return:

    CH
    Hour (0-23)
    CL
    Minutes (0-59)
    DH
    Seconds (0-59)
    DL
    Hundredths (0-99)


    Comments:

    Function 2CH returns the current time set in the operating system (and
    sets the CMOS clock, if one exists) as binary numbers in CX and DX:

    Register           Contents
    ──────────────────────────────────────────────────────────────────────────
    CH                 Hour (0-23)
    CL                 Minutes (0-59)
    DH                 Seconds (0-59)
    DL                 Hundredths of a second (0-99)
    ──────────────────────────────────────────────────────────────────────────

    Depending on how your hardware keeps time, some of these fields may be
    irrelevant. As an example, many CMOS clock chips do not resolve more than
    seconds. In such a case, the value in DL will probably always be 0.

    Macro Definition:

    get_time  macro
            mov   ah,2CH
            int   21H
            endm

    Example:

    The following program displays the time continuously until you press any
    key.

    time         db    "00:00:00.00",0DH,"$"
    ;
    begin:       get_time               ; THIS FUNCTION
                byte_to_dec ch,time    ; see end of chapter
                byte_to_dec cl,time[3] ; see end of chapter
                byte_to_dec dh,time[6] ; see end of chapter
                byte_to_dec dl,time[9] ; see end of chapter
                display time           ; see Function 09H
                check_kbd_status       ; see Function 0BH
                cmp     al,0FFH        ; has a key been pressed?
                je      return         ; yes, terminate
                jmp     begin          ; no, display time


────────────────────────────────────────────────────────────────────────────
Set Time (Function 2DH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 2DH
    CH
    Hour (0-23)
    CL
    Minutes (0-59)
    DH
    Seconds (0-59)
    DL
    Hundredths (0-99)

    Return:

    AL
    00H        = Time was valid
    FFH        = Time was invalid


    Comments:

    Function 2DH sets the time in the operating system. Registers CX and DX
    must contain a valid time in binary:

    Register           Contents
    ──────────────────────────────────────────────────────────────────────────
    CH                 Hour (0-23)
    CL                 Minutes (0-59)
    DH                 Seconds (0-59)
    DL                 Hundredths of a second (0-99)
    ──────────────────────────────────────────────────────────────────────────

    Depending on how your hardware keeps time, some of these fields may be
    irrelevant. As an example, many CMOS clock chips do not resolve more than
    seconds. In such a case, the value in DL will not be relevant.

    If the time is valid, the call sets it and AL returns 0. If the time is
    not valid, the function aborts and AL returns FFH.

    Macro Definition:

    set_time  macro  hour,minutes,seconds,hundredths
            mov    ch,hour
            mov    cl,minutes
            mov    dh,seconds
            mov    dl,hundredths
            mov    ah,2DH
            int    21H
            endm

    Example:

    The following program sets the system clock to 0 and displays the time
    continuously. When you type a character, the display freezes; when you
    type another character, the clock is reset to 0 and the display starts
    again.

    time          db  "00:00:00.00",0DH,0AH,"$"
    ;
    begin:        set_time  0,0,0,0       ; THIS FUNCTION
    read_clock:   get_time                ; see Function 2CH
                byte_to_dec  ch,time    ; see end of chapter
                byte_to_dec  cl,time[3] ; see end of chapter
                byte_to_dec  dh,time[6] ; see end of chapter
                byte_to_dec  dl,time[9] ; see end of chapter
                display  time           ; see Function 09H
                dir_console_io 0FFH     ; see Function 06H
                cmp      al,00H         ; was a char. typed?
                jne      stop           ; yes, stop the timer
                jmp      read_clock     ; no keep timer on
    stop:         read_kbd                ; see Function 08H
                jmp      begin          ; keep displaying time


────────────────────────────────────────────────────────────────────────────
Set/Reset Verify Flag (Function 2EH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 2EH
    AL
    0 = Do not verify
    1 = Verify

    Return:

    None


    Comments:

    Function 2EH tells MS-DOS whether or not to verify each disk write. If AL
    is 1, verify is on; if AL is 0, verify is off. MS-DOS checks this flag
    each time it writes to a disk.

    The flag is normally off; you may wish to turn it on when writing critical
    data to disk. Because disk errors are rare and verification slows writing,
    you will probably want to leave it off at other times. You can check the
    setting with Function 54H (Get Verify State).

    Macro Definition:

    verify  macro  switch
            mov    al,switch
            mov    ah,2EH
            int    21H
            endm

    Example:

    The following program copies the contents of a single-sided disk in drive
    A to the disk in drive B, verifying each write. It uses a buffer of 32
    kilobytes.

    on           equ   1
    off          equ   0
    ;
    prompt       db   "Source in A, target in B",0DH,0AH
                db   "Any key to start. $"
    first        dw    0
    buffer       db    60 dup (512 dup(?))   ; 60 sectors
    ;
    begin:       display prompt              ; see Function 09H
                read_kbd                    ; see Function 08H
                verify on                   ; THIS FUNCTION
                mov    cx,6                 ; copy 60 sectors
                                            ; 6 times
    copy:        push   cx                   ; save counter
                abs_disk_read  0,buffer,60,first ; see Int 25H
                abs_disk_write  1,buffer,64,first ; see Int 26H
                add    first,60             ; do next 60 sectors
                pop    cx                   ; restore counter
                loop   copy                 ; do it again
                verify off                  ; THIS FUNCTION


────────────────────────────────────────────────────────────────────────────
Get Disk Transfer Address (Function 2FH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 2FH

    Return:

    ES:BX
    Pointer to Disk Transfer Address


    Comments:

    Function 2FH returns the segment address of the current Disk Transfer
    Address in ES and the offset in BX.

    Macro Definition:

    get_dta  macro
            mov    ah,2fH
            int    21H
            endm

    Example:

    The following program displays the current Disk Transfer Address in the
    form: segment:offset.

    message   db      "DTA --        :    ",0DH,0AH,"$"
    sixteen   db       10H
    temp      db       2 dup (?)
    ;
    begin:    get_dta                      ; THIS FUNCTION
            mov    word ptr temp,ex      ; To access each byte
            convert temp[1],sixteen,message[07H]  ; See end of
            convert temp,sixteen,message[09H]    ; chapter for
            convert bh,sixteen,message[0CH]      ; description
            convert bl,sixteen,message[0EH]      ; of CONVERT
            display message              ; See Function 09H


────────────────────────────────────────────────────────────────────────────
Get MS-DOS Version Number (Function 30H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 30H

    Return:

    AL
    Major version number
    AH
    Minor version number
    BH
    OEM serial number
    BL:CX
    24-bit user (serial) number


    Comments:

    Function 30H returns the MS-DOS version number. AL returns the major
    version number; AH returns the minor version number. (For example, MS-DOS
    3.0 returns 3 in AL and 0 in AH.)

    If AL returns 0, the MS-DOS version is earlier than 2.0.

    Macro Definition:

    get_version  macro
                mov    ah,30H
                int    21H
                endm

    Example:

    The following program displays the MS-DOS version if it is 1.28 or
    greater.

    message   db      "MS-DOS Version  . ",0DH,0AH,"$"
    ten       db       0AH                 ; For CONVERT
    ;
    begin:    get_version                  ; THIS FUNCTION
            cmp     al,0                 ; 1.28 or later?
            jng     return               ; No, go home
            convert al,ten,message[0FH]  ; See end of chapter
            convert ah,ten,message[12H]  ; for description
            display message              ; See Function 9


────────────────────────────────────────────────────────────────────────────
Keep Process (Function 31H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 31H
    AL
    Return code
    DX
    Memory size, in paragraphs

    Return:

    None


    Comments:

    Function 31H makes a program remain resident after it terminates. You can
    use it to install device-specific interrupt handlers. But unlike Interrupt
    27H (Terminate But Stay Resident), this function request allows more than
    64K bytes to remain resident and does not require CS to contain the
    segment address of the Program Segment Prefix. You should use Function 31H
    to install a resident program unless your program must be compatible with
    MS-DOS versions earlier than 2.0.

    DX must contain the number of paragraphs of memory required by the program
    (one paragraph = 16 bytes). AL contains an exit code.

    Be careful when using this function with .exe programs. The value in DX
    must be the total size to remain resident, not just the size of the code
    segment which is to remain resident. A typical error is to forget about
    the 100H-byte program-header-prefix and give a value in DX that doesn't
    account for the size of this prefix. MS-DOS terminates the current process
    and tries to set the memory allocation to the number of paragraphs in DX.
    No other allocation blocks belonging to the process are released.

    By using Function 4DH (Get Return Code of Child Process), the parent
    process can retrieve the process's exit code from AL. (You can test this
    exit code by using the if command with errorlevel.)

    Macro Definition:

    keep_process  macro return_code,last_byte
                mov   al,return_code
                mov   dx,offset last_byte
                mov   cl,4
                shr   dx,cl
                inc   dx
                mov   ah,31H
                int   21H
                endm

    Example:

    Because the most common use of this call is to install a machine-specific
    routine, an example is not shown. The macro definition, however, shows the
    calling syntax.


────────────────────────────────────────────────────────────────────────────
CONTROL+C Check (Function 33H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 33H
    AL
        0 = Get state of CONTROL+C
        1 = Set state of CONTROL+C
        5 = Query DOS boot drive
    DL (if AL=1)
        0 = Off
        1 = On

    Return:

    DL (if AL=0)
        0 = Off
        1 = On
    DL (if AL=5)
        1 = A, 2 = B, etc.
    AL
        FFH = error (AL was neither 0, 1, nor 5
                    when call was made)


    Comments:

    Function 33H gets or sets the state of CONTROL+C (or CONTROL+BREAK for IBM
    compatibles) checking in MS-DOS and can query the DOS boot drive. AL must
    contain a code that specifies the requested action:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    0                  Current state of CONTROL+C checking in DL
    1                  Set state of CONTROL+C checking to the value in DL
    5                  Query DOS boot drive in DL
    ──────────────────────────────────────────────────────────────────────────

    If AL is 0, DL returns the current state (0=off, 1=on). If AL is 1, the
    value in DL specifies the state to be set (0=off, 1=on). If AL is neither
    0, 1, nor 5, AL returns FFH and the state of CONTROL+C checking is
    unaffected.

    MS-DOS normally checks for CONTROL+C only when carrying out certain
    function requests in the 01H through 0CH group (see the description of
    specific calls for details). When CONTROL+C checking is on, MS-DOS checks
    for CONTROL+C when carrying out any function request. For example, if
    CONTROL+C checking is off, all disk I/O proceeds without interruption, but
    if it is on, the CONTROL+C interrupt is issued at the function request
    that initiates the disk operation.

    ──────────────────────────────────────────────────────────────────────────
    Note
    Programs that use Function 06H (Direct Console I/O) or 07H (Direct
    Console Input) to read CONTROL+C as data must ensure that the CONTROL+C
    checking is off.
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    ctrl_c_ck  macro  action,state
                mov    al,action
                mov    dl,state
                mov    ah,33H
                int    21H
                endm

    Example:

    The following program displays a message that tells whether CONTROL+C
    checking is on or off:

    message   db       "CONTROL+C checking ","$"
    on        db       "on","$",0DH,0AH,"$"
    off       db       "off","$",0DH,0AH,"$"
    ;
    begin:    display   message          ; See Function 09H
            ctrl_c_ck 0                ; THIS FUNCTION
            cmp       dl,0             ; Is checking off?
            jg        ck_on            ; No
            display   off              ; See Function 09H
            jmp       return           ; Go home
    ck_on:    display   on               ; See Function 09H

    To query the DOS boot drive value, add the following programs segment:

            mov       AH,33H           ; Query DOS value
            mov       AL,05H           ; DOS boot drive
            int       21H
            jc        ERROR            ; DL=Boot drive (A=1)


────────────────────────────────────────────────────────────────────────────
Get Interrupt Vector (Function 35H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 35H
    AL
        Interrupt number

    Return:

    ES:BX
        Pointer to interrupt routine


    Comments:

    Function 35H gets the address from the interrupt-vector table for the
    specified interrupt. AL must contain the number of an interrupt.

    ES returns the segment address of the interrupt handler; BX returns the
    offset.

    To avoid compatibility problems, programs should never read an interrupt
    vector directly from memory, nor set an interrupt vector by writing it
    into memory. Use this function request to get a vector and Function 25H
    (Set Interrupt Vector) to set a vector, unless your program must be
    compatible with MS-DOS versions earlier than 2.0.

    Macro Definition:

    get_vector  macro  interrupt
                mov    al,interrupt
                mov    ah,35H
                int    21H
                endm

    Example:

    The following program displays the segment and offset (CS:IP) for the
    handler for Interrupt 25H (Absolute Disk Read).

    message   db     "Interrupt 25H -- CS:0000 IP:0000"
            db      0DH,0AH,"$"
    vec_seg   db      2 dup (?)
    vec_off   db      2 dup (?)
    ;
    begin:    push    es                    ; save ES
            get_vector 25H                ; THIS FUNCTION
            mov     ax,es                 ; INT25H segment in AX
            pop     es                    ; save ES
            convert ax,16,message[20]     ; see end of chapter
            convert bx,16,message[28]     ; see end of chapter
            display message               ; See Function 9


────────────────────────────────────────────────────────────────────────────
Get Disk Free Space (Function 36H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 36H
    DL
        Drive (0=default, 1=A, etc.)

    Return:

    AX
        0FFFFH if drive number is invalid;
        otherwise, sectors per cluster
    BX
        Available clusters
    CX
        Bytes per sector
    DX
        Clusters per drive


    Comments:

    Function 36H returns the number of clusters available on the disk in the
    specified drive and the information necessary to calculate the number of
    bytes available on the disk. DL must contain a drive number (0=default,
    1=A, etc.). If the drive number is valid, MS-DOS returns the information
    in the following registers:

    Register           Contents
    ──────────────────────────────────────────────────────────────────────────
    AX                 Sectors per cluster
    BX                 Available clusters
    CX                 Bytes per sector
    DX                 Total clusters
    ──────────────────────────────────────────────────────────────────────────

    If the drive number is invalid, AX returns 0FFFFH.

    This call supersedes Functions 1BH and 1CH in earlier MS-DOS versions.

    Macro Definition:

    get_disk_space  macro  drive
                    mov    dl,drive
                    mov    ah,36H
                    int    21H
                    endm

    Example:

    The following program displays space information for the disk in drive B.

    message   db "      clusters on drive B.",0DH,0AH   ; DX
            db "      clusters available.",0DH,0AH    ; BX
            db "      sectors per cluster.",0DH,0AH   ; AX
            db "      bytes per sector,",0DH,0AH,"$"  ; CX
    ;
    begin:    get_disk_space 2                 ; THIS FUNCTION
            convert    ax,10,message[55]     ; see end of chapter
            convert    bx,10,message[28]     ; see end of chapter
            convert    cx,10,message[83]     ; see end of chapter
            convert    dx,10,message         ; see end of chapter
            display message                  ; See Function 09H


────────────────────────────────────────────────────────────────────────────
Get Country Data (Function 38H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 38H
    AL
        00H = Current country
        1-0FEH = Country code
        0FFH = BX contains country code
    BX (if AL = 0FFH)
        Country code 255 or higher
    DS:DX
        Pointer to 32-byte memory area

    Return:

    Carry set:
    AX
        2 = Invalid country code
    DS:DX
        Segment:offset pointer to 32-byte memory area
    Carry not set:
    BX
        Country code


    Comments:

    Function 38H gets the country-dependent information that MS-DOS uses to
    control the keyboard and display, or it sets the currently defined country
    (to set the country code, see the next function request description, Set
    Country Data). To get the information, DX must contain the offset (from
    the segment address in DS) of a 32-byte memory area to which the country
    data returns. AL specifies the country code:

    Value in AL        Meaning
    ──────────────────────────────────────────────────────────────────────────
    00H                Retrieves information about the country currently set
    1 to 0FEH          Retrieves information about the country identified by
                        this code
    0FFH               Retrieves information about the country identified by
                        the code in BX
    ──────────────────────────────────────────────────────────────────────────

    BX must contain the country code if the code is 255 or greater. The
    country code is usually the international telephone-prefix code. The
    country-dependent information returns in the following form:

    Offset
    Hex        Decimal   Field Name                      Length in bytes
    ──────────────────────────────────────────────────────────────────────────
    00H        0         Date format                     2 (word)
    02H        2         Currency symbol                 5 (ASCIZ string)
    07H        7         Thousands separator             2 (ASCIZ string)
    09H        9         Decimal separator               2 (ASCIZ string)
    0BH        11        Date separator                  2 (ASCIZ string)
    0DH        13        Time separator                  2 (ASCIZ string)
    0FH        15        Bit field                       1
    10H        16        Currency places                 1
    11H        17        Time format                     1
    12H        18        Case-map call address           4 (DWORD)
    16H        22        Data-list separator             2 (ASCIZ string)
    18H        24        Reserved                        10
    ──────────────────────────────────────────────────────────────────────────

    Date Format:

    0 = USA        (month/day/year)
    1 = Europe        (day/month/year)
    2 = Japan        (year/month/day)


    Bit Field:

    Bit         Value        Meaning
    ──────────────────────────────────────────────────────────────────────────
    0           0            Currency symbol precedes amount
                1            Currency symbol follows amount
    1           0            No space between symbol and amount
                1            One space between symbol and amount
    ──────────────────────────────────────────────────────────────────────────

    All other bits are undefined.

    Time Format:

    0 = 12-hour clock
    1 = 24-hour clock

    Currency Places:

    Specifies the number of places that appear after the decimal point on
    currency amounts.

    Case-Mapping Call Address:

    Specifies the segment and offset of a FAR procedure that performs
    country-specific lowercase-to-uppercase mapping on character values from
    80H to 0FFH. You call it with the character to be mapped in AL. If there
    is an uppercase code for the character, it is returned in AL; if there is
    not, or if you call it with a value less than 80H in AL, AL returns
    unchanged. AL and the FLAGS are the only altered registers.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  Invalid country code (no table for it).
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    get_country macro  country,buffer
                local     gc_01
                mov       dx,offset buffer
                mov       ax,country
                cmp       ax,OFFH
                jl        gc_01
                mov       al,OFFh
                mov       bx,country
    gc_01:      mov       ah,38h
                int       21H
                endm

    Example:

    The following program displays the time and date in the format appropriate
    to the current country code, and the number 999,999 and 99/100 as a
    currency amount with the proper currency symbol and separators.

    time       db     "  :  :  ",5 dup (20H),"$"
    date       db     "  /  /  ",5 dup (20H),"$"
    number     db     "999?999?99",0DH,0AH,"$"
    data_area  db      32 dup (?)
    ;
    begin:    get_country  0,data_area       ; THIS FUNCTION
            get_time                       ; See Function 2CH
            byte_to_dec  ch,time           ; See end of chapter
            byte_to_dec  cl,time[03H]      ; for description of
            byte_to_dec  dh,time[06H]      ; CONVERT macro
            get_date                       ; See Function 2AH
            sub      cx,1900               ; Want last 2 digits
            byte_to_dec  cl,date[06H]      ; See end of chapter
            cmp      word ptr data_area,0  ; Check country code
            jne      not_usa               ; It's not USA
            byte_to_dec  dh,date           ; See end of chapter
            byte_to_dec  dl,date[03H]      ; See end of chapter
            jmp      all_done              ; Display data
    not_usa:  byte_to_dec  dl,date           ; See end of chapter
            byte_to_dec  dh,date[03H]      ; See end of chapter
    all_done: mov      al,data_area[07H]     ; Thousand separator
            mov      number[03H],al        ; Put in NUMBER
            mov      al,data_area[09H]     ; Decimal separator
            mov      number[07H],al        ; Put in AMOUNT
            display  time                  ; See Function 09H
            display  date                  ; See Function 09H
            display_char  data_area[02H]   ; See Function 02H
            display  number                ; See Function 09H


────────────────────────────────────────────────────────────────────────────
Set Country Data (Function 38H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 38H
    DX = -1 (0FFFFH)
    AL
        Country code less than 255, or
        0FFH if the country code is in BX
    BX (if AL=0FFH)
        Country code 255 or higher

    Return:

    Carry set:
    AX
        2 = Invalid country code
    Carry not set:
        No error


    Comments:

    Function 38H sets the country code that MS-DOS uses to control the
    keyboard and the display, or it retrieves the country-dependent
    information (to get the country data, see the previous function request
    description, Get Country Data). To set the information, DX must contain
    0FFFFH. AL must contain either the country code, if it is less than 255,
    or 255 to indicate that the country code is in BX. If AL contains 0FFH, BX
    must contain the country code.

    The country code is usually the international telephone prefix-code. See
    "Get Country Data" for a description of the country data and how it is
    used.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  Invalid country code (no table for it).
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    set_country macro  country
                local  sc_01
                mov    dx,0FFFFH
                mov    ax,country
                cmp    ax,0FFH
                jl     sc_01
                mov    bx,country
                mov    al,0ffh
    sc_01:      mov    ah,38H
                int    21H
                endm

    Example:

    The following program sets the country code to the United Kingdom (44).

    uk        equ         44
    ;
    begin:    set_country uk     ; THIS FUNCTION
            jc          error  ; routine not shown


────────────────────────────────────────────────────────────────────────────
Create Directory (Function 39H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 39H
    DS:DX
        Pointer to pathname

    Return:

    Carry set:
    AX
        2 = File not found
        3 = Path not found
        5 = Access denied
    Carry not set:
        No error


    Comments:

    Function 39H creates a new subdirectory. DX must contain the offset (from
    the segment address in DS) of an ASCIZ string that specifies the pathname
    of the new subdirectory.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  File not found

    3                  Path not found

    5                  No room in the parent directory, a file with the same
                        name exists in the current directory, or the path
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    make_dir  macro path
            mov    dx,offset path
            mov    ah,39H
            int    21H
            endm

    Example:

    The following program adds a subdirectory named new_dir to the root
    directory on the disk in drive B and changes the current directory to
    new_dir. The program then changes the current directory back to the
    original directory and then deletes new_dir. It displays the current
    directory after each step to confirm the changes.

    old_path  db        "b:",0,63 dup (?)
    new_path  db        "b:\new_dir",0
    buffer    db        "b:",0,63 dup (?)
    ;
    begin:    get_dir    2,old_path[03H]  ; See Function 47H
            jc         error_get        ; Routine not shown
            display_asciz  old_path     ; See end of chapter
            make_dir   new_path         ; THIS FUNCTION
            jc         error_make       ; Routine not shown
            change_dir new_path         ; See Function 3BH
            jc         error_change     ; Routine not shown
            get_dir    2,buffer[03H]    ; See Function 47H
            jc         error_get        ; Routine not shown
            display_asciz  buffer       ; See end of chapter
            change_dir old_path         ; See Function 3BH
            jc         error_change     ; Routine not shown
            rem_dir    new_path         ; See Function 3AH
            jc         error_rem        ; Routine not shown
            get_dir    2,buffer[03H]    ; See Function 47H
            jc         error_get        ; Routine not shown
            display_asciz  buffer      ; See end of chapter


────────────────────────────────────────────────────────────────────────────
Remove Directory (Function 3AH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 3AH
    DS:DX
        Pointer to pathname

    Return:

    Carry set:
    AX
        2        = File not found
        3        = Path not found
        5        = Access denied
    16        = Current directory
    Carry not set:
        No error


    Comments:

    Function 3AH deletes a subdirectory. DX must contain the offset (from the
    segment address in DS) of an ASCIZ string that specifies the pathname of
    the subdirectory you want to delete.

    The subdirectory must not contain any files. You cannot erase the current
    directory. If there is an error, the carry flag (CF) is set and the error
    code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  File not found

    3                  Path not found

    5                  Directory not empty, or path doesn't specify a
                        directory, or it specifies the root directory, or it is
                        invalid

    16                 Path specifies current directory
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    rem_dir  macro  path
            mov    dx,offset path
            mov    ah,3AH
            int    21H
            endm

    Example:

    The following program adds a subdirectory named new_dir to the root
    directory on the disk in drive B and changes the current directory to
    new_dir. The program then changes the current directory back to the
    original directory and deletes new_dir. It displays the current directory
    after each step to confirm the changes.

    old_path  db        "b:",0,63 dup (?)
    new_path  db        "b:\new_dir",0
    buffer    db        "b:",0,63 dup (?)
    ;
    begin:    get_dir    2,old_path[03H]  ; See Function 47H
            jc         error_get        ; Routine not shown
            display_asciz  old_path    ; See end of chapter
            make_dir   new_path         ; See Function 39H
            jc         error_make       ; Routine not shown
            change_dir new_path         ; See Function 3BH
            jc         error_change     ; Routine not shown
            get_dir    2,buffer[03H]    ; See Function 47H
            jc         error_get        ; Routine not shown
            display_asciz  buffer      ; See end of chapter
            change_dir old_path         ; See Function 3BH
            jc         error_change     ; Routine not shown
            rem_dir    new_path         ; THIS FUNCTION
            jc         error_rem        ; Routine not shown
            get_dir    2,buffer[03H]    ; See Function 47H
            jc         error_get        ; Routine not shown
            display_asciz  buffer      ; See end of chapter


────────────────────────────────────────────────────────────────────────────
Change Current Directory (Function 3BH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 3BH
    DS:DX
        Pointer to pathname

    Return:

    Carry set:
    AX
        2 = File not found
        3 = Path not found
    Carry not set:
        No error


    Comments:

    Function 3BH changes the current directory. DX must contain the offset
    (from the segment address in DS) of an ASCIZ string that specifies the
    pathname of the new current directory.

    The directory string is limited to 64 characters.

    If any member of the path doesn't exist, the path is unchanged. If there
    is an error, the carry flag (CF) is set and the error code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  File not found

    3                  Path either doesn't exist or it specifies a file
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    change_dir  macro  path
                mov    dx,offset path
                mov    ah,3BH
                int    21H
                endm

    Example:

    The following program adds a subdirectory named new_dir to the root
    directory that is on the disk in drive B and changes the current directory
    to new_dir. The program then changes the current directory back to the
    original directory and deletes new_dir. It displays the current directory
    after each step to confirm the changes.

    old_path  db        "b:",0,63 dup (?)
    new_path  db        "b:\new_dir",0
    buffer    db        "b:",0,63 dup (?)
    ;
    begin:    get_dir    2,old_path[03H]  ; See Function 47H
            jc         error_get        ; Routine not shown
            display_asciz  old_path    ; See end of chapter
            make_dir   new_path         ; See Function 39H
            jc         error_make       ; Routine not shown
            change_dir new_path         ; THIS FUNCTION
            jc         error_change     ; Routine not shown
            get_dir    2,buffer[03H]    ; See Function 47H
            jc         error_get        ; Routine not shown
            display_asciz  buffer      ; See end of chapter
            change_dir old_path         ; See Function 3BH
            jc         error_change     ; Routine not shown
            rem_dir    new_path         ; See Function 3AH
            jc         error_rem        ; Routine not shown
            get_dir    2,buffer[03H]    ; See Function 47H
            jc         error_get        ; Routine not shown
            display_asciz  buffer      ; See end of chapter


────────────────────────────────────────────────────────────────────────────
Create Handle (Function 3CH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 3CH
    DS:DX
        Pointer to pathname
    CX
        File attribute

    Return:

    Carry set:
    AX
        2        = File not found
        3        = Path not found
        4        = Too many open files
        5        = Access denied
    Carry not set:
    AX
        Handle


    Comments:

    Function 3CH creates a file and assigns it the first available handle. DX
    must contain the offset (from the segment address in DS) of an ASCIZ
    string that specifies the pathname of the file to be created. CX must
    contain the attribute to be assigned to the file, as described under "File
    Attributes" earlier in this chapter.

    If the specified file does not exist, this function creates it. But if the
    file already exists, it is truncated to a length of 0. Function 3CH then
    assigns the attribute in CX to the file and opens it for read/write. AX
    returns the file handle. If there is an error, the carry flag (CF) is set
    and the error code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  File not found

    3                  Path is invalid

    4                  Too many open files (no handle available)

    5                  Directory is full, a directory with the same name
                        exists, or a file with the same name exists with more
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    create_handle  macro  path,attrib
                    mov    dx,offset path
                    mov    cx,attrib
                    mov    ah,3CH
                    int    21H
                    endm

    Example:

    The following program creates a file named dir.tmp, containing the name
    and extension of each file in the current directory, on the disk in drive
    B.

    srch_file db      "b:*.*",0
    tmp_file  db      "b:dir.tmp",0
    buffer    db       43 dup (?)
    handle    dw       ?
    ;
    begin:    set_dta buffer                ; See Function 1AH
            find_first_file  srch_file,16H  ; See Function 4EH
            cmp     ax,12H                ; Directory empty?
            je      all_done              ; Yes, go home
            create_handle  tmp_file,0     ; THIS FUNCTION
            jc      error                 ; Routine not shown
            mov     handle,ax             ; Save handle
    write_it: write_handle handle,buffer[1EH],12 ; Function 40H
            find_next_file                ; See Function 4FH
            cmp     ax,12H                ; Another entry?
            je      all_done              ; No, go home
            jmp     write_it              ; Yes, write record
    all_done: close_handle  handle          ; See Function 3EH


────────────────────────────────────────────────────────────────────────────
Open Handle (Function 3DH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 3DH
    AL
        Access code (see text)
    DS:DX
        Pointer to pathname

    Return:

    Carry set:
    AX
        2        = File not found
        3        = Path not found
        4        = Too many open files
        5        = Access denied
    12        = Invalid access
    Carry not set:
        No error


    Comments:

    Function 3DH opens any file, including hidden and system files, for input
    or output. DX contains the offset (from the segment address in DS) of an
    ASCIZ string that specifies the pathname of the file to be opened. AL
    contains a code that specifies how the file is to be opened. This code is
    described later under "Controlling Access to the File."

    If there is no error, AX returns the file handle. MS-DOS sets the
    read/write pointer to the first byte of the file.

    Controlling Access to the File

    The value in AL is made up of three parts that specify whether the file is
    to be opened for read, write, or both (access code); what access other
    processes have to the file (sharing mode); and whether a child process
    inherits the file (inherit bit).

        ┌───┬───────────┬───────────────┐
    Bit  │ 7 │ 6   5   4 │ 3   2   1   0 │
        └───┴───────────┴───────────────┘

        \_/ \_________/ \______________/
            │       │             │
            │       │             └────────>  Access code
            │       │
            │       └──────────────────────>  Sharing mode
            │
            └──────────────────────────────>  Inherit bit

    Inherit Bit

    The high-order bit (bit 7) specifies whether the file is inherited by a
    child process created with Function 4B00H or 4B03H (Load and Execute
    Program or Overlay). If the bit is 0, the child process inherits the file;
    if the bit is 1, it doesn't.

    Sharing Mode

    The sharing mode bits (bits 4-6) specify what access, if any, other
    processes have to the open file. It can have the following values:

    Table 1.21
    Sharing Mode Bit Values
    Bits 4-6   Sharing Mode         Description
    ──────────────────────────────────────────────────────────────────────────
    000        Compatibility        On a given machine, any process can open
                                    the file any number of times with this
                                    mode. Fails if the file has been opened
                                    with any of the other sharing modes.

    001        Deny both            Fails if the file has been opened in
                                    compatibility mode or for read or write
                                    access, even if by the current process.

    010        Deny write           Fails if the file has been opened in
                                    compatibility mode or for write access by
                                    any other process.

    011        Deny read            Fails if the file has been opened in
                                    compatibility mode or for read access by
                                    any other process.

    100        Deny none            Fails if the file has been opened in
    ──────────────────────────────────────────────────────────────────────────

    Access Code

    The access code (bits 0-3) specifies how the file is to be used. It can
    have the following values:

    Table 1.22
    Access Code Bit Values
                Access
    Bits 0-3     Allowed       Description
    ──────────────────────────────────────────────────────────────────────────
    0000         Read          Fails if the file has been opened in deny read
                                or deny both sharing mode.

    0001         Write         Fails if the file has been opened in deny write
                                or deny both sharing mode.

    0010         Both          Fails if the file has been opened in deny read,
    ──────────────────────────────────────────────────────────────────────────

    If there is an error, the carry flag (CF) is set and the error code is
    returned in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  Specified file is invalid or doesn't exist

    3                  Specified path is invalid or doesn't exist

    4                  No handles are available in the current process or the
                        internal system tables are full

    5                  Program attempted to open a directory or VolumeID, or
                        tried to open a read-only file for writing

    12                 Access code (bits 0-3 of AL) not 0, 1, or 2
    ──────────────────────────────────────────────────────────────────────────

    If this system call fails because of a file-sharing error, MS-DOS issues
    Interrupt 24H with error code 2 (Drive Not Ready). A subsequent Function
    59H (Get Extended Error) returns the extended error code that specifies a
    sharing violation.

    When opening a file, it is important to inform MS-DOS of any operations
    that other processes may perform on this file (sharing mode). The default
    (compatibility mode) denies all other processes access to the file, unless
    they also attempt to open the file in compatibility mode. The following
    table shows the effect of opening a file with compatibility mode set:

    Type of File Opening            Read-Only File       Not Read-Only
    ──────────────────────────────────────────────────────────────────────────
    First open for read, write, or  Succeeds             Succeeds
    both by machine/process "N"

    Subsequent opens by machine or  Succeeds             Succeeds
    process "N"

    An open by another machine or   Succeeds             Fails
    process
    ──────────────────────────────────────────────────────────────────────────

    Files may be read-only with the MS-DOS attrib command or by a read-only
    share over the network.

    It may be all right for other processes to continue to read the file while
    your process is operating on it. In this case, you should specify "Deny
    Write," which inhibits other processes from writing to your files but
    allows them to read from these files.

    Similarly, it is important for you to specify what operations your process
    will perform ("Access" mode). If another process has the file open with
    any sharing mode other than "Deny" mode, then the default mode
    ("Read/Write") causes the open request to fail. If you only want to read
    the file, your open request succeeds unless all other processes have
    specified "Deny" mode or "Deny write."

    Macro Definition:

    open_handle  macro  path,access
                mov    dx, offset path
                mov    al, access
                mov    ah, 3DH
                int    21H
                endm

    Example:

    The following program prints a file named textfile.asc that is on the disk
    in drive B.

    file       db  "b:textfile.asc",0
    buffer     db   ?
    handle     dw   ?
    ;
    begin:     open_handle  file,0          ; THIS FUNCTION
                mov  handle,ax               ; Save handle
    read_char: read_handle handle,buffer,1  ; Read 1 character
                jc   error_read              ; Routine not shown
                cmp  ax,0                    ; End of file?
                je   return                  ; Yes, go home
                print_char  buffer           ; See Function 05H
                jmp  read_char               ; Read another


────────────────────────────────────────────────────────────────────────────
Close Handle (Function 3EH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 3EH
    BX
        Handle

    Return:

    Carry set:
    AX
        6 = Invalid handle
    Carry not set:
        No error


    Comments:

    Function 3EH closes a file opened with Function 3DH (Open Handle) or
    3CH (Create Handle). BX must contain the handle of the open file that you
    want to close.

    If there is no error, MS-DOS closes the file and flushes all internal
    buffers. If there is an error, the carry flag (CF) is set and the error
    code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    6                  Handle not open or invalid
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    close_handle  macro  handle
                mov    bx,handle
                mov    ah,3EH
                int    21H
                endm

    Example:

    The following program creates a file named dir.tmp, containing the
    filename and extension of each file in the current directory, in the
    current directory on the disk in drive B.

    srch_file  db  "b:*.*",0
    tmp_file   db  "b:dir.tmp",0
    buffer     db   43 dup (?)
    handle     dw   ?
    ;
    begin:     set_dta  buffer              ; See Function 1AH
                find_first_file  srch_file,16H  ; See Function 4EH
                cmp      ax,12H              ; Directory empty?
                je       all_done            ; Yes, go home
                create_handle  tmp_file,0    ; See Function 3CH
                jc       error_create        ; Routine not shown
                mov      handle,ax           ; Save handle
    write_it:  write_handle  handle,buffer[1EH],12 ; See Function
                jc       error_write                ; 40H
                find_next_file               ; See Function 4FH
                cmp      ax,12H              ; Another entry?
                je       all_done            ; No, go home
                jmp      write_it            ; Yes, write record
    all_done:  close_handle  handle         ; See Function 3EH
                jc       error_close         ; Routine not shown


────────────────────────────────────────────────────────────────────────────
Read Handle (Function 3FH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 3FH
    BX
        Handle
    CX
        Bytes to read
    DS:DX
        Pointer to buffer

    Return:

    Carry set:
    AX
        5 = Access denied
        6 = Invalid handle
    Carry not set:
    AX
        Bytes read


    Comments:

    Function 3FH reads from the file or device associated with the specified
    handle. BX must contain the handle. CX must contain the number of bytes to
    be read. DX must contain the offset (to the segment address in DS) of the
    buffer.

    If there is no error, AX returns the number of bytes read; if you attempt
    to read starting at end of file, AX returns 0. The number of bytes
    specified in CX is not necessarily transferred to the buffer; if you use
    this call to read from the keyboard, for example, it reads only up to the
    first carriage-return.

    If you use this function request to read from standard input, you can
    redirect the input.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    5                  Handle not open for reading
    6                  Handle not open or invalid
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    read_handle  macro  handle,buffer,bytes
                mov    bx,handle
                mov    dx,offset buffer
                mov    cx,bytes
                mov    ah,3FH
                int    21H
                endm

    Example:

    The following program displays a file named textfile.asc that is on the
    disk in drive B.

    filename   db     "b:\textfile.asc",0
    buffer     db      129 dup (?)
    handle     dw      ?
    ;
    begin:     open_handle  filename,0        ; See Function 3DH
                jc           error_open        ; Routine not shown
                mov          handle,ax         ; Save handle
    read_file: read_handle  buffer,file_handle,128
                jc           error_open        ; Routine not shown
                cmp          ax,0              ; End of file?
                je           return            ; Yes, go home
                mov          bx,ax             ; # of bytes read
                mov          buffer[bx],"$"    ; Make a string
                display      buffer            ; See Function 09H
                jmp          read_file         ; Read more


────────────────────────────────────────────────────────────────────────────
Write Handle (Function 40H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 40H
    BX
        Handle
    CX
        Bytes to write
    DS:DX
        Pointer to buffer

    Return:

    Carry set:
    AX
        5 = Access denied
        6 = Invalid handle
    Carry not set:
    AX
        Bytes written


    Comments:

    Function 40H writes to the file or device associated with the specified
    handle. BX must contain the handle. CX must contain the number of bytes to
    be written. DX must contain the offset (to the segment address in DS) of
    the data to be written.

    If you set CX to zero, the file will be truncated at the current position
    of the file pointer. MS-DOS will not perform the write if the handle is
    read-only.

    If there is no error, AX returns the number of bytes written. Be sure to
    check AX after performing a write. If its value is less than the number in
    CX when the call was made, it indicates an error, even though the carry
    flag isn't set. If AX contains 0, and if the target is a disk file, the
    disk is full.

    If you use this function request to write to standard output, you can
    redirect the output. If you call this request with CX=0, the file size is
    set to the value of the read/write pointer. To satisfy the new file size,
    allocation units are allocated or released, as required. If there is an
    error, the carry flag (CF) is set and the error code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    5                  Handle not open for writing
    6                  Handle not open or invalid
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    write_handle  macro  handle,buffer,bytes
                mov    bx,handle
                mov    dx,offset buffer
                mov    cx,bytes
                mov    ah,40H
                int    21H
                endm

    Example:

    The following program creates a file named dir.tmp, containing the
    filename and extension of each file in the current directory, in the
    current directory on the disk in drive B.

    srch_file db     "b:*.*",0
    tmp_file  db     "b:dir.tmp",0
    buffer    db      43 dup (?)
    handle    dw      ?
    ;
    begin:    set_dta buffer                ; See Function 1AH
            find_first_file  srch_file,16H ; Check directory
            cmp     ax,12H                ; Directory empty?
            je      return                ; Yes, go home
            create_handle  tmp_file,0     ; See Function 3CH
            jc      error_create          ; Routine not shown
            mov     handle,ax             ; Save handle
    write_it: write_handle handle,buffer[1EH],12 ; THIS FUNCTION
            jc      error_write           ; Routine not shown
            find_next_file                ; Check directory
            cmp     ax,12H                ; Another entry?
            je      all_done              ; No, go home
            jmp     write_it              ; Yes, write record
    all_done: close_handle  handle          ; See Function 3EH
            jc      error_close           ; Routine not shown


────────────────────────────────────────────────────────────────────────────
Delete Directory Entry [Unlink] (Function 41H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 41H
    DS:DX
        Pointer to pathname

    Return:

    Carry set:
    AX
        2 = File not found
        3 = Path not found
        5 = Access denied
    Carry not set:
        No error


    Comments:

    Function 41H erases a file by deleting its directory entry. DX must
    contain the offset (from the segment address in DS) of an ASCIZ string
    that specifies the pathname of the file that you want to delete. You
    cannot use wildcard characters.

    If the file exists and is not read-only, the call deletes it. If there is
    an error, the call sets the carry flag (CF) and the error code returns in
    AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  File doesn't exist, or specifies a directory
    3                  Path is invalid
    5                  File is read-only
    ──────────────────────────────────────────────────────────────────────────

    To delete a file with the read-only attribute, first change its attribute
    to 0 with Function 43H (Get/Set File Attributes).

    Macro Definition:

    delete_entry  macro  path
                mov    dx,offset path
                mov    ah,41H
                int    21H
                endm

    Example:

    The following program deletes all files, dated before December 31, 1986,
    from the disk in drive B.

    year      db       1986
    month     db       12
    day       db       31
    files     db       ?
    message   db      "NO FILES DELETED.",0DH,0AH,"$"
    path      db      "b:*.*", 0
    buffer    db       43 dup (?)
    ;
    begin:    set_dta  buffer          ; See Function 1AH
            select_disk "B"          ; See Function 0EH
            find_first_file  path,0  ; See Function 4EH
            jnc      compare         ; got one
            jmp      all_done        ; no match, go home
    compare:  convert_date  buffer[-1] ; See end of chapter
            cmp      cx,year         ; After 1986?
            jg       next            ; Yes, don't delete
            cmp      dl,month        ; After December?
            jg       next            ; Yes, don't delete
            cmp      dh,day          ; 31st or after?
            jge      next            ; Yes, don't delete
            delete_entry  buffer[1EH] ; THIS FUNCTION
            jc       error_delete    ; Routine not shown
            inc      files           ; Bump file counter
    next:     find_next_file           ; Check directory
            jnc      compare         ; Go home if done
    how_many: cmp      files,0         ; Was directory empty?
            je       all_done        ; Yes, go home
            convert  files,10,message ; See end of chapter
    all_done: display  message         ; See Function 09H
            select_disk "A"          ; See Function 0EH


────────────────────────────────────────────────────────────────────────────
Move File Pointer (Function 42H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 42H
    AL
        Method of moving
    BX
        Handle
    CX:DX
        Distance in bytes (offset)

    Return:

    Carry set:
    AX
        1 = Invalid function
        6 = Invalid handle
    Carry not set:
    DX:AX
        New read/write pointer location


    Comments:

    Function 42H moves the read/write pointer of the file associated with the
    specified handle. BX must contain the handle. CX and DX must contain a
    32-bit offset (CX contains the most significant byte). AL must contain a
    code that specifies how to move the pointer:

    Code               Cursor Moved to
    ──────────────────────────────────────────────────────────────────────────
    0                  Beginning of file plus the offset
    1                  Current pointer location plus the offset
    2                  End of file plus the offset
    ──────────────────────────────────────────────────────────────────────────

    DX and AX return the new location of the read/write pointer (a 32-bit
    integer; DX contains the most significant byte). You can determine the
    length of a file by setting CX:DX to 0, AL to 2, and calling this
    function. DX:AX returns the offset of the byte following the last byte in
    the file (size of the file in bytes). If there is an error, the carry flag
    (CF) is set and the error code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  AL not 0, 1, or 2
    6                  Handle not open
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    move_ptr  macro  handle,high,low,method
            mov    bx,handle
            mov    cx,high
            mov    dx,low
            mov    al,method
            mov    ah,42H
            int    21H
            endm

    Example:

    The following program prompts for a letter, converts it to its alphabetic
    sequence (A=1, B=2, etc.), then reads and displays the corresponding
    record from the file named alphabet.dat that is in the current directory
    on the disk in drive B. The file contains 26 records, each 28 bytes long.

    file      db      "b:alphabet.dat",0
    buffer    db       28 dup (?),"$"
    prompt    db      "Enter letter: $"
    crlf      db       0DH,0AH,"$"
    handle    db       ?
    record_length  dw  28
    ;
    begin:    open_handle  file,0    ; See Function 3DH
            jc       error_open    ; Routine not shown
            mov      handle,ax     ; Save handle
    get_char: display  prompt        ; See Function 09H
            read_kbd_and_echo      ; See Function 01H
            sub      al,41h        ; Convert to sequence
            mul      byte ptr record_length  ; Calculate offset
            move_ptr handle,0,ax,0 ; THIS FUNCTION
            jc       error_move    ; Routine not shown
            read_handle handle,buffer,record_length
            jc       error_read    ; Routine not shown
            cmp      ax,0          ; End of file?
            je       return        ; Yes, go home
            display  crlf          ; See Function 09H
            display  buffer        ; See Function 09H
            display  crlf          ; See Function 09H
            jmp      get_char      ; Get another character


────────────────────────────────────────────────────────────────────────────
Get/Set File Attributes (Function 43H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 43H
    AL
        0 = Get attributes
        1 = Set attributes
    CX (if AL=1)
        Attributes to be set
    DS:DX
        Pointer to pathname

    Return:

    Carry set:
    AX
        1 = Invalid function
        2 = File not found
        3 = Path not found
        5 = Access denied
    Carry not set:
    CX
        Attribute byte (if AL=0)


    Comments:

    Function 43H gets or sets the attributes of a file. DX must contain the
    offset (from the segment address in DS) of an ASCIZ string that specifies
    the pathname of a file. AL must specify whether to get or set the
    attribute (0=get, 1=set).

    If AL is 0 (get the attribute), the attribute byte returns in CX. If AL is
    1 (set the attribute), CX must contain the attributes to be set. See
    Section 1.5.5, "File Attributes," for a description of the attributes.

    You cannot change the VolumeID bit (08H) or the Subdirectory bit (10H) of
    the attribute byte with this function.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  AL not 0 or 1

    2                  File doesn't exist

    3                  Path invalid

    5                  Attribute in CX cannot be changed (Subdirectory or
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    change_attr  macro  path,action,attrib
                mov    dx,offset path
                mov    al,action
                mov    cx,attrib
                mov    ah,43H
                int    21H
                endm

    Example:

    The following program displays the attributes assigned to the file named
    report.asm that is in the current directory on the disk in drive B.

    header    db      15 dup (20h),"Read-",0DH,0AH
            db     "Filename       Only      Hidden    "
            db     "System    Volume    Sub-Dir   Archive"
            db      0DH,0AH,0DH,0AH,"$"
    path      db     "b:report.asm",3 dup (0),"$"
    attribute dw      ?
    blanks    db      9 dup (20h),"$"
    ;
    begin:    change_attr  path,0,0  ; THIS FUNCTION
            jc      error_mode   ; Routine not shown
            mov     attribute,cx ; Save attribute byte
            display header       ; See Function 09H
            display path         ; See Function 09H
            mov     cx,6         ; Check 6 bits (0-5)
            mov     bx,1         ; Start with bit 0
    chk_bit:  test    attribute,bx ; Is the bit set?
            jz      no_attr      ; No
            display_char "X"     ; See Function 02H
            jmp short  next_bit  ; Done with this bit
    no_attr:  display_char  20h    ; See Function 02H
    next_bit: display blanks       ; See Function 09H
            shl     bx,1         ; Move to next bit
            loop    chk_bit      ; Check it


────────────────────────────────────────────────────────────────────────────
IOCtl Data (Function 44H, Codes 0 and 1)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 44H
    AL
        0 = Get device data
        1 = Set device data
    BX
        Handle
    DX
        Device data (see text)

    Return:

    Carry set:
    AX
        1 = Invalid function
        6 = Invalid handle
    Carry not set:
    DX
    Device data


    Comments:

    Function 44H, Codes 0 and 1, either gets or sets the data MS-DOS uses to
    control the device. AL must contain 0 to get the data or 1 to set it. BX
    must contain the handle. If AL is 1, DH must contain 0.

    The device-data word is specified or returned in DX. If bit 7 of the data
    is 1, the handle refers to a device and the other bits have the meanings
    shown in Table 1.23.

    Table 1.23
    MS-DOS Data Bit Values
╓┌─┌──────────┌────────────────────┌─────────────────────────────────────────╖
    Bit        Value                Meaning
    ──────────────────────────────────────────────────────────────────────────
    Bit        Value                Meaning
    ──────────────────────────────────────────────────────────────────────────
    0          1                    Console input device

    1          1                    Console output device

    2          1                    Null device

    3          1                    Clock device

    4          1                    Reserved

    5          1                    Don't check for control characters

                0                    Check for control characters

    6          0                    End of file on input

    8-10                            Reserved

    11         1                    Device understands open/close
    Bit        Value                Meaning
    ──────────────────────────────────────────────────────────────────────────
    11         1                    Device understands open/close

    12                              Reserved

    13         1                    Device supports output until busy

    14         1                    Device can process control strings sent
                                    with Functions 4402H and 4403H (IOCtl
                                    character); bit can be read only, but not
                                    set

    15                              Reserved
    ──────────────────────────────────────────────────────────────────────────


    You must set the reserved bits to zero.

    The control characters referred to in the description of bit 5 are
    CONTROL+C, CONTROL+P, CONTROL+S, and CONTROL+Z. To read these characters
    as data, instead of as control characters, you must set bit 5 and use
    either Function 33H, CONTROL+C Check, or the MS-DOS break command to turn
    off CONTROL+C checking.

    If bit 7 of DX is 0, the handle refers to a file and the other bits have
    the following meanings:

    Bit        Value                Meaning
    ──────────────────────────────────────────────────────────────────────────
    0-5                             Drive number (0=A, 1=B, etc.)
    6          0                    The file has been written
    8-15                            Reserved
    ──────────────────────────────────────────────────────────────────────────

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  AL not 0 or 1, or AL is 1 but DH is not 0
    6                  Handle in BX not open or is invalid
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    ioctl_data macro  code,handle
                mov    bx,handle
                mov    al,code
                mov    ah,44H
                int    21H
                endm

    Example:

    The following program gets the device data for standard output, sets the
    bit that specifies not to check for control characters (bit 5), and then
    clears the bit.

    get     equ         0
    set     equ         1
    stdout  equ         1
    ;
    begin:  ioctl_data  get,stdout          ; THIS FUNCTION
            jc          error               ; routine not shown
            mov         dh,0                ; clear DH
            or          dl,20H              ; set bit 5
            ioctl_data  set,stdout          ; THIS FUNCTION
            jc          error               ; routine not shown
    ;
    ;   <control characters now treated as data, or "raw mode">
    ;
            ioctl_data  get,stdout          ; THIS FUNCTION
            jc          error               ; routine not shown
            mov         dh,0                ; clear DH
            and         dl,0DFH             ; clear bit 5
            ioctl_data  set,stdout          ; THIS FUNCTION
    ;
    ;  <control characters now interpreted, or "cooked mode">
    ;


────────────────────────────────────────────────────────────────────────────
IOCtl Character (Function 44H, Codes 2 and 3)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 44H
    AL
        2 = Send control data
        3 = Receive control data
    BX
        Handle
    CX
        Bytes to read or write
    DS:DX
        Pointer to buffer

    Return:

    Carry set:
    AX
        1 = Invalid function
        6 = Invalid handle
    Carry not set:
    AX
        Bytes transferred


    Comments:

    Function 44H, Codes 2 and 3, sends or receives control data to or from a
    character device. AL must contain 2 to send data or 3 to receive. BX must
    contain the handle of a character device, such as a printer or serial
    port. CX must contain the number of bytes to be read or written. DX must
    contain the offset (to the segment address in DS) of the data buffer.

    AX returns the number of bytes transferred. The device driver must support
    the IOCtl interface. If there is an error, the carry flag (CF) is set and
    the error code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  AL not 2 or 3, or device cannot perform the specified
                        function

    6                  Handle in BX not open or doesn't exist
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    ioctl_char  macro  code,handle,buffer
                mov    bx,handle
                mov    dx,offset buffer
                mov    al,code
                mov    ah,44H
                int    21H
                endm


    Example:

    No general example is applicable, since processing of IOCtl control data
    depends on the device being used, as well as the device driver.


────────────────────────────────────────────────────────────────────────────
IOCtl Block (Function 44H, Codes 4 and 5)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 44H
    AL
        4 = Send control data
        5 = Receive control data
    BL
        Drive number (0=default, 1=A, etc.)
    CX
        Bytes to read or write
    DS:DX
        Pointer to buffer

    Return:

    Carry set:
    AX
        1 = Invalid function
        5 = Invalid drive
    Carry not set:
    AX
        Bytes transferred


    Comments:

    Function 44H, Codes 4 and 5, sends or receives control data to or from a
    block device. AL must contain 4 to send data or 5 to receive. BL must
    contain the drive number (0=default, 1=A, etc.). CX must contain the
    number of bytes to be read or written. DX must contain the offset (to the
    segment address in DS) of the data buffer.

    AX returns the number of bytes transferred. The device driver must support
    the IOCtl interface. To determine whether or not it does, use Function
    4400H to get the device data, and test bit 14; if the bit is set, the
    driver supports IOCtl. If there is an error, the carry flag (CF) is set
    and the error code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  AL not 4 or 5, or device cannot perform the specified
                        function

    5                  Number in BL not a valid drive number
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    ioctl_status macro  code,drive,buffer
                mov    bl,drive
                mov    dx,offset buffer
                mov    al,code
                mov    ah,44H
                int    21H
                endm

    Example:

    No general example is applicable, since processing of IOCtl control data
    depends on the device being used, as well as the device driver.


────────────────────────────────────────────────────────────────────────────
IOCtl Status (Function 44H, Codes 6 and 7)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 44H
    AL
        6 = Check input status
        7 = Check output status
    BX
        Handle

    Return:

    Carry set:
    AX
        1        = Invalid function
        5        = Access denied
        6        = Invalid handle
    13        = Invalid data
    Carry not set:
    AL
        00H = Not ready
        0FFH = Ready


    Comments:

    Function 44H, Codes 6 and 7, checks whether or not the file or device
    associated with a handle is ready. AL must contain 6 to check whether the
    handle is ready for input or 7 to check whether the handle is ready for
    output. BX must contain the handle.

    AL returns the status:

                    Meaning for    Meaning for            Meaning for
    Value          Device         Input File            Output File
    ──────────────────────────────────────────────────────────────────────────
    00H            Not ready      Pointer is at EOF     Ready
    0FFH           Ready          Ready                 Ready
    ──────────────────────────────────────────────────────────────────────────

    An output file always returns ready, even if the disk is full. bp If there
    is an error, the carry flag (CF) is set and the error code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  AL not 6 or 7
    5                  Access denied
    6                  Number in BX not a valid, open handle
    13                 Invalid data
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    ioctl_status  macro  code,handle
                mov    bx,handle
                mov    al,code
                mov    ah,44H
                int    21H
                endm

    Example:

    The following program displays a message that tells whether the file
    associated with handle 6 is ready for input or whether it is at
    end-of-file.

    stdout      equ          1
    ;
    message     db           "File is "
    ready       db           "ready."
    at_eof      db           "at EOF."
    crlf        db           ODH,OAH
    ;
    begin:      write_handle stdout,message,8   ; display message
                jc           write_error        ; routine not shown
                ioctl_status 6                  ; THIS FUNCTION
                jc           ioctl_error        ; routine not shown
                cmp          al,0               ; check status code
                jne          not_eof            ; file is ready
                write_handle stdout,at_eof,7    ; see Function 40H
                jc           write_error        ; routine not shown
                jmp          all_done           ; clean up & go home
    not_eof:    write_handle stdout,ready,6     ; see Function 40H
    all_done:   write_handle stdout,crlf,2      ; see Function 40H
                jc           write_error        ; routine not shown


────────────────────────────────────────────────────────────────────────────
IOCtl Is Changeable (Function 44H, Code 08H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 44H
    AL = 08H
    BL
        Drive number (0=default, 1=A, etc.)

    Return:

    Carry set:
    AX
        1        = Invalid function
        15        = Invalid drive
    Carry not set:
    AX
        0 = Changeable
        1 = Not changeable


    Comments:

    Function 44H, Code 08H, checks whether a drive contains a removable or
    nonremovable disk. BL must contain the drive number (0=default, 1=A,
    etc.). AX returns 0 if the disk can be changed, 1 if it cannot.

    This call lets a program determine whether or not to issue a message to
    change disks.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  Device does not support this call
    15                 Number in BL not a valid drive number
    ──────────────────────────────────────────────────────────────────────────

    When the call returns error 1 (because the driver doesn't support it), the
    caller assumes that the driver cannot be changed.

    Macro Definition:

    ioctl_change  macro  drive
                mov    bl, drive
                mov    al, 08H
                mov    ah, 44H
                int    21H
                endm

    Example:

    The following program checks whether or not the current drive contains a
    removable disk. If not, processing continues; if so, the program prompts
    the user to replace the disk in the current drive.

    stdout    equ        1
    ;
    message   db        "Please replace disk in drive "
    drives    db        "ABCD"
    crlf      db         0DH,0AH
    ;
    begin:    ioctl_change 0          ; THIS FUNCTION
            jc           ioctl_error ; routine not shown
            cmp          ax,0       ; current drive changeable?
            jne          continue   ; no, continue processing
            write_handle stdout,message,29 ; see Function 40H
            jc           write_error ; routine not shown
            current_disk            ; see Function 19H
            xor          bx,bx      ; clear index
            mov          bl,al      ; get current drive
            display_char drives[bx] ; see Function 02H
            write_handle stdout,crlf,2 ; see Function 40H
            jc           write_error ; routine not shown
    continue:
    ;          (Further processing here)


────────────────────────────────────────────────────────────────────────────
IOCtl Is Redirected Block (Function 44H, Code 09H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 44H
    AL = 09H
    BL
        Drive number (0=default, 1=A, etc.)

    Return:

    Carry set:
    AX
        1        = Invalid function code
    15        = Invalid drive number
    Carry not set:
    DX
        Device-attribute bits


    Comments:

    Function 44H, Code 09H, checks whether a drive letter refers to a drive on
    a Microsoft Networks workstation (local) or is redirected to a server
    (remote). BL must contain the drive number (0=default, 1=A, etc.).

    If the block device is local, DX returns the attribute word from the
    device header. If the block device is remote, only bit 12 (1000H) is set;
    the other bits are 0 (reserved).

    An application program should not test bit 12, because applications should
    not make distinctions between local and remote files (or devices).
    Programs should be written so that they will be independent of the
    location of a device that has been removed.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  File sharing must be loaded to use this system call
    15                 Number in BL not a valid drive number
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    ioctl_rblock  macro  drive
                mov    bl, drive
                mov    al, 09H
                mov    ah, 44H
                int    21H
                endm

    Example:

    The following program checks whether or not drive B is local or remote and
    displays the appropriate message.

    stdout    equ        1
    ;
    message   db        "Drive B: is "
    loc       db        "local."
    rem       db        "remote."
    crlf      db         0DH,0AH
    ;
    begin:    write_handle stdout,message,12  ; display message
            jc           write_error      ; routine not shown
            ioctl_rblock 2                ; THIS FUNCTION
            jc           ioctl_error      ; routine not shown
            test         dx,1000h         ; bit 12 set?
            jnz          not_loc          ; yes, it's remote
            write_handle stdout,loc,6     ; see Function 40H
            jc           write_error      ; routine not shown
            jmp          done
    not_loc:  write_handle stdout,rem,7     ; see Function 40H
            jc           write_error      ; routine not shown
    done:     write_handle stdout,crlf,2    ; see Function 40H
            jc           write_error      ; routine not shown


────────────────────────────────────────────────────────────────────────────
IOCtl Is Redirected Handle (Function 44H, Code 0AH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 44H
    AL = 0AH
    BX
        Handle

    Return:

    Carry set:
    AX
        1 = Invalid function code
        6 = Invalid handle
    Carry not set:
    DX
        IOCtl bit field


    Comments:

    Function 44H, Code 0AH, checks whether a handle refers to a file or a
    device on a Microsoft Networks workstation (local) or is redirected to a
    server (remote). BX must contain the file handle. DX returns the IOCtl bit
    field; bit 15 is set if the handle refers to a remote file or device.

    An application program should not test bit 15, because applications should
    not make distinctions between local and remote files (or devices).
    Programs should be written so that they will be independent of the
    location of a device that has been removed.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  Network must be loaded to use this system call
    6                  Handle in BX not a valid, open handle
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    ioctl_rhandle macro  handle
                mov    bx, handle
                mov    al, 0AH
                mov    ah, 44H
                int    21H
                endm

    Example:

    The following program checks whether handle 5 refers to a local or remote
    file or a device and displays the appropriate message.

    stdout    equ        1
    ;
    message   db        "Handle 5 is "
    loc       db        "local."
    rem       db        "remote."
    crlf      db         0DH,0AH
    ;
    begin:    write_handle stdout,message,12; display message
            jc           write_error      ; routine not shown
            ioctl_rhandle 5               ; THIS FUNCTION
            jc           ioctl_error      ; routine not shown
            test         dx,8000h         ; bit 15 set?
            jnz          not_loc          ; yes, it's remote
            write_handle stdout,loc,6     ; see Function 40H
            jc           write_error      ; routine not shown
            jmp          done
    not_loc:  write_handle stdout,rem,7     ; see Function 40H
            jc           write_error      ; routine not shown
    done:     write_handle stdout,crlf,2    ; see Function 40H
            jc           write_error      ; routine not shown


────────────────────────────────────────────────────────────────────────────
IOCtl Retry (Function 44H, Code 0BH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 44H
    AL = 0BH
    DX
        Number of retries
    CX
        Wait time

    Return:

    Carry set:
    AX
        1 = Invalid function code
    Carry not set:
        No error


    Comments:

    Function 44H, Code 0BH, specifies how many times MS-DOS should retry a
    disk operation that fails because of a file-sharing violation. DX must
    contain the number of retries. CX controls the pause between retries.

    MS-DOS retries this type of disk operation three times, unless you use
    this system call to specify a different number. After the specified number
    of retries, MS-DOS issues Interrupt 24H (Critical-Error-Handler Address)
    for the requesting process.

    The effect of the delay parameter in CX is machine-dependent because it
    specifies how many times MS-DOS should execute an empty loop. The actual
    time varies, depending on the processor and clock speed. You can determine
    the effect on your machine by using debug. Set the number of retries to 1
    and then time several values of CX. If there is an error, the carry flag
    (CF) is set and the error code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  File sharing must be loaded to use this system call
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    ioctl_retry  macro  retries, wait
                mov    dx, retries
                mov    cx, wait
                mov    al, 0BH
                mov    ah, 44H
                int    21H
                endm

    Example:

    The following program sets the number of sharing retries to 10 and
    specifies a delay of 1000 between retries.

    begin:    ioctl_retry  10,1000          ; THIS FUNCTION
            jc           error            ; routine not shown


────────────────────────────────────────────────────────────────────────────
Generic IOCtl (for Handles) (Function 44H, Code 0CH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 44H
    AL = 0CH
    BX
        Handle
    CH = 05H
        Category code (printer device)
    CL
        Function (minor) code
    DS:DX
        Pointer to data buffer

    Return:

    Carry set:
    AX
        1        = Invalid function code
        --        = Any device error
    Carry not set:
        No error


    Comments:

    This call loads and selects code pages for devices on a per-device basis.
    It also sets or gets the output iteration count for a printer that
    supports "PRINT 'TIL BUSY."

    The video functions (type=display) are supported only if ansi.sys has been
    installed. If ansi.sys is not installed, mode sets the video state
    directly. This information reflects the latest mode command or any changes
    made by use of BIOS. This call uses BIO to get and set the states.

    The MS-DOS 4.0 mode command sets the values through this interface and the
    DOS utilities access the information through this interface.

    The category code may be one of the following:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    00                 Unknown device
    01                 Serial printer
    03                 Console (display) device
    05                 Parallel printer
    ──────────────────────────────────────────────────────────────────────────

    The function code may be one of the following:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    45H                Sets iteration count for printer
    4AH                Selects code page
    4CH                Starts prepare list
    4DH                Ends prepare list
    5FH                Sets display device
    65H                Gets iteration count for printer
    6AH                Query code page selected
    6BH                Query code page prepare list
    7FH                Gets display character (screen width, length, and
                        color)
    ──────────────────────────────────────────────────────────────────────────

    ──────────────────────────────────────────────────────────────────────────
    Note
    DS:DX points to a word that contains the new value for the total number
    of output iterations performed before proceeding. Thus, DS:DX points to
    a word that contains the character iteration count for the "PRINT 'TIL
    BUSY" loop. This is the number of times the device driver will wait for
    the device to signal "ready" before acknowledging "Device busy."
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    ioctl_handles        macro handle,function,category,buffer
                    mov ch,05H
                    mov cl,function
                    mov dx,offset buffer
                    mov bx,handle
                    mov ah,44H
                    mov al,0CH
                    int 21H
                    endm

    Example:

    The following program obtains device characteristics.

            mov    ax,440CH    ; Handle based generic IOCTL
            mov    bx,handle   ; Handle of open device
            mov    ch,03H      ; Type = display
            mov    cl,7FH      ; Subfunction for get
            lds    dx,buffer   ; Info buffer
            int    21H
            jc     error
        buffer  label  byte   ; Return buffer
            db     0           ; Info level (set to 0
                                ; before call)
            db     0           ; reserved    (")
                                ; for type display
            dw     7*2         ; length of data
            dw     flags       ; control flags
                                ; 0001H=intense (vs blink)
                                ; display mode
                                ; 1=text
                                ; 2=APA
            db     0           ; reserved
            dw     colors      ; # of colors (monochrome=0)
            dw     width       ; width of display (pixels)
            dw     length      ; length of display (pixels)
            dw     cols        ; width of display (chars)
            dw     rows        ; length of display (lines)

    To fetch code-page information, return the DBCS range vector and add the
    following:

            mov    ax=440Ch    ; Handle generic IOCTL
            mov    cx,036AH    ; Query display
            mov    bx,handle
            lds    dx,buffer   ; change buffer content
            int    21H
            jc     error

        Buffer   Label   Word
            dw   end-start     ; length
    start:
            dw   codepage      ; code page
            db   s1,e1         ; DBCS range
            db   0,0           ; end of list

    ──────────────────────────────────────────────────────────────────────────
    Note
    Some device drivers may only support the codepage value. As a result,
    the user of this call must check the returned length before assuming the
    DBCS information is present. The display.sys and printer.sys device
    drivers do not need to support the DBCS information. Only the drivers
    supplied with the Asian MS-DOS 4.0 will provide this support.
    ──────────────────────────────────────────────────────────────────────────


────────────────────────────────────────────────────────────────────────────
Generic IOCtl (for Devices) (Function 44H, Code 0DH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 44H
    AL = 0DH
    BL
        Drive number
            (0 = default, 1 = A, etc.)
    CH = 08H
        Category (major) code
    CL
        Function (minor) code
    DS:DX
        Pointer to parameter block -1

    Return:

    Carry set:
    AX
        1        = Invalid function code
        2        = Invalid drive
        -        = Any device error
    Carry not set:
        No error


    Comments:

    The function code may be one of the following:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    40                 Set device parameters
    41                 Write track on logical device
    42                 Format track on logical device
    60                 Get device parameters
    61                 Read track on logical device
    62                 Verify track on logical device
    ──────────────────────────────────────────────────────────────────────────

    ──────────────────────────────────────────────────────────────────────────
    Note
    You must issue "Set Parameters Device" before you can read, write,
    format, or verify a logical drive.
    ──────────────────────────────────────────────────────────────────────────

    You should use the following procedure when you want to read, write,
    format, or verify a logical drive:

    1. Save drive parameters using "Get Device Parameters."

    2. Set desired drive parameters using "Set Device Parameters."

    3. Perform the I/O operation.

    4. Restore the original drive parameters using "Set Device Parameters."

    Set Device Parameters (Function 440DH, CL=40H)

    When CL=40H, the parameter block has the following field format:

    ┌────────────────────────────────────┐
    │ BYTE      Special Functions        │
    ├────────────────────────────────────┤
    │ BYTE      Device Type              │
    ├────────────────────────────────────┤
    │ WORD      Device Attributes        │
    ├────────────────────────────────────┤
    │ WORD      Number of Cylinders      │
    ├────────────────────────────────────┤
    │ BYTE      Media Type               │
    ├────────────────────────────────────┤
    │           Device BPB               │
    ├────────────────────────────────────┤
    │           Track Layout             │
    └────────────────────────────────────┘

    These fields have the following meanings:

    Special Functions

╓┌─┌──────────┌─────────┌────────────────────────────────────────────────────╖
    Bit        Value     Meaning
    ──────────────────────────────────────────────────────────────────────────
    Bit        Value     Meaning
    ──────────────────────────────────────────────────────────────────────────
    0          0         The Device BPB (BIOS Parameter Block) field contains
                        the new default BPB for this device. If a previous
                        Set Device Parameters call set this bit, Build BPB
                        returns the actual media BPB otherwise, it returns
                        the default BPB for the device.

                1         All subsequent Build BPB requests return the device
                        BPB.

    1          0         Read all fields of the parameter block.

                1         Ignore all fields of the parameter block except for
                        the Track Layout field.

    2          0         The sectors in the track may not all be the same
                        size. (You should not use this setting.)

                1         The sectors in the track are all the same size and
                        the sector numbers range between 1 and the total
    Bit        Value     Meaning
    ──────────────────────────────────────────────────────────────────────────
                        the sector numbers range between 1 and the total
                        number of sectors actually in the track. You should
                        always set this bit.

    3-7        0         These bits must be zero.
    ──────────────────────────────────────────────────────────────────────────


    Device Type

    This byte describes the physical device and is set by the device. When
    set, it has the following meanings:

    Value                                Meaning
    ──────────────────────────────────────────────────────────────────────────
    0                                    320/360kilobytes
    1                                    1.2 megabytes
    2                                    720 kilobytes
    3                                    8-inch, single-density
    4                                    8-inch, double-density
    5                                    Hard disk
    6                                    Tape drive
    7                                    1.44 megabytes
    ──────────────────────────────────────────────────────────────────────────

    Device Attributes

    Bit     Value            Meaning
    ──────────────────────────────────────────────────────────────────────────
            0                The media is removable.
    0       1                The media is not removable.
    1       0                Disk change-line is not supported (no door lock
                            support).
            1                Disk change-line is supported (door lock
                            support).
    2-7     0                These bits must be zero.
    ──────────────────────────────────────────────────────────────────────────

    Number of Cylinders

    This field indicates the maximum number of cylinders that the physical
    device can support. This information is set by the device.

    Media Type

    For drives that may contain different media, this field (which is
    device-dependent) indicates which media the drive expects.

    For a 1.2-megabyte disk, bit zero has the following meaning:

    Bit        Value                Meaning
    ──────────────────────────────────────────────────────────────────────────
    0          0                    Quad-density, 1.2-megabyte disk
                1                    Double-density, 320/360-kilobyte disk
    ──────────────────────────────────────────────────────────────────────────

    The default media type is a quad-density, 1.2-megabyte disk.

    Device BPB

    If bit 0 of the Special Functions field is clear, the BPB in this field is
    the new default BPB for the device.

    If bit 0 of the Special Functions field is set, the device driver returns
    the BPB from this field for subsequent Build BPB requests.

    Track Layout

    This field contains a table of variable length for each logical device and
    indicates the expected layout of the sectors on the media track. The field
    has the following format:

    ┌──────────────────────────────────────────────┐
    │ WORD      Sector Count -- total # of sectors │
    ├──────────────────────────────────────────────┤
    │ WORD      Sector Number -- sector #1         │
    ├──────────────────────────────────────────────┤
    │ WORD      Sector Size -- sector #1           │
    ├──────────────────────────────────────────────┤
    │ WORD      Sector Number -- sector #2         │
    ├──────────────────────────────────────────────┤
    │ WORD      Sector Size -- sector #2           │
    └──────────────────────────────────────────────┘
                        .
                        .
                        .
    ┌──────────────────────────────────────────────┐
    │ WORD      Sector Number -- sector #n         │
    ├──────────────────────────────────────────────│
    │ WORD      Sector Size -- sector #n           │
    └──────────────────────────────────────────────┘

    The Sector Count field indicates the total number of sectors. Each sector
    number must be unique and in the range of 1 to sector count (n).

    If bit 2 of the Special Functions field is set, all sector sizes must be
    the same.

    Get Device Parameters (Function 440DH, CL=60H)

    When CL=60H, the parameter block has the same field layout as for CL=40H.
    However, some of the fields have different meanings. These are described
    as follows:

    Special Functions

    Bit     Value            Meaning
    ──────────────────────────────────────────────────────────────────────────
    0       0                Returns the default BPB for the device.
            1                Returns the BPB that Build BPB would return.
    1-7     0                These bits must be zero.
    ──────────────────────────────────────────────────────────────────────────

    Track Layout

    The Get Device Parameters call does not use this field.

    Read/Write Track on Logical Drive (Function 440DH, CL=61H/CL=41H)

    To write to a track on a logical drive, set CL=41H. To read a track on a
    logical drive, set CL=61H.

    When CL=41H or 61H, the parameter block has the following format:

    ┌──────────────────────────────┐
    │ BYTE      Special Functions  │
    ├──────────────────────────────┤
    │ WORD      Head               │
    ├──────────────────────────────┤
    │ WORD      Cylinder           │
    ├──────────────────────────────┤
    │ WORD      First Sector       │
    ├──────────────────────────────┤
    │ WORD      Number of Sectors  │
    ├──────────────────────────────│
    │ DWORD     Transfer Address   │
    └──────────────────────────────┘

    These fields are described as follows:

    Special Functions

    This byte must be zero.

    Head

    This field contains the number of the head on which you perform the write
    or read.

    Cylinder

    This field contains the number of the cylinder on which you perform the
    write or read.

    First Sector

    This field contains the number of the first sector on which you perform
    the write or read. Sectors are numbered starting with zero, so the fourth
    sector is numbered 3.

    Number of Sectors

    This field contains the total number of sectors.

    Transfer Address

    This field contains the address for storing the data to be written or the
    data just read.

    Format/Verify Track on Logical Drive (Function 440DH, CL=42H/CL=62H)

    To format and verify a track on a logical drive, set CL=42H. To verify a
    track on a logical drive, set CL=62H.

    When CL=42H or 62H, the parameter block has the following format:

    ┌──────────────────────────────┐
    │ BYTE      Special Functions  │
    ├──────────────────────────────┤
    │ WORD      Head               │
    │──────────────────────────────┤
    │ WORD      Cylinder           │
    └──────────────────────────────┘

    These fields are described as follows:

    Special Functions

    This byte must be zero.

    Head

    This field contains the number of the head on which you perform the format
    or verify.

    Cylinder

    This field contains the number of the cylinder on which you perform the
    format or verify.


────────────────────────────────────────────────────────────────────────────
Get/Set IOCtl Drive Map (Function 44H, Codes 0EH and 0FH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 44H
    AL
        OEH = Get logical drive map
        OFH = Set logical drive map
    BX
        Drive number
            (0 = default, 1 = A, etc.)

    Return:

    Carry set:
    AX
        1 = Invalid function code
        5 = Invalid drive
    Carry not set:
    AL = Logical drive mapped onto physical drive
        (= 0 if only one drive is
        assigned to this physical drive)


    Comments:

    MS-DOS supports the mapping of multiple logical drives onto a single
    physical block device. Get IOCtl Drive Map lets you query the DOS about
    which logical drive is currently mapped onto the corresponding physical
    device. Set IOCtl Drive Map alters the device that is currently mapped
    onto the physical device. These functions are only useful if there is more
    than one logical block device mapped onto a single physical device.

    A possible use for these functions is with applications that want to
    disable the DOS prompt in order to place the correct floppy disk in the
    drive when accessing the other logical drive.

    To detect whether or not a logical device currently owns the physical
    device to which it is mapped, a program needs to check the value in AL
    after calling Function 440EH or 440FH (Get/Set IOCtl Drive Map).


────────────────────────────────────────────────────────────────────────────
Duplicate File Handle (Function 45H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 45H
    BX
        Handle

    Return:

    Carry set:
    AX
        4 = Too many open files
        6 = Invalid handle
    Carry not set:
    AX
        New handle


    Comments:

    Function 45H creates an additional handle for a file. BX must contain the
    handle of an open file.

    MS-DOS returns the new handle in AX. The new handle refers to the same
    file as the handle in BX, with the file pointer at the same position.

    After you use this function request, moving the read/write pointer of
    either handle also moves the pointer for the other handle. You usually use
    this function request to redirect standard input (handle 0) and standard
    output (handle 1). For a description of standard input, standard output,
    and the advantages and techniques of manipulating them, see Software Tools
    by Brian W. Kernighan and P.J. Plauger (Addison-Wesley Publishing Co.,
    1976).

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    4                  Too many open files (no handle available)
    6                  Handle not open or is invalid
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    xdup  macro  handle
        mov    bx,handle
        mov    ah,45H
        int    21H
        endm

    Example:

    The following program redirects standard output (handle 1) to a file named
    dirfile, invokes a second copy of command.com to list the directory (which
    writes the directory to dirfile), and then restores standard input to
    handle 1.

    pgm_file  db    "command.com",0
    cmd_line  db    9,"/c dir /w",0dH
    parm_blk  db    14 dup (0)
    path            db  "dirfile",0
    dir_file        dw      ?       ;  For handle
    sav_stdout dw   ?               ;  For handle
    ;
    begin:    set_block  last_inst  ;  See Function 4AH
            jc      error_setblk  ;  Routine not shown
            create_handle  path,0 ;  See Function 3CH
            jc      error_create  ;  Routine not shown
            mov     dir_file,ax   ;  Save handle
            xdup    1             ;  THIS FUNCTION
            jc      error_xdup    ;  Routine not shown
            mov     sav_stdout,ax ;  Save handle
            xdup2   dir_file,1    ;  See Function 46H
            jc      error_xdup2   ;  Routine not shown
            exec    pgm_file,cmd_line,parm_blk ;  See Function 4BH
            jc      error_exec    ;  Routine not shown
            xdup2   sav_stdout,1  ;  See Function 46H
            jc      error_xdup2   ;  Routine not shown
            close_handle sav_stdout ;  See Function 3EH
            jc      error_close   ;  Routine not shown
            close_handle dir_file ;  See Function 3EH
            jc      error_close   ;  Routine not shown


────────────────────────────────────────────────────────────────────────────
Force Duplicate File Handle (Function 46H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 46H
    BX
        Handle
    CX
        Second handle

    Return:

    Carry set:
    AX
        4 = Too many open files
        6 = Invalid handle
    Carry not set:
        No error


    Comments:

    Function 46H forces a specified handle to refer to the same file as a
    second handle already associated with an open file. BX must contain the
    handle of the open file; CX must contain the second handle.

    On return, the handle in CX now refers to the same file at the same
    position as the handle in BX. If the file referred to by the handle in CX
    was open at the time of the call, this function closes it.

    After you use this call, moving the read/write pointer of either handle
    also moves the pointer for the other handle. Normally, you would use this
    function request to redirect standard input (handle 0) and standard output
    (handle 1). For a description of standard input, standard output, and the
    advantages and techniques of manipulating them, see Software Tools by
    Brian W. Kernighan and P.J. Plauger (Addison-Wesley Publishing Co., 1976).

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    4                  Too many open files (no handle available)
    6                  Handle not open or is invalid
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    xdup2  macro  handle1,handle2
            mov    bx,handle1
            mov    cx,handle2
            mov    ah,46H
            int    21H
            endm

    Example:

    The following program redirects standard output (handle 1) to a file named
    dirfile, invokes a second copy of command.com to list the directory (which
    writes the directory to dirfile), and then restores standard input to
    handle 1.

    pgm_file  db    "command.com",0
    cmd_line  db    9,"/c dir /w",0dH
    parm_blk  db    14 dup (0)
    path            db  "dirfile",0
    dir_file        dw      ?       ;  For handle
    sav_stdout dw   ?               ;  For handle
    ;
    begin:    set_block  last_inst  ;  See Function 4AH
            jc      error_setblk  ;  Routine not shown
            create_handle  path,0 ;  See Function 3CH
            jc      error_create  ;  Routine not shown
            mov     dir_file,ax   ;  Save handle
            xdup    1             ;  See Function 45H
            jc      error_xdup    ;  Routine not shown
            mov     sav_stdout,ax ;  Save handle
            xdup2   dir_file,1    ;
            jc      error_xdup2   ;  Routine not shown
            exec    pgm_file,cmd_line,parm_blk ;  See Function 4BH
            jc      error_exec    ;  Routine not shown
            xdup2   sav_stdout,1  ;  THIS FUNCTION
            jc      error_xdup2   ;  Routine not shown
            close_handle sav_stdout ;  See Function 3EH
            jc      error_close   ;  Routine not shown
            close_handle dir_file ;  See Function 3EH
            jc      error_close   ;  Routine not shown


────────────────────────────────────────────────────────────────────────────
Get Current Directory (Function 47H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 47H
    DS:SI
        Pointer to 64-byte memory area
    DL
        Drive number
            (0 = default, 1 = A, etc.)

    Return:

    Carry set:
    AX
        15 = Invalid drive number
    Carry not set:
        No error


    Comments:

    Function 47H returns the pathname of the current directory on a specified
    drive. DL must contain a drive number (0=default, 1=A, etc.). SI must
    contain the offset (from the segment address in DS) of a 64-byte memory
    area.

    MS-DOS places an ASCIZ string in the memory area, consisting of the path
    (starting from the root directory) of the current directory for the drive
    specified in DL. The string does not begin with a backslash and does not
    include the drive letter.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    15                 Number in DL not a valid drive number
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    get_dir  macro   drive,buffer
            mov     dl,drive
            mov     si,offset buffer
            mov     ah,47H
            int     21H
            endm

    Example:

    The following program displays the current directory that is on the disk
    in drive B.

    disk        db      "b:\#"
    buffer      db       64 dup (?)
    ;
    begin:      get_dir  2,buffer       ; THIS FUNCTION
                jc       error_dir      ; Routine not shown
                display  disk           ; See Function 09H
                display_asciz  buffer  ; See end of chapter


────────────────────────────────────────────────────────────────────────────
Allocate Memory (Function 48H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 48H
    BX
    Paragraphs of memory requested

    Return:

    Carry set:
    AX
    7 = Memory-control blocks damaged
    8 = Insufficient memory
    BX
    Paragraphs of memory available
    Carry not set:
    AX
    Segment address of allocated memory


    Comments:

    Function 48H tries to allocate the specified amount of memory to the
    current process. BX must contain the number of paragraphs of memory (one
    paragraph is 16 bytes).

    If sufficient memory is available to satisfy the request, AX returns the
    segment address of the allocated memory (the offset is 0). If sufficient
    memory is not available, BX returns the number of paragraphs of memory in
    the largest available block.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    7                  Memory-control blocks damaged (a user program changed
                        memory that didn't belong to it)

    8                  Not enough free memory to satisfy the request
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    allocate_memory  macro   bytes
                    mov     bx,bytes
                    mov     cl,4
                    shr     bx,cl
                    inc     bx
                    mov     ah,48H
                    int     21H
                    endm

    Example:

    The following program opens the file named textfile.asc, calculates its
    size with Function 42H (Move File Pointer), allocates a block of memory
    the size of the file, reads the file into the allocated memory block, and
    then frees the allocated memory.

    path      db      "textfile.asc",0
    msg1      db      "File loaded into allocated memory block.",
                        0DH,0AH
    msg2      db      "Allocated memory now being freed
                        (deallocated).",0DH,0AH
    handle    dw       ?
    mem_seg   dw       ?
    file_len  dw       ?
    ;
    begin:    open_handle  path,0
            jc       error_open    ; Routine not shown
            mov      handle,ax     ; Save handle
            move_ptr handle,0,0,2  ; See Function 42H
            jc       error_move    ; Routine not shown
            mov      file_len,ax   ; Save file length
            set_block  last_inst   ; See Function 4AH
            jc       error_setblk  ; Routine not shown
            allocate_memory  file_len ; THIS FUNCTION
            jc       error_alloc   ; Routine not shown
            mov      mem_seg,ax    ; Save address of new memory
            move_ptr handle,0,0,0  ; See Function 42H
            jc       error_move    ; Routine not shown
            push     ds            ; Save DS
            mov      ax,mem_seg    ; Get segment of new memory
            mov      ds,ax         ; Point DS at new memory
            read_handle  cs:handle,0,cs:file_len ; Read file into
    ;                                                new memory
            pop      ds            ; Restore DS
            jc       error_read    ; Routine not shown
    ;          (CODE TO PROCESS FILE GOES HERE)
            write_handle stdout,msg1,42 ; See Function 40H
            jc       write_error   ; Routine not shown
            free_memory  mem_seg   ; See Function 49H
            jc       error_freemem ; Routine not shown
            write_handle stdout,msg2,49 ; See Function 40H
            jc       write_error   ; Routine not shown


────────────────────────────────────────────────────────────────────────────
Free Allocated Memory (Function 49H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 49H
    ES
        Segment address of memory to be
        freed

    Return:

    Carry set:
    AX
        7 = Memory-control blocks damaged
        9 = Incorrect segment
    Carry not set:
        No error


    Comments:

    Function 49H frees (makes available) a block of memory previously
    allocated with Function 48H (Allocate Memory). ES must contain the
    segment address of the memory block to be freed.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    7                  Memory-control blocks damaged (a user program changed
                        memory that didn't belong to it)

    9                  Memory pointed to by ES was not allocated with
                        Function 48H
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    free_memory  macro   seg_addr
                mov     ax,seg_addr
                mov     es,ax
                mov     ah,49H
                int     21H
                endm

    Example:

    The following program opens a file named textfile.asc, calculates its size
    with Function 42H (Move File Pointer), allocates a block of memory the
    size of the file, reads the file into the allocated memory block, and then
    frees the allocated memory.

    path      db      "textfile.asc",0
    msg1      db      "File loaded into allocated memory block.",
                        0DH,0AH
    msg2      db      "Allocated memory now being freed
                        (deallocated).",0DH,0AH
    handle    dw       ?
    mem_seg   dw       ?
    file_len  dw       ?
    ;
    begin:    open_handle  path,0
            jc       error_open    ; Routine not shown
            mov      handle,ax     ; Save handle
            move_ptr handle,0,0,2  ; See Function 42H
            jc       error_move    ; Routine not shown
            mov      file_len,ax   ; Save file length
            set_block  last_inst   ; See Function 4AH
            jc       error_setblk  ; Routine not shown
            allocate_memory  file_len ; See Function 48H
            jc       error_alloc   ; Routine not shown
            mov      mem_seg,ax    ; Save address of new memory
            mov_ptr  handle,0,0,0  ; See Function 42H
            jc       error_move    ; Routine not shown
            push     ds            ; Save DS
            mov      ax,mem_seg    ; Get segment of new memory
            mov      ds,ax         ; Point DS at new memory
            read_handle  handle,code,file_len ; Read file into
    ;                                             new memory
            pop      ds            ; Restore DS
            jc       error_read    ; Routine not shown
    ;          (CODE TO PROCESS FILE GOES HERE)
            write_handle stdout,msg1,42 ; See Function 40H
            jc       write_error   ; Routine not shown
            free_memory  mem_seg   ; THIS FUNCTION
            jc       error_freemem ; Routine not shown
            write_handle stdout,msg2,49 ; See Function 40H
            jc       write_error   ; Routine not shown


────────────────────────────────────────────────────────────────────────────
Set Block (Function 4AH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 4AH
    BX
        Paragraphs of memory
    ES
        Segment address of memory area

    Return:

    Carry set:
    AX
        7 = Memory-control blocks damaged
        8 = Insufficient memory
        9 = Incorrect segment
    BX
        Paragraphs of memory available
    Carry not set:
        No error


    Comments:

    Function 4AH changes the size of a memory-allocation block. ES must
    contain the segment address of the memory block. BX must contain the new
    size of the memory block, in paragraphs (one paragraph is 16 bytes).

    MS-DOS attempts to change the size of the memory block. If the call fails
    on a request to increase memory, BX returns the maximum size (in
    paragraphs) to which the block can be increased.

    Since MS-DOS allocates all available memory to a .com program, you would
    use this call most often to reduce the size of a program's initial
    memory-allocation block.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    7                  Memory-control blocks destroyed (a user program changed
                        memory that didn't belong to it)

    8                  Not enough free memory to satisfy the request

    9                  Wrong address in ES (the memory block it points to
    ──────────────────────────────────────────────────────────────────────────

    The following macro shrinks the initial memory-allocation block of a .com
    program. It takes as a parameter the offset of the first byte following
    the last instruction of a program (LAST_INST in the sample programs), uses
    it to calculate the number of paragraphs in the program, and then adds 17
    to the result: one to round up and 16 to set aside 256 bytes for a stack.
    It then sets up SP and BP to point to this stack.

    Macro Definition:

    set_block  macro   last_byte
                mov     bx,offset last_byte
                mov     cl,4
                shr     bx,cl
                add     bx,17
                mov     ah,4AH
                int     21H
                mov     ax,bx
                shl     ax,cl
                dec     ax
                mov     sp,ax
                mov     bp,sp
                endm

    Example:

    The following program invokes a second copy of command.com and executes a
    dir (directory) command.

    pgm_file  db      "command.com",0
    cmd_line  db       9,"/c dir /w",0DH
    parm_blk  db       14 dup (?)
    reg_save  db       10 dup (?)
    ;
    begin: set_block  last_inst                   ; THIS FUNCTION
            exec       pgm_file,cmd_line,parm_blk,0 ; See Function 4BH


────────────────────────────────────────────────────────────────────────────
Load and Execute Program (Function 4BH, Code 00H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 4BH
    AL = 00H
    DS:DX
        Pointer to pathname
    ES:BX
        Pointer to parameter block

    Return:

    Carry set:
    AX
        1        = Invalid function
        2        = File not found
        3        = Path not found
        4        = Too many open files
        5        = Access denied
        8        = Insufficient memory
    10        = Bad environment
    11        = Bad format
    Carry not set:
    No error


    Comments:

    Function 4BH, Code 00H, loads and executes a program. DX must contain the
    offset (from the segment address in DS) of an ASCIZ string that specifies
    the drive and pathname of an executable program file. BX must contain the
    offset (from the segment address in ES) of a parameter block. AL must
    contain 0.

    There must be enough free memory for MS-DOS to load the program file.
    MS-DOS allocates all available memory to a program when it loads it, so
    you must free some memory with Function 4AH (Set Block) before using this
    function request to load and execute another program. Unless you or MS-DOS
    needs the memory for some other purpose, you should shrink the memory to
    the minimum amount required by the current process before issuing this
    request. MS-DOS creates a Program Segment Prefix for the program being
    loaded and sets the terminate and CONTROL+C addresses to the instruction
    that immediately follows the call to Function 4BH in the invoking program.

    The parameter block consists of four addresses:

    Table 1.24
    Contents of the Parameter Block
    Offset     Length
    (Hex)      (Bytes)              Description
    ──────────────────────────────────────────────────────────────────────────
    00         2 (word)             Segment address of the environment to be
                                    passed; 00H means copy the parent
                                    program's environment

    02         4 (dword)            Segment: Offset of command line to be
                                    placed at offset 80H of the new Program
                                    Segment Prefix. Must be a correctly formed
                                    command line no longer than 128 bytes

    06         4 (dword)            Segment: Offset of FCB to be placed at
                                    offset 5CH of the new Program Segment
                                    Prefix (the Program Segment Prefix is
                                    described in Chapter 4, "MS-DOS Control
                                    Blocks and Work Areas")

    0A         4 (dword)            Segment: Offset of FCB to be placed at
                                    offset 6CH of the new Program Segment
    ──────────────────────────────────────────────────────────────────────────

    All open files of a program are available to the newly loaded program,
    giving the parent program control over the definition of standard input,
    output, auxiliary, and printer devices. For example, a program could write
    a series of records to a file, open the file as standard input, open a
    second file as standard output, and then use Function 4BH, Code 00H, (Load
    and Execute Program) to load and execute a program that takes its input
    from standard input, sorts records, and writes to standard output.

    The loaded program also receives an environment, a series of ASCIZ strings
    of the form parameter=value (for example, verify= on). The environment
    must begin on a paragraph boundary, be less than 32 kilobytes long, and
    end with a byte of 00H (that is, the final entry consists of an ASCIZ
    string followed by two bytes of 00H). Following the last byte of zeros is
    a set of initial arguments passed to a program containing a word count
    followed by an ASCIZ string. If the call finds the file in the current
    directory, the ASCIZ string contains the drive and pathname of the
    executable program as passed to Function 4BH. If the call finds the file
    in the path, it concatenates the filename with the path information. (A
    program may use this area to determine from where it was loaded.) If the
    word-environment address is 0, the loaded program either inherits a copy
    of the parent program's environment or receives a new environment built
    for it by the parent. Place the segment address of the environment at
    offset 2CH of the new Program Segment Prefix. To build an environment for
    the loaded program, put it on a paragraph boundary and place the segment
    address of the environment in the first word of the parameter block. To
    pass a copy of the parent's environment to the loaded program, put 00H in
    the first word of the parameter block.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  AL not 0 or 3

    2                  Program file not found

    3                  Path invalid or not found

    4                  Too many open files (no handle available)

    5                  Directory full, a directory with the same name exists,
                        or a file with the same name exists

    8                  Not enough memory to load the program

    10                 Environment appears longer than 32 kilobytes

    11                 Program file is a .exe file that contains internally
    ──────────────────────────────────────────────────────────────────────────

    Executing Another Copy of Command.com

    Since command.com builds pathnames, searches command paths for program
    files, and relocates .exe files, the simplest way to load and execute
    another program is to load and execute an additional copy of command.com,
    passing it a command line that includes the /c switch, which invokes the
    .com or .exe file.

    This action requires 17 kilobytes of available memory, so a program that
    does it should be sure to shrink its initial memory-allocation block with
    Function 4AH (Set Block). The following is the format of a command line
    that contains the /c switch:

    length,/c command,0DH

    where:

    Length is the length of the command line, counting the length byte but not
    the ending carriage-return (0DH).

    Command is any valid MS-DOS command.

    0DH is a carriage-return character.

    If a program executes another program directly──naming it as the program
    file to Function 4BH instead of command.com──it must perform all the
    processing normally done by command.com.

    Macro Definition:

    exec  macro  path,command,parms
        mov    dx,offset path
        mov    bx,offset parms
        mov    word ptr parms[02H],offset command
        mov    word ptr parms[04H],cs
        mov    word ptr parms[06H],5CH
        mov    word ptr parms[08H],es
        mov    word ptr parms[0AH],6CH
        mov    word ptr parms[0CH],es
        mov    al,0
        mov    ah,4BH
        int    21H
        endm

    Example:

    The following program invokes a second copy of command.com and executes a
    dir (directory) command by using the /w (wide) switch:

    pgm_file  db      "command.com",0
    cmd_line  db       9,"/c dir /w",0DH
    parm_blk  db       14 dup (?)
    reg_save  db       10 dup (?)
    ;
    begin:
        set_block  last_inst                    ; See Function 4AH
        exec       pgm_file,cmd_line,parm_blk,0 ; THIS FUNCTION


────────────────────────────────────────────────────────────────────────────
Load Overlay (Function 4BH, Code 03H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 4BH
    AL = 03H
    DS:DX
        Pointer to pathname
    ES:BX
        Pointer to parameter block

    Return:

    Carry set:
    AX
        1        = Invalid function
        2        = File not found
        3        = Path not found
        4        = Too many open files
        5        = Access denied
        8        = Insufficient memory
    10        = Bad environment
    Carry not set:
    No error


    Comments:

    Function 4BH, Code 03H, loads a program segment (overlay). DX must contain
    the offset (from the segment address in DS) of an ASCIZ string that
    specifies the drive and pathname of the program file. BX must contain the
    offset (from the segment address in ES) of a parameter block. AL must
    contain 3.

    MS-DOS assumes that since the invoking program is loading into its own
    address space, it requires no free memory. This call does not create a
    Program Segment Prefix. The parameter block is four bytes long:

    Table 1.25
    Contents of the Parameter Block
    Offset     Length
    (Hex)      (Bytes)              Description
    ──────────────────────────────────────────────────────────────────────────
    00         2 (word)             Segment address at which program is to be
                                    loaded

    02         2 (word)             Relocation factor; usually the same as the
                                    first word of the parameter block (for a
                                    description of a .exe file and of
                                    relocation, see Chapter 6, ".Exe File
    ──────────────────────────────────────────────────────────────────────────

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  AL not 00H or 03H

    2                  Program file not found

    3                  Path invalid or not found

    4                  Too many open files (no handle available)

    5                  Directory is full, a directory with the same name
                        exists, or a file with the same name exists

    8                  Not enough memory to load the program

    10                 Environment appears longer than 32 kilobytes
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    exec_ovl  macro  path,parms,seg_addr
            mov    dx,offset path
            mov    bx,offset parms
            mov    parms,seg_addr
            mov    parms[02H],seg_addr
            mov    al,3
            mov    ah,4BH
            int    21H
            endm

    Example:

    The following program opens a file named textfile.asc, redirects standard
    input to that file, loads more.com as an overlay, and calls an overlay
    named bit.com, which reads textfile.asc as standard input. The overlay
    must establish its own addressability and end with a FAR return.

    stdin     equ       0
    ;
    file      db    "TEXTFILE.ASC",0
    cmd_file  db    "\bit.com",0
    parm_blk  dw     4 dup (?)
    overlay   label  dword
            dw     0
    handle    dw     ?
    new_mem   dw     ?
    ;
    begin:    set_block   last_inst        ; see Function 4AH
            jc          setblock_error   ; routine not shown
            allocate_memory  2000        ; see Function 48H
            jc          allocate_error   ; routine not shown
            mov         new_mem,ax       ; save seg of memory
            open_handle file,0           ; see Function 3DH
            jc          open_error       ; routine not shown
            mov         handle,ax        ; save handle
            xdup2       handle,stdin     ; see Function 45H
            jc          dup2_error       ; routine not shown
            close_handle handle          ; see Function 3EH
            jc          close_error      ; routine not shown
            mov         ax,new_mem       ; addr of new memory
            exec_ovl cmd_file,parm_blk,ax    ; THIS FUNCTION
            jc          exec_error       ; routine not shown
            call        overlay          ; call the overlay
            free_memory new_mem          ; see Function 49H
            jc          free_error       ; routine not shown
    ;


────────────────────────────────────────────────────────────────────────────
End Process (Function 4CH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 4CH
    AL
        Return code

    Return:

    None


    Comments:

    Function 4CH terminates a process and returns to MS-DOS. AL contains a
    return code that can be retrieved by the parent process with Function
    4DH (Get Return Code of Child Process) or the if command, using
    errorlevel.

    MS-DOS closes all open handles, ends the current process, and returns
    control to the invoking process.

    This function request doesn't require CS to contain the segment address of
    the Program Segment Prefix. You should use it to end a program (rather
    than Interrupt 20H or a jump to location 0) unless your program must be
    compatible with MS-DOS versions earlier than 2.0.

    ──────────────────────────────────────────────────────────────────────────
    Note
    If you use file sharing, you must remove all locks issued by this
    process or DOS will be in an uncertain state.
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    end_process  macro  return_code
                mov    al,return_code
                mov    ah,4CH
                int    21H
                endm

    Example:

    The following program displays a message and returns to MS-DOS with a
    return code of 8. It uses only the opening portion of the sample program
    skeleton shown at the beginning of this chapter.

    message   db     "Displayed by FUNC_4CH example",0DH,0AH,"$"
    ;
    begin:    display      message   ; See Function 09H
            end_process  8         ; THIS FUNCTION
    code      ends
            end          code


────────────────────────────────────────────────────────────────────────────
Get Return Code of Child Process (Function 4DH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 4DH

    Return:

    AX
        Return code


    Comments:

    Function 4DH retrieves the return code specified when a child process
    terminates via either Function 31H (Keep Process) or Function 4CH (End
    Process). The code returns in AL. AH returns a code that specifies why the
    program ended:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    0                  Normal termination
    1                  Terminated by CONTROL+C
    2                  Critical device error
    3                  Function 31H (Keep Process)
    ──────────────────────────────────────────────────────────────────────────

    This call can retrieve the exit code only once.

    Macro Definition:

    ret_code  macro
            mov    ah,4DH
            int    21H
            endm

    Example:

    No example is included for this function request because the meaning of a
    return code varies.


────────────────────────────────────────────────────────────────────────────
Find First File (Function 4EH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 4EH
    DS:DX
        Pointer to pathname
    CX
        Attributes to match

    Return:

    Carry set:
    AX
        2        = File not found
        3        = Path not found
    18        = No more files
    Carry not set:
        No error


    Comments:

    Function 4EH searches the current or specified directory for the first
    entry that matches the specified pathname. DX must contain the offset
    (from the segment address in DS) of an ASCIZ string that specifies the
    pathname, which can contain wildcard characters. CX must contain the
    attribute to be used in searching for the file, as described in Section
    1.5.5, "File Attributes."

    If the attribute field is hidden file, system file, or subdirectory entry
    (02H, 04H, or 10H), or any combination of these values, all normal file
    entries are also searched. To search all directory entries except the
    volume label, set the attribute byte to 16H (hidden file, system file, and
    directory entry). If this function finds a directory entry that matches
    the name and attribute, it fills the current DTA as follows:

    Table 1.26
    DTA Values After Successful Find First File
    Offset   Length    Description
    ──────────────────────────────────────────────────────────────────────────
    00H      21        Reserved for subsequent Function 4FH (Find Next File)

    15H      1         Attribute found

    16H      2         Time file was last written

    18H      2         Date file was last written

    1AH      2         Low word of file size

    1CH      2         High word of file size

    1EH      13        Name and extension of the file, followed by 00H. All
                        blanks are removed; if there is an extension, it is
                        preceded by a period. (Volume labels include a period
    ──────────────────────────────────────────────────────────────────────────

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  Specified file invalid or doesn't exist
    3                  Specified path invalid or doesn't exist
    18                 No matching directory entry found
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    find_first_file  macro  path,attrib
                    mov    dx,offset path
                    mov    cx,attrib
                    mov    ah,4EH
                    int    21H
                    endm

    Example:

    The following program displays a message that specifies whether a file
    named report.asm exists in the current directory on the disk in drive B.

    yes        db      "File exists.",0DH,0AH,"$"
    no         db      "File does not exist.",0DH,0AH,"$"
    path       db      "b:report.asm",0
    buffer     db       43 dup (?)
    ;
    begin:     set_dta  buffer           ; See Function 1AH
                find_first_file  path,0   ; THIS FUNCTION
                jc       error_findfirst  ; Routine not shown
                cmp      al,12H           ; File found?
                je       not_there        ; No
                display  yes              ; See Function 09H
                jmp      return           ; All done
    not_there: display  no               ; See Function 09H


────────────────────────────────────────────────────────────────────────────
Find Next File (Function 4FH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 4FH

    Return:

    Carry set:
    AX
    18 = No more files
    Carry not set:
        No error


    Comments:

    Function 4FH searches for the next directory entry that matches the name
    and attributes specified in a previous Function 4EH (Find First File).
    The current DTA must contain the information filled in by Function 4EH
    (Find First File).

    If the function finds a matching entry, it fills the current DTA just as
    it did for Find First File (see Function 4EH (Find First File)).

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  Specified path invalid or doesn't exist
    18                 No matching directory entry found
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    find_next_file  macro
                    mov    ah,4FH
                    int    21H
                    endm

    Example:

    The following program displays the number of files contained in the
    current directory on the disk in drive B.

    message     db      "No files",0DH,0AH,"$"
    files       dw       ?
    path        db      "b:*.*",0
    buffer      db       43 dup (?)
    ;
    begin:      set_dta  buffer          ; See Function 1AH
                find_first_file  path,0  ; See Function 4EH
                jc       error_findfirst ; Routine not shown
                cmp      al,12H          ; Directory empty?
                je       all_done        ; Yes, go home
                inc      files           ; No, bump file counter
    search_dir: find_next_file           ; THIS FUNCTION
                jc       error_findnext  ; Routine not shown
                cmp      al,12H          ; Any more entries?
                je       done            ; No, go home
                inc      files           ; Yes, bump file counter
                jmp      search_dir      ; And check again
    done:       convert  files,10,message ; See end of chapter
    all_done:   display  message         ; See Function 09H


────────────────────────────────────────────────────────────────────────────
Get Verify State (Function 54H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 54H

    Return:

    AL
        0 = No verify after write
        1 = Verify after write


    Comments:

    Function 54H checks whether MS-DOS verifies write operations to disk
    files. The status returns in AL: 0 if verify is off, 1 if verify is on.

    You can set the verify status with Function 2EH (Set/Reset Verify Flag).

    Macro Definition:

    get_verify  macro
                mov    ah,54H
                int    21H
                endm

    Example:

    The following program displays the verify status:

    message   db     "Verify ","$"
    on        db     "on.",0DH,0AH,"$"
    off       db     "off.",0DH,0AH,"$"
    ;
    begin:    display message      ; See Function 09H
            get_verify           ; THIS FUNCTION
            cmp     al,0         ; Is flag off?
            jg      ver_on       ; No, it's on
            display off          ; See Function 09H
            jmp     return       ; Go home
    ver_on:   display on           ; See Function 09H


────────────────────────────────────────────────────────────────────────────
Change Directory Entry (Function 56H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 56H
    DS:DX
        Pointer to pathname
    ES:DI
        Pointer to second pathname

    Return:

    Carry set:
    AX
        2        = File not found
        3        = Path not found
        5        = Access denied
    17        = Not same device
    Carry not set:
        No error


    Comments:

    Function 56H renames a file by changing its directory entry. DX must
    contain the offset (from the segment address in DS) of an ASCIZ string
    that contains the pathname of the entry to be changed. DI must contain the
    offset (from the segment address in ES) of an ASCIZ string that contains a
    second pathname to which the first is to be changed.

    If a directory entry for the first pathname exists, it is changed to the
    second pathname.

    The directory paths need not be the same; in effect, you can move the file
    to another directory by renaming it. You cannot use this function request
    to copy a file to another drive, however; if the second pathname specifies
    a drive, the first pathname must specify or default to the same drive.

    You cannot use this function request to rename an open file, a hidden
    file, a system file, or a subdirectory, because it may corrupt your disk.
    If there is an error, the carry flag (CF) is set and the error code
    returns in AX.

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  One of files is invalid or not open

    3                  One of paths is invalid or not open

    5                  First pathname specifies a directory, second pathname
                        specifies an existing file; or second directory entry
                        could not be opened

    17                 Both files not on the same drive
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    rename_file  macro  old_path,new_path
                mov    dx,offset old_path
                push   ds
                pop    es
                mov    di,offset new_path
                mov    ah,56H
                int    21H
                endm

    Example:

    The following program prompts for the name of a file and a new name, then
    renames the file.

    prompt1   db     "Filename: $"
    prompt2   db     "New name: $"
    old_path  db      15,?,15 dup (?)
    new_path  db      15,?,15 dup (?)
    crlf      db      0DH,0AH,"$"
    ;
    begin:    display prompt1           ; See Function 09H
            get_string  15,old_path   ; See Function 0AH
            xor     bx,bx             ; To use BL as index
            mov     bl,old_path[1]    ; Get string length
            mov     old_path[bx+2],0  ; Make an ASCIZ string
            display crlf              ; See Function 09H
            display prompt2           ; See Function 09H
            get_string  15,new_path   ; See Function 0AH
            xor     bx,bx             ; To use BL as index
            mov     bl,new_path[1]    ; Get string length
            mov     new_path[bx+2],0  ; Make an ASCIZ string
            display crlf              ; See Function 09H
            rename_file old_path[2],new_path[2]; THIS FUNCTION
            jc      error_rename      ; Routine not shown




────────────────────────────────────────────────────────────────────────────
Get/Set Date/Time of File (Function 57H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 57H
    AL = Function code
        0 = Get date and time
        1 = Set date and time
    BX
        Handle
    CX (if AL = 1)
        Time to be set
    DX (if AL = 1)
        Date to be set

    Return:

    Carry set:
    AX
        1 = Invalid function
        6 = Invalid handle
    Carry not set:
    CX (if AL = 0)
        Time file last written
    DX (if AL = 0)
        Date file last written


    Comments:

    Function 57H gets or sets the time and date when a file was last written.
    To get the time and date, AL must contain 0; the time and date return in
    CX and DX. To set the time and date, AL must contain 1; CX and DX must
    contain the time and date. BX must contain the file handle. The time and
    date are in the form described in "Fields of the FCB" in Section 1.9.1.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  AL not 0 or 1
    6                  Handle in BX invalid or not open
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    get_set_date_time macro  handle,action,time,date
                    mov    bx,handle
                    mov    al,action
                    mov    cx,word ptr time
                    mov    dx,word ptr date
                    mov    ah,57H
                    int    21H
                    endm

    Example:

    The following program gets the date of a file named report.asm in the
    current directory on the disk in drive B, increments the day, increments
    the month and/or year, if necessary, and sets the new date of the file.

    month     db      31,28,31,30,31,30,31,31,30,31,30,31
    path      db     "b:report.asm",0
    handle    dw      ?
    time      db      2 dup (?)
    date      db      2 dup (?)
    ;
    begin:    open_handle  path,0         ; See Function 3DH
            mov     handle,ax           ; Save handle
            get_set_date_time handle,0,time,date ; THIS FUNCTION
            jc      error_time          ; Routine not shown
            mov     word ptr time,cx    ; Save time
            mov     word ptr date,dx    ; Save date
            convert_date  date[-24]     ; See end of chapter
            inc     dh                  ; Increment day
            xor     bx,bx               ; To use BL as index
            mov     bl,dl               ; Get month
            cmp     dh,month[bx-1]      ; Past last day?
            jle     month_ok            ; No, go home
            mov     dh,1                ; Yes, set day to 1
            inc     dl                  ; Increment month
            cmp     dl,12               ; Is it past December?
            jle     month_ok            ; No, go home
            mov     dl,1                ; Yes, set month to 1
            inc     cx                  ; Increment year
    month_ok: pack_date  date             ; See end of chapter
            get_set_date_time handle,1,time,date ; THIS FUNCTION
            jc      error_time          ; Routine not shown
            close_handle  handle        ; See Function 3EH
            jc      error_close         ; Routine not shown


────────────────────────────────────────────────────────────────────────────
Get/Set Allocation Strategy (Function 58H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 58H
    AL
        0 = Get strategy
        1 = Set strategy
    BX (AL = 1)
        0 = First fit
        1 = Best fit
        2 = Last fit

    Return:

    Carry set:
    AX
        1 = Invalid function code
    Carry not set:
    AX (AL = 0)
        0 = First fit
        1 = Best fit
        2 = Last fit


    Comments:

    Function 58H gets or sets the strategy that MS-DOS uses to allocate memory
    when a process requests it. If AL contains 0, the strategy is returned in
    AX. If AL contains 1, BX must contain the strategy. The three possible
    strategies are shown in the following table.

    Table 1.27
    Allocation Strategy
    Value      Name                 Description
    ──────────────────────────────────────────────────────────────────────────
    0          First fit            MS-DOS starts searching at the lowest
                                    available block and allocates the first
                                    block it finds (the allocated memory is
                                    the lowest available block). This is the
                                    default strategy.

    1          Best fit             MS-DOS searches each available block and
                                    allocates the smallest available block
                                    that satisfies the request.

    2          Last fit             MS-DOS starts searching at the highest
                                    available block and allocates the first
                                    block it finds (the allocated memory is
    ──────────────────────────────────────────────────────────────────────────

    You can use this function request to control how MS-DOS uses its memory
    resources.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  AL doesn't contain 0 or 1, or BX doesn't contain 0, 1,
                        or 2.
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    alloc_strat macro  code,strategy
                mov    bx,strategy
                mov    al,code
                mov    ah,58H
                int    21H
                endm

    Example:

    The following program displays the memory-allocation strategy in effect,
    then forces subsequent memory allocations to the top of memory by setting
    the strategy to last fit (code 2).

    get       equ       0
    set       equ       1
    stdout    equ       1
    last_fit  equ       2
    ;
    first     db       "First fit     ",0DH,0AH
    best      db       "Best fit      ",0DH,0AH
    last      db       "Last fit      ",0DH,0AH
    ;
    begin:    alloc_strat get               ; THIS FUNCTION
            jc         alloc_error        ; routine not shown
            mov        cl,4               ; multiply code by 16
            shl        ax,cl              ; to calculate offset
            mov        dx,offset first    ; point to first msg
            add        dx,ax              ; add to base address
            mov        bx,stdout          ; handle for write
            mov        cs,16              ; write 16 bytes
            mov        ah,40h             ; write handle
            int        21H                ; system call
    ;          jc         write_error        ; routine not shown
            alloc_strat set,last_fit      ; THIS FUNCTION
    ;          jc         alloc_error        ; routine not shown


────────────────────────────────────────────────────────────────────────────
Get Extended Error (Function 59H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 59H
    BX = 00H

    Return:

    AX
        Extended-error code
    BH
        Error class (see text)
    BL
        Suggested action (see text)
    CH
        Locus (see text)

    CL, DX, SI, DI, DS, ES destroyed


    Comments:

    Function 59H retrieves an extended-error code for the preceding system
    call. Each release of MS-DOS extends the error codes to cover new
    capabilities. These new codes are mapped to a simpler set of error codes
    based on MS-DOS Version 2.0, so that existing programs can continue to
    operate correctly. Notice that this call destroys all registers except
    CS:IP and SS:SP.

    A user-written Interrupt 24H (Critical-Error Handler Address) can use
    Function 59H to get detailed information about the error that caused the
    interrupt to be issued.

    The input BX is a version indicator that specifies for what level of error
    handling the application was written. The current level is 00H.

    The extended-error code consists of four separate codes in AX, BH, BL, and
    CH that give as much detail as possible about the error and suggest how
    the issuing program should respond.

    BH──Error Class

    BH returns a code that describes the class of error that occurred:

╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
    Class              Description
    ──────────────────────────────────────────────────────────────────────────
    1                  Out of a resource, such as storage or channels

    2                  Not an error, but a temporary situation (such as a
                        locked region in a file) that is expected to end

    3                  Authorization problem

    4                  Internal error in system software

    5                  Hardware failure

    6                  System software failure not the fault of the active
                        process (could be caused by missing or incorrect
                        configuration files, for example)

    7                  Application program error

    8                  File or item not found

    Class              Description
    ──────────────────────────────────────────────────────────────────────────

    9                  File or item of invalid format or type, or that is
                        otherwise invalid or unsuitable

    10                 Interlocked file or item

    11                 Wrong disk in drive, bad spot on disk, or other problem
                        with storage medium

    12                 Other error
    ──────────────────────────────────────────────────────────────────────────


    BL──Suggested Action

    BL returns a code that suggests how the issuing program can respond to the
    error:

    Action             Description
    ──────────────────────────────────────────────────────────────────────────
    1                  Retry, then prompt user

    2                  Retry after a Pause

    3                  If user entered data such as drive letter or filename,
                        prompt for it again

    4                  Terminate with cleanup

    5                  Terminate immediately; system so unhealthy that program
                        should exit as soon as possible without taking time to
                        close files and update indexes

    6                  Error is informational

    7                  Prompt user to perform some action, such as changing
    ──────────────────────────────────────────────────────────────────────────

    CH──Locus

    CH returns a code that provides additional information to help locate the
    area involved in the failure. This code is particularly useful for
    hardware failures (BH=5).

    Locus          Description
    ──────────────────────────────────────────────────────────────────────────
    1              Unknown
    2              Related to random-access block devices, such as a disk
                    drive
    3              Related to Network
    4              Related to serial-access character devices, such as a
                    printer
    5              Related to random-access memory
    ──────────────────────────────────────────────────────────────────────────

    Your programs should handle errors by noting the error return from the
    original system call and then issuing this system call to get the
    extended-error code. If the program does not recognize the extended-error
    code, it should respond to the original error code.

    This system call is available during Interrupt 24H and may be used to
    return network-related errors.

    Macro Definition:

    get_error  macro
                mov    ah, 59H
                int    21H
                endm

    Example:

    Since this function request provides such detailed information, a general
    example is not practical. User programs can interpret the various codes to
    determine what sort of messages or prompts should be displayed, what
    action to take, and whether or not to terminate the program if recovery
    from the errors isn't possible.


────────────────────────────────────────────────────────────────────────────
Create Temporary File (Function 5AH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 5AH
    CX
        Attribute
    DS:DX
        Pointer to pathname, followed by a
        byte of 0, and then by 13 bytes of memory

    Return:

    Carry set:
    AX
        2 = File not found
        3 = Path not found
        4 = Too many open files
        5 = Access denied
    Carry not set:
    AX
        Handle


    Comments:

    Function 5AH creates a file with a unique name. DX must contain the offset
    (from the segment address in DS) of an ASCIZ string that specifies a
    pathname and 13 bytes of memory (to hold the filename). CX must contain
    the attribute to be assigned to the file, as described in Section 1.5.5,
    "File Attributes."

    MS-DOS creates a unique filename and appends it to the pathname pointed to
    by DS:DX, creates the file and opens it in compatibility mode, then
    returns the file handle in AX. A program that needs a temporary file
    should use this function request to avoid name conflicts.

    When the creating process exits, MS-DOS does not automatically delete a
    file created with Function 5AH. When you no longer need the file, you
    should delete it. If there is an error, the carry flag (CF) is set and the
    error code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  File is invalid or doesn't exist

    3                  Directory pointed to by DS:DX is invalid or doesn't
                        exist

    4                  Too many open files (no handle available)

    5                  Access denied
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    create_temp macro  pathname,attrib
                mov    cx,attrib
                mov    dx,offset pathname
                mov    ah,5AH
                int    21H
                endm

    Example:

    The following program creates a temporary file in the directory named
    \wp\docs, copies a file named textfile.asc that is in the current
    directory into the temporary file, and then closes both files.

    stdout   equ       1
    ;
    file     db       "TEXTFILE.ASC",0
    path     db       "\WP\DOCS",0
    temp     db        13 dup (0)
    open_msg db       " opened.",0DH,0AH
    crl_msg  db       " created.",0DH,0AH
    rd_msg   db       " read into buffer.",0DH,0AH
    wr_msg   db       "Buffer written to "
    cl_msg   db       "Files closed.",0DH,0AH
    crlf     db       0DH,0AH
    handle1  dw        ?
    handle2  dw        ?
    buffer   db        512 dup (?)
    ;



    begin:   open_handle file,0            ; see Function 3DH
            jc         open_error         ; routine not shown
            mov        handle1,ax         ; save handle
            write_handle stdout,file,12   ; see Function 40H
            jc         write_error        ; routine not shown
            write_handle stdout,open_msg,10 ; see Function 40H
            jc         write_error        ; routine not shown
            create_temp path,0            ; THIS FUNCTION
            jc         create_error       ; routine not shown
            mov        handle2,ax         ; save handle
            write_handle stdout,path,8    ; see Function 40H
            jc         write_error        ; routine not shown
            display_char ""              ; see Function 02H
            write_handle stdout,temp,12   ; see Function 40H
            jc         write_error        ; routine not shown
            write_handle stdout,crl_msg,11 ; See Function 40H
            jc         write_error        ; routine not shown
            read_handle handle1,buffer,512  ; see Function 3FH
            jc        read_error          ; routine not shown
            write_handle stdout,file,12   ; see Function 40H
            jc        write_error         ; routine not shown
            write_handle stdout,rd_msg,20 ; see Function 40H
            jc        write_error         ; routine not shown
            write_handle handle2,buffer,512 ; see Function 40H
            jc        write_error         ; routine not shown
            write_handle stdout,wr_msg,18 ; see Function 40H
            jc         write_error        ; routine not shown
            write_handle stdout,temp,12   ; see Function 40H
            jc         write_error        ; routine not shown
            write_handle stdout,crlf,2    ; see Function 40H
            jc        write_error         ; routine not shown
            close_handle handle1          ; see Function 3EH
            jc        close_error         ; routine not shown
            close_handle handle2          ; see Function 3EH
            jc        close_error         ; routine not shown
            write_handle stdout,cl_msg,15 ; see Function 40H
            jc        write_error         ; routine not shown


────────────────────────────────────────────────────────────────────────────
Create New File (Function 5BH)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 5BH
    CX
        Attribute
    DS:DX
        Pointer to pathname

    Return:

    Carry set:
    AX
        2        = File not found
        3        = Path not found
        4        = Too many open files
        5        = Access denied
        80        = File already exists
    Carry not set:
    AX
        Handle


    Comments:

    Function 5BH creates a new file. DX must contain the offset (from the
    segment address in DS) of an ASCIZ string that specifies a pathname. CX
    contains the attribute to be assigned to the file, as described in Section
    1.5.5, "File Attributes."

    If there is no existing file with the same filename, MS-DOS creates the
    file, opens it in compatibility mode, and returns the file handle in AX.

    This function request fails if the specified file exists, unlike Function
    3CH (Create Handle), which, under the same circumstances, truncates the
    file to a length of 0. In a multitasking system, the existence of a file
    is used as a semaphore; you can use this system call as a test-and-set
    semaphore. If there is an error, the carry flag (CF) is set and the error
    code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    2                  File is invalid or doesn't exist

    3                  Directory pointed to by DS:DX is invalid or doesn't
                        exist

    4                  No free handles are available in the current process,
                        or internal system tables are full

    5                  Access denied

    80                 File with the same specification pointed to by DS:DX
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    create_new macro  pathname,attrib
                mov    cx, attrib
                mov    dx, offset pathname
                mov    ah, 5BH
                int    21H
                endm

    Example:

    The following program attempts to create a new file named report.asm in
    the current directory. If the file already exists, the program displays an
    error message and returns to MS-DOS. If the file doesn't exist and there
    are no other errors, the program saves the handle and continues
    processing.

    err_msg  db       "FILE ALREADY EXISTS",0DH,0AH,"$"
    path     db       "report.asm",0
    handle   dw        ?
    ;
    begin:    create_new path,0             ; THIS FUNCTION
            jnc        continue           ; further processing
            cmp        ax,80              ; file already exist?
            jne        error              ; routine not shown
            display    err_msg            ; see Function 09H
            jmp        return             ; return to MS-DOS
    continue: mov        handle,ax          ; save handle
    ;
    ;          (further processing here)


────────────────────────────────────────────────────────────────────────────
Lock (Function 5CH, Code 00H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 5CH
    AL = 00H
    BX
        Handle
    CX:DX
        Offset of region to be locked
    SI:DI
        Length of region to be locked

    Return:

    Carry set:
    AX
        1        = Invalid function code
        6        = Invalid handle
    33        = Lock violation
    36        = Sharing buffer exceeded
    Carry not set:
        No error


    Comments:

    Function 5CH, Code 00H, denies all access (read or write) by any other
    process to the specified region of the file. BX must contain the handle of
    the file that contains the region to be locked. CX:DX (a four-byte
    integer) must contain the offset in the file of the beginning of the
    region. SI:DI (a four-byte integer) must contain the length of the region.

    If another process attempts to use (read or write) a locked region, MS-DOS
    retries three times; if the retries fail, MS-DOS issues Interrupt 24H for
    the requesting process. You can change the number of retries with Function
    440BH (IOCtl Retry).

    The locked region can be anywhere in the file. For instance, locking
    beyond the end of the file is not an error. A region should be locked for
    only a brief period, so if it is locked for more than ten seconds you
    should consider it to be an error. Function 45H (Duplicate File Handle)
    and Function 46H (Force Duplicate File Handle) duplicate access to any
    locked region. Passing an open file to a child process with Function
    4B00H (Load and Execute Program) does not duplicate access to locked
    regions.

    ──────────────────────────────────────────────────────────────────────────
    Warning
    If a program closes a file that contains a locked region or terminates
    with an open file that contains a locked region, the result is
    undefined.
    ──────────────────────────────────────────────────────────────────────────

    Programs that might be terminated by Interrupt 23H (CONTROL+C Handler
    Address) or Interrupt 24H (Critical-Error-Handler Address) should trap
    these interrupts and unlock any locked regions before exiting.

    Programs should not rely on being denied access to a locked region. A
    program can determine the status of a region (locked or unlocked) by
    attempting to lock the region and examining the error code.

    If there is an error, the carry flag (CF) is set and the error code is
    returned in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  File sharing must be loaded to use this function
                        request.

    6                  The handle in BX is not a valid, open handle.

    33                 All or part of the specified region is already locked.

    36                 There is no more room for lock entries in the buffer.
                        Refer to the share command in the MS-DOS User's
                        Reference for information on allocating more lock
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    lock  macro  handle,start,bytes
        mov    bx, handle
        mov    cx, word ptr start
        mov    dx, word ptr start+2
        mov    si, word ptr bytes
        mov    di, word ptr bytes+2
        mov    al, 0
        mov    ah, 5CH
        int    21H
        endm

    Example:

    The following program opens a file named finalrpt in "Deny None" mode and
    locks two portions of it: the first 128 bytes and bytes 1024 through 5119.
    After some (unspecified) processing, it unlocks the same portions and
    closes the file.

    stdout    equ        1
    ;
    start1    dd         0
    lgth1     dd         128
    start2    dd         1023
    lgth2     dd         4096
    file      db        "FINALRPT",0
    op_msg    db        " opened.",0DH,0AH
    11_msg    db        "First 128 bytes locked.",0DH,0AH
    12_msg    db        "Bytes 1024-5119 locked.",0DH,0AH
    u1_msg    db        "First 128 bytes unlocked.",0DH,0AH
    u2_msg    db        "Bytes 1024-5119 unlocked.",0DH,0AH
    cl_msg    db        " closed.:,0DH,0AH
    handle    dw         ?
    ;
    begin:    open_handle file,01000010b     ; see Function 3DH
            jc         open_error          ; routine not shown
            write_handle stdout,file,8     ; see Function 40H
            jc         write_error         ; routine not shown
            write_handle stdout,op_msg,10  ; see Function 40H
            jc         write_error         ; routine not shown
            mov        handle,ax           ; save handle
            lock       handle,start1,lgth1 ; THIS FUNCTION
            jc         lock_error          ; routine not shown
            write_handle stdout,11_msg,25  ; see Function 40H
            jc         write_error         ; routine not shown
            lock       handle,start2,lgth2 ; THIS FUNCTION
            jc         lock_error          ; routine not shown
            write_handle stdout,12_msg,25  ; see Function 40H
            jc         write_error         ; routine not shown
    ;
    ;  ( Further processing here )
    ;
            unlock     handle,start1,lgth1 ; See Function 5C01H
            jc         unlock_error        ; routine not shown
            write_handle stdout,ul_msg,27  ; see Function 40H
            jc         write_error         ; routine not shown
            unlock     handle,start2,lgth2 ; See Function 5C01H
            jc         unlock_error        ; routine not shown
            write_handle stdout,u2_msg,27  ; See Function 40H
            jc         write_error         ; routine not shown
            close_handle handle            ; See Function 3EH
            jc         close_error         ; routine not shown
            write_handle stdout,file,8     ; see Function 40H
            jc         write_error         ; routine not shown
            write_handle stdout,cl_msg,10  ; see Function 40H
            jc         write_error         ; routine not shown


────────────────────────────────────────────────────────────────────────────
Unlock (Function 5CH, Code 01H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 5CH
    AL = 01H
    BX
        Handle
    CX:DX
        Offset of area to be unlocked
    SI:DI
        Length of area to be unlocked

    Return:

    Carry set:
    AX
        1        = Invalid function code
        6        = Invalid handle
    33        = Lock violation
    36        = Sharing buffer exceeded
    Carry not set:
        No error


    Comments:

    Function 5CH, Code 01H, unlocks a region previously locked by the same
    process. BX must contain the handle of the file that contains the region
    to be unlocked. CX:DX (a four-byte integer) must contain the offset in the
    file of the beginning of the region. SI:DI (a four-byte integer) must
    contain the length of the region. The offset and length must be exactly
    the same as the offset and length specified in the previous Function
    5C00H (Lock).

    The description of Function 5C00H (Lock) describes how to use locked
    regions. If there is an error, the carry flag (CF) is set and the error
    code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  File sharing must be loaded to use this function
                        request.

    6                  The handle in BX is not a valid, open handle.

    33                 The region specified is not identical to one that was
                        previously locked by the same process.

    36                 There is no more room for lock entries in the buffer.
                        Refer to the share command in the MS-DOS User's
                        Reference for information on allocating more lock
    ──────────────────────────────────────────────────────────────────────────

    You should issue Function 59H (Get Extended Error) to list the possible
    errors returned by this function.

    Macro Definition:

    unlock  macro  handle,start,bytes
            mov    bx, handle
            mov    cx, word ptr start
            mov    dx, word ptr start+2
            mov    si, word ptr bytes
            mov    di, word ptr bytes+2
            mov    al, 1
            mov    ah, 5CH
            int    21H
            endm

    Example:

    The following program opens a file named finalrpt in "Deny None" mode and
    locks two portions of it: the first 128 bytes and bytes 1024 through 5119.
    After some (unspecified) processing, it unlocks the same portions and
    closes the file.

    stdout    equ        1
    ;
    start1    dd         0
    lgth1     dd         128
    start2    dd         1023
    lgth2     dd         4096
    file      db        "FINALRPT",0



    op_msg    db        " opened.",0DH,0AH
    11_msg    db        "First 128 bytes locked.",0DH,0AH
    12_msg    db        "Bytes 1024-5119 locked.",0DH,0AH
    u1_msg    db        "First 128 bytes unlocked.",0DH,0AH
    u2_msg    db        "Bytes 1024-5119 unlocked.",0DH,0AH
    cl_msg    db        " closed.",0DH,0AH
    handle    dw         ?
    ;
    begin:    open_handle file,01000010b     ; see Function 3DH
            jc         open_error          ; routine not shown
            write_handle stdout,file,8     ; see Function 40H
            jc         write_error         ; routine not shown
            write_handle stdout,op_msg,10  ; see Function 40H
            jc         write_error         ; routine not shown
            mov        handle,ax           ; save handle
            lock       handle,start1,lgth1 ; See Function 5C00H
            jc         lock_error          ; routine not shown
            write_handle stdout,11_msg,25  ; see Function 40H
            jc         write_error         ; routine not shown
            lock       handle,start2,lgth2 ; See Function 5C00H
            jc         lock_error          ; routine not shown
            write_handle stdout,12_msg,25  ; see Function 40H
            jc         write_error         ; routine not shown
    ;
    ;  ( Further processing here )
    ;
            unlock     handle,start1,lgth1 ; THIS FUNCTION
            jc         unlock_error        ; routine not shown
            write_handle stdout,u1_msg,27  ; see Function 40H
            jc         write_error         ; routine not shown
            unlock     handle,start2,lgth2 ; THIS FUNCTION
            jc         unlock_error        ; routine not shown
            write_handle stdout,u2_msg,27  ; see Function 40H
            jc         write_error         ; routine not shown
            close_handle handle            ; See Function 3EH
            jc         close_error         ; routine not shown
            write_handle stdout,file,8     ; see Function 40H
            jc         write_error         ; routine not shown
            write_handle stdout,cl_msg,10  ; see Function 40H
            jc         write_error         ; routine not shown


────────────────────────────────────────────────────────────────────────────
Get Machine Name (Function 5EH, Code 00H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 5EH
    AL = 0
    DS:DX
        Pointer to 16-byte buffer

    Return:

    Carry set:
    AX
        1 = Invalid function code
    Carry not set:
    CX
        Identification number of local
        computer
    CH
        Validity of machine name:
        00H = invalid
        nonzero = valid
    CL
        NETBIOS number assigned to machine name
    DS:DX
        Segment:offset of ASCIZ machine name


    Comments:

    Function 5EH, Code 00H, retrieves the net name of the local computer. DX
    must contain the offset (to the segment address in DS) of a 16-byte
    buffer. Microsoft Networks must be running.

    MS-DOS returns the local computer name (a 16-byte ASCIZ string, padded
    with blanks) in the buffer pointed to by DS:DX. CX returns the
    identification number of the local computer. If the network was never
    installed, the CH register returns with zero and the value in the CL
    register is invalid. If there is an error, the carry flag (CF) is set and
    the error code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  Microsoft Networks must be running to use this function
                        request.
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    get_machine_name  macro  buffer
                    mov    dx,offset buffer
                    mov    al,0
                    mov    ah,5EH
                    int    21H
                    endm

    Example:

    The following program displays the name of a Microsoft Networks
    workstation.

    stdout equ 1
    ;
    msg        db   "Netname: "
    mac_name   db   16 dup (?),0DH,0AH
    ;
    begin:    get_machine_name  mac_name        ; THIS FUNCTION
            jc                name_error      ; routine not shown
            write_handle      stdout,msg,27   ; see Function 40H
            jc                write_error     ; routine not shown


────────────────────────────────────────────────────────────────────────────
Get/Set Printer Setup (Function 5EH, Codes 02H and 03H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 5EH
    AL
        02H = Set printer setup string
        03H = Get printer setup string
    BX
        Assign-list index
    If AL = 02H
        CX
            Length of setup string

        DS:SI
        Pointer to setup string
    If AL = 03H
        ES:DI = Segment: offset of 64-byte buffer
                    to receive string

    Return:

    Carry set:
    AX
        1 = Invalid function code
    Carry not set:
    CX = Length of printer setup string in bytes
            (if AL=03H)
    ES:DI = Segment:offset of ASCII printer setup
                string (if AL=03H)


    Comments:

    Function 5EH, Code 02H, defines a string of control characters that MS-DOS
    adds to the beginning of each file sent to the network printer. BX must
    contain the index into the assign list that identifies the printer (entry
    0 is the first entry). CX must contain the length of the string. SI must
    contain the offset (to the segment address in DS) of the string itself.
    Microsoft Networks must be running.

    MS-DOS adds the setup string to the beginning of each file sent to the
    printer, which is specified by the assign-list index in BX. This function
    request lets each program that shares a printer have its own printer
    configuration. You can use Function 5F02H (Get Assign-List Entry) to
    determine which entry in the assign list refers to the printer. If there
    is an error, the carry flag (CF) is set and the error code returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  Microsoft Networks must be running to use this function
                        request.
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    printer_setup  macro  index,lgth,string
                    mov    bx, index
                    mov    cx, lgth
                    mov    dx, offset string
                    mov    al, 2
                    mov    ah, 5EH
                    int    21H
                    endm

    Example:

    The following program defines a printer-setup string that consists of the
    control character to print expanded type on Epson-compatible printers. The
    printer cancels this mode at the first carriage return, so the effect is
    to print the first line of each file sent to the network printer as a
    title in expanded characters. The setup string is one character. This
    example assumes that the printer is the entry number 3 (the fourth entry)
    in the assign list. Use Function 5F02H (Get Assign-List Entry) to
    determine this value.

    setup     db   0EH
    ;
    begin:    printer_setup 3,1,setup       ; THIS FUNCTION
            jc            error           ; routine not shown


────────────────────────────────────────────────────────────────────────────
Get Assign-List Entry (Function 5FH, Code 02H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 5FH
    AL = 02H
    BX
        Assign-list index
    DS:SI
        Pointer to buffer for local name
    ES:DI
        Pointer to buffer for remote name

    Return:

    Carry set:
    AX
        1        = Invalid function code
    18        = No more files
    Carry not set:
    BL
        3 = Printer
        4 = Drive
    CX
        Stored user value


    Comments:

    Function 5FH, Code 02H, retrieves the specified entry from the network
    list of assignments. BX must contain the assign-list index (entry 0 is the
    first entry). SI must contain the offset (to the segment address in DS) of
    a 16-byte buffer for the local name. DI must contain the offset (to the
    segment address in ES) of a 128-byte buffer for the remote name. Microsoft
    Networks must be running.

    MS-DOS puts the local name in the buffer pointed to by DS:SI and the
    remote name in the buffer pointed to by ES:DI. The local name can be a
    null ASCIZ string. BL returns 3 if the local device is a printer or 4 if
    the local device is a drive. CX returns the stored user value set with
    Function 5F03H (Make Network Connection). The contents of the assign list
    can change between calls. You can use this function request to retrieve
    any entry, or to make a copy of the complete list by stepping through the
    table. To detect the end of the assign list, check for error code 18 (no
    more files), as you would when stepping through a directory by using
    Functions 4EH (Find First File) and 4FH (Find Next File).

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  Microsoft Networks must be running to use this function
                        request.

    18                 The index passed in BX is greater than the number of
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    get_list  macro  index,local,remote
            mov    bx, index
            mov    si, offset local
            mov    di, offset remote
            mov    al,2
            mov    ah, 5FH
            int    21H
            endm

    Example:

    The following program displays the assign list on a Microsoft Networks
    workstation, showing the local name, remote name, and device type (drive
    or printer) for each entry.

    stdout    equ        1
    printer   equ        3
    ;
    local_nm  db         16 dup (?),2 dup (20h)
    remote_nm db         128 dup (?),2 dup (20h)
    header    db        "Local name",8 dup (20h)
            db        "Remote name",7 dup (20h)
            db        "Device Type"
    crlf      db         0dh,0ah,0dh,0ah
    drive_msg db        "drive"
    print_msg db        "printer"
    index     dw         ?
    ;



    begin:    write_handle stdout,header,51   ; see Function 40H
            jc         write_error          ; routine not shown
            mov        index,0              ; assign list index
    ck_list:  get_list   index,local_nm,remote_nm ; THIS FUNCTION
            jnc        got_one              ; got an entry
    error:    cmp        ax,18

            je         last_one             ; yes
            jmp        error                ; routine not shown
    got_one:  push       bx                   ; save device type
            write_handle  stdout,local_nm,148 ; see Function 40H
            jc         write_error          ; routine not shown
            pop        bx                   ; get device type
            cmp        bl,printer           ; is it a printer?
            je         prntr                ; yes
            write_handle stdout,drive_msg,5 ; see Function 40H
            jc         write_error          ; routine not shown
            jmp        get_next             ; finish message
    prntr:    write_handle stdout,print_msg,7 ; see Function 40H
            jc         write_error          ; routine not shown
    get_next: write_handle stdout,crlf,2      ; see Function 40H
            jc         write_error          ; routine not shown
            inc        index                ; bump index
            jmp        ck_list              ; get next entry
    last_one: write_handle stdout,crlf,4      ; see Function 40H
            jc         write_error          ; routine not shown
    ;


────────────────────────────────────────────────────────────────────────────
Make Network Connection (Function 5FH, Code 03H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 5FH
    AL = 03H
    BL
        3 = Printer
        4 = Drive
    CX
        User value
    DS:SI
        Pointer to name of source device
    ES:DI
        Pointer to name of destination
        device

    Return:

    Carry set:
    AX
        1 = Invalid function code
        5 = Access denied
        3 = Path not found
        8 = Insufficient memory
        (Other errors particular to the
        network may occur.)
    Carry not set:
        No error


    Comments:

    Function 5FH, Code 03H, redirects a printer or disk drive (source device)
    to a network directory (destination device). BL must contain 3 if the
    source device is a printer or 4 if it is a disk drive. SI must contain the
    offset (to the segment address in DS) of an ASCIZ string that specifies
    the name of the printer, or a drive letter followed by a colon, or a null
    string (one byte of 00H). DI must contain the offset (to the segment
    address in ES) of an ASCIZ string that specifies the name of a network
    directory. CX contains a user-specified 16-bit value that MS-DOS
    maintains. Microsoft Networks must be running. The destination string must
    be an ASCIZ string of the following form:

    machine-name pathname 00H password 00H

    where:

    Machine-name is the net name of the server that contains the network
    directory.

    Pathname is the alias of the network directory (not the directory path) to
    which the source device is to be redirected.

    00H is a null byte.

    Password is the password for access to the network directory. If no
    password is specified, both null bytes must immediately follow the
    pathname.

    If BL=3, the source string must be PRN, LPT1, LPT2, or LPT3. This function
    buffers and sends all output for the named printer to the remote-printer
    spooler named in the destination string.

    If BL=4, the source string can be either a drive letter followed by a
    colon, or a null string. If the source string contains a valid drive
    letter and colon, this call redirects all subsequent drive-letter
    references to the network directory named in the destination string. If
    the source string is a null string, MS-DOS attempts to grant access to the
    network directory with the specified password.

    The maximum length of the destination string is 128 bytes. You can
    retrieve the value in CX by using Function 5F02H (Get Assign-List Entry).

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  Microsoft Networks must be running to use this function
                        request; the value in BX is not 1 to 4, the source
                        string is in the wrong format; the destination string
                        is in the wrong format; or the source device is already
                        redirected.

    3                  The network directory path is invalid or doesn't exist.

    5                  The network directory/password combination is not
                        valid. This does not mean that the password itself was
                        invalid; the directory might not exist on the server.

    8                  There is not enough memory for string substitutions.
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    redir macro  device,value,source,destination
        mov    bl, device
        mov    cx, value
        mov    si, offset source
        mov    es, seg destination
        mov    di, offset destination
        mov    al, 03H
        mov    ah, 5FH
        int    21H
        endm

    Example:

    The following program redirects two drives and a printer from a
    workstation to a server named harold. It assumes the machine name,
    directory names, and driver letters shown:

    Local drive     Netname
    or printer      on server    Password

    E:              WORD         none
    F:              COMM         fred
    PRN:            PRINTER      quick

    printer   equ  3
    drive     equ  4
    ;
    local_1   db  "e:",0
    local_2   db  "f:",0
    local_3   db  "prn",0
    remote_1  db  "\harold\word",0,0
    remote_2  db  "\harold\comm",0,"fred",0
    remote_3  db  "\harold\printer",0,"quick",0
    ;
    begin:    redir  local_1,remote_1,drive,0   ; THIS FUNCTION
            jc     error                      ; routine not shown
            redir  local_2,remote_2,drive,0   ; THIS FUNCTION
            jc     error                      ; routine not shown
            redir  local_3,remote_3,printer,0 ; THIS FUNCTION
            jc     error                      ; routine not shown


────────────────────────────────────────────────────────────────────────────
Delete Network Connection (Function 5FH, Code 04H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 5FH
    AL = 04H
    DS:SI
        Pointer to name of source device

    Return:

    Carry set:
    AX
        1        = Invalid function code
    15        = Redirection paused on server
    (Other errors particular to the network
    may occur.)
    Carry not set:
        No error


    Comments:

    Function 5FH, Code 04H, cancels the redirection of a printer or disk drive
    (source device) to a network directory (destination device) made with
    Function 5F03H (Make Network Connection). SI must contain the offset (to
    the segment address in DS) of an ASCIZ string that specifies the name of
    the printer or drive whose redirection is to be canceled. Microsoft
    Networks must be running.

    The ASCIZ string pointed to by DS:SI can contain one of three values:

    ■  The letter of a redirected drive, followed by a colon. Cancels the
        redirection and restores the drive to its physical meaning.

    ■  The name of a redirected printer (PRN, LPT1, LPT2, LPT3, or their
        machine-specific equivalents). Cancels the redirection and restores the
        printer name to its physical meaning.

    ■  A string starting with \\ (2 backslashes). Terminates the connection
        between the local machine and the network directory.

    If there is an error, the carry flag (CF) is set and the error code
    returns in AX:

    Code               Meaning
    ──────────────────────────────────────────────────────────────────────────
    1                  Microsoft Networks must be running to use this function
                        request; or the ASCIZ string names a nonexistent source
                        device.

    15                 Disk or printer redirection on the network server is
    ──────────────────────────────────────────────────────────────────────────

    Macro Definition:

    cancel_redir  macro  local
                mov    si, offset local
                mov    al, 4
                mov    ah, 5FH
                int    21H
                endm

    Example:

    The following program cancels the redirection of drives E and F and the
    printer (PRN) of a Microsoft Networks workstation. It assumes that these
    local devices were redirected previously.

    local_1   db        "e:",0
    local_2   db        "f:",0
    local_3   db        "prn",0
    ;
    begin:    cancel_redir  local_1    ; THIS FUNCTION
            jc            error      ; routine not shown
            cancel_redir  local_2    ; THIS FUNCTION
            jc            error      ; routine not shown
            cancel_redir  local_3    ; THIS FUNCTION
            jc            error      ; routine not shown


────────────────────────────────────────────────────────────────────────────
Get PSP (Function 62H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 62H

    Return:

    BX
        Segment address of the Program
        Segment Prefix of the current process


    Comments:

    Function 62H retrieves the segment address of the currently active process
    (the start of the Program Segment Prefix). The address returns in BX.

    Macro Definition:

    get_psp macro
            mov    ah, 62H
            int    21H
            endm

    Example:

    The following program displays the segment address of its Program Segment
    Prefix (PSP) in hexadecimal.

    msg       db       "PSP segment address:  H",0DH,0AH,"$"
    ;
    begin:    get_psp                       ; THIS FUNCTION
            convert   bx,16,msg[21]       ; see end of chapter
            display   msg                 ; see Function 09H


────────────────────────────────────────────────────────────────────────────
Get Extended Country Information (Function 65H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 65H
    AL
    Function (minor) code

    BX
    Code page (-1 = active CON device)
    CX
    Amount of data to return
    DX
    Country ID for which information is to be returned
    (-1=default country)
    ES:DI
    Address of country information buffer

    Return:


    Carry set:
    1 = Buffer has been filled
    2 = File not found
    Carry not set:
    ES:DI = Pointer to country information buffer


    Comments:

    Function 65H retrieves standard country information. This information
    includes country ID, code page, date and time format, currency symbol,
    separators (for thousands, decimals, data list, date and time) currency
    format flags, digits in currency, and case-mapping information. The
    function code passed in AL may be one of the following:

    Code               Description
    ──────────────────────────────────────────────────────────────────────────
    1                  Return standard information
    2                  Return pointer to uppercase table
    4                  Return pointer to filename uppercase table
    6                  Return pointer to collating table
    7                  Selects the Double Byte Character Set (DBCS)
    ──────────────────────────────────────────────────────────────────────────

    Only the information for the default country is kept in the kernel.
    Country-dependent information for all other countries is contained in the
    country.sys file. The MS-DOS nlsfunc command is used to access the
    country-dependent information in country.sys using this call. If the
    country code and code page number do not match, or if either is invalid,
    error code 2 is returned to AX. If CX is less than 5, error code 1 is
    returned. If the amount of information requested is greater than the value
    of CX, only CX bytes are returned and no error is reported.

    If AL = 1, the buffer is filled with the following information:

    db    1    ;   Information ID
    dw    ?    ;   Size (<=38)
    dw    ?    ;   Country ID
    dw    ?    ;   Code page

    If AL = 2, the buffer is filled with the following information:

    db    2    ;   Information ID
    dd    ?    ;   Double-word pointer to uppercase table

    If AL = 4, the buffer is filled with the following information:

    db    4    ;   Information ID
    dd    ?    ;   Double-word pointer to filename uppercase table

    Both of these tables consist of a length field (two bytes) followed by 128
    uppercase values for the upper 128 ASCII characters. The following formula
    is used to compute the address of an uppercase equivalent in the table:

    Address of outchar = inchar - (256-table──len) = table──start

    where:

    Parameter          Meaning
    ──────────────────────────────────────────────────────────────────────────
    inchar             Character to be generated
    table──len         Length of list of uppercase values (two bytes)
    table──start       Starting address of uppercase table
    outchar            Uppercase value for inchar
    ──────────────────────────────────────────────────────────────────────────

    If inchar is greater than or equal to (256 - table──len), then there is an
    uppercase equivalent in the table; otherwise, there is not.

    If AL = 6, the buffer is filled with the following information:

    db    6    ;   Information ID
    dd    ?    ;   Double-word pointer to collating sequence

    The table is 258 bytes long. The first word is the length of the table.
    The rest of the table is 256 ASCII values in the appropriate order.


────────────────────────────────────────────────────────────────────────────
Get/Set Global Code Page (Function 66H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 66H
    AL
    Function (minor) code
    BX
    Code page to set (AL = 2)

    Return:

    Carry set:
    AX
    02 = File not found
    65 = Device not selected
    Carry not set:
    No error


    Comments:

    Function 66H gets or sets the code page used by the kernel and all
    devices. If no other code page has been set, this function gets the
    default code page from DX. If another code page is set, this function
    retrieves the active code page from BX.

    The MS-DOS nlsfunc command and country.sys must be on the system if this
    function is to be used to change the global code page. The function code
    may be one of the following:

    Code               Description
    ──────────────────────────────────────────────────────────────────────────
    1                  Get code page
    2                  Set code page
    ──────────────────────────────────────────────────────────────────────────

    MS-DOS gets the new code page from the country.sys file. Devices must be
    prepared for code page switching before a code page can be selected. To
    prepare a device, a device driver that supports code-page switching must
    be installed by using the device command in the config.sys file. The user
    must also use the prepare keyword with the MS-DOS mode command to prepare
    the device for code-page switching.

    The code page selected must be compatible with the country code specified
    in the config.sys file. If MS-DOS cannot read country.sys or another
    specified country information file, error code 02 is returned to AX.


────────────────────────────────────────────────────────────────────────────
Set Handle Count (Function 67H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 67H
    BX
    Number of allowed handles

    Return:

    Carry set:
    AX
    Carry not set:
    No error


    Comments:

    Function 67H increases or decreases the number of files a program can have
    open at one time. The maximum number of file handles is 64K. If less than
    20 are specified, the minimum handle number, 20, is assumed. If this call
    is used to reduce the number of allowed handles, the new limit does not
    take affect until any handles above the new limit are closed.

    The user should use Function 4AH (Set Block) to allocate memory for the
    extended handle list if BX is greater than 255. The maximum number for the
    value of the config.sys command files is 255.


────────────────────────────────────────────────────────────────────────────
Commit File (Function 68H)
────────────────────────────────────────────────────────────────────────────

    Call:

    AH = 68H
    BX
    File handle

    Return:

    Carry set:
    AX = error
    Carry not set
    No error


    Comments:

    Function 68H flushes all buffered data for a file without closing it.
    Using this call is more efficient than using the traditional close-open
    sequence, and is more effective for network environments. This call makes
    sure that the disk image of a file is current.

    ;  Macro Definitions for MS-DOS System Call Examples
    ;
    ; *******************
    ;  Interrupts
    ; *******************
    ;                                 Interrupt 25H
    ABS_DISK_READ  macro  disk,buffer,num_sectors,first_sector
        mov    al,disk
        mov    bx,offset buffer
        mov    cx,num_sectors
        mov    dx,first_sector
        int    25H
        popf
        endm
    ;                                 Interrupt 26H
    ABS_DISK_WRITE  macro  disk,buffer,num_sectors,first_sector
        mov    al,disk
        mov    bx,offset buffer
        mov    cx,num_sectors
        mov    dx,first_sector
        int    26H
        popf
        endm
    ;                                 Interrupt 27H
    STAY_RESIDENT  macro  last_instruc
        mov    dx,offset last_instruc
        inc    dx
        int    27H
        endm
    ;
    ;
    ; *******************
    ;  Function Requests
    ; *******************
    ;                                 Function Request 00H
    TERMINATE_PROGRAM  macro
        xor    ah,ah
        int    21H
        endm
    ;                                 Function Request 01H
    READ_KBD_AND_ECHO  macro
        mov    ah,01H
        int    21H
        endm
    ;                                 Function Request 02H
    DISPLAY_CHAR  macro  character
        mov    dl,character
        mov    ah,02H
        int    21H
        endm
    ;                                 Function Request 03H
    AUX_INPUT    macro
        mov     ah,03H
        int     21H
        endm
    ;                                 Function Request 04H
    AUX_OUTPUT  macro
        mov    ah,04H
        int    21H
        endm
    ;                                 Function Request 05H
    PRINT_CHAR  macro  character
        mov    dl,character
        mov    ah,05H
        int    21H
        endm
    ;                                 Function Request 06H
    DIR_CONSOLE_IO  macro  switch
        mov    dl,switch
        mov    ah,06H
        int    21H
        endm
    ;                                 Function Request 07H
    DIR_CONSOLE_INPUT  macro
        mov    ah,07H
        int    21H
        endm
    ;                                 Function Request 08H
    READ_KBD  macro
        mov    ah,08H
        int    21H
        endm
    ;                                 Function Request 09H
    DISPLAY  macro  string
        mov    dx,offset string
        mov    ah,09H
        int    21H
        endm
    ;                                 Function Request 0AH
    GET_STRING  macro  limit,string
        mov    dx,offset string
        mov    string,limit
        mov    ah,0AH
        int    21H
        endm
    ;                                 Function Request 0BH
    CHECK_KBD_STATUS  macro
        mov    ah,0BH
        int    21H
        endm
    ;                                 Function Request 0CH
    FLUSH_AND_READ_KBD  macro  switch
        mov    al,switch
        mov    ah,0CH
        int    21H
        endm
    ;                                 Function Request 0DH
    RESET_DISK  macro
        mov    ah,0DH
        int    21H
        endm
    ;                                 Function Request 0EH
    SELECT_DISK  macro  disk
        mov    dl,disk[-65]
        mov    ah,0EH
        int    21H
        endm
    ;                                 Function Request 0FH
    OPEN  macro  fcb
        mov    dx,offset fcb
        mov    ah,0FH
        int    21H
        endm
    ;                                 Function Request 10H
    CLOSE  macro  fcb
        mov    dx,offset fcb
        mov    ah,10H
        int    21H
        endm
    ;                                 Function Request 11H
    SEARCH_FIRST  macro  fcb
        mov    dx,offset fcb
        mov    ah,11H
        int    21H
        endm
    ;                                 Function Request 12H
    SEARCH_NEXT  macro  fcb
        mov    dx,offset fcb
        mov    ah,12H
        int    21H
        endm
    ;                                 Function Request 13H
    DELETE  macro  fcb
        mov    dx,offset fcb
        mov    ah,13H
        int    21H
        endm
    ;                                 Function Request 14H
    READ_SEQ  macro  fcb
        mov    dx,offset fcb
        mov    ah,14H
        int    21H
        endm
    ;                                 Function Request 15H
    WRITE_SEQ  macro  fcb
        mov    dx,offset fcb
        mov    ah,15H
        int    21H
        endm
    ;                                 Function Request 16H
    CREATE  macro  fcb
        mov    dx,offset fcb
        mov    ah,16H
        int    21H
        endm
    ;                                 Function Request 17H
    RENAME  macro  fcb,newname
        mov    dx,offset fcb
        mov    ah,17H
        int    21H
        endm
    ;                                 Function Request 19H
    CURRENT_DISK  macro
        mov    ah,19H
        int    21H
        endm
    ;                                 Function Request 1AH
    SET_DTA  macro  buffer
        mov    dx,offset buffer
        mov    ah,1AH
        endm
    ;                                 Function Request 1BH
    DEF_DRIVE_DATA  macro
        mov    ah,1BH
        int    21H
        endm
    ;                                 Function Request 1CH
    DRIVE_DATA  macro  drive
        mov    dl,drive
        mov    ah,1CH
        int    21H
        endm
    ;                                 Function Request 21H
    READ_RAN  macro  fcb
        mov    dx,offset fcb
        mov    ah,21H
        int    21H
        endm
    ;                                 Function Request 22H
    WRITE_RAN  macro  fcb
        mov    dx,offset fcb
        mov    ah,22H
        int    21H
        endm
    ;                                 Function Request 23H
    FILE_SIZE  macro  fcb
        mov    dx,offset fcb
        mov    ah,23H
        int    21H
        endm
    ;                                 Function Request 24H
    SET_RELATIVE_RECORD  macro  fcb
        mov    dx,offset fcb
        mov    ah,24H
        int    21H
        endm
    ;                                 Function Request 25H
    SET_VECTOR  macro  interrupt,handler_start
        mov    al,interrupt
        mov    dx,offset handler_start
        mov    ah,25H
        int    21H
        endm
    ;                                 Function Request 26H
    CREATE_PSP  macro  seg_addr
        mov    dx,offset seg_addr
        mov    ah,26H
        int    21H
        endm
    ;                                  Function Request 27H
    RAN_BLOCK_READ  macro  fcb,count,rec_size
        mov    dx,offset fcb
        mov    cx,count
        mov    word ptr fcb[14],rec_size
        mov    ah,27H
        int    21H
        endm
    ;                                  Function Request 28H
    RAN_BLOCK_WRITE  macro  fcb,count,rec_size
        mov    dx,offset fcb
        mov    cx,count
        mov    word ptr fcb[14],rec_size
        mov    ah,28H
        int    21H
        endm
    ;                                 Function Request 29H
    PARSE  macro  string,fcb
        mov    si,offset string
        mov    di,offset fcb
        push   es
        push   ds
        pop    es
        mov    al,0FH
        mov    ah,29H
        int    21H
        pop    es
        endm
    ;                                  Function Request 2AH
    GET_DATE  macro
        mov    ah,2AH
        int    21H
        endm
    ;                                  Function Request 2BH
    SET_DATE  macro  year,month,day
        mov    cx,year
        mov    dh,month
        mov    dl,day
        mov    ah,2BH
        int    21H
        endm
    ;                                  Function Request 2CH
    GET_TIME  macro
        mov    ah,2CH
        int    21H
        endm
    ;                                  Function Request 2DH
    SET_TIME  macro  hour,minutes,seconds,hundredths
        mov    ch,hour
        mov    cl,minutes
        mov    dh,seconds
        mov    dl,hundredths
        mov    ah,2DH
        int    21H
        endm
    ;                                  Function Request 2EH
    VERIFY  macro  switch
        mov    al,switch
        mov    ah,2EH
        int    21H
        endm
    ;                                  Function Request 2FH
    GET_DTA  macro
        mov    ah,2FH
        int    21H
        endm
    ;                                  Function Request 30H
    GET_VERSION  macro
        mov    ah,30H
        int    21H
        endm
    ;                                  Function Request 31H
    KEEP_PROCESS  macro  return_code,last_byte
        mov    al,return_code
        mov    dx,offset last_byte
        mov    cl,4
        shr    dx,cl
        inc    dx
        mov    ah,31H
        int    21H
        endm
    ;                                  Function Request 33H
    CTRL_C_CK  macro  action,state
        mov    al,action
        mov    dl,state
        mov    ah,33H
        int    21H
        endm
    ;                                  Function Request 35H
    GET_VECTOR  macro  interrupt
        mov    al,interrupt
        mov    ah,35H
        int    21H
        endm
    ;                                  Function Request 36H
    GET_DISK_SPACE  macro  drive
        mov    dl,drive
        mov    ah,36H
        int    21H
        endm
    ;                                  Function Request 38H
    GET_COUNTRY  macro  country,buffer
                local  gc_01
                mov    dx,offset buffer
                mov    ax,country
                cmp    ax,0FFH
                jl     gc_01
                mov    al,0ffh
                mov    bx,country
    gc_01:       mov    ah,38H
                int    21H
                endm
    ;                                  Function Request 38H
    SET_COUNTRY  macro  country
                local  sc_01
                mov    dx,0FFFFH
                mov    ax,country
                cmp    ax,0FFH
                jl     sc_01
                mov    al,0ffh
                mov    bx,country
    sc_01:       mov    ah,38H
                int    21H
                endm
    ;                                  Function Request 39H
    MAKE_DIR  macro  path
        mov    dx,offset path
        mov    ah,39H
        int    21H
        endm
    ;                                  Function Request 3AH
    REM_DIR  macro  path
        mov    dx,offset path
        mov    ah,3AH
        int    21H
        endm
    ;                                  Function Request 3BH
    CHANGE_DIR  macro  path
        mov    dx,offset path
        mov    ah,3BH
        int    21H
        endm
    ;                                  Function Request 3CH
    CREATE_HANDLE  macro  path,attrib
        mov    dx,offset path
        mov    cx,attrib
        mov    ah,3CH
        int    21H
        endm
    ;                                  Function Request 3DH
    OPEN_HANDLE  macro  path,access
        mov    dx,offset path
        mov    al,access
        mov     ah,3DH
        int    21H
        endm
    ;                                  Function Request 3EH
    CLOSE_HANDLE  macro  handle
        mov    bx,handle
        mov    ah,3EH
        int    21H
        endm
    ;                                  Function Request 3FH
    READ_HANDLE  macro  handle,buffer,bytes
        mov    bx,handle
        mov    dx,offset buffer
        mov    cx,bytes
        mov    ah,3FH
        int    21H
        endm
    ;                                  Function Request 40H
    WRITE_HANDLE  macro  handle,buffer,bytes
        mov    bx,handle
        mov    dx,offset buffer
        mov    cx,bytes
        mov    ah,40H
        int    21H
        endm
    ;                                  Function Request 41H
    DELETE_ENTRY  macro  path
        mov    dx,offset path
        mov    ah,41H
        int    21H
        endm
    ;                                  Function Request 42H
    MOVE_PTR  macro  handle,high,low,method
        mov    bx,handle
        mov    cx,high
        mov    dx,low
        mov    al,method
        mov    ah,42H
        int    21H
        endm
    ;                                  Function Request 43H
    CHANGE_MODE  macro  path,action,attrib
        mov    dx,offset path
        mov    al,action
        mov    cx,attrib
        mov    ah,43H
        int    21H
        endm
    ;                                  Function Request 4400H,01H
    IOCTL_DATA  macro  code,handle
        mov    bx,handle
        mov    al,code
        mov    ah,44H
        int    21H
        endm
    ;                                  Function Request 4402H,03H
    IOCTL_CHAR  macro  code,handle,buffer
        mov    bx,handle
        mov    dx,offset buffer
        mov    al,code
        mov    ah,44H
        int    21H
        endm
    ;                                  Function Request 4404H,05H
    IOCTL_STATUS  macro  code,drive,buffer
        mov    bl,drive
        mov    dx,offset buffer
        mov    al,code
        mov    ah,44H
        int    21H
        endm
    ;                                  Function Request 4406H,07H
    IOCTL_STATUS macro  code,handle
        mov    bx,handle
        mov    al,code
        mov    ah,44H
        int    21H
        endm
    ;                                  Function Request 4408H
    IOCTL_CHANGE  macro  drive
        mov    bl,drive
        mov    al,08H
        mov    ah,44H
        int    21H
        endm
    ;                                  Function Request 4409H
    IOCTL_RBLOCK  macro  drive
        mov    bl,drive
        mov    al,09H
        mov    ah,44H
        int    21H
        endm
    ;                                  Function Request 440AH
    IOCTL_RHANDLE  macro  handle
        mov    bx,handle
        mov    al,0AH
        mov    ah,44H
        int    21H
        endm
    ;                                  Function Request 440BH
    IOCTL_RETRY  macro  retries,wait
        mov    dx,retries
        mov    cx,wait
        mov    al,0BH
        mov    ah,44H
        int    21H
        endm
    ;                                  Function Request 440CH
    GENERIC_IOCTL_HANDLES macro handle,function,category,buffer
        mov    ch,05H
        mov    cl,function
        mov    dx,offset buffer
        mov    bx,handle
        mov    ah,44H
        mov    al,0CH
        int    21H
        endm
    ;                                  Function Request 440DH
    GENERIC_IOCTL_BLOCK macro drive_num,function,category,parm_blk
        mov    ch,08H
        mov    cl,function
        mov    dx,offset parm_blk - 1
        mov    bx,drive_num
        mov    ah,44H
        mov    al,0DH
        int    21H
        endm
    ;                                  Function Request 440EH
    IOCTL_GET_DRIVE_MAP macro   logical_drv
        mov    bx,logical_drv
        mov    ah,44H
        mov    al,0EH
        int    21H
        endm
    ;                                  Function Request 440FH
    IOCTL_SET_DRIVE_MAP macro   logical_drv
        mov    bx,logical_drv
        mov    ah,44H
        mov    al,0FH
        int    21H
        endm
    ;                                  Function Request 45H
    XDUP  macro  handle
        mov    bx,handle
        mov    ah,45H
        int    21H
        endm
    ;                                  Function Request 46H
    XDUP2  macro  handle1,handle2
        mov    bx,handle1
        mov    cx,handle2
        mov    ah,46H
        int    21H
        endm
    ;                                  Function Request 47H
    GET_DIR  macro  drive,buffer
        mov    dl,drive
        mov    si,offset buffer
        mov    ah,47H
        int    21H
        endm
    ;                                  Function Request 48H
    ALLOCATE_MEMORY  macro  bytes
        mov    bx,bytes
        mov    cl,4
        shr          bx,cl
        inc    bx
        mov    ah,48H
        int    21H
        endm
    ;                                  Function Request 49H
    FREE_MEMORY  macro  seg_addr
        mov    ax,seg_addr
        mov    es,ax
        mov    ah,49H
        int    21H
        endm
    ;                                  Function Request 4AH
    SET_BLOCK  macro  last_byte
        mov    bx,offset last_byte
        mov    cl,4
        shr    bx,cl
        add    bx,17
        mov    ah,4AH
        int    21H
        mov     ax,bx
        shl     ax,cl
        dec     ax
        mov     sp,ax
        mov     bp,sp
        endm
    ;                                  Function Request 4B00H
    EXEC macro  path,command,parms
        mov    dx,offset path
        mov    bx,offset parms
        mov    word ptr parms[02h],offset command
        mov    word ptr parms[04h],cs
        mov    word ptr parms[06h],5ch
        mov    word ptr parms[08h],es
        mov    word ptr parms[0ah],6ch
        mov    word ptr parms[0ch],es
        mov    al,0
        mov    ah,4BH
        int    21H
        endm
    ;                                  Function Request 4B03H
    EXEC_OVL  macro  path,parms,seg_addr
        mov    dx,offset path
        mov    bx,offset parms
        mov    parms,seg_addr
        mov    parms[02H],seg_addr
        mov    al,3
        mov    ah,4BH
        int    21H
        endm
    ;                                  Function Request 4CH
    END_PROCESS  macro  return_code
                mov    al,return_code
                mov    ah,4CH
                int    21H
                endm
    ;                                  Function Request 4DH
    RET_CODE macro
        mov    ah,4DH
        int    21H
        endm
    ;                                  Function Request 4EH
    FIND_FIRST_FILE  macro  path,attrib
        mov    dx,offset path
        mov    cx,attrib
        mov    ah,4EH
        int    21H
        endm
    ;                                  Function Request 4FH
    FIND_NEXT_FILE  macro
        mov    ah,4FH
        int    21H
        endm
    ;                                  Function Request 54H
    GET_VERIFY  macro
        mov    ah,54H
        int    21H
        endm
    ;                                  Function Request 56H
    RENAME_FILE  macro  old_path,new_path
        mov    dx,offset old_path
        push   ds
        pop    es
        mov    di,offset new_path
        mov    ah,56H
        int    21H
        endm
    ;                                  Function Request 57H
    GET_SET_DATE_TIME  macro  handle,action,time,date
        mov     bx,handle
        mov    al,action
        mov    cx,word ptr time
        mov    dx,word ptr date
        mov    ah,57H
        int    21H
        endm
    ;                                  Function Request 58H
    ALLOC_STRAT  macro  code,strategy
        mov    bx,strategy
        mov    al,code
        mov    ah,58H
        int    21H
        endm
    ;                                  Function Request 59H
    GET_ERROR  macro
        mov    ah,59
        int    21H
        endm
    ;                                  Function Request 5AH
    CREATE_TEMP  macro  pathname,attrib
                mov    cx,attrib
                mov    dx,offset pathname
                mov    ah,5AH
                int    21H
                endm
    ;                                  Function Request 5BH
    CREATE_NEW  macro  pathname,attrib
                mov    cx,attrib
                mov    dx,offset pathname
                mov    ah,5BH
                int    21H
                endm
    ;                                  Function Request 5C00H
    LOCK        macro  handle,start,bytes
                mov    bx,handle
                mov    cx,word ptr start
                mov    dx,word ptr start+2
                mov    si,word ptr bytes
                mov    di,word ptr bytes+2
                mov    al,0
                mov    ah,5CH
                int    21H
                endm
    ;                                  Function Request 5C01H
    UNLOCK      macro  handle,start,bytes
                mov    bx,handle
                mov    cx,word ptr start
                mov    dx,word ptr start+2
                mov    si,word ptr bytes
                mov    di,word ptr bytes+2
                mov    al,1
                mov    ah,5CH
                int    21H
                endm
    ;                                  Function Request 5E00H
    GET_MACHINE_NAME  macro  buffer
                mov    dx,offset buffer
                mov    al,0
                mov    ah,5EH
                int    21H
                endm
    ;                                  Function Request 5E02H
    PRINTER_SETUP  macro  index,lgth,string
                mov    bx,index
                mov    cx,lgth
                mov    dx,offset string
                mov    al,2
                mov    ah,5EH
                int    21H
                endm
    ;                                  Function Request 5F02H
    GET_LIST    macro  index,local,remote
                mov    bx,index
                mov    si,offset local
                mov    di,offset remote
                mov    al,2
                mov    ah,5FH
                int    21H
                endm
    ;                                  Function Request 5F03H
    REDIR       macro  device,value,source,destination
                mov    bl,device
                mov    cx,value
                mov    si,offset source
                mov    es,seg destination
                mov    di,offset destination
                mov    al,03H
                mov    ah,5FH
                int    21H
                endm
    ;                                  Function Request 5F04H
    CANCEL_REDIR  macro  local
                mov    si,offset local
                mov    al,4
                mov    ah,5FH
                int    21H
                endm
    ;                                  Function Request 62H
    GET_PSP     macro
                mov    ah,62H
                int    21H
                endm
    ;
    ;
    ; *******************
    ;  General
    ; *******************
    ;
    DISPLAY_ASCIZ  macro  asciz_string
        local  search,found_it
        mov    bx,offset asciz_string

    search:
        cmp    byte ptr [bx],0
        je     found_it
        inc    bx
        jmp short search

    found_it:
        mov    byte ptr [bx],"$"
        display asciz_string
        mov    byte ptr [bx],0
        display_char 0DH
        display_char 0AH
        endm
    ;
    MOVE_STRING  macro  source,destination,count
        push   es
        push   ds
        pop    es
        assume es:code
        mov    si,offset source
        mov    di,offset destination
        mov    cx,count
    rep movs   es:destination,source
        assume es:nothing
        pop    es
        endm
    ;
    CONVERT  macro  value,base,destination
        local  table,start
        jmp    start
    table  db   "0123456789ABCDEF"

    start:
        push   ax
        push   bx
        push   dx
        mov    al,value
        xor    ah,ah
        xor    bx,bx
        div    base
        mov    bl,al
        mov    al,cs:table[bx]
        mov    destination,al
        mov    bl,ah
        mov    al,cs:table[bx]
        mov    destination[1],al
        pop    dx
        pop    bx
        pop    ax
        endm
    ;
    CONVERT_TO_BINARY  macro  string,number,value
        local  ten,start,calc,mult,no_mult
        jmp    start
    ten  db     10

    start:
        mov    value,0
        xor    cx,cx
        mov    cl,number
        xor    si,si

    calc:
        xor    ax,ax
        mov    al,string[si]
        sub    al,48
        cmp    cx,2
        jl     no_mult
        push   cx
        dec    cx

    mult:
        mul    cs:ten
        loop   mult
        pop    cx

    no_mult:
        add    value,ax
        inc    si
        loop   calc
        endm
    ;
    CONVERT_DATE  macro  dir_entry
        mov    dx,word ptr dir_entry[24]
        mov    cl,5
        shr    dl,cl
        mov    dh,dir_entry[24]
        and    dh,1FH
        xor    cx,cx
        mov    cl,dir_entry[25]
        shr    cl,1
        add    cx,1980
        endm
    ;
    PACK_DATE  macro  date
        local set_bit
    ;
    ;  On entry: DH=day, DL=month, CX=(year-1980)
    ;
        sub   cx,1980
        push  cx
        mov   date,dh
        mov   cl,5
        shl   dl,cl
        pop   cx
        jnc   set_bit
        or    cl,80h

    set_bit:
        or    date,dl
        rol   cl,1
        mov   date[1],cl
        endm
    ;






────────────────────────────────────────────────────────────────────────────
Chapter 2  MS-DOS Device Drivers

    2.1   Introduction
    2.2   Format of a Device Driver
    2.3   How to Create a Device Driver
            2.3.1   Device Strategy Routine
            2.3.2   Device Interrupt Routine
    2.4   Installing Device Drivers
    2.5   Device Headers
            2.5.1   Pointer to Next Device Field
            2.5.2   Attribute Field
            2.5.3   Strategy and Interrupt Routines
            2.5.4   Name Field
    2.6   Request Header
            2.6.1   Length of Record
            2.6.2   Unit Code Field
            2.6.3   Command Code Field
            2.6.4   Status Field
    2.7   Device Driver Functions
            2.7.1   The Init Function
            2.7.2   The Media Check Function
            2.7.3   The Build BPB Function
            2.7.4   The Read or Write Function
            2.7.5   The Non-destructive Read, No Wait Function
            2.7.6   The Open or Close Function
            2.7.7   The Removable Media Function
            2.7.8   The Status Function
            2.7.9   The Flush Function
            2.7.10  The Generic IOCtl Function
            2.7.11  The Get/Set Logical Drive Map Function
    2.8   The Media Descriptor Byte
    2.9   Format of a Media Descriptor Table
    2.10  The CLOCK Device
    2.11  Anatomy of a Device Call
    2.12  Two Sample Device Drivers


2.1  Introduction

    The io.sys file comprises the "resident" device drivers, which form the
    MS-DOS BIOS. These drivers are called upon by MS-DOS to handle
    input/output (I/O) requests initiated by application programs.

    One of the most powerful features of MS-DOS is the ability to add new
    devices such as printers, plotters, and mouse input devices without
    rewriting the BIOS. The MS-DOS BIOS is configurable; that is, new drivers
    can be added and existing drivers can be preempted. Nonresident, or
    installable, device drivers may be easily added at boot time by including
    a device command line in the config.sys file.

    At boot time, a minimum of five resident device drivers must be present.
    These drivers are in a linked list: the header of each one contains a
    DWORD pointer to the next. The last driver in the chain has an end-of-list
    marker of -1, -1 (all bits on).

    Each driver in the chain has two entry points: the strategy entry point
    and the interrupt entry point. MS-DOS does not take advantage of the two
    entry points: it calls the strategy routine, then immediately calls the
    interrupt routine.

    The dual entry points will accomodate future multitasking versions of
    MS-DOS. In multitasking environments, I/O must be asynchronous; to
    accomplish this, the strategy routine will be called to (internally) queue
    a request and return quickly. It is then the responsibility of the
    interrupt routine to perform the I/O at interrupt time by getting requests
    from the internal queue and processing them. When a request is completed,
    it is flagged as "done" by the interrupt routine. MS-DOS periodically
    scans the list of requests looking for those that are flagged as done, and
    "wakes up" the process waiting for the completion of the request.

    When requests are queued in this manner, it is no longer sufficient to
    pass I/O information in registers, since many requests may be pending at
    any time. Therefore, the MS-DOS device interface uses "packets" to pass
    request information. These request packets vary in size and format, and
    are composed of two parts:

    1. The static request header section, which has the same format for all
        requests

    2. A section which has information specific to the type of request

    A driver is called with a pointer to a packet. In multitasking versions,
    this packet will be linked into a global chain of all pending I/O requests
    maintained by MS-DOS.

    MS-DOS does not implement a global or local queue. Only one request is
    pending at any one time. The strategy routine must store the address of
    the packet at a fixed location, and the interrupt routine, which is called
    immediately after the strategy routine, should process the packet by
    completing the request and returning. It is assumed that the request is
    completed when the interrupt routine returns.

    To make a device driver that sysinit can install, a .bin (core image) or
    .exe format file must be created with the device driver header at the
    beginning of the file. The link field should be initialized to -1 (sysinit
    fills it in). Device drivers which are part of the BIOS should have their
    headers point to the next device in the list and the last header should be
    initialized to -1,-1. The BIOS must be a .bin (core image) format file.

    The .exe format installable device drivers may be used in non-IBM versions
    of MS-DOS. On the IBM Personal Computer, the .exe loader is located in
    command.com, which is not present at the time that installable devices are
    being loaded.


2.2  Format of a Device Driver

    A device driver is a program segment responsible for communication between
    DOS and the system hardware. It has a special header at the beginning
    identifying it as a device driver, defining entry points, and describing
    various attributes of the device.

    ──────────────────────────────────────────────────────────────────────────
    Note
    For device drivers, the file must not use the ORG 100H (like .com
    files). Because it does not use the Program Segment Prefix (PSP), the
    device driver is simply loaded; therefore, the file must have an origin
    of zero (ORG 0 or no ORG statement).
    ──────────────────────────────────────────────────────────────────────────

    There are two kinds of device drivers:

    ■  Character device drivers

    ■  Block device drivers

    Character devices perform serial character I/O. Examples are the console,
    communications port, and printer. These devices are named (i.e., CON, AUX,
    CLOCK, etc.), and programs may open channels (handles or file control
    blocks) to do I/O to them.

    Block devices include all the disk drives on the system. They can perform
    random I/O in structured pieces called blocks (usually the physical sector
    size). These devices are not named as the character devices are and,
    therefore, cannot be opened directly. Instead they have unit numbers and
    are identified by drive letters such as A, B, and C.

    A single block device driver may be responsible for one or more logically
    contiguous disk drives. For example, block device driver ALPHA may be
    responsible for drives A, B, C, and D. This means that it has four units
    defined (0-3) and, therefore, takes up four drive letters. The position of
    the driver in the list of all drivers determines which units correspond to
    which driver letters. If driver ALPHA is the first block driver in the
    device list, and it defines four units (0-3), then they will be A, B, C,
    and D. If BETA is the second block driver and defines three units (0-2),
    then they will be E, F, and G, and so on. The theoretical limit is 63, but
    it should be noted that the device installation code will not allow the
    installation of a device if it would result in a drive letter greater than
    Z (5AH). All block device drivers present in the standard resident BIOS
    will be placed ahead of installable block device drivers in the list.

    ──────────────────────────────────────────────────────────────────────────
    Note
    Because they have only one name, character devices cannot define
    multiple units.
    ──────────────────────────────────────────────────────────────────────────


2.3  How to Create a Device Driver

    To create a device driver that MS-DOS can install, you must create a
    binary file (.com or .exe format) with a device header at the beginning of
    the file. Notice that for device drivers, the code should not be
    originated at 100H, but at 0. The device header contains a link field (a
    pointer to the next device header) that should be -1, unless there is more
    than one device driver in the file. The attribute field and entry points
    must be set correctly.

    If it is a character device, the name field should be filled in with the
    name of that character device. The name can be any legal eight-character
    filename. If the name is less than eight characters, it should be padded
    out to eight characters with spaces (20H). Notice that device names do not
    include colons (:). The fact that CON is the same as CON: is a property of
    the default MS-DOS command interpreter (command.com) and not of the device
    driver or the MS-DOS interface. All character device names are handled in
    this way.

    MS-DOS always processes installable device drivers before handling the
    default devices; so, to install a new CON device, simply name the device
    CON. Remember to set the standard input device and standard output device
    bits in the attribute word on a new CON device. The scan of the device
    list stops on the first match, so the installable device driver takes
    precedence.

    It is not possible to replace the resident disk block device driver with
    an installable device driver the same way you can replace the other device
    drivers in the BIOS. Block drivers can be used only for devices not
    directly supported by the default disk drivers in AE@%
    database installed. As an example, if your SQL Server is named sql_svr,
    e
    Note
    Because MS-DOS can install the driver anywhere in memory, care must be
    taken when making far memory references. You should not expect that your
    driver will always be loaded in the same place every time.
    ──────────────────────────────────────────────────────────────────────────

2.3.1  Device Strategy Routine

    The device strategy routine, which is called by MS-DOS for each device
    driver service request, is primarily responsible for queuing these
    requests in the order in which they are to be processed by the device
    interrupt routine. Such queuing can be a very important performance
    feature in a multitasking environment, or where asynchronous I/O is
    supported. As MS-DOS does not currently support these facilities, only one
    request can be serviced at a time, and this routine is usually very short.
    In the coding examples in Section 2.12, "Two Sample Device Drivers," each
    request is simply stored in a single pointer area.

2.3.2  Device Interrupt Routine

    The device interrupt routine contains the code necessary to process the
    service request. It may interface to the hardware, or it may use ROM BIOS
    calls. It usually consists of a series of procedures that handle the
    specific command codes to be supported as well as some exit and
    error-handling routines. See the coding examples in Section 2.12, "Two
    Sample Device Drivers."


2.4  Installing Device Drivers

    MS-DOS allows new device drivers to be installed dynamically at boot time.
    This is accomplished by initialization code in the io.sys file that reads
    and processes the config.sys file.

    MS-DOS calls upon the device drivers to perform their function in the
    following manner:

    1. MS-DOS makes a FAR call to the strategy entry.

    2. MS-DOS passes device driver information in a request header to the
        strategy routine.

    3. MS-DOS makes a FAR call to the interrupt entry.

    This calling structure is designed to be easily upgraded to support any
    future multitasking environment.


2.5  Device Headers

    A device header is required at the beginning of a device driver. A device
    header looks like this:

    ┌──────────────────────────────────────┐
    │ DWORD Pointer to next device         │
    │ (Usually set to -1 if this driver    │
    │ is the last or only driver in the    │
    │ file)                                │
    ├──────────────────────────────────────┤
    │ WORD Attributes                      │
    ├──────────────────────────────────────┤
    │ WORD Pointer to device strategy      │
    │      entry point                     │
    ├──────────────────────────────────────┤
    │ WORD Pointer to device interrupt     │
    │      entry point                     │
    ├──────────────────────────────────────┤
    │ 8-BYTE Character device name field   │
    │ Character devices set a device name. │
    │ For block devices the first byte is  │
    │ the number of units.                 │
    └──────────────────────────────────────┘

    Figure 2.1  Sample Device Header

    Notice that the device entry points are words. They must be offsets from
    the same segment number used to point to this table. For example, if
    xxx:yyy points to the start of this table, then xxx:strategy and
    xxx:interrupt are the entry points.

    The device header fields are described in the following section.

2.5.1  Pointer to Next Device Field

    The pointer to the next device header field is a double-word field (offset
    followed by segment) that is set by MS-DOS to point at the next driver in
    the system list at the time the device driver is loaded. It is important
    that this field be set to -1 prior to load (when it is on the disk as a
    file) unless there is more than one device driver in the file. If there is
    more than one driver in the file, the first word of the double-word
    pointer should be the offset of the next driver's device header.

    ──────────────────────────────────────────────────────────────────────────
    Note
    If there is more than one device driver in the file, the last driver in
    the file must have the pointer to the next device header field set to
    -1.
    ──────────────────────────────────────────────────────────────────────────

2.5.2  Attribute Field

    The attribute field is used to identify the type of device for which this
    driver is responsible. In addition to distinguishing between block and
    character devices, these bits are used to give selected character devices
    special treatment. (Notice that if a bit in the attribute word is defined
    only for one type of device, a driver for the other type of device must
    set that bit to 0.)

    Table 2.1
    For Character Devices:
    Bit        Value                Meaning
    ──────────────────────────────────────────────────────────────────────────
    0          1                    Device is console input (sti) device
    1          1                    Device is console output (sto) device
    2          1                    Device is nul device
    3          1                    Device is clock device
    4-5                             Reserved (must be 0)
    6          1                    Device supports 3.2 functions
    7-10                            Reserved (must be 0)
    11         1                    Device understands Open/Close
    12                              Reserved (must be 0)
    13         1                    Device supports Output Until Busy (OUB)
    14         1                    Device supports IOCtl control strings
    15         1                    Character device
    ──────────────────────────────────────────────────────────────────────────

    Table 2.2
    For Block Devices:
    Bit        Value                Meaning
    ──────────────────────────────────────────────────────────────────────────
    0                               Reserved (must be 0)
    1          1                    Device supports 32-bit sector addressing
    2-5                             Reserved (must be 0)
    6          1                    Device supports 3.2 functions and Generic
                                    IOCtl function calls
    7-10                            Reserved (must be 0)
    11         1                    Device understands Open/Close/Removable
                                    Media
    12                              Reserved (must be 0)
    13         1                    Device determines the media by examining
                                    the FATID byte
    14         1                    Device supports IOCtl control strings
    15         0                    Block device
    ──────────────────────────────────────────────────────────────────────────

    For example, assume that you have a new device driver that you want to use
    as the standard input and output. In addition to installing the driver,
    you must tell MS-DOS that you want the new driver to override the current
    standard input and standard output (the CON device). This is accomplished
    by setting the attributes to the desired characteristics, so you would set
    bits 0 and 1 to 1 (notice that they are separate). Similarly, a new CLOCK
    device could be installed by setting that attribute. (Refer to Section
    2.10, "The CLOCK Device," in this chapter for more information.) Although
    there is a NUL device attribute, the NUL device cannot be reassigned. This
    attribute exists so that MS-DOS can determine if the NUL device is being
    used.

    Bit 13 for block devices affects the operation of the Build BPB (BIOS
    Parameter Block) device call. If set, it requires the first sector of the
    FAT always to reside in the same place. This bit has a different meaning
    on character devices. It indicates that the device implements the Output
    Until Busy device call.

    The IOCtl bit (bit 14) has meaning on character and block devices. The
    IOCtl functions allow data to be sent and received by the device for its
    own use (to set baud rate, stop bits, form length, etc.) instead of
    passing data over the device channel as a normal read or write does. The
    interpretation of the passed information is up to the device but it must
    not be treated as normal I/O. This bit tells MS-DOS whether the device can
    handle control strings by using the IOCtl system call, Function 44H.

    If a driver cannot process control strings, it should initially set this
    bit to 0. This tells MS-DOS to return an error if an attempt is made (via
    Function 44H) to send or receive control strings to this device. A device
    which can process control strings should initialize the IOCtl bit to 1.
    For drivers of this type, MS-DOS will make calls to the IOCtl input and
    output device functions to send and receive IOCtl strings. The IOCtl
    functions allow data to be sent and received by the device for its own use
    (for example, to set baud rate, stop bits, and form length), instead of
    passing data over the device channel as does a normal read or write. The
    interpretation of the passed information is up to the device, but it must
    not be treated as a normal I/O request.

    The Open/Close/Removable Media bit (bit 11) signals to MS-DOS 3.x and
    later versions whether this driver supports additional MS-DOS 3.x
    functionality. To support these old drivers, it is necessary to detect
    them. This bit was reserved in MS-DOS 2.x, and is 0. All new devices
    should support the Open, Close, and Removable Media calls and set this bit
    to 1. Since MS-DOS 2.x never makes these calls, the driver will be
    backward-compatible.

    The MS-DOS 3.2 bit (bit 6) signals whether the device supports logical
    drive mapping via Function 440EH (Get Logical Drive Map) and Function
    440FH (Set Logical Drive Map). This bit also supports generic IOCtl
    functions via Function 440CH (Generic IOCtl for Handles) and Function
    440DH (Generic IOCtl for Block Devices).

    Bit 1 for block devices indicates the driver's ability to manipulate
    32-bit sector addresses. If bit 1 = 1, 32-bit sector addressing is
    supported. If bit 1 is set, the sector number field of all requests is a
    DWORD added to the end of the request header. The old WORD length sector
    number should be -1.

    The driver requests affected are:

    ■  BUILD BPD command 2

    ■  INPUT/OUTPUT command 3 ,4, 8, 9, and 12

    ■  GENERIC IOCTL command 19

    If bit 1 for block devices is 0, only 16-bit sector addressing is
    available.

    15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0
    ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
    │ C │ I │   │   │ O │   │   │   │   │ 3 │   │   │ C │ N │ S │ S │
    │ H │ O │   │   │ P │   │   │   │   │ . │   │   │ L │ U │ T │ T │
    │ R │ C │   │   │ N │   │   │   │   │ 2 │   │   │ K │ L │ O │ I │
    └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘

    Figure 2.2  Attribute Word for Character Devices



    15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0
    ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
    │   │ I │ F │   │ O │   │   │   │   │ 3 │   │   │   │   │ > │   │
    │   │ O │ A │   │ P │   │   │   │   │ . │   │   │   │   │ 3 │   │
    │   │ C │ T │   │ N │   │   │   │   │ 2 │   │   │   │   │ 2 │   │
    └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘

    Figure 2.3  Attribute Word for Block Devices

2.5.3  Strategy and Interrupt Routines

    These two fields are the pointers to the entry points of the strategy and
    interrupt routines. They are word values, so they must be in the same
    segment as the device header.

2.5.4  Name Field

    This is an eight-byte field that contains the name of a character device
    or the number of units of a block device. If the field refers to a block
    device, the number of units can be put in the first byte. This is
    optional, because MS-DOS will fill in this location with the value
    returned by the driver's Init code. For more information, see Section
    2.4, "Installing Device Drivers."


2.6  Request Header

    When MS-DOS calls a device driver to perform a function, it passes a
    request header in ES:BX to the strategy entry point. This is a fixed
    length header, followed by data pertinent to the operation being
    performed. Notice that it is the device driver's responsibility to
    preserve the machine state (for example, save all registers, including
    flags, on entry, and restore them on exit). There is enough room on the
    stack when the strategy or interrupt routine is called to do about 20
    pushes. If more room on the stack is needed, the driver should set up its
    own stack.

    The following figure illustrates a request header.

    REQUEST HEADER ->
    ┌─────────────────────────────┐
    │ BYTE Length of record       │
    │  Length in bytes of this    │
    │  request header             │
    ├─────────────────────────────┤
    │ BYTE Unit code              │
    │  The subunit the operation  │
    │  is for (minor device)      │
    │  (no meaning on character   │
    │   devices)                  │
    ├─────────────────────────────┤
    │ BYTE Command code           │
    ├─────────────────────────────┤
    │ WORD Status                 │
    ├─────────────────────────────┤
    │ 8 BYTES Reserved            │
    │                             │
    └─────────────────────────────┘

    Figure 2.4  Request Header

    The request header fields are described below.

2.6.1  Length of Record

    This field contains the length (in bytes) of the request header.

2.6.2  Unit Code Field

    The unit code field identifies which unit in your device driver the
    request is for. For example, if your device driver has three units
    defined, then the possible values of the unit code field would be 0, 1,
    and 2.

2.6.3  Command Code Field

    The command code field in the request header can have the following
    values:

╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
    Code               Function
    ──────────────────────────────────────────────────────────────────────────
    0                  Init
    1                  Media Check (Block devices only)
    2                  Build BPB (Block devices only)
    3                  IOCtl Input (Only called if device has IOCtl)
    4                  Input (Read)
    5                  Non-destructive Read, No Wait (Character devices only)
    6                  Input Status (Character devices only)
    7                  Input Flush (Character devices only)
    8                  Output (Write)
    9                  Output (Write) with Verify
    10                 Output Status (Character devices only)
    11                 Output Flush (Character devices only)
    Code               Function
    ──────────────────────────────────────────────────────────────────────────
    11                 Output Flush (Character devices only)
    12                 IOCtl Output (Only called if device has IOCtl)
    13                 Device Open (Only called if Open/Close/Removable Media
                        bit set)
    14                 Device Close (Only called if Open/Close/Removable Media
                        bit set)
    15                 Removable Media (Only called if Open/Close/Removable
                        Media bit set and device is block)
    16                 Output Until Busy (Only called if bit 13 is set on
                        character devices)
    19                 Generic IOCtl Request
    23                 Get Logical Device
    24                 Set Logical Device
    ──────────────────────────────────────────────────────────────────────────


2.6.4  Status Field

    The following figure illustrates the status field in the request header.

    15  14 13 12 11 10  9   8   7  6  5  4  3  2  1  0
    ┌───┬───────────────┬───┬───┬───────────────────────┐
    │ E │               │ B │ D │                       │
    │ R │   Reserved    │ U │ O │ Error code (bit 15 on)│
    │ R │               │ S │ N │                       │
    │   │               │ Y │ E │                       │
    └───┴───────────────┴───┴───┴───────────────────────┘

    Figure 2.5  Status Field

    The status word is zero on entry and is set by the driver interrupt
    routine on return.

    Bit 8 is the done bit. When set, it means the operation has completed. The
    driver sets it to 1 when it exits.

    Bit 15 is the error bit. If it is set, then the low eight bits indicate
    the error. The errors are as follows:

    Error              Meaning
    ──────────────────────────────────────────────────────────────────────────
    0                  Write protect violation
    1                  Unknown unit
    2                  Drive not ready
    3                  Unknown command
    4                  CRC error
    5                  Bad drive request structure length
    6                  Seek error
    7                  Unknown media
    8                  Sector not found
    9                  Printer out of paper
    ──────────────────────────────────────────────────────────────────────────

    Error              Meaning
    ──────────────────────────────────────────────────────────────────────────
    A                  Write fault
    B                  Read fault
    C                  General failure
    D                  Reserved
    E                  Reserved
    F                  Invalid disk change
    ──────────────────────────────────────────────────────────────────────────

    Bit 9 is the busy bit, which is set only by Status calls and the Removable
    Media call.


2.7  Device Driver Functions

    Device drivers may perform all or some of these general functions. In some
    cases, these functions break down into several command codes, for specific
    cases. Each of the following general functions is described in this
    section.

    ■  Init

    ■  Media Check

    ■  Build BPB

    ■  Read, or Write, or Write Until Busy, or Write with Verify, or Read
        IOCtl, or Write IOCtl

    ■  Non-destructive Read, No Wait

    ■  Open or Close (3.x)

    ■  Removable Media (3.x)

    ■  Status

    ■  Flush

    ■  Generic IOCtl

    ■  Get or Set Logical Device

    All strategy routines are called with ES:BX pointing to the request
    header. The interrupt routines get the pointers to the request header from
    the queue that the strategy routines store them in. The command code in
    the request header tells the driver which function to perform and what
    data follows the request header.

    ──────────────────────────────────────────────────────────────────────────
    Note
    All DWORD pointers are stored offset first, then segment.
    ──────────────────────────────────────────────────────────────────────────

2.7.1  The Init Function

    Command code = 0

    INIT - ES:BX ->
    ┌────────────────────────────────────┐
    │ 13-BYTE Request header             │
    ├────────────────────────────────────┤
    │ BYTE Number of units               │
    ├────────────────────────────────────┤
    │ DWORD End Address                  │
    ├────────────────────────────────────┤
    │ DWORD Pointer to BPB array         │
    │ (Not set by character devices)     │
    ├────────────────────────────────────┤
    │ BYTE Block device number           │
    └────────────────────────────────────┘

    One of the functions defined for each device driver is Init. This routine
    is called only once when the device is installed. The Init routine must
    return the end address, which is a DWORD pointer to the end of the portion
    of the device driver to remain resident. To save space, you can use this
    pointer method to delete initialization code that is needed only once.

    The number of units, end address, and BPB pointer are to be set by the
    driver. However, on entry for installable device drivers, the DWORD that
    is to be set by the driver to the BPB array (on block devices) points to
    the character after the "=" on the line in config.sys that caused this
    device driver to be loaded. This allows drivers to scan the config.sys
    invocation line for parameters that might be passed to the driver. This
    line is terminated by an ENTER or a linefeed character. This data is
    read-only and allows the device to scan the config.sys command line for
    arguments.

        device=\dev\vt52.sys /l
                │
                └─────BPB address points here

    Also, for block devices only, the drive number assigned to the first unit
    defined by this driver (A=0) is contained in the block device number
    field. This is also read-only.

    ──────────────────────────────────────────────────────────────────────────
    Note
    The Init routine can issue only Functions 01H-0CH, 25H, 30H, and
    35H.
    ──────────────────────────────────────────────────────────────────────────

    For installable character devices, the end address parameter must be
    returned. This is a pointer to the first available byte of memory above
    the driver and may be used to throw away initialization code.

    Block devices must return the following information:

    1. The number of units. MS-DOS uses this number to determine logical
        device names. If the current maximum logical device letter is F at the
        time of the install call, and the Init routine returns 4 as the number
        of units, then they will have logical names G, H, I, and J. This
        mapping is determined by the position of the driver in the device list,
        and by the number of units on the device (stored in the first byte of
        the device name field).

    2. A DWORD pointer to an array of word offsets (pointers) to BPBs (BIOS
        Parameter Blocks). The BPBs passed by the device driver are used by
        MS-DOS to create an internal structure. There must be one entry in this
        array for each unit defined by the device driver. In this way, if all
        units are the same, all the pointers can point to the same BPB, thereby
        saving space. If the device driver defines two units, then the DWORD
        pointer points to the first of two one-word offsets which in turn point
        to BPBs. The format of the BPB is described in Section 2.7.3, "The
        Build BPB Function."

        Notice that this array of word offsets must be protected (below the
        free pointer set by the return), since an internal DOS structure will
        be built starting at the byte pointed to by the free pointer. The
        defined sector size must be less than or equal to the maximum sector
        size defined by the resident device drivers (BIOS) during
        initialization. If it isn't, the installation will fail.

    3. The media descriptor byte. This byte means nothing to MS-DOS, but is
        passed to devices so that they know what parameters MS-DOS is currently
        using for a particular drive.

    ──────────────────────────────────────────────────────────────────────────
    Note
    If there are multiple device drivers in a single file, MS-DOS uses the
    ending address returned by the last Init function called. All device
    drivers in a single file should return the same ending address. All
    devices in a single file should be grouped together low in memory with
    the initialization code for all devices following it in memory.
    ──────────────────────────────────────────────────────────────────────────

2.7.2  The Media Check Function

    Command Code = 1

    MEDIA CHECK - ES:BX ->
    ┌────────────────────────────────────┐
    │ 13-BYTE Request header             │
    ├────────────────────────────────────┤
    │ BYTE Media descriptor from BPB     │
    ├────────────────────────────────────┤
    │ BYTE Returned                      │
    │────────────────────────────────────┤
    │ Returned DWORD pointer to previous │
    │ Volume ID if bit 11 set and        │
    │ Disk Changed is returned           │
    └────────────────────────────────────┘

    The Media Check function is used only with block devices. It is called
    when there is a pending drive-access call other than a file read or write,
    such as Open, Close, delete, and rename. Its purpose is to determine
    whether the media in the drive has been changed. If the driver can assure
    that the media has not been changed (through a door-lock or other
    interlock mechanism), MS-DOS performance is enhanced, because MS-DOS does
    not need to reread the FAT and invalidate in-memory buffers for each
    directory access.

    When a disk-access call to the DOS occurs (other than a file read or
    write), the following sequence of events takes place:

    1. The DOS converts the drive letter into a unit number of a particular
        block device.

    2. The device driver is then called to request a media check on that
        subunit to see if the disk might have been changed. MS-DOS passes the
        old media descriptor byte. The driver returns one of the following:

        Return            Meaning
        ───────────────────────────────────────────────────────────────────────
        (1)               Media not changed
        (0)               Don't know if changed
        (-1)              Media changed
        value             Error (value is a standard error code value)
        ───────────────────────────────────────────────────────────────────────

        If the media has not been changed, MS-DOS proceeds with the disk
        access.

        If the value returned is -1, and there are any disk sectors that have
        been modified and not written back out to the disk for this unit,
        MS-DOS assumes that the disk has not been changed and proceeds. MS-DOS
        invalidates any other buffers for the unit and does a Build BPB call
        (see Step 3, following).

        If the media has been changed, MS-DOS invalidates all buffers
        associated with this unit including buffers with modified data that are
        waiting to be written, and requests a new BIOS Parameter Block via the
        Build BPB call (see Step 3).

    3. Once the BPB has been returned, MS-DOS corrects its internal structure
        for the drive from the new BPB and proceeds with the access after
        reading the directory and the FAT.

    Notice that the previous media ID byte is passed to the device driver. If
    the old media ID byte is the same as the new one, the disk might have been
    changed and a new disk may be in the drive; therefore, all FAT, directory,
    and data sectors that are buffered in memory for the drive are considered
    invalid.

    If the driver has bit 11 of the device attribute word set to 1, and the
    driver returns -1 (Media Changed), the driver must set the DWORD pointer
    to the previous Volume ID field. If the DOS determines that "Media
    changed" is an error based on the state of the DOS buffer cache, the DOS
    will generate a 0FH error on behalf of the device. If the driver does not
    implement volume ID support, but has bit 11 set, it should set a static
    pointer to the string "NO NAME" ,0. It is not possible for a user to
    change a disk in less than two seconds. So when Media Check occurs within
    two seconds of a disk access, the driver reports "Media not changed (1)."
    This improves performance tremendously.

    ──────────────────────────────────────────────────────────────────────────
    Note
    If the media ID byte in the returned BPB is the same as the previous
    media ID byte, MS-DOS will assume that the format of the disk is the
    same (even though the disk may have been changed) and will skip the step
    of updating its internal structure. Therefore, all BPBs must have unique
    media bytes regardless of FAT ID bytes.
    ──────────────────────────────────────────────────────────────────────────

2.7.3  The Build BPB Function

    Command code = 2

    BUILD BPB - ES:BX ->
    ┌────────────────────────────────────┐
    │ 13-BYTE Request header             │
    ├────────────────────────────────────┤
    │ BYTE Media descriptor from BPB     │
    ├────────────────────────────────────┤
    │ DWORD Transfer address             │
    │ (Points to one sector worth of     │
    │  scratch space or first sector     │
    │  of FAT depending on the value     │
    │  of Bit 13 in the device attribute │
    │  word.)                            │
    ├────────────────────────────────────┤
    │ DWORD Pointer to BPB               │
    └────────────────────────────────────┘

    The Build BPB function is used with block devices only. As described in
    the Media Check function, the Build BPB function will be called any time
    that a preceding Media Check call indicates that the disk has been or
    might have been changed. The device driver must return a pointer to a BPB.
    This is different from the Init call where the device driver returns a
    pointer to an array of word offsets to BPBs. The Build BPB call gets a
    DWORD pointer to a one-sector buffer. The contents of this buffer are
    determined by the non-FAT ID bit (bit 13) in the attribute field. If the
    bit is zero, then the buffer contains the first sector of the first FAT.
    The FAT ID byte is the first byte of this buffer. In this case, the driver
    must not alter this buffer. Notice that the location of the FAT must be
    the same for all possible media because this first FAT sector must be read
    before the actual BPB is returned. If the non-FAT ID bit is set, the
    pointer points to one sector of scratch space (which may be used for
    anything). For information on how to construct the BPB, see Section 2.8,
    "The Media Descriptor Byte," and Section 2.9, "Format of a Media
    Descriptor Table."

    MS-DOS 4.0 includes additional support for devices that have door-locks or
    some other means of telling when a disk has been changed. There is an
    error that can be returned from the device driver (error 15). The error
    means "the disk has been changed when it shouldn't have been," and the
    user is prompted for the correct disk using a volume ID. The driver may
    generate this error on read or write. The DOS may generate the error on
    Media Check calls if the driver reports media changed and there are
    buffers in the DOS buffer cache that need to be flushed to the previous
    disk.

    For drivers that support this error, the Build BPB function is a trigger
    that causes a new volume ID to be read off the disk. This action indicates
    that the disk has been legally changed. A volume ID is placed on a disk by
    the format command and is simply an entry in the root directory of the
    disk that has the Volume ID attribute. It is stored by the driver as an
    ASCIZ string.

    The requirement that the driver return a volume ID does not exclude some
    other volume identifier scheme as long as the scheme uses ASCIZ strings. A
    NUL (nonexistent or unsupported) volume ID is by convention the following
    string:

    DB     "NO NAME     ",0

2.7.4  The Read or Write Function

    Command codes = 3,4,8,9,12, and 16

    READ OR WRITE (Including IOCtl) or
        OUTPUT UNTIL BUSY - ES:BX ->
    ┌────────────────────────────────────┐
    │ 13-BYTE Request header             │
    ├────────────────────────────────────┤
    │ BYTE Media descriptor from BPB     │
    ├────────────────────────────────────┤
    │ DWORD Transfer address             │
    ├────────────────────────────────────┤
    │ WORD Byte/sector count             │
    ├────────────────────────────────────┤
    │ WORD Starting sector number        │
    │ or -1 if the drive supports        │
    │ greater than 16-bit sector         │
    │ addresses                          │
    │  (Ignored on character devices)    │
    ├────────────────────────────────────┤
    │ DWORD pointer to the Volume ID     │
    ├────────────────────────────────────┤
    │ Starting sector number             │
    │ (high-word first) if WORD starting │
    │ sector number is -1                │
    │ or                                 │
    │ Returned DWORD pointer to requested│
    │ Volume ID if error 0FH             │
    └────────────────────────────────────┘

    Code               Request
    ──────────────────────────────────────────────────────────────────────────
    3                  IOCtl read
    4                  Read (block or character device)
    8                  Write (block or character device)
    9                  Write with Verify
    12                 IOCtl Write
    16                 Output Until Busy (character device only)
    ──────────────────────────────────────────────────────────────────────────

    The driver must perform the Read or Write call depending on which command
    code is set. Block devices read or write sectors; character devices read
    or write bytes.

    When I/O completes, the device driver must set the status word and report
    the number of sectors or bytes successfully transferred. This should be
    done even if an error prevented the transfer from being completed. Setting
    the error bit and error code alone is not sufficient. In addition to
    setting the status word, the driver must set the sector count to the
    actual number of sectors (or bytes) transferred. No error check is
    performed on an IOCtl I/O call. The device driver must always set the
    return byte/sector count to the actual number of bytes/sectors
    successfully transferred.

    If the verify switch is on, the device driver will be called with command
    code 9 (Write with Verify). Your device driver will be responsible for
    verifying the write.

    If the driver returns error code 0FH (Invalid disk change), it must return
    a DWORD pointer to an ASCIZ string (which is the correct volume ID).
    Returning this error code triggers the DOS to prompt the user to re-insert
    the disk. The device driver should have read the volume ID as a result of
    the Build BPB function.

    Drivers may maintain a reference count of open files on the disk by
    monitoring the Open and Close functions. This allows the driver to
    determine when to return error 0FH. If there are no open files (reference
    count = 0), and the disk has been changed, the I/O is okay. If there are
    open files, however, an 0FH error may exist.

    The Output Until Busy call is a speed optimization on character devices
    only for print spoolers. The device driver is expected to output all the
    characters possible until the device returns busy. Under no circumstances
    should the device driver block during this function. Notice that it is not
    an error for the device driver to return the number of bytes output as
    being less than the number of bytes requested (or = 0).

    The Output Until Busy call allows spooler programs to take advantage of
    the "burst" behavior of most printers. Many printers have on-board RAM
    buffers that typically hold a line or a fixed amount of characters. These
    buffers fill up without the printer going busy, or going busy for a short
    period (less than ten instructions) between characters. A line of
    characters can be output quickly to the printer, after which the printer
    is busy for a long time while the characters are being printed. This new
    device call allows background spooling programs to use this burst behavior
    efficiently. Rather than take the overhead of a device driver call for
    each character, or risk getting stuck in the device driver outputting a
    block of characters, this call allows a burst of characters to be output
    without the device driver having to wait for the device to be ready.

    The Following Applies to Block Device Drivers:

    Under certain circumstances, the BIOS may be asked to perform a write
    operation of 64 kilobytes, which seems to be a "wrap-around" of the
    transfer address in the BIOS I/O packet. This request, which arises due to
    an optimization added to the write code in MS-DOS, will manifest itself
    only on user writes that are within a sector size of 64 kilobytes on files
    "growing" past the current end-of-file (EOF) mark. It is allowable for the
    BIOS to ignore the balance of the write that "wraps around" if it so
    chooses. For example, a write of 10000H bytes-worth of sectors with a
    transfer address of xxx:1 could ignore the last two bytes. A user program
    can never request an I/O of more than FFFFH bytes and cannot wrap around
    (even to 0) in the transfer segment. Therefore, in this case, the last two
    bytes can be ignored.

    MS-DOS maintains two FATs. If the DOS has problems reading the first, it
    automatically tries the second before reporting the error. The BIOS is
    responsible for all retries.

    Although the command.com handler does no automatic retries, there are
    applications that have their own Interrupt 24H handlers that do automatic
    retries on certain types of Interrupt 24H errors before reporting them.

2.7.5  The Non-destructive Read, No Wait Function

    Command code = 5

    NON-DESTRUCTIVE READ NO WAIT - ES:BX ->
    ┌────────────────────────────────────┐
    │ 13-BYTE Request header             │
    ├────────────────────────────────────┤
    │ BYTE Read from device              │
    └────────────────────────────────────┘

    The Non-destructive Read, No Wait function allows MS-DOS to look ahead one
    input character. The device sets the done bit in the status word.

    If the character device returns busy bit = 0 (there are characters in the
    buffer), then the next character that would be read is returned. This
    character is not removed from the input buffer (hence the term
    "Non-destructive Read"). If the character device returns busy bit = 1,
    there are no characters in the buffer.

2.7.6  The Open or Close Function

    Command codes = 13 and 14

    OPEN or CLOSE  - ES:BX ->
    ┌────────────────────────────────────┐
    │ 13-BYTE Static request header      │
    └────────────────────────────────────┘

    The Open and Close functions are called by MS-DOS only if the device
    driver sets the Open/Close/Removable Media attribute bit in the device
    header. They are designed to inform the device about current file activity
    on the device. On block devices, they can be used to manage local
    buffering. The device can keep a reference count. Every Open causes the
    device to increment the count, every Close to decrement. When the count
    goes to zero, it means there are no open files on the device. Therefore,
    the device should flush any buffers that have been written to that may
    have been used inside the device, because it is now "legal" for the user
    to change the media on a removable media drive.

    There are problems with this mechanism on block devices because programs
    that use FCB calls can open files without closing them. It is, therefore,
    advisable to reset the count to zero without flushing the buffers when the
    answer to "Has the media been changed?" is yes, and the Build BPB call is
    made to the device.

    These calls are more useful on character devices. The Open call, for
    instance, can be used to send a device initialization string. On a
    printer, this could cause a string for setting font and page size
    characteristics to be sent to the printer so that it would always be in a
    known state at the start of an I/O stream. Using IOCtl to set these pre-
    and post- strings provides a flexible mechanism of serial I/O device
    stream control. The reference count mechanism can also be used to detect a
    simultaneous access error. It may be desirable to disallow more than one
    Open on a device at any given time. In this case, a second Open would
    result in an error.

    Notice that since all processes have access to stdin, stdout, stderr,
    stdaux, and stdprn (handles 0,1,2,3,4), the CON, AUX, and PRN devices are
    always open.

2.7.7  The Removable Media Function

    Command code = 15

    REMOVABLE MEDIA - ES:BX ->
    ┌────────────────────────────────────┐
    │ 13-BYTE Static request header      │
    └────────────────────────────────────┘

    The Removable Media function is called by MS-DOS only if the device driver
    sets the Open/Close/Removable Media attribute bit in the device header.
    This call is given only to block devices by a subfunction of the IOCtl
    system call. It is sometimes desirable for a utility to know whether it is
    dealing with a nonremovable media drive (such as a hard disk), or a
    removable media drive (like a floppy). An example is the format command,
    which prints different versions of some of the prompts.

    The information is returned in the busy bit of the status word. If the
    busy bit is 1, then the media is nonremovable. If the busy bit is 0, then
    the media is removable. Notice that the error bit is not checked. It is
    assumed that this call always succeeds.

2.7.8  The Status Function

    Command codes = 6 and 10

    STATUS Calls ES:BX ->
    ┌────────────────────────────────────┐
    │ 13-BYTE Request header             │
    └────────────────────────────────────┘

    The Status function returns information to the DOS as to whether data is
    waiting for input or output. All the driver must do is set the status word
    and the busy bit as follows:

    ■  For output on character devices ── if the driver sets bit 9 to 1 on
        return, it informs the DOS that a write request (if made) would wait
        for completion of a current request. If it is 0, there is no current
        request, and a write request (if made) would start immediately.

    ■  For input on character devices with a buffer ── A return of 1 implies
        that no characters are buffered and that a read request (if made) would
        go to the physical device. If it is 0 on return, then there are
        characters in the device buffer and a read would not be blocked. A
        return of 0 implies that the user has typed something. MS-DOS assumes
        that all character devices have an input type-ahead buffer.

    Devices that do not have a type-ahead buffer should always return busy = 0
    so that the DOS will not "hang" waiting for something to get into a
    non-existent buffer.

2.7.9  The Flush Function

    Command codes = 7 and 11

    FLUSH Calls - ES:BX ->
    ┌────────────────────────────────────┐
    │ 13-BYTE Request header             │
    └────────────────────────────────────┘

    The Flush function tells the driver to flush (terminate) all pending
    requests. This call is used to flush the input queue on character devices.

    The device driver performs the flush function, sets the status word, and
    returns.

2.7.10  The Generic IOCtl Function

    Command code = 19

    ES:BX ->
    ┌────────────────────────────────────┐
    │ 13-BYTE Static request header      │
    ├────────────────────────────────────┤
    │ BYTE Category (Major) code         │
    ├────────────────────────────────────┤
    │ BYTE Function (Minor) code         │
    ├────────────────────────────────────┤
    │ WORD (SI) Contents                 │
    ├────────────────────────────────────┤
    │ WORD (DI) Contents                 │
    ├────────────────────────────────────┤
    │ DWORD Pointer to data buffer       │
    └────────────────────────────────────┘

    The Generic IOCtl function provides a generic, expandable IOCtl facility
    that replaces and makes the Read IOCtl and Write IOCtl device driver
    functions obsolete. The MS-DOS 2.0 IOCtl functions remain to support
    existing uses of the IOCtl system call (Subfunctions 2, 3, 4, and 5), but
    new device drivers should use this generic MS-DOS IOCtl facility. The
    Generic IOCtl function contains both a category and function code. The DOS
    examines the category field in order to intercept and obey device commands
    that are actually serviced by the DOS code; all other command categories
    are forwarded to the device driver for servicing.

    For more information on these category and function codes, refer to
    Function 440CH (Generic IOCtl for Handles) and Function 440DH (Generic
    IOCtl for Block Devices) in Chapter 1, "System Calls."

2.7.11  The Get/Set Logical Drive Map Function

    Command codes = 23 (Get) or 24 (Set)

    ┌────────────────────────────────────────┐
    │ 13-BYTE Static request header          │
    ├────────────────────────────────────────┤
    │ BYTE    Input (unit code)              │
    ├────────────────────────────────────────┤
    │ BYTE    Output (last device referenced)│
    ├────────────────────────────────────────┤
    │ BYTE    Command code                   │
    ├────────────────────────────────────────┤
    │ WORD    Status                         │
    ├────────────────────────────────────────┤
    │ DWORD   Reserved                       │
    └────────────────────────────────────────┘

    The Get/Set Logical Drive Map function is called by MS-DOS only if the
    device driver sets the DOS 3.2 attribute bit in the device header. The
    call is issued only to block devices by the subfunction of the IOCtl
    system call. The logical drive to be mapped is passed in the Unit field of
    the header to the device driver. The device driver returns the current
    logical drive owner of the physical device that maps to the requested
    physical drive. To detect whether or not a logical device currently owns
    the physical device to which it is mapped, a program needs to verify that,
    after a call of Function 440EH or 440FH (Get/Set Logical Drive Map), the
    value of the Unit field is unchanged.


2.8  The Media Descriptor Byte

    In MS-DOS, the media descriptor byte is used to inform the DOS that a
    different type of media is present. The media descriptor byte can be any
    value between 0 and FFH. It does not have to be the same as the FAT ID
    byte. The FAT ID byte, which is the first byte of the FAT, was used in
    MS-DOS 1.00 to distinguish between different types of disk media, and may
    be used as well under 2.x and 3.x disk device drivers. However, FAT ID
    bytes have significance only for block device drivers where the non-FAT ID
    bit is not set (0).

    Values of the media descriptor byte or the FAT ID byte have no
    significance to MS-DOS. They are passed to the device driver so that
    programs can determine the media type.


2.9  Format of a Media Descriptor Table

    The MS-DOS file system uses a linked list of pointers (one for each
    cluster or allocation unit) called the File Allocation Table (FAT). Unused
    clusters are represented by zero and end-of-file by FFFH (or FFFFH on
    units with 16-bit FAT entries). No valid entry should ever point to a zero
    entry, but if one does, the first FAT entry (which would be pointed to by
    a zero entry) was reserved and set to end of chain. Eventually, several
    end of chain values were defined ([F]FF8H[F]FFFH), and these were used to
    distinguish different types of media.

    A preferable technique is to write a complete media descriptor table in
    the boot sector and use it for media identification. To ensure backward
    compatibility for systems whose drivers do not set the non-FAT ID bit
    (including the IBM PC implementation), it is also necessary to write the
    FAT ID bytes during the format process.

    To allow more flexibility for supporting many different disk formats in
    the future, it is recommended that the information relating to the BPB for
    a particular piece of media be kept in the boot sector. Figure 2.6 shows
    the format of such a boot sector.

        ┌────────────────────────────────────┐
        │ 3 BYTE Near JUMP to boot code      │
        ├────────────────────────────────────┤
        │ 8 BYTES OEM name and version       │
        ├────────────────────────────────────┤
    B  │ WORD Bytes per sector              │
    P  ├────────────────────────────────────┤
    B  │ BYTE Sectors per allocation unit   │
        ├────────────────────────────────────┤
    ↓  │ WORD Reserved sectors              │
        ├────────────────────────────────────┤
        │ BYTE Number of FATs                │
        ├────────────────────────────────────┤
        │ WORD Number of root dir entries    │
        ├────────────────────────────────────┤
        │ WORD Number of sectors in logical  │
    ↑  │ | image or 0                       │
        ├────────────────────────────────────┤
    B  │ BYTE Media descriptor              │
    P  ├────────────────────────────────────┤
    B  │ WORD Number of FAT sectors         │
        ├────────────────────────────────────┤
        │ WORD Sectors per track             │
        ├────────────────────────────────────┤
        │ WORD Number of heads               │
        ├────────────────────────────────────┤
        │ WORD Number of hidden sectors      │
        ├────────────────────────────────────┤
        │ WORD High order number of hidden   │
        │ sectors                            │
        ├────────────────────────────────────┤
        │ DWORD Number of logical sectors    │
        └────────────────────────────────────┘

    Figure 2.6  Format of a Boot Sector

    Although MS-DOS does not use the five fields that follow the BPB, these
    fields may be used by a device driver to help it understand the media.

    The "Sectors per track" and "Number of heads" fields are useful for
    supporting different media that may have the same logical layout, but a
    different physical layout (for example, 40-track, double-sided versus
    80-track, single-sided). "Sectors per track" tells the device driver how
    the logical disk format is laid out on the physical disk. The "Number of
    hidden sectors" and "High order number of hidden sectors" fields may be
    used to support drive-partitioning schemes.

    The "Number of logical sectors" field tells the device driver how many
    sectors to reserve if the "Number of sectors in logical image" field is
    zero. (Notice that this is intended for supporting devices that access
    more than 32 megabytes.)

    The following procedure is recommended for media determination by NON FAT
    ID format drivers:

    1. Read the boot sector of the drive into the one-sector scratch space
        pointed to by the DWORD transfer address.

    2. Determine if the first byte of the boot sector is an E9H or EBIT (the
        first byte of a 3-byte NEAR or 2-byte SHORT jump) or an EBH (the first
        byte of a 2-byte jump followed by an NOP). If so, a BPB is located
        beginning at offset 3. Return a pointer to it.

    3. If the boot sector does not have a BPB table, it is probably a disk
        formatted under a 1.x version of MS-DOS and probably uses a FAT ID byte
        for determining media.

        The driver may optionally attempt to read the first sector of the FAT
        into the one-sector scratch area and read the first byte to determine
        media type based upon whatever FAT ID bytes may have been used on disks
        that are expected to be read by this system. Return a pointer to a
        hard-coded BPB.


2.10  The CLOCK Device

    MS-DOS assumes that some sort of clock is available in the system. This
    may either be a CMOS real-time clock or an interval timer that is
    initialized at boot time by the user. The CLOCK device defines and
    performs functions like any other character device, except that it is
    identified by a bit in the attribute word. The DOS uses this bit to
    identify it; consequently, the CLOCK device may take any name. The IBM
    implementation uses the name $CLOCK so as not to conflict with existing
    files named clock.

    The CLOCK device is unique in that MS-DOS will read or write a 6-byte
    sequence that encodes the date and time. A write to this device will set
    the date and time; a read will get the date and time. Figure 2.7
    illustrates the binary time format used by the CLOCK device:

    byte 0   byte 1   byte 2    byte 3   byte 4   byte 5
    ┌────────┬────────┬─────────┬────────┬────────┬─────────┐
    │        │        │         │        │        │         │
    │days since 1-1-80│ minutes │  hours │ sec/100│ seconds │
    │low byte|hi byte │         │        │        │         │
    └─────────────────┴─────────┴────────┴────────┴─────────┘

    Figure 2.7  Format of a Clock Device


2.11  Anatomy of a Device Call

    The following steps illustrate what happens when MS-DOS calls on a block
    device driver to perform a Write request:

    1. MS-DOS writes a request packet in a reserved area of memory.

    2. MS-DOS calls the strategy entry point of the block device driver.

    3. The device driver saves the ES and BX registers (ES:BX points to the
        request packet) and does a FAR return.

    4. MS-DOS calls the interrupt entry point.

    5. The device driver retrieves the pointer to the request packet and reads
        the command code (offset 2) to determine that this is a Write request.
        The device driver converts the command code to an index into a dispatch
        table and control passes to the Write routine.

    6. The device driver reads the unit code (offset 1) to determine which
        disk drive it is supposed to write to.

    7. Since the command is a disk Write, the device driver must get the
        transfer address (offset 14), the sector count (offset 18), and the
        start sector (offset 20) in the request packet.

    8. The device driver translates the first logical sector number into a
        track, head, and sector number.

    9. The device driver writes the specified number of sectors, starting at
        the beginning sector on the drive defined by the unit code (the subunit
        defined by this device driver), and transfers data from the transfer
        address indicated in the request packet. Notice that this may involve
        multiple Write commands to the disk controller.

    10.After the transfer is complete, the device driver must report the
        status of the request to MS-DOS by setting the done bit in the status
        word (offset 3 in the request packet). It reports the number of sectors
        actually transferred in the sector count area of the request packet.

    11.If an error occurs, the driver sets the done bit and the error bit in
        the status word and fills in the error code in the lower half of the
        status word. The number of sectors actually transferred must be written
        in the request header. It is not sufficient just to set the error bit
        of the status word.

    12.The device driver does a FAR return to MS-DOS.

    The device drivers should preserve the state of MS-DOS. This means that
    all registers (including flags) should be preserved. The direction flag
    and interrupt enable bits are critical. When the interrupt entry point in
    the device driver is called, MS-DOS has room for about 40 to 50 bytes on
    its internal stack. Your device driver should switch to a local stack if
    it uses extensive stack operations.


2.12  Two Sample Device Drivers

    The following two examples illustrate a block device driver program and a
    character device driver program. These examples are provided as guides for
    writing your own device drivers. However, since device drivers are
    hardware-dependent, your device drivers will differ.

    Block Device Driver



    ; ********************* A Block Device *******************

            Title   5.25-inch Disk Driver

    ; This driver is intended to drive up to four 5.25-inch
    ; drives hooked to a single disk controller. All standard
    ; IBM PC formats are supported.


    FALSE   EQU     0
    TRUE    EQU     NOT FALSE




    ; The I/O port address of the disk controller
    DISK    EQU     0E0H
    ; DISK+0
    ;        1793    Command/Status
    ; DISK+1
    ;        1793    Track
    ; DISK+2
    ;        1793    Sector
    ; DISK+3
    ;        1793    Data
    ; DISK+4
    ;        Aux Command/Status
    ; DISK+5
    ;        Wait Sync

    ; Back side select bit
    BACKBIT EQU     04H
    ; 5 1/4" select bit
    SMALBIT EQU     10H
    ; Double Density bit
    DDBIT   EQU     08H

    ; Done bit in status register
    DONEBIT EQU     01H

    ; Use table below to select head step speed.
    ; Step times for 5" drives
    ; are double that shown in the ; Step value    1771    1793
    ;
    ;     0          6ms     3ms
    ;     1          6ms     6ms
    ;     2         10ms    10ms
    ;     3         20ms    15ms
    ;
    STPSPD  EQU     1

    NUMERR  EQU     ERROUT-ERRIN

    CR      EQU     0DH
    LF      EQU     0AH

    CODE    SEGMENT
    ASSUME  CS:CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
    ; -----------------------------------------------------
    ;
    ;        Device Header
    ;
    DRVDEV  LABEL   WORD
            DW      -1,-1
            DW      0000     ; IBM format-compatible, Block
            DW      STRATEGY
            DW      DRV$IN
    DRVMAX  DB      4

    DRVTBL  LABEL   WORD
            DW      DRV$INIT
            DW      MEDIA$CHK
            DW      GET$BPB
            DW      CMDERR
            DW      DRV$READ
            DW      EXIT
            DW      EXIT
            DW      EXIT
            DW      DRV$WRIT
            DW      DRV$WRIT
            DW      EXIT
            DW      EXIT
            DW      EXIT

    ; ------------------------------------
    ;
    ;        Strategy

    PTRSAV  DD      0

    STRATP  PROC    FAR
    STRATEGY:
            MOV     WORD PTR [PTRSAV],BX
            MOV     WORD PTR [PTRSAV+2],ES
            RET
    STRATP  ENDP

    ; --------------------------------------
    ;
    ;        Main Entry


    CMDLEN  =       0       ; Length of this command
    UNIT    =       1       ; Subunit specified
    CMDC    =       2       ; Command Code
    STATUS  =       3       ; Status
    MEDIA   =       13      ; Media Descriptor
    TRANS   =       14      ; Transfer Address
    COUNT   =       18      ; Count of blocks or characters
    START   =       20      ; First block to transfer

    DRV$IN:
            PUSH    SI
            PUSH    AX
            PUSH    CX
            PUSH    DX
            PUSH    DI
            PUSH    BP
            PUSH    DS
            PUSH    ES
            PUSH    BX

            LDS     BX,[PTRSAV]     ; Get pointer to I/O packet

            MOV     AL,BYTE PTR [BX].UNIT   ; AL = Unit Code
            MOV     AH,BYTE PTR [BX].MEDIA  ; AH = Media Descrip
            MOV     CX,WORD PTR [BX].COUNT  ; CX = Count
            MOV     DX,WORD PTR [BX].START  ; DX = Start Sector
            PUSH    AX
            MOV     AL,BYTE PTR [BX].CMDC   ; Command code
            CMP     AL,15
            JA      CMDERRP                 ; Bad command
            CBW
            SHL     AX,1                    ; 2 times command =
                                            ; word table index
            MOV     SI,OFFSET DRVTBL
            ADD     SI,AX                   ; Index into table
            POP     AX                      ; Get back media
                                            ; and unit

            LES     DI,DWORD PTR [BX].TRANS ; ES:DI = Transfer
                                            ; Address

            PUSH    CS
            POP     DS

    ASSUME  DS:CODE

            JMP     WORD PTR [SI]             ; GO DO COMMAND

    ; ----------------------------------------------------------
    ;
    ;        EXIT - All Routines return through this path
    ;
    ASSUME  DS:NOTHING
    CMDERRP:
            POP     AX                    ; Clean stack
    CMDERR:
            MOV     AL,3                  ; Unknown command error
            JMP     SHORT ERR$EXIT

    ERR$CNT:LDS     BX,[PTRSAV]
            SUB     WORD PTR [BX].COUNT,CX ; # OF SUCCESS. I/Os

    ERR$EXIT:
    ; AL has error code
            MOV     AH,10000001B            ; Mark error return
            JMP     SHORT ERR1

    EXITP   PROC    FAR

    EXIT:   MOV     AH,00000001B
    ERR1:   LDS     BX,[PTRSAV]
            MOV     WORD PTR [BX].STATUS,AX
                                        ; Mark Operation
    CompleteE

            POP     BX
            POP     ES
            POP     DS
            POP     BP
            POP     DI
            POP     DX
            POP     CX
            POP     AX
            POP     SI
            RET                         ; Restore REGS and return
    EXITP   ENDP

    CURDRV  DB      -1

    TRKTAB  DB      -1,-1,-1,-1

    SECCNT  DW      0

    DRVLIM  =       8       ; Number of sectors on device
    SECLIM  =       13      ; Maximum Sector
    HDLIM   =       15      ; Maximum Head

    ; WARNING - preserve order of drive and curhd!

    DRIVE   DB      0       ; Physical Drive Code
    CURHD   DB      0       ; Current Head
    CURSEC  DB      0       ; Current Sector
    CURTRK  DW      0       ; Current Track


    ;
    MEDIA$CHK:              ; Always indicates Don't know
    ASSUME  DS:CODE
            TEST    AH,00000100B       ; Test if Media Removable
            JZ      MEDIA$EXT
            XOR     DI,DI              ; Say I Don't know
    MEDIA$EXT:
            LDS     BX,[PTRSAV]
            MOV     WORD PTR [BX].TRANS,DI
            JMP     EXIT

    BUILD$BPB:
    ASSUME  DS:CODE
            MOV     AH,BYTE PTR ES:[DI]       ; Get FAT ID Byte
            CALL    BUILDBP                   ; Translate
    SETBPB: LDS     BX,[PTRSAV]
            MOV     [BX].MEDIA,AH
            MOV     [BX].COUNT,DI
            MOV     [BX].COUNT+2,CS
            JMP     EXIT

    BUILDBP:
    ASSUME  DS:NOTHING
    ; AH is media byte on entry
    ; DI points to correct BPB on return
            PUSH    AX
            PUSH    CX
            PUSH    DX
            PUSH    BX
            MOV     CL,AH       ; Save Media
            AND     CL,0F8H     ; Normalize
            CMP     CL,0F8H     ; Compare with Good Media Byte
            JZ      GOODID
            MOV     AH,0FEH     ; Default to 8-sector,
                                ; Single-sided
    GOODID:
            MOV     AL,1        ; Set number of FAT sectors
            MOV     BX,64*256+8 ; Set Dir Entries and Sector Max
            MOV     CX,40*8     ; Set Size of Drive
            MOV     DX,01*256+1 ; Set Head Limit & Sec/All Unit
            MOV     DI,OFFSET DRVBPB
            TEST    AH,00000010B ; Test for 8 OR 9 Sectors
            JNZ     HAS8        ; NZ = has 8 sectors
            INC     AL          ; Inc Number of FAT sectors
            INC     BL          ; Inc Sector Max
            ADD     CX,40       ; Increase Size
    HAS8:   TEST    AH,00000001B    ; Test for 1 or 2 Heads
            JZ      HAS1        ; Z = 1 Head
            ADD     CX,CX       ; Double Size of Disk
            MOV     BH,112      ; Increase # of Dir Entries
            INC     DH          ; Inc Sec/All Unit
            INC     DL          ; Inc Head Limit
    HAS1:   MOV     BYTE PTR [DI].2,DH
            MOV     BYTE PTR [DI].6,BH
            MOV     WORD PTR [DI].8,CX
            MOV     BYTE PTR [DI].10,AH
            MOV     BYTE PTR [DI].11,AL
            MOV     BYTE PTR [DI].13,BL
            MOV     BYTE PTR [DI].15,DL
            POP     BX
            POP     DX
            POP     CX
            POP     AX
            RET


    ; ----------------------------------------------------------
    ;
    ;        Disk I/O Handlers
    ;
    ; ENTRY:
    ;        AL = Drive Number (0-3)
    ;        AH = Media Descriptor
    ;        CX = Sector Count
    ;        DX = First Sector
    ;        DS = CS
    ;        ES:DI = Transfer Address
    ; EXIT:
    ;        IF Successful Carry Flag = 0
    ;          ELSE CF=1 AND AL contains (MS-DOS) Error Code,
            CX # sectors NOT transferred

    DRV$READ:
    ASSUME  DS:CODE
            JCXZ    DSKOK
            CALL    SETUP
            JC      DSK$IO
            CALL    DISKRD
            JMP     SHORT DSK$IO

    DRV$WRIT:
    ASSUME  DS:CODE
            JCXZ    DSKOK
            CALL    SETUP
            JC      DSK$IO
            CALL    DISKWRT
    ASSUME  DS:NOTHING
    DSK$IO: JNC     DSKOK
            JMP     ERR$CNT
    DSKOK:  JMP     EXIT


    SETUP:
    ASSUME  DS:CODE
    ; Input same as above
    ; On output
    ;  ES:DI = Trans addr
    ;  DS:BX Points to BPB
    ;  Carry set if error (AL is error code (MS-DOS))
    ;  else
    ;        [DRIVE] = Drive number (0-3)
    ;        [SECCNT] = Sectors to transfer
    ;        [CURSEC] = Sector number of start of I/O
    ;        [CURHD]  = Head number of start of I/O   ; Set
    ;        [CURTRK] = Track # of start of I/O ; Seek performed
    ;  All other registers destroyed

            XCHG   BX,DI              ; ES:BX = Transfer Address
            CALL   BUILDBP            ; DS:DI = PTR to B.P.B
            MOV    SI,CX
            ADD    SI,DX
            CMP    SI,WORD PTR [DI].DRVLIM
                                    ; Compare Against Drive Max
            JBE    INRANGE
            MOV    AL,8
            STC
            RET

    INRANGE:
            MOV    [DRIVE],AL
            MOV    [SECCNT],CX     ; Save Sector Count
            XCHG   AX,DX           ; Set Up Logical Sector
                                    ; For Divide
            XOR    DX,DX
            DIV    WORD PTR [DI].SECLIM ; Divide by Sec per Track
            INC    DL
            MOV    [CURSEC],DL          ; Save Current Sector
            MOV    CX,WORD PTR [DI].HDLIM ; Get Number of Heads
            XOR    DX,DX   ; Divide Tracks by Heads per Cylinder
            DIV    CX
            MOV    [CURHD],DL      ; Save Current Head
            MOV    [CURTRK],AX     ; Save Current Track
    SEEK:
            PUSH   BX              ; Xaddr
            PUSH   DI              ; BPB pointer
            CALL   CHKNEW          ; Unload head if change drives
            CALL   DRIVESEL
            MOV    BL,[DRIVE]
            XOR    BH,BH           ; BX drive index
            ADD    BX,OFFSET TRKTAB        ; Get current track
            MOV    AX,[CURTRK]
            MOV    DL,AL         ; Save desired track
            XCHG   AL,DS:[BX]    ; Make desired track current
            OUT    DISK+1,AL     ; Tell Controller current track
            CMP    AL,DL         ; At correct track?
            JZ     SEEKRET       ; Done if yes
            MOV    BH,2          ; Seek retry count
            CMP    AL,-1         ; Position Known?
            JNZ    NOHOME        ; If not home head
    TRYSK:
            CALL   HOME
            JC     SEEKERR
    NOHOME:
            MOV    AL,DL
            OUT    DISK+3,AL       ; Desired track
            MOV    AL,1CH+STPSPD   ; Seek
            CALL   DCOM
            AND    AL,98H    ; Accept not rdy, seek, & CRC errors
            JZ     SEEKRET
            JS     SEEKERR         ; No retries if not ready
            DEC    BH
            JNZ    TRYSK
    SEEKERR:
            MOV    BL,[DRIVE]
            XOR    BH,BH           ; BX drive index
            ADD    BX,OFFSET TRKTAB        ; Get current track
            MOV    BYTE PTR DS:[BX],-1     ; Make current track
                                            ; unknown
            CALL   GETERRCD
            MOV    CX,[SECCNT]     ; Nothing transferred
            POP    BX              ; BPB pointer
            POP    DI              ; Xaddr
            RET

    SEEKRET:
            POP    BX              ; BPB pointer
            POP    DI              ; Xaddr
            CLC
            RET

    ; ---------------------------------------------
    ;
    ;        Read
    ;

    DISKRD:
    ASSUME  DS:CODE
            MOV    CX,[SECCNT]
    RDLP:
            CALL   PRESET
            PUSH   BX
            MOV    BL,10              ; Retry count
            MOV    DX,DISK+3          ; Data port
    RDAGN:
            MOV    AL,80H             ; Read command
            CLI                       ; Disable for 1793
            OUT    DISK,AL            ; Output read command
            MOV    BP,DI              ; Save address for retry
            JMP    SHORT RLOOPENTRY
    RLOOP:
            STOSB
    RLOOPENTRY:
            IN     AL,DISK+5          ; Wait for DRQ or INTRQ
            SHR    AL,1
            IN     AL,DX              ; Read data
            JNC    RLOOP
            STI                       ; Ints OK now
            CALL   GETSTAT
            AND    AL,9CH
            JZ     RDPOP              ; Ok
            MOV    DI,BP              ; Get back transfer
            DEC    BL
            JNZ    RDAGN
            CMP    AL,10H             ; Record not found?
            JNZ    GOT_CODE           ; No
            MOV    AL,1               ; Map it
    GOT_CODE:
            CALL   GETERRCD
            POP    BX
            RET

    RDPOP:
            POP    BX
            LOOP   RDLP
            CLC
            RET


    ; ---------------------------------------------
    ;
    ;        Write
    ;

    DISKWRT:
    ASSUME  DS:CODE
            MOV     CX,[SECCNT]
            MOV     SI,DI
            PUSH    ES
            POP     DS
    ASSUME  DS:NOTHING
    WRLP:
            CALL    PRESET
            PUSH    BX
            MOV     BL,10                   ; Retry count
            MOV     DX,DISK+3               ; Data port
    WRAGN:
            MOV     AL,0A0H            ; Write command
            CLI                        ; Disable for 1793
            OUT     DISK,AL            ; Output write command
            MOV     BP,SI              ; Save address for retry
    WRLOOP:
            IN      AL,DISK+5
            SHR     AL,1
            LODSB                      ; Get data
            OUT     DX,AL              ; Write data
            JNC     WRLOOP
            STI                        ; Ints OK now
            DEC     SI
            CALL    GETSTAT
            AND     AL,0FCH
            JZ      WRPOP              ; Ok
            MOV     SI,BP              ; Get back transfer
            DEC     BL
            JNZ     WRAGN
            CALL    GETERRCD
            POP     BX
            RET

    WRPOP:
            POP     BX
            LOOP    WRLP
            CLC
            RET


    PRESET:
    ASSUME  DS:NOTHING
            MOV     AL,[CURSEC]
            CMP     AL,CS:[BX].SECLIM
            JBE     GOTSEC
            MOV     DH,[CURHD]
            INC     DH
            CMP     DH,CS:[BX].HDLIM
            JB      SETHEAD            ; Select new head
            CALL    STEP               ; Go on to next track
            XOR     DH,DH              ; Select head zero
    SETHEAD:
            MOV     [CURHD],DH
            CALL    DRIVESEL
            MOV     AL,1               ; First sector
            MOV     [CURSEC],AL        ; Reset CURSEC
    GOTSEC:
            OUT     DISK+2,AL     ; Tell controller which sector
            INC     [CURSEC]      ; We go on to next sector
            RET

    STEP:
    ASSUME  DS:NOTHING
            MOV     AL,58H+STPSPD  ; Step in w/ update, no verify
            CALL    DCOM
            PUSH    BX
            MOV     BL,[DRIVE]
            XOR     BH,BH           ; BX drive index
            ADD     BX,OFFSET TRKTAB        ; Get current track
            INC     BYTE PTR CS:[BX]        ; Next track
            POP     BX
            RET

    HOME:
    ASSUME  DS:NOTHING
            MOV     BL,3
    TRYHOM:
            MOV     AL,0CH+STPSPD   ; Restore with verify
            CALL    DCOM
            AND     AL,98H
            JZ      RET3
            JS      HOMERR          ; No retries if not ready
            PUSH    AX              ; Save real error code
            MOV     AL,58H+STPSPD   ; Step in w/ update no verify
            CALL    DCOM
            DEC     BL
            POP     AX              ; Get back real error code
            JNZ     TRYHOM
    HOMERR:
            STC
    RET3:   RET


    CHKNEW:
    ASSUME  DS:NOTHING
            MOV     AL,[DRIVE]      ; Get disk drive number
            MOV     AH,AL
            XCHG    AL,[CURDRV]     ; Make new drive current.
            CMP     AL,AH           ; Changing drives?
            JZ      RET1            ; No
    ;  If changing drives, unload head so the head load delay
    ; one-shot will fire again. Do it by seeking to the same
    ; track with the H bit reset.
    ;
            IN      AL,DISK+1       ; Get current track number
            OUT     DISK+3,AL       ; Make it the track to seek
            MOV     AL,10H          ; Seek and unload head

    DCOM:
    ASSUME  DS:NOTHING
            OUT     DISK,AL
            PUSH    AX
            AAM                     ; Delay 10 microseconds
            POP     AX
    GETSTAT:
            IN      AL,DISK+4
            TEST    AL,DONEBIT
            JZ      GETSTAT
            IN      AL,DISK
    RET1:   RET

    DRIVESEL:
    ASSUME  DS:NOTHING
    ; Select the drive based on current info
    ; Only AL altered
            MOV     AL,[DRIVE]
            OR      AL,SMALBIT + DDBIT      ; 5 1/4" IBM PC disks
            CMP     [CURHD],0
            JZ      GOTHEAD
            OR      AL,BACKBIT      ; Select side 1
    GOTHEAD:
            OUT     DISK+4,AL       ; Select drive and side
            RET

    GETERRCD:
    ASSUME  DS:NOTHING
            PUSH    CX
            PUSH    ES
            PUSH    DI
            PUSH    CS
            POP     ES              ; Make ES the local segment
            MOV     CS:[LSTERR],AL ; Terminate list w/ error code
            MOV     CX,NUMERR       ; Number of error conditions
            MOV     DI,OFFSET ERRIN ; Point to error conditions
            REPNE   SCASB
            MOV     AL,NUMERR-1[DI] ; Get translation
            STC                     ; Flag error condition
            POP     DI
            POP     ES
            POP     CX
            RET                     ; and return

    ; *********************************************************
    ;        BPB for an IBM floppy disk, Various parameters are
    ;        patched by BUILDBP to reflect the type of Media
    ;        inserted
    ;        This is a 9-sector, single-side BPB
    DRVBPB:
            DW      512          ; Physical sector size in bytes
            DB      1            ; Sectors/allocation unit
            DW      1            ; Reserved sectors for DOS
            DB      2            ; # of allocation tables
            DW      64           ; Number directory entries
            DW      9*40         ; Number 512-byte sectors
            DB      11111100B    ; Media descriptor
            DW      2            ; Number of FAT sectors
            DW      9            ; Sector limit
            DW      1            ; Head limit


    INITAB  DW      DRVBPB               ; Up to four units
            DW      DRVBPB
            DW      DRVBPB
            DW      DRVBPB

    ERRIN:  ; DISK ERRORS RETURNED FROM THE CONTROLLER
            DB      80H             ; No response
            DB      40H             ; Write protect
            DB      20H             ; Write Fault
            DB      10H             ; SEEK error
            DB      8               ; CRC error
            DB      1               ; Mapped from 10H
                                    ; (record not found) on Read
    LSTERR  DB      0               ; All other errors

    ERROUT: ; RETURNED ERROR CODES CORRESPONDING TO ABOVE
            DB      2               ; No response
            DB      0               ; Write Attempt
                                    ; On Write-protected disk
            DB      0AH             ; Write fault
            DB      6               ; SEEK Failure
            DB      4               ; Bad CRC
            DB      8               ; Sector not found
            DB      12              ; General error


    DRV$INIT:
    ;
    ;  Determine number of physical drives by reading config.sys
    ;
    ASSUME  DS:CODE
            PUSH    DS
            LDS     SI,[PTRSAV]
    ASSUME  DS:NOTHING
            LDS     SI,DWORD PTR [SI.COUNT] ; DS:SI points to
                                            ; config.sys
    SCAN_LOOP:
            CALL    SCAN_SWITCH
            MOV     AL,CL
            OR      AL,AL
            JZ      SCAN4
            CMP     AL,"s"
            JZ      SCAN4

    WERROR: POP     DS
    ASSUME  DS:CODE
            MOV     DX,OFFSET ERRMSG2
    WERROR2: MOV    AH,9
            INT     21H
            XOR     AX,AX
            PUSH    AX                      ; No units
            JMP     SHORT ABORT

    BADNDRV:
            POP     DS
            MOV     DX,OFFSET ERRMSG1
            JMP     WERROR2

    SCAN4:
    ASSUME  DS:NOTHING
    ; BX is number of floppies
            OR      BX,BX
            JZ      BADNDRV                 ; User error
            CMP     BX,4
            JA      BADNDRV                 ; User error
            POP     DS
    ASSUME  DS:CODE
            PUSH    BX                      ; Save unit count
    ABORT:  LDS     BX,[PTRSAV]
    ASSUME  DS:NOTHING
            POP     AX
            MOV     BYTE PTR [BX].MEDIA,AL           ; Unit count
            MOV     [DRVMAX],AL
            MOV     WORD PTR [BX].TRANS,OFFSET DRV$INIT ; SET
                                                ; BREAK ADDRESS
            MOV     [BX].TRANS+2,CS
            MOV     WORD PTR [BX].COUNT,OFFSET INITAB
                                    ; SET POINTER TO BPB ARRAY
            MOV     [BX].ceOUNT+2,CS
            JMP     EXIT
    ;
    ;  Put switch in CL, value in BX
    ;
    SCAN_SWITCH:
            XOR     BX,BX
            MOV     CX,BX
            LODSB
            CMP     AL,10
            JZ      NUMRET
            CMP     AL,"-"
            JZ      GOT_SWITCH
            CMP     AL,"/"
            JNZ     SCAN_SWITCH
    GOT_SWITCH:
            CMP     BYTE PTR [SI+1],":"
            JNZ     TERROR
            LODSB
            OR      AL,20H          ;  Convert to lowercase
            MOV     CL,AL           ;  Get switch
            LODSB                   ;  Skip ":"
    ;
    ;   Get number pointed to by [SI]
    ;
    ;   Wipes out AX,DX only     BX returns number
    ;
    GETNUM1:LODSB
            SUB     AL,"0"
            JB      CHKRET
            CMP     AL,9
            JA      CHKRET
            CBW
            XCHG    AX,BX
            MOV     DX,10
            MUL     DX
            ADD     BX,AX
            JMP     GETNUM1

    CHKRET: ADD     AL,"0"
            CMP     AL," "
            JBE     NUMRET
            CMP     AL,"-"
            JZ      NUMRET
            CMP     AL,"/"
            JZ      NUMRET
    TERROR:
            POP     DS              ;  Get rid of return address
            JMP     WERROR
    NUMRET: DEC     SI
            RET

    ERRMSG1 DB      "SMLDRV: Bad number of drives",13,10,"$"
    ERRMSG2 DB      "SMLDRV: Invalid parameter",13,10,"$"
    CODE    ENDS
            END

    Character Device Driver

    The following program illustrates a character device driver program.


    ; ******************** A Character Device *******************

    Title   VT52 Console for 2.0    (IBM)

    ; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ;
    ;        IBM Addresses for I/O
    ;
    ; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

            CR=13              ; Carriage-Return
            BACKSP=8           ; BACKSPACE
            ESC=1BH
            BRKADR=6CH         ; 006C  Break vector address
            ASNMAX=200         ; Size of key assignment buffer

    CODE    SEGMENT BYTE

        ASSUME CS:CODE,DS:NOTHING,ES:NOTHING
    ; ----------------------------------------------------------
    ;
    ;        C O N - Console Device Driver
    ;
    CONDEV:                            ; Header for device "CON"
            DW      -1,-1
            DW      1000000000010011B  ; CON IN AND CON OUT
            DW      STRATEGY
            DW      ENTRY
            DB      'CON     '

    ; -----------------------------------------------------------
    ;
    ;        Command JUMP Tables
    CONTBL:
            DW      CON$INIT
            DW      EXIT
            DW      EXIT
            DW      CMDERR
            DW      CON$READ
            DW      CON$RDND
            DW      EXIT
            DW      CON$FLSH
            DW      CON$WRIT
            DW      CON$WRIT
            DW      EXIT
            DW      EXIT

    CMDTABL DB      'A'
            DW      CUU             ; cursor up
            DB      'B'
            DW      CUD             ; cursor down
            DB      'C'
            DW      CUF             ; cursor forward
            DB      'D'
            DW      CUB             ; cursor back
            DB      'H'
            DW      CUH             ; cursor position
            DB      'J'
            DW      ED              ; erase display
            DB      'K'
            DW      EL              ; erase line
            DB      'Y'
            DW      CUP             ; cursor position
            DB      'j'
            DW      PSCP            ; save cursor position
            DB      'k'
            DW      PRCP            ; restore cursor position
            DB      'y'
            DW      RM              ; reset mode
            DB      'x'
            DW      SM              ; set mode
            DB      00

    PAGE
    ; ---------------------------------------------------
    ;
    ;        Device entry point
    ;
    CMDLEN  =       0       ; Length of this command
    UNIT    =       1       ; Subunit Specified
    CMD     =       2       ; Command Code
    STATUS  =       3       ; Status
    MEDIA   =       13      ; Media Descriptor
    TRANS   =       14      ; Transfer Address
    COUNT   =       18      ; Count of blocks or characters
    START   =       20      ; First block to transfer

    PTRSAV  DD      0

    STRATP  PROC    FAR

    STRATEGY:
            MOV     WORD PTR CS:[PTRSAV],BX
            MOV     WORD PTR CS:[PTRSAV+2],ES
            RET

    STRATP  ENDP

    ENTRY:
            PUSH    SI
            PUSH    AX
            PUSH    CX
            PUSH    DX
            PUSH    DI
            PUSH    BP
            PUSH    DS
            PUSH    ES
            PUSH    BX

            LDS     BX,CS:[PTRSAV]  ; GET POINTER TO I/O PACKET

            MOV     CX,WORD PTR DS:[BX].COUNT    ; CX = COUNT

            MOV     AL,BYTE PTR DS:[BX].CMD
            CBW
            MOV     SI,OFFSET CONTBL
            ADD     SI,AX
            ADD     SI,AX
            CMP     AL,11
            JA      CMDERR

            LES     DI,DWORD PTR DS:[BX].TRANS

            PUSH    CS
            POP     DS

            ASSUME  DS:CODE

            JMP     WORD PTR [SI]              ; GO DO COMMAND

    PAGE
    ; =====================================================
    ; =
    ; =      Subroutines Shared by Multiple Devices
    ; =
    ; =====================================================
    ; -----------------------------------------------------
    ;
    ;        EXIT - All routines return through this path
    ;
    BUS$EXIT:                               ; Device Busy Exit
            MOV     AH,00000011B
            JMP     SHORT ERR1

    CMDERR:
            MOV     AL,3                ; Unknown command error

    ERR$EXIT:
            MOV     AH,10000001B            ; Mark error Return
            JMP     SHORT ERR1

    EXITP   PROC    FAR

    EXIT:   MOV     AH,00000001B
    ERR1:   LDS     BX,CS:[PTRSAV]
            MOV     WORD PTR [BX].STATUS,AX ; Mark
                                            ; Operation Complete

            POP     BX
            POP     ES
            POP     DS
            POP     BP
            POP     DI
            POP     DX
            POP     CX
            POP     AX
            POP     SI
            RET                        ; Restore REGS and Return
    EXITP   ENDP
    ; -----------------------------------------------
    ;
    ;        BREAK Key Handling
    ;
    BREAK:
            MOV     CS:ALTAH,3         ; Indicate BREAK key Set
    INTRET: IRET

    PAGE
    ;
    ;        WARNING - Variables are very order dependent,
                    so be careful when adding new ones!
    ;
    WRAP    DB      0               ;  0 = WRAP, 1 = NO WRAP
    STATE   DW      S1
    MODE    DB      3
    MAXCOL  DB      79
    COL     DB      0
    ROW     DB      0
    SAVCR   DW      0
    ALTAH   DB      0               ; Special key handling

    ; -------------------------------------------------------
    ;
    ;     CHROUT - Write out Char in AL using current attribute
    ;
    ATTRW   LABEL   WORD
    ATTR    DB      00000111B       ; Character Attribute
    BPAGE   DB      0               ; Base Page
    base    dw      0b800h

    chrout: cmp     al,13
            jnz     trylf
            mov     [col],0
            jmp     short setit

    trylf:  cmp     al,10
            jz      lf
            cmp     al,7
            jnz     tryback
    torom:
            mov     bx,[attrw]
            and     bl,7
            mov     ah,14
            int     10h
    ret5:   ret

    tryback:
            cmp     al,8
            jnz     outchr
            cmp     [col],0
            jz      ret5
            dec     [col]
            jmp     short setit

    outchr:
            mov     bx,[attrw]
            mov     cx,1
            mov     ah,9
            int     10h
            inc     [col]
            mov     al,[col]
            cmp     al,[maxcol]
            jbe     setit
            cmp     [wrap],0
            jz      outchr1
            dec     [col]
            ret
    outchr1:
            mov     [col],0
    lf:     inc     [row]
            cmp     [row],24
            jb      setit
            mov     [row],23
            call    scroll

    setit:  mov     dh,row
            mov     dl,col
            xor     bh,bh
            mov     ah,2
            int     10h
            ret

    scroll: call    getmod
            cmp     al,2
            jz      myscroll
            cmp     al,3
            jz      myscroll
            mov     al,10
            jmp     torom
    myscroll:
            mov     bh,[attr]
            mov     bl,' '
            mov     bp,80
            mov     ax,[base]
            mov     es,ax
            mov     ds,ax
            xor     di,di
            mov     si,160
            mov     cx,23*80
            cld
            cmp     ax,0b800h
            jz      colorcard

            rep     movsw
            mov     ax,bx
            mov     cx,bp
            rep     stosw
    sret:   push    cs
            pop     ds
            ret

    colorcard:
            mov     dx,3dah
    wait2:  in      al,dx
            test    al,8
            jz      wait2
            mov     al,25h
            mov     dx,3d8h
            out     dx,al           ; turn off video
            rep     movsw
            mov     ax,bx
            mov     cx,bp
            rep     stosw
            mov     al,29h
            mov     dx,3d8h
            out     dx,al           ; turn on video
            jmp     sret

    GETMOD: MOV     AH,15
            INT     16             ; get column information
            MOV     BPAGE,BH
            DEC     AH
            MOV     WORD PTR MODE,AX
            RET
    ; ------------------------------------------------------
    ;
    ;        Console Read Routine
    ;
    CON$READ:
            JCXZ    CON$EXIT
    CON$LOOP:
            PUSH    CX              ; Save Count
            CALL    CHRIN           ; Get Char in AL
            POP     CX
            STOSB                   ; Store Char at ES:DI
            LOOP    CON$LOOP
    CON$EXIT:
            JMP     EXIT
    ; ---------------------------------------------------------
    ;
    ;        Input Single Char into AL
    ;
    CHRIN:  XOR     AX,AX
            XCHG    AL,ALTAH     ; Get Character & Zero ALTAH
            OR      AL,AL
            JNZ     KEYRET

    INAGN:  XOR     AH,AH
            INT     22
    ALT10:
            OR      AX,AX       ; Check for non-key after BREAK
            JZ      INAGN
            OR      AL,AL       ; Special case?
            JNZ     KEYRET
            MOV     ALTAH,AH        ; Store special key
    KEYRET: RET
    ; ----------------------------------------------------------
    ;
    ;        Keyboard Non-destructive Read, No Wait
    ;
    CON$RDND:
            MOV     AL,[ALTAH]
            OR      AL,AL
            JNZ     RDEXIT

    RD1:    MOV     AH,1
            INT     22
            JZ      CONBUS
            OR      AX,AX
            JNZ     RDEXIT
            MOV     AH,0
            INT     22
            JMP     CON$RDND

    RDEXIT: LDS     BX,[PTRSAV]
            MOV     [BX].MEDIA,AL
    EXVEC:  JMP     EXIT
    CONBUS: JMP     BUS$EXIT
    ; ----------------------------------------------------------
    ;
    ;        Keyboard Flush Routine
    ;
    CON$FLSH:
            MOV     [ALTAH],0         ; Clear out holding buffer

            PUSH    DS
            XOR     BP,BP
            MOV     DS,BP                   ; Select segment 0
            MOV     DS:BYTE PTR 41AH,1EH    ; Reset KB queue head
                                            ; pointer
            MOV     DS:BYTE PTR 41CH,1EH    ; Reset tail pointer
            POP     DS
            JMP     EXVEC
    ; ----------------------------------------------------------
    ;
    ;        Console Write Routine
    ;
    CON$WRIT:
            JCXZ    EXVEC
            PUSH    CX
            MOV     AH,3            ; Set current cursor position
            XOR     BX,BX
            INT     16
            MOV     WORD PTR [COL],DX
            POP     CX

    CON$LP: MOV     AL,ES:[DI]      ; Get Char
            INC     DI
            CALL    OUTC            ; Output Char
            LOOP    CON$LP          ; Repeat until all through
            JMP     EXVEC

    COUT:   STI
            PUSH    DS
            PUSH    CS
            POP     DS
            CALL    OUTC
            POP     DS
            IRET

    OUTC:   PUSH    AX
            PUSH    CX
            PUSH    DX
            PUSH    SI
            PUSH    DI
            PUSH    ES
            PUSH    BP
            CALL    VIDEO
            POP     BP
            POP     ES
            POP     DI
            POP     SI
            POP     DX
            POP     CX
            POP     AX
            RET


    ; ----------------------------------------------------------
    ;
    ;        Output Single Char in AL to Video Device
    ;
    VIDEO:  MOV     SI,OFFSET STATE
            JMP     [SI]

    S1:     CMP     AL,ESC                 ; Escape sequence?
            JNZ     S1B
            MOV     WORD PTR [SI],OFFSET S2
            RET

    S1B:    CALL    CHROUT
    S1A:    MOV     WORD PTR [STATE],OFFSET S1
            RET


    S2:     PUSH    AX
            CALL    GETMOD
            POP     AX
            MOV     BX,OFFSET CMDTABL-3
    S7A:    ADD     BX,3
            CMP     BYTE PTR [BX],0
            JZ      S1A
            CMP     BYTE PTR [BX],AL
            JNZ     S7A
            JMP     WORD PTR [BX+1]


    MOVCUR: CMP     BYTE PTR [BX],AH
            JZ      SETCUR
            ADD     BYTE PTR [BX],AL
    SETCUR: MOV     DX,WORD PTR COL
            XOR     BX,BX
            MOV     AH,2
            INT     16
            JMP     S1A


    CUP:    MOV     WORD PTR [SI],OFFSET CUP1
            RET
    CUP1:   SUB     AL,32
            MOV     BYTE PTR [ROW],AL
            MOV     WORD PTR [SI],OFFSET CUP2
            RET
    CUP2:   SUB     AL,32
            MOV     BYTE PTR [COL],AL
            JMP     SETCUR

    SM:     MOV     WORD PTR [SI],OFFSET S1A
            RET


    CUH:    MOV     WORD PTR COL,0
            JMP     SETCUR

    CUF:    MOV     AH,MAXCOL
            MOV     AL,1
    CUF1:   MOV     BX,OFFSET COL
            JMP     MOVCUR

    CUB:    MOV     AX,00FFH
            JMP     CUF1

    CUU:    MOV     AX,00FFH
    CUU1:   MOV     BX,OFFSET ROW
            JMP     MOVCUR

    CUD:    MOV     AX,23*256+1
            JMP     CUU1

    PSCP:   MOV     AX,WORD PTR COL
            MOV     SAVCR,AX
            JMP     SETCUR

    PRCP:   MOV     AX,SAVCR
            MOV     WORD PTR COL,AX
            JMP     SETCUR

    ED:     CMP     BYTE PTR [ROW],24
            JAE     EL1

            MOV     CX,WORD PTR COL
            MOV     DH,24
            JMP     ERASE

    EL1:    MOV     BYTE PTR [COL],0
    EL:     MOV     CX,WORD PTR [COL]
    EL2:    MOV     DH,CH
    ERASE:  MOV     DL,MAXCOL
            MOV     BH,ATTR
            MOV     AX,0600H
            INT     16
    ED3:    JMP     SETCUR


    RM:     MOV     WORD PTR [SI],OFFSET RM1
            RET
    RM1:    XOR     CX,CX
            MOV     CH,24
            JMP     EL2

    CON$INIT:
            int     11h
            and     al,00110000b
            cmp     al,00110000b
            jnz     iscolor
            mov     [base],0b000h          ; look for bw card
    iscolor:
            cmp     al,00010000b           ; look for 40 col mode
            ja      setbrk
            mov     [mode],0
            mov     [maxcol],39

    setbrk:
            XOR     BX,BX
            MOV     DS,BX
            MOV     BX,BRKADR
            MOV     WORD PTR [BX],OFFSET BREAK
            MOV     WORD PTR [BX+2],CS

            MOV     BX,29H*4
            MOV     WORD PTR [BX],OFFSET COUT
            MOV     WORD PTR [BX+2],CS

            LDS     BX,CS:[PTRSAV]
            MOV     WORD PTR [BX].TRANS,OFFSET CON$INIT
                                ; SET BREAK ADDRESS
            MOV     [BX].TRANS+2,CS
            JMP     EXIT

    CODE    ENDS
            END



────────────────────────────────────────────────────────────────────────────
Chapter 3  MS-DOS Technical Information

    3.1   Introduction
    3.2   MS-DOS Initialization
    3.3   The Command Processor
    3.4   MS-DOS Disk Allocation
    3.5   MS-DOS Disk Directory
    3.6   File Allocation Table (FAT)
            3.6.1   How to Use the FAT (12-Bit FAT Entries)
            3.6.2   How to Use the FAT (16-Bit FAT Entries)
    3.7   MS-DOS Standard Disk Formats


3.1  Introduction

    This chapter describes how MS-DOS initializes and how it allocates disk
    space for the root directory, the File Allocation Tables (FAT), and the
    data area. For programmers writing installable device drivers, this
    chapter explains MS-DOS disk directory entries and File Allocation Tables.
    At the end of the chapter, Tables 3.1 and 3.2 describe MS-DOS standard
    formats for floppy disks.


3.2  MS-DOS Initialization

    MS-DOS initialization consists of several steps. When you reset your
    computer or turn on its power, the ROM (Read Only Memory) BIOS is invoked
    and performs hardware checks and initialization. The ROM BIOS then
    examines drive A for the boot sector. If it locates a boot sector, the ROM
    BIOS reads it into low memory and gives it control. If it doesn't find the
    boot sector, the ROM BIOS then looks in the active partition of the hard
    disk. If it still doesn't find the boot sector, then the ROM BIOS invokes
    ROM BASIC.

    On a removable disk (3.5-inch, 5.25-inch, or 8-inch disk), the boot sector
    is always located on track 0, sector 1, side 0 of the disk. On a hard
    disk, the boot sector begins on the first sector of the MS-DOS partition.
    The hard disk boot sector also includes a partition table. This table
    identifies the active MS-DOS partition and any other partitions, such as
    an extended MS-DOS partition, on the hard disk. Notice that extended
    MS-DOS partitions are not bootable.

    The boot sector then reads the following files, in the order listed:

    io.sys
    msdos.sys


    ──────────────────────────────────────────────────────────────────────────
    Note
    Versions of MS-DOS prior to 3.3 required the io.sys file to be
    contiguous. This is no longer a requirement.
    ──────────────────────────────────────────────────────────────────────────

    Next, the system initialization routine SYSINIT loads all of the resident
    device drivers. Then, it searches for a config.sys file on the boot disk.
    SYSINIT allocates memory for buffers and files, based on settings in the
    config.sys file or system default settings. If the config.sys file
    specifies any installable device drivers, these are installed next.

    Finally, SYSINIT executes the MS-DOS command processor, command.com.


3.3  The Command Processor

    The command processor command.com consists of three parts:

    ■  A resident part resides in memory immediately following msdos.sys and
        its data area. This part contains routines to process Interrupts 22H
        (Terminate Process Exit Address), 23H (CONTROL+C Exit Address), and 24H
        (Critical-Error-Handler Address), as well as a routine to reload the
        transient part, if needed. All standard MS-DOS error handling is done
        within this part of command.com. This includes displaying error
        messages and processing the Abort, Retry, Fail, or Ignore messages.

    ■  An initialization part follows the resident part. During startup, the
        initialization part is given control; it contains the processor setup
        routine in the autoexec.bat file. The initialization part determines
        the segment address at which programs can be loaded, and because it is
        no longer needed, is overlaid by the first program that command.com
        loads.

    ■  A transient part is loaded at the high end of memory. This part
        contains all the internal command processors and the batch file
        processor.

        The transient part of the command processor produces the system prompt
        (A>, for example), reads commands from the keyboard (or from batch
        files), and causes them to be executed. For external commands, the
        transient part builds a command line and issues Function 4B00H (Load
        and Execute Program) to load and transfer control to the program.


3.4  MS-DOS Disk Allocation

    The area on a disk partitioned for use by MS-DOS is formatted as follows:

    1. Reserved area──variable size

    2. First copy of File Allocation Table──variable size

    3. Additional copies of File Allocation Table──variable size (optional)

    4. Root directory──variable size

    5. File data area

    Space for a file in the data area is not preallocated. The space is
    allocated one cluster at a time. A cluster consists of one or more
    consecutive sectors (the number of sectors in a cluster must be a power of
    2); the cluster size is determined at format time. All the clusters for a
    file are "chained" together in the File Allocation Table, discussed in
    greater detail in Section 3.6, "File Allocation Table (FAT)." MS-DOS
    normally keeps a second copy of the FAT for consistency, except in the
    case of reliable storage such as a virtual RAM disk. Should the disk
    develop a bad sector in the middle of the first FAT, MS-DOS can use the
    second. This avoids loss of data due to an unreadable FAT.


3.5  MS-DOS Disk Directory

    The format utility builds the root directory for all disks. This
    directory's location on the disk and the maximum number of entries are
    dependent on the media. Specifications for standard removable-disk formats
    are outlined later in this chapter. Notice, however, that MS-DOS regards
    directories, other than the root directory, as files, so there is no limit
    to the number of files that the subdirectories under the root directory
    may contain. All directory entries are 32 bytes in length and are in the
    following format (notice that byte offsets are in hexadecimal):

    Byte               Function
    ──────────────────────────────────────────────────────────────────────────
    0-7                Filename. Eight characters, left-aligned and padded, if
                        necessary, with blanks. The first byte of this field
                        indicates the file status as follows:

╓┌─┌──────────────────┌─────────────────┌────────────────────────────────────╖
                        Byte              Status
    ──────────────────────────────────────────────────────────────────────────
                        00H               The directory entry has never been
                                        used. This is used to limit the
                                        length of directory searches, for
                                        performance reasons.

                        05H               The first character of the filename
                                        contains an E5H character.

                        2EH               The entry is for a directory. If the
                                        second byte is also 2EH, the cluster
                                        field contains the cluster number of
                                        this directory's parent directory
                                        (0000H if the parent directory is the
                                        root directory). Otherwise, bytes 01H
                        Byte              Status
    ──────────────────────────────────────────────────────────────────────────
                                        root directory). Otherwise, bytes 01H
                                        through 0AH are all spaces, and the
                                        cluster field contains the cluster
                                        number of this directory.

                        E5H               The file was used, but it has since
                                        been erased.
                        Any other character is the first character of a
                        filename.

    8-0A               Filename extension.

    0B                 File attribute. The attribute byte is mapped as follows
    ──────────────────────────────────────────────────────────────────────────


╓┌─┌──────────────────┌─────────────────┌────────────────────────────────────╖
                        Byte              Contents
    ──────────────────────────────────────────────────────────────────────────
                        Byte              Contents
    ──────────────────────────────────────────────────────────────────────────
                        01H               File is marked read-only. An attempt
                                        to open the file for writing using
                                        Function 3DH (Open Handle) results
                                        in an error code being returned. This
                                        value can be used in programs along
                                        with the other attributes in this
                                        list. Attempts to delete the file
                                        with Function 13H (Delete File) or
                                        Function 41H (Delete Directory
                                        Entry) will also fail.

                        02H               Hidden file. The file is excluded
                                        from normal directory searches.

                        04H               System file. The file is excluded
                                        from normal directory searches.
                        08H               The entry contains the volume label
                                        in the first 11 bytes. The entry
                                        contains no other usable information
                        Byte              Contents
    ──────────────────────────────────────────────────────────────────────────
                                        contains no other usable information
                                        (except date and time of creation)
                                        and may exist only in the root
                                        directory.

                        10H               The entry defines a subdirectory and
                                        is excluded from normal directory
                                        searches.

                        20H               Archive bit. The bit is set to "on"
                                        whenever the file has been written to
                                        and closed.

                                        Note: The system files (io.sys and
                                        msdos.sys) are marked as read-only,
                                        hidden, and system files. Files can
                                        be marked hidden when they are
                                        created. Also, you may change the
                                        read-only, hidden, system, and
                        Byte              Contents
    ──────────────────────────────────────────────────────────────────────────
                                        read-only, hidden, system, and
                                        archive attributes through Function
                                        43H (Get/Set File Attributes).
    ──────────────────────────────────────────────────────────────────────────


╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
    Byte               Function
    ──────────────────────────────────────────────────────────────────────────
    0C-15              Reserved.

    16-17              Time the file was created or last updated. The hour,
                        minutes, and seconds are mapped into two bytes as
                        follows (bit 7 on the left, 0 on the right):

                        Offset 17H
                        | H | H | H | H | H | M | M | M |

                        Offset 16H
    Byte               Function
    ──────────────────────────────────────────────────────────────────────────
                        Offset 16H
                        | M | M | M | S | S | S | S | S |

                        where:


                        H is the binary number of hours (0-23).
                        M is the binary number of minutes (0-59).
                        S is the binary number of two-second increments.


    18-19              Date the file was created or last updated. The year,
                        month, and day are mapped into two bytes as follows:

                        Offset 19H
                        | Y | Y | Y | Y | Y | Y | Y | M |

                        Offset 18H
                        | M | M | M | D | D | D | D | D |
    Byte               Function
    ──────────────────────────────────────────────────────────────────────────
                        | M | M | M | D | D | D | D | D |

                        where:


                        Y is the year, 0-119 (1980-2099).
                        M is the month (1-12).
                        D is the day of the month (1-31).
    ──────────────────────────────────────────────────────────────────────────


    Byte               Function
    ──────────────────────────────────────────────────────────────────────────
    1A-1B              Starting cluster; the number of the first cluster in
                        the file.

                        ■ Notice that the first cluster for data space on all
                        disks is cluster 002.

                        ■ The cluster number is stored with the least
                        significant byte first.

                        ■ For details about converting cluster numbers to
                        logical sector numbers, see Sections 3.6.1 and 3.6.2.

    1C-1F              File size in bytes. The first word of this four-byte
    ──────────────────────────────────────────────────────────────────────────


3.6  File Allocation Table (FAT)

    This section explains how MS-DOS allocates disk space in the data area for
    a file by using the File Allocation Table to convert the clusters of a
    file to logical sector numbers. The device driver is then responsible for
    locating the logical sector on the disk. Programs should use the MS-DOS
    file management function calls for accessing files. Programs that access
    the FAT are not guaranteed to be upwardly-compatible with future releases
    of MS-DOS. The following information is useful to system programmers who
    wish to write installable device drivers.

    The File Allocation Table is an array of 12-bit entries (1.5 bytes) for
    each cluster on the disk. For disks containing more than 4085 clusters, a
    16-bit FAT entry is used.

    The first two FAT entries are reserved; however, the device driver may use
    the first byte as a FAT ID byte for determining media. For hard disks, the
    value of this byte is F8H. See Tables 3.1 and 3.2 for the media byte
    descriptors used for 8-inch, 5.25-inch, and 3.5-inch disks.

    The third FAT entry, which starts at byte offset 4, begins the mapping of
    the data area (cluster 002). The operating system does not always
    sequentially write (to the disk) files in the data area. Instead, the
    system allocates the data area one cluster at a time, skipping over
    clusters it has already allocated. The first free cluster following the
    last cluster allocated for that file is the next cluster allocated,
    regardless of its physical location on the disk. This permits the most
    efficient use of disk space, since if you erase old files, you can free
    clusters, which the operating system can then allocate for new files. Each
    FAT entry contains three or four hexadecimal characters, depending on
    whether it is a 12-bit or 16-bit entry:

    Entry              Contents
    ──────────────────────────────────────────────────────────────────────────
    (0)000             If the cluster is unused and available.

    (F)FF7             The cluster has a bad sector in it if it is not part of
                        any cluster chain. MS-DOS will not allocate such a
                        cluster. So for its report, the chkdsk command counts
                        the number of bad clusters, which are not part of any
                        allocation chain.

    (F)FF8-FFF         The last cluster of a file.

    (X)XXX             Any other characters that are the cluster number of the
                        next cluster in the file. The number of the first
    ──────────────────────────────────────────────────────────────────────────

    The File Allocation Table always begins on the first sector after the
    reserved sectors. If the FAT is larger than one sector, the sectors are
    contiguous. The operating system usually writes two copies of the FAT to
    preserve data integrity. MS-DOS reads the FAT into one of its buffers,
    whenever needed (open, read, write, etc.). The operating system also gives
    this buffer a high priority to keep it in memory as long as possible.

3.6.1  How to Use the FAT (12-Bit FAT Entries)

    To get the starting cluster of a file, examine its directory entry (in the
    FAT). Then, to locate each subsequent cluster of the file, follow these
    steps:

    1. Multiply the cluster number just used by 1.5 (each FAT entry is 1.5
        bytes in length).

    2. The whole part of the product is an offset into the FAT, pointing to
        the entry that maps the cluster just used. That entry contains the
        cluster number of the next cluster of the file.

    3. Use a MOV instruction to move the word at the calculated FAT offset
        into a register.

    4. If the last cluster used was an even number, keep the low-order 12 bits
        of the register by using the AND operator with 0FFFH and the register.
        If the last cluster used was an odd number, keep the high-order 12 bits
        by using the SHR instruction to shift the register right four bits.

    5. If the resultant 12 bits are 0FF8H-0FFFH, the file contains no more
        clusters. Otherwise, the 12 bits contain the number of the next cluster
        in the file.

    To convert the cluster to a logical sector number (relative sector, such
    as that used by Interrupts 25H and 26H (Absolute Disk Read/Write) and by
    debug), follow these steps:

    1. Subtract two from the cluster number.

    2. Multiply the result by the number of sectors per cluster.

    3. To this result, add the logical sector number of the beginning of the
        data area.

3.6.2  How to Use the FAT (16-Bit FAT Entries)

    To get the starting cluster of a file, examine its directory entry (in the
    FAT). Then, to find the next file cluster, follow these steps:

    1. Multiply the cluster number last used by 2 (each FAT entry is 2 bytes).

    2. Use a MOV WORD instruction to move the word at the calculated FAT
        offset into a register.

    3. If the resultant 16 bits are 0FFF8-0FFFH, no more clusters are in the
        file. Otherwise, the 16 bits contain the number of the next cluster in
        the file.


3.7  MS-DOS Standard Disk Formats

    MS-DOS arranges data clusters on a disk to minimize head movement. MS-DOS
    then allocates all the space on one track (or cylinder) before moving to
    the next. It uses the sequential sectors on the lowest-numbered head, then
    all the sectors on the next head, and so on, until it has used all the
    sectors on all the heads of the track.

    The size of the MS-DOS partition on a hard disk determines the size of the
    FAT and root directory. Likewise, the type of floppy disk (tracks per
    side, sectors per track, etc.) determines how MS-DOS uses the disk. The
    removable disk formats listed in Tables 3.1 and 3.2 are standard and
    should be readable in the appropriate standard drive.

    Table 3.1
    MS-DOS Standard Removable-Disk Formats
    Disk Size in inches               5.25                         8
    ──────────────────────────────────────────────────────────────────────────
    WORD no. heads                  1     1     2    2           1    2      1
    Tracks/side                    40    40    40   40          77   77     77
    WORD sectors/track              8     9     8    9          26   26      8
    WORD bytes/sector             512   512   512  512         128  128    024
    BYTE sectors/ cluster           1     1     2    2           4    4      1
    WORD reserved sectors           1     1     1    1           1    4      1
    Byte no. FATs                   2     2     2    2           2    2      2
    WORD root directory entries    64    64   112  112          68   68    192
    WORD no. sectors              320   360   640  720        2002 2002    616
    BYTE media descriptor          FE    FC    FF   FD         FE☼   FD    FE☼
    WORD sectors/FAT                1     2     1    2           6    6      2
    WORD no. hidden sectors         0     0     0    0           0    0      0
    ──────────────────────────────────────────────────────────────────────────

    Table 3.2
    MS-DOS Standard Removable-Disk Formats (High-Density)
    Disk Size in inches                  3.5 or 5.25            3.5      5.25
    ──────────────────────────────────────────────────────────────────────────
    WORD no. heads                  1     2     2    2           2           2
    ──────────────────────────────────────────────────────────────────────────
    Tracks/side                    80    80    80   80          80          80
    WORD sectors/track              8     9     8    9          18          15
    WORD bytes/sector             512   512   512  512         512         512
    BYTE sectors/cluster            2     2     2    2           1           1
    WORD reserved sectors           1     1     1    1           1           1
    BYTE no. FATs                   2     2     2    2           2           2
    WORD root dir entries         112   112   112  112         224         224
    WORD no. sectors              640   720  1280 1440        2880        2400
    BYTE media descriptor☼         FA    FC    FB   F9          F0          F9
    WORD sectors/FAT                1     2     2    3           9           7
    WORD no. hidden sectors         0     0     0    0           0           0
    ──────────────────────────────────────────────────────────────────────────



────────────────────────────────────────────────────────────────────────────
Chapter 4  MS-DOS Control Blocks and Work Areas

    4.1   Introduction
    4.2   Typical Contents of an MS-DOS Memory Map
    4.3   The MS-DOS Program Segment


4.1  Introduction

    This chapter describes a typical MS-DOS memory map and explains how a
    program is loaded into memory. It also describes the structure of an
    MS-DOS program segment and the contents of segment registers for .exe and
    .com program files.


4.2  Typical Contents of an MS-DOS Memory Map

    A typical MS-DOS memory map contains the following information:

    ┌─────────────────────────────────────────────────────┐
    │             ROM and Video Buffers                   │
    ├─────────────────────────────────────────────────────┤
    │          Transient Part of COMMAND.COM              │
    ├─────────────────────────────────────────────────────┤
    │                                                     │
    │                                                     │
    │                                                     │
    │                                                     │
    │              Transient Program Area                 │
    ├─────────────────────────────────────────────────────┤
    │                                                     │
    │                                                     │
    │                                                     │
    │                                                     │
    │          External Commands and Utilities            │
    │                                                     │
    ├─────────────────────────────────────────────────────┤
    │           Resident Part of COMMAND.COM              │
    ├─────────────────────────────────────────────────────┤
    │ MS-DOS buffers, control areas, & installed drivers  │
    ├─────────────────────────────────────────────────────┤
    │                                                     │
    │                MSDOS.SYS                            │
    ├─────────────────────────────────────────────────────┤
    │        IO.SYS and resident device drivers           │
    ├─────────────────────────────────────────────────────┤
    │                 Interrupt Vectors                   │
    0 └─────────────────────────────────────────────────────┘

    During system initialization, MS-DOS loads the io.sys and msdos.sys files
    into low memory (Notice that in MS-DOS 4.0, the msdos.sys file is not
    required to be written contiguously to the disk). The io.sys system file
    is the MS-DOS interface to hardware. The msdos.sys system file includes
    MS-DOS interrupt handlers and service routines (Interrupt 21H functions).

    Next, the system initialization routine loads the resident and installable
    device drivers. Above the installable device drivers, MS-DOS writes the
    resident part of command.com. This part includes interrupt handlers for
    Interrupts 22H (Terminate Process Exit Address), 23H (CONTROL+C Handler
    Address), 24H (Critical-Error-Handler Address) and code to reload the
    transient part. The transient part of command.com is reloaded into high
    memory. It includes the command interpreter, the internal MS-DOS commands,
    and the batch processor.

    External command and utility (.com and .exe) files are loaded into the
    transient program area. MS-DOS also allocates 256 bytes of user stack used
    with .com files. User memory is allocated from the lowest end of available
    memory that fulfills the allocation request.


4.3  The MS-DOS Program Segment

    When you type an external command or execute a program through Function
    4B00H or 4B03H (Load and Execute Program or Overlay, also called EXEC),
    MS-DOS determines the lowest available free memory address to use as the
    start of the program. The memory starting at this address is called the
    Program Segment.

    The EXEC system call sets up the first 256 bytes of the Program Segment
    for the program being loaded into memory. The program is then loaded
    following this block. A .exe file with minalloc and maxalloc both set to
    zero is loaded as high as possible.

    At offset 0 within the Program Segment, MS-DOS builds the Program Segment
    Prefix control block. The program returns from EXEC by one of five
    methods:

    ■  By issuing an Interrupt 21H with AH=4CH (End Process)

    ■  By issuing an Interrupt 21H with AH=31H (Keep Process)

    ■  By a long jump to offset 0 in the Program Segment Prefix

    ■  By issuing an Interrupt 20H (Progam Terminate) with CS:0 pointing at
        the PSP

    ■  By issuing an Interrupt 21H with register AH=0 and with CS:0 pointing
        at the PSP

    ──────────────────────────────────────────────────────────────────────────
    Note
    The first two methods are preferred for functionality, compatibility,
    and efficiency in future versions of MS-DOS.
    ──────────────────────────────────────────────────────────────────────────

    All five methods transfer control to the program that issued the EXEC
    call. The first two methods return a completion code. They also restore
    the addresses of Interrupts 22H, 23H, and 24H (Terminate Process Exit
    Address, CONTROL+C Handler Address, and Critical-Error-Handler Address)
    from the values saved in the Program Segment Prefix of the terminating
    program. Control then passes to the terminate address.

    If a program returns to command.com, control transfers to the resident
    portion. If the program is a batch file (in process), it continues.
    Otherwise, command.com performs a checksum on the transient part, reloads
    it if necessary, issues the system prompt, and waits for you to type
    another command.

    When a program receives control, the following conditions are in effect:

    For All Programs:

    ■  The segment address of the passed environment is at offset 2CH in the
        Program Segment Prefix.

    ■  The environment is a series of ASCII strings (totaling less than 32K)
        in the form:

        NAME=parameter


    ■  A byte of zeros terminates each string, and another byte of zeros
        terminates the set of strings.

        Following the last byte of zeros is a set of initial arguments that the
        operating system passes to a program. This set of arguments contains a
        word count followed by an ASCII string. If the file is in the current
        directory, the ASCII string contains the drive and pathname of the
        executable program as passed to the EXEC function call. If the file is
        not in the current directory, EXEC concatenates the name of the file
        with the name of the path. Programs may use this area to determine
        where the program was loaded.

    ■  The environment built by the command processor contains at least a
        comspec=string (the parameters on comspec define the path that MS-DOS
        uses to locate command.com on the disk). The last path and prompt
        commands issued are also in the environment, along with any environment
        strings you have defined with the MS-DOS set command.

    ■  EXEC passes a copy of the invoking process environment. If your
        application uses a "keep process" concept, you should be aware that the
        copy of the environment passed to you is static. That is, it will not
        change even if you issue subsequent set, path, or prompt commands.
        Conversely, any modification of the passed environment by the
        application is not reflected in the parent process environment. For
        instance, a program cannot change the MS-DOS environment values as the
        set command does.

    ■  The Disk Transfer Address (DTA) is set to 80H (default DTA in the
        Program Segment Prefix). The Program Segment Prefix contains file
        control blocks at 5CH and 6CH. MS-DOS formats these blocks using the
        first two parameters that you typed when entering the command. If
        either parameter contained a pathname, then the corresponding FCB
        contains only the valid drive number. The filename field is not valid.

    ■  An unformatted parameter area at 81H contains all the characters typed
        after the command (including leading and embedded delimiters), with the
        byte at 80H set to the number of characters. If you type <, >, or
        parameters on the command line, they do not appear in this area (nor
        the filenames associated with them). Redirection of standard input and
        output is transparent to applications.

    ■  Register AX indicates whether the drive specifiers (entered with the
        first two parameters) are valid, as follows:

        AL=FF if the first parameter contained an invalid drive specifier
        (otherwise AL=00)

        AH=FF if the second parameter contained an invalid drive specifier
        (otherwise AH=00)

    ■  Offset 2 (one word) contains the segment address of the first byte of
        unavailable memory. Programs must not modify addresses beyond this
        point unless these addresses were obtained by allocating memory via
        Function 48H (Allocate Memory).

    For Executable (.exe) Programs:

    ■  DS and ES registers point to the Program Segment Prefix.

    ■  CS,IP,SS, and SP registers contain the values that Microsoft link sets
        in the .exe image.

    For Executable (.com) Programs:

    ■  All four segment registers contain the segment address of the initial
        allocation block that starts with the Program Segment Prefix control
        block.

    ■  .com programs allocate all of user memory. If the program invokes
        another program through Function 4B00H or 4B03H (EXEC), it must first
        free some memory through Function 4AH (Set Block) to provide space for
        the program being executed.

    ■  The Instruction Pointer (IP) is set to 100H.

    ■  The Stack Pointer register is set to the end of the program's segment.

    ■  A .com program places a word of zeros on top of the stack. Then, by
        doing a RET instruction last, your program can exit to command.com.
        This method assumes, however, that you have maintained your stack and
        code segments.

    Figure 4.1 illustrates the format of the Program Segment Prefix. All
    offsets are in hexadecimal.

                        (Offsets in Hex)
    0 ┌─────────────┬─────────────┬─────────────────────────────┐
    │             │ End of      │                             │
    │   INT 20H   │ alloc.      │        Reserved             │
    │             │ block       │         04H                 │
    8 ├─────────────┼─────────────┴──────────┬──────────────────┤
    │             │   Terminate address    │   CONTROL+C exit │
    │  Reserved   │       (IP, CS)         │   address (IP)   │
    │             │                        │                  │
    10├────────────┬┴────────────────────────┼──────────────────┤
    │CONTROL+C   │ Hard error exit address │                  │
    │exit        │       (IP, CS)          │                  │
    │address (CS)│                         │                  │
    ├────────────┴─────────────────────────┘                  │
    │                                                         │
    │                   Used by MS-DOS                        │
    │                                                         │
    │                         5CH                             │
    │                                                         │
    ├─────────────────────────────────────────────────────────┤
    │                                                         │
    │    Formatted Parameter Area 1 formatted as standard     │
    │                    unopened FCB 6CH                     │
    ├─────────────────────────────────────────────────────────┤
    │                                                         │
    │    Formatted Parameter Area 2 formatted as standard     │
    │     unopened FCB (overlaid if FCB at 5CH is opened)     │
    80├─────────────────────────────────────────────────────────┤
    │             Unformatted Parameter Area                  │
    │            (default Disk Transfer Area)                 │
    │       Initially contains command invocation line.       │
    └─────────────────────────────────────────────────────────┘
    100

    Figure 4.1  Program Segment Prefix

    ──────────────────────────────────────────────────────────────────────────
    Important
    Programs must not alter any part of the Program Segment Prefix below
    offset 5CH.
    ──────────────────────────────────────────────────────────────────────────



────────────────────────────────────────────────────────────────────────────
Chapter 5  National Language Support

    5.1   Introduction
    5.2   National Language Support Calls
    5.3   Font Files
            5.3.1   Font File Structure


5.1  Introduction

    National language support for this version of MS-DOS 4.0 includes these
    major features:

    ■  Country-dependent information

    ■  Support for national keyboard layouts

    ■  Programming interfaces for national language support

    ■  Utility commands

    Country-dependent information is available on a per-country basis and
    includes the following:

    ■  Time, date, and currency

    ■  Lowercase-to-uppercase character-conversion tables

    ■  Collating sequence for character sorting

    ■  Valid single-byte characters used in filenames

    Selectable keyboard support for different keyboard layouts is provided.

    The MS-DOS 4.0 programming interfaces for national language support allow
    applications to use the country-dependent information just described. To
    access this information, applications do not need to change the current
    country code of the system.

    Utility commands allow the user to select the keyboard layout and system
    country code.

    This version of MS-DOS does not support right-to-left national languages.


5.2  National Language Support Calls

    The following function calls allow an application to tailor its operation
    to the current country code and to accept or change the current code page.
    A country code defines the country in which you live or work. MS-DOS uses
    this code to prepare and assign default code pages for your system. A code
    page is a table that defines the character set you are using. A character
    set is a country-specific or language-specific group of characters that
    are translated from the code page table and displayed on your screen or
    printer. Each code page character set contains 256 characters. The
    following function calls are also used by MS-DOS 4.0 to support the
    National Language requirements:

    ■  Function 440CH (Generic IOCtl) ── supports code page switching on a
        per-device basis.

    ■  Function 65H (Get Extended Country Information) ── returns standard
        country information and points to related case-map or collating tables.

    ■  Function 66H (Get/Set Global Code Page) ── gets or sets the code page
        used by the kernel and by all devices.

    These functions support access to country-dependent information, all of
    which resides in one file named country.sys.


5.3  Font Files

    Font files, also called code page information files, contain the images of
    code page character sets for use by display or printer devices. These font
    files are identified by a filename extension of .cpi. Four font files are
    included with MS-DOS 4.0:

    Font file          Supported device
    ──────────────────────────────────────────────────────────────────────────
    ega.cpi            Color console used with an EGA card

    lcd.cpi            Liquid crystal display

    4201.cpi           IBM 4201 Proprinter family and IBM 4202 Proprinter
                        printers

    4208.cpi           IBM 4207 Proprinter X24 and IBM 4208 Proprinter XL24
                        printers

    5202.cpi           IBM 5202 Quietwriter III printer
    ──────────────────────────────────────────────────────────────────────────

5.3.1  Font File Structure

    The contents of printer or display font files are structured as follows:

    ┌──────────────────────────────────────┐
    │ 23-BYTE File Header                  │
    ├──────────────────────────────────────┤
    │ WORD Information Header              │
    ├──────────────────────────────────────┤
    │ 28-BYTE Code Page Entry Header(s)    │
    ├──────────────────────────────────────┤
    │ Variable size Font Data Block(s)     │
    ├──────────────────────────────────────┤
    │ 150-BYTE Copyright Notice            │
    └──────────────────────────────────────┘


    Figure 5.1  Font File Structure

    The first code page entry header immediately follows the information
    header. The copyright notice is always at the end of the file. Between
    these, the file consists of arbitrarily intermingled code page entry
    headers and font data blocks.

    The font file fields are described in the following sections.

    File Header

    Each file must begin with a file header. There is only one of these
    headers per file. This header identifies the file as a valid font file and
    has the following format:

    Length             Parameter
    ──────────────────────────────────────────────────────────────────────────
    8 BYTES            File tag
    8 BYTES            Reserved
    WORD               Number of pointers
    BYTE               Type of pointer
    2 WORDS            Offset
    ──────────────────────────────────────────────────────────────────────────

    where:

    File tag begins with the byte 0FFH and is followed by the seven-byte
    string "font."

    Reserved is eight bytes of zeros.

    Number of pointers is the number of information pointers in the header.
    For MS-DOS 4.0, the value of this word should be 1.

    Type of pointer is the type of information pointers in the header. For
    MS-DOS 4.0, the value of this word should be 1.

    Offset is the offset, in bytes, from the beginning of the file to the
    information header.

    Information Header

    The information header begins at the offset given in the file header. It
    describes how many code pages are contained in the font file. The format
    of this one-word information header is:

    Length             Parameter
    ──────────────────────────────────────────────────────────────────────────
    WORD               Number of code pages
    ──────────────────────────────────────────────────────────────────────────

    where:

    Number of code pages is the number of code page entries in the file.

    Code Page Entry Header

    The number of code page entries in a font file is defined in the preceding
    section, "Information Header." For each code page in the font file, there
    is a code page entry header that gives general information about that code
    page entry in the file. The header has the following format:

    Length             Parameter
    ──────────────────────────────────────────────────────────────────────────
    WORD               Length
    2 WORDS            Pointer
    WORD               Device type
    8 BYTES            Device subtype
    WORD               Code page ID
    3 WORDS            Reserved
    2 WORDS            Offset
    ──────────────────────────────────────────────────────────────────────────

    where:

    Length is the size of the code page entry header. Since this header is a
    fixed size, it can be used for testing if the font file has been read in
    correctly.

    Pointer is a two-word pointer to the next code page entry header. The last
    header will point to nothing and will have a value of 0,0.

    Device type is 1 if the device is a raster display, or 2 if the device is
    a printer.

    Device subtype names the type of display or printer. This field also
    determines the name of the font file. For example, if the subtype is
    "EGA," the font file name is ega.cpi

    Code page ID defines a valid three-digit code page identification number.
    Valid code page numbers are 437, 850, 860, 863, and 865.

    Reserved is three words of zeros.

    Offset is a double-word pointer to the font data block associated with
    this code page and described in the next section.

    Font Data Block

    The Font Data Block consists of the following fields:

    ┌─────────────────────────────────────────┐
    │   6-BYTE Font Data Header               │
    ├─────────────────────────────────────────┤
    │   Variable length Font Description(s)   │
    └─────────────────────────────────────────┘

    The code page of a font data block is specified by the code page entry
    header whose Offset field points to that font data block. The device type
    contained in this code page entry header determines whether the font
    description(s) should be interpreted as raster-style (display) fonts or
    escape code sequences for particular downloadable device (printer) fonts.

    The Font Data Header, the Display Font Description, and the Printer Font
    Description fields are described in the following sections.

    Font Data Header:

    The Font Data Header indicates how many font descriptions exist for the
    current code page. This header has the following fields:

    Length             Parameter
    ──────────────────────────────────────────────────────────────────────────
    WORD               Reserved
    WORD               Number of fonts
    WORD               Length of font data
    ──────────────────────────────────────────────────────────────────────────

    where:

    Reserved must be 1.

    Number of fonts specifies the number of fonts (font descriptions) that
    immediately follow this font data header. These font descriptions are all
    associated with the font data block's code page. For printer devices, the
    number of fonts must be equal to one.

    Length of font data is equal to the sum of the sizes (in bytes) of the
    font descriptions, as described in the next two sections.

    Display Font Description:

    Each font description for a display device begins with a header that
    specifies the raster dimensions of each character in the font and the
    number of characters in the font. This is followed by the raster bitmaps
    for each character. The actual fields are:

    Length             Parameter
    ──────────────────────────────────────────────────────────────────────────
    BYTE               Height
    BYTE               Width
    BYTE               Relative height
    BYTE               Relative width
    WORD               Number of characters
    Variable           Bitmap
    ──────────────────────────────────────────────────────────────────────────

    where:

    Height is the number of rows in pixels that this character occupies on the
    screen. Width is the number of columns in pixels that this character
    occupies on the screen.

    Relative height is part of the aspect ratio. This is currently unused
    (zero).

    Relative width is part of the aspect ratio. This is currently unused
    (zero).

    Number of characters is the number of characters that are defined in the
    bitmap immediately following this header. Normally, the entire ASCII
    character set is defined, so that this value is usually 256.

    Bitmap is a sequence of bitmaps, one for each character in the font. Each
    character bitmap is a packed array of bits organized in rows by columns,
    starting at the upper left corner of the character's image. Since all
    current display fonts are eight bits wide, the number of bytes needed to
    encode this packed array is simply equal to the square area of a character
    in the font divided by 8 (the number of bits per byte).

    The total length of the display font description is six bytes plus the
    product of the number of characters in the descriptions and the number of
    bytes needed to encode a character bitmap.

    Printer Font Description:

    There are two formats for printer font descriptions. These vary only in
    the number of control sequences present in the font description.

    Unlike the display font description, the number of bytes in the printer
    font description cannot be directly determined. Instead, the size of the
    printer font description must be calculated from the font header's length
    value. As a result, only one printer font description can be contained in
    a given font data block (otherwise, the point at which the first ended and
    the second printer font description began could not be determined).

    The format for the printer font desription is:

    Length             Parameter
    ──────────────────────────────────────────────────────────────────────────
    WORD               Selection type
    WORD               Control sequence length
    Variable           Control sequence data
    Variable           Downloadable font data
    ──────────────────────────────────────────────────────────────────────────

    where:

    Selection type is either 1 or 2. It identifies which of the two formats
    for the printer font description is being used. These existing printer
    files use the following selection types:

        Selection type    Printer font file
        ───────────────────────────────────────────────────────────────────────
        1                 4201.cpi
        2                 4208.cpi
        2                 5202.cpi
        ───────────────────────────────────────────────────────────────────────

    Control sequence length is the number of bytes the control sequence data
    field takes. This length must always be less than 31 (bytes).

    Control sequence data is used for initializing the printer with this code
    page. The size of this field is defined in the control sequence length
    field.

    If the selection type field is 2, this control sequence information
    consists of a single escape sequence that selects the font for this code
    page (this font may have been downloaded).

    If the selection type field is 1, this control sequence information is
    further divided into two escape sequences. In this case, the control
    sequence data field consists of:

        Length            Parameter
        ───────────────────────────────────────────────────────────────────────
        BYTE              Hardware sequence length
        Variable          Hardware sequence
        BYTE              Downloadable sequence length
        Variable          Downloadable sequence
        ───────────────────────────────────────────────────────────────────────

        where:

        Hardware sequence length is the number of bytes contained in the
        hardware sequence.

        Hardware sequence is the escape sequence that selects the hardware
        (default) font of the printer.

        Downloadable sequence length is the number of bytes contained in the
        downloadable sequence.

        Downloadable sequence is the escape sequence that selects the
        downloaded font currently resident in the printer.

        Notice that the total number of bytes in these subfields must be equal
        to the number of bytes given by the control sequence length field in
        the printer font description.

    Downloadable font data consists of the escape sequence required to
    download the font description. This escape sequence is highly printer
    specific. The number of bytes in it is determined by subtracting the size
    of the initial portion of the printer font description from the Length
    field of the font data block.

    Since the 4208 and 5202 printers have hardware support for code pages,
    they do not need any font data to be downloaded. Therefore, this field is
    nonexistent in those font files.



────────────────────────────────────────────────────────────────────────────
Chapter 6  .Exe File Structure and Loading

    6.1   Introduction
    6.2   Format of a File Header
    6.3   The Relocation Table


6.1  Introduction

    ──────────────────────────────────────────────────────────────────────────
    Note
    This chapter describes .exe file structure and loading procedures for
    systems that use a version of MS-DOS earlier than 2.0. For MS-DOS
    versions 2.0 and later, use Function B00H (Load and Execute Program) to
    load (or load and execute) a .exe file.
    ──────────────────────────────────────────────────────────────────────────

    The .exe files produced by link consist of two parts:

    ■  Control and relocation information

    ■  The load module

    The control and relocation information is at the beginning of the file in
    an area called the header. Immediately following this header is the load
    module.


6.2  Format of a File Header

    The header is formatted as follows (notice that offsets are in
    hexadecimal):

╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
    Offset             Contents
    ──────────────────────────────────────────────────────────────────────────
    0-1                Must contain 4DH, 5AH.

    2-3                Number of bytes contained in last page; useful for
                        reading overlays.

    4-5                Size of the file in 512-byte pages, including the
                        header.

    6-7                Number of relocation entries in table.

    8-9                Size of the header in 16-byte paragraphs. Used to
    Offset             Contents
    ──────────────────────────────────────────────────────────────────────────
    8-9                Size of the header in 16-byte paragraphs. Used to
                        locate the beginning of the load module in the file.

    AH-BH              Minimum number of 16-byte paragraphs required above the
                        end of the loaded program.

    CH-DH              Maximum number of 16-byte paragraphs required above the
                        end of the loaded program. If both minalloc and
                        maxalloc are 0, the program is loaded as high as
                        possible.

    EH-FH              Initial value to be loaded into stack segment before
                        starting program execution. Must be adjusted by
                        relocation.

    10-11              Value to be loaded into the SP register before starting
                        program execution.

    12-13              Negative sum of all the words in the file.
    Offset             Contents
    ──────────────────────────────────────────────────────────────────────────
    12-13              Negative sum of all the words in the file.

    14-15              Initial value to be loaded into the IP register before
                        starting program execution.

    16-17              Initial value to be loaded into the CS register before
                        starting program execution. Must be adjusted by
                        relocation.

    18-19              Relative byte offset from beginning of run file to
                        relocation table.

    1AH-1BH            The number of the overlay as generated by link.
    ──────────────────────────────────────────────────────────────────────────



6.3  The Relocation Table

    The relocation table that follows the formatted area above consists of a
    variable number of relocation items. Each relocation item contains two
    fields: a two-byte offset value, followed by a two-byte segment value.
    These two fields contain the offset into a word's load module. This item
    requires modification before the module is given control. The following
    steps describe this process:

    1. The formatted part of the header is read into memory. Its size is 1BH.

    2. MS-DOS allocates a portion of memory depending on the size of the load
        module and the allocation numbers (AH-BH and CH-DH). MS-DOS then
        attempts to allocate 0FFFH paragraphs. This attempt always fails and
        returns the size of the largest free block. If this block is smaller
        than minalloc and loadsize, there is no memory error. But if this block
        is larger than maxalloc and loadsize, MS-DOS allocates (maxalloc +
        loadsize). Otherwise, it allocates the largest free block of memory.

    3. A Program Segment Prefix is built in the lowest part of the allocated
        memory.

    4. MS-DOS calculates the load module size (using offsets 4-5 and 8-9) by
        subtracting the header size from the file size. The actual size is
        adjusted down based on the contents of offsets 2-3. The operating
        system determines (based on the setting of the high/low load switch) an
        appropriate segment, called the start segment, where it loads the load
        module.

    5. The load module is read into memory beginning with the start segment.

    6. The items in the relocation table are read into a work area.

    7. MS-DOS adds the segment value of each relocation table item to the
        start segment value. This calculated segment, plus offset, points to
        the module to which the start segment value is added. The result is
        then placed back into the word in the load module.

    8. Once all relocation items have been processed, the operating system
        sets the SS and SP registers, using the values in the header. MS-DOS
        then adds the start segment value to SS and sets the ES and DS
        registers to the segment address of the Program Segment Prefix. The
        start segment value is then added to the header CS register value. The
        result, along with the header IP value, is the initial CS:IP to
        transfer to before starting execution of the program.



────────────────────────────────────────────────────────────────────────────
Chapter 7  Relocatable Object Module Formats

    7.1   Introduction
            7.1.1   Definition of Terms
    7.2   Module Identification and Attributes
            7.2.1   Segment Definition
            7.2.2   Addressing a Segment
            7.2.3   Symbol Definition
            7.2.4   Indices
    7.3   Conceptual Framework for Fixups
            7.3.1   Self-Relative Fixup
            7.3.2   Segment-Relative Fixup
    7.4   Record Sequence
    7.5   Introducing the Record Formats
            7.5.1   Sample Record Format (SAMREC)
            7.5.2   T-Module Header Record (THEADR)
            7.5.3   L-Module Header Record (LHEADR)
            7.5.4   List of Names Record (LNAMES)
            7.5.5   Segment Definition Record (SEGDEF)
            7.5.6   Group Definition Record (GRPDEF)
            7.5.7   Public Names Definition Record (PUBDEF)
            7.5.8   Communal Names Definition Record (COMDEF)
            7.5.9   Local Symbols Record (LOCSYM)
            7.5.10  External Names Definition Record (EXTDEF)
            7.5.11  Line Numbers Record (LINNUM)
            7.5.12  Logical Enumerated Data Record (LEDATA)
            7.5.13  Logical Iterated Data Record (LIDATA)
            7.5.14  Fixup Record (FIXUPP)
            7.5.15  Module End Record (MODEND)
            7.5.16  Comment Record (COMENT)
    7.6   Microsoft Type Representations for Communal Variables


7.1  Introduction

    This chapter presents the object record formats that define the
    relocatable object language for the 8086, 80186, and 80286
    microprocessors. The 8086 object language is the output of all language
    translators that have an 8086 processor and that will be linked by the
    Microsoft linker. The 8086 object language is used for input and output
    for object language processors such as linkers and librarians, and in the
    XENIX, PC-DOS, and MS-DOS operating systems.

    The 8086 object module formats let you specify relocatable memory images
    that may be linked together. These formats also allow efficient use of the
    memory mapping facilities of the 8086 family of microprocessors.

    The following table lists the record formats that Microsoft supports.
    Their abbreviations appear in parentheses. Each is described in this
    chapter.

    Table 7.1
    Object Module Record Formats
    Symbol Definition Records
    ──────────────────────────────────────────────────────────────────────────
    Public Names Definition Record       (PUBDEF)
    Communal Names Definition Record     (COMDEF)
    Local Symbols Record                 (LOCSYM)
    External Names Definition Record     (EXTDEF)
    Line Numbers Record                  (LINNUM)

    Data Records
    Logical Enumerated Data Record       (LEDATA)
    Logical Iterated Data Record         (LIDATA)
    T-Module Header Record               (THEADR)
    L-Module Header Record               (LHEADR)
    List of Names Record                 (LNAMES)
    Segment Definition Record            (SEGDEF)
    Group Definition Record              (GRPDEF)

    Fixup Record                         (FIXUPP)
    Module End Record                    (MODEND)
    Comment Record                       (COMENT)
    ──────────────────────────────────────────────────────────────────────────

    ──────────────────────────────────────────────────────────────────────────
    Note
    If an object module contains any undefined values, the behavior of the
    Microsoft linker is undefined. All undefined values should be considered
    reserved by Microsoft for future use.
    ──────────────────────────────────────────────────────────────────────────

7.1.1  Definition of Terms

    The following terms are fundamental to 8086 relocation and linkage:

    OMF - Object Module Formats

    MAS - Memory Address Space

    The 8086 MAS is one megabyte (1,048,576 bytes). Notice that the MAS is
    distinguished from actual memory, which may occupy only a portion of the
    MAS.

    Module

    A module is an "inseparable" collection of object code and other
    information produced by a translator.

    T-Module

    A T-module is a module created by a translator, such as Pascal or FORTRAN.

    The following restrictions apply to object modules:

    ■  Every module should have a name. Translators provide default names
        (possibly filenames or null names) for T-modules if neither the source
        code nor the user specifies otherwise.

    ■  Every T-module in a collection of linked modules should have a
        different name so that symbolic debugging systems can distinguish the
        various line numbers and local symbols. The Microsoft linker does not
        require or enforce this restriction.

    Frame

    A frame is a contiguous region of 64K of memory address space (MAS),
    beginning on a paragraph boundary (i.e., on a multiple of 16 bytes) or on
    a selector on the 80286 processor. This concept is useful because the
    contents of the four 8086 segment registers define four (possibly
    overlapping) frames; no 16-bit address in the 8086 code can access a
    memory location outside the current four frames.

    LSEG (Logical Segment)

    A logical segment (LSEG) is a contiguous region of memory whose contents
    are determined at translation time (except for address-binding). Neither
    the size nor the location in MAS are necessarily determined during
    translation: the size, although partially fixed, may not be final because
    the linker may combine the LSEG when linking with other LSEGs, forming a
    single LSEG. So that it can fit in a frame, an LSEG must not be larger
    than 64K. Thus, a 16-bit offset, from the base of a frame that covers the
    LSEG, may address any byte in that LSEG.

    PSEG (Physical Segment)

    This term is equivalent to frame. Some prefer "PSEG" to "frame" because
    the terms PSEG and LSEG reflect the "physical" and "logical" nature of the
    underlying segments.

    Frame Number

    Every frame begins on a paragraph boundary. The "paragraphs" in MAS can be
    numbered from 0 through 65,535. These numbers, each of which defines a
    frame, are called frame numbers.

    Group

    A group is a collection of LSEGs defined at translation time, whose final
    locations in MAS have been constrained so that at least one frame exists
    that covers (contains) every LSEG in the collection.

    The notation "Gr A(X,Y,Z,)" means that LSEGs X, Y, and Z form a group
    named A. That X, Y, and Z are all LSEGs in the same group does not imply
    any ordering of X, Y, and Z in MAS, nor does it imply any contiguity
    between X, Y, and Z.

    The Microsoft linker does not currently allow an LSEG to be a member of
    more than one group.

    Canonic

    On the 8086 processor, any location in MAS is contained in exactly 4096
    distinct frames, but one of these frames can be distinguished because it
    has a higher frame number. This frame is called the canonic frame of the
    location. In other words, the canonic frame of a given byte is the frame
    chosen so that the byte's offset from that frame lies in the range 0 to 15
    (decimal).

    For example, suppose FOO is a symbol defining a memory location. You would
    then refer to this frame as the "canonic frame of FOO." Similarly, if S is
    any set of memory locations, then a unique frame exists that has the
    lowest frame number in the set of canonic frames of the locations in S.
    This unique frame is called the canonic frame of the set S. You might
    refer similarly to the canonic frame of an LSEG or of a group of LSEGs.

    Segment Name

    LSEGs are assigned segment names at translation time. These names serve
    two purposes:

    ■  During linking, they play a role in determining which LSEGs are
        combined with other LSEGs.

    ■  They are used in assembly source code to specify membership in groups.

    Class Name

    The translator may optionally assign class names to LSEGs during
    translation. Classes define a partition on LSEGs: two LSEGs are in the
    same class if they have the same class name.

    The Microsoft linker applies the following semantics to class names: the
    class name "CODE", or any class name whose suffix is "CODE", implies that
    all segments of that class contain only code and may be considered
    read-only. Such segments may be overlaid if you specify the module
    containing the segment as part of an overlay.

    Overlay Name

    The linker may optionally assign an overlay name to LSEGs. The overlay
    name of an LSEG is ignored by Microsoft language linkers for version 3.0
    and later languages, but the standard MS-DOS linker supports it.

    Complete Name

    The complete name of an LSEG consists of the segment name, class name, and
    overlay name. The linker combines LSEGs from different modules if their
    complete names are identical.


7.2  Module Identification and Attributes

    A module header record, which provides a module name, is always the first
    record in a module. In addition to having a name, a module may represent a
    main program and may have a specified starting address. When linking
    multiple modules together, you should give only one module which has the
    main attribute. If more than one main module appears, the first takes
    precedence.

    In summary, modules may or may not be main and may or may not have a
    starting address.

7.2.1  Segment Definition

    A module is a collection of object code defined by a sequence of records
    that a translator produces. The object code represents contiguous regions
    of memory whose contents the linker determines during translation. These
    regions are LSEGs. A module defines the attributes of each LSEG. The
    segment definition record (SEGDEF) is responsible for maintaining all LSEG
    information (name, length, memory alignment, etc.). The linker requires
    the LSEG information when you combine multiple LSEGs and when it
    establishes segment addressability. The SEGDEF records must follow the
    first header record.

7.2.2  Addressing a Segment

    The 8086 addressing mechanism provides segment base registers from which
    you may address a 64-kilobyte region of memory (a frame). There is one
    code segment base register (CS), two data segment base registers (DS, ES),
    and one stack segment base register (SS).

    The possible number of LSEGs that may make up a memory image far exceeds
    the number of available base registers. Thus, base registers may require
    frequent loading. This would be the case in a modular program with many
    small data and/or code LSEGs.

    Since such frequent loading of base registers is undesirable, it is a good
    strategy to collect many small LSEGs together into a single unit that will
    fit in one memory frame. Then all the LSEGs may be addressed using the
    same base register value. This addressable unit is a group (see the
    definition of a group in Section 7.1.1, "Definition of Terms").

    To establish addressability of objects within a group, you must explicitly
    define each group in the module. The group definition record (GRPDEF)
    lists constituent segments by their segment names.

    The GRPDEF records within a module must follow all SEGDEF records because
    GRPDEF records will reference SEGDEF records in defining a group. The
    GRPDEF records must also precede all other records except header records,
    which the linker must process first.

7.2.3  Symbol Definition

    The Microsoft linker supports three different types of records belonging
    to the class of symbol definition records. The types are public names
    definition records (PUBDEFs), communal names definition records (COMDEFs),
    and external names definition records (EXTDEFs). You use these record
    types to define globally visible procedures and data items and to resolve
    external references.

7.2.4  Indices

    "Index" fields appear throughout this chapter. An index is an integer that
    selects a particular item from a collection of items; for example: name
    index, segment index, group index, external index, type index, etc.

    ──────────────────────────────────────────────────────────────────────────
    Note
    An index is normally a positive number. The index value zero is
    reserved, and may carry a special meaning depending on the type of index
    (for example, a segment index of zero specifies the "Unnamed" absolute
    pseudo-segment; a type index of zero specifies the "Untyped type").
    ──────────────────────────────────────────────────────────────────────────

    In general, indices must assume values that are quite large (that is, much
    larger than 255). Nevertheless, a great number of object files contain no
    indices with values greater than 50 or 100. Therefore, indices are encoded
    in one or two bytes, as required.

    The high-order (left-most) bit of the first (and possibly the only) byte
    determines whether the index occupies one byte or two. If the bit is 0,
    the index is a number between 0 and 127, occupying one byte. If the bit is
    1, the index is a number between 0 and 32K-1, occupying two bytes, and is
    determined as follows: the low-order eight bits are in the second byte,
    and the high-order seven bits are in the first byte.


7.3  Conceptual Framework for Fixups

    A fixup is a modification to object code that achieves address binding
    that a translator requested and a linker performed.

    ──────────────────────────────────────────────────────────────────────────
    Note
    This is the linker's definition of fixup. Nevertheless, the linker can
    modify object code (make a "fixup") that does not conform to this
    definition. For example, binding code to either hardware or software
    floating-point subroutines is a modification to an operation code, which
    is treated as an address. The previous definition of fixup is not
    intended to disallow or discourage modifications to the object code.
    ──────────────────────────────────────────────────────────────────────────

    8086-family translators need four kinds of data to specify a fixup:

    ■  The place and type of a location to be fixed up.

    ■  One of two possible fixup modes.

    ■  A target, which is the memory address to which the location must refer.

    ■  A frame that defines a context in which the reference takes place.

    Location ── There are five types of locations: a pointer, a base, an
    offset, a hibyte, and a lobyte.

    The vertical alignment of Figure 7.1 illustrates four points (remember
    that the high-order byte of a word in 8086 memory is the byte with the
    higher address):

    ■  A base is the high-order word of a pointer (the linker doesn't care
        whether the low-order word of the pointer is present).

    ■  An offset is the low-order word of a pointer (the linker doesn't care
        whether the high-order word follows).

    ■  A hibyte is the high-order half of an offset (the linker doesn't care
        whether the low-order half precedes).

    ■  A lobyte is the low-order half of an offset (the linker doesn't care
        whether the high-order half follows).

            ┌───────────────────┐
    Pointer:  │                   │
            └───────────────────┘

                        ┌─────────┐
    Base:               │         │
                        └─────────┘

            ┌─────────┐
    Offset:   │         │
            └─────────┘

                    ┌────┐
    Hibyte:        │    │
                    └────┘

            ┌────┐
    Lobyte:   │    │
            └────┘

    Figure 7.1  Location Types

    A Location is specified by two kinds of data: the location type, and where
    it is located.

    The location type is specified by the LOC field in the FIXUPP record's
    LOCAT field; where it is located is specified by the Data Record Offset
    field in the same LOCAT field.

    Mode ── The Microsoft linker supports two kinds of fixups: self-relative
    and segment-relative.

    Self-relative fixups support the 8-bit and 16-bit offsets used in CALL,
    JUMP, and SHORT-JUMP instructions. Segment-relative fixups support all
    other addressing modes of the 8086.

    Target ── The target is the location in MAS that the linker references.
    (More explicitly, the linker considers the target the lowest byte in the
    object that it is referencing.) The linker specifies a target by one of
    five methods. There are three "primary" methods and three "secondary"
    ones. Each primary method of specifying a target uses two kinds of data:
    an index number X, and a displacement D.

    Method             Explanation
    ──────────────────────────────────────────────────────────────────────────
    (T0)               X is a segment index. The target is the Dth byte in the
                        LSEG that the segment index identifies.

    (T1)               X is a group index. The target is the Dth byte in the
                        LSEG that the group index identifies.

    (T2)               X is an external index. The external index identifies
                        the external name that (eventually) gives the address
                        of a byte. The Dth byte following this byte is the
    ──────────────────────────────────────────────────────────────────────────

    Each secondary method of specifying a target uses only one item of data ──
    the index number X; this assumes an implicit displacement equal to zero.

    Method             Explanation
    ──────────────────────────────────────────────────────────────────────────
    (T4)               X is a segment index. The target is the 0th (first)
                        byte in the LSEG that the segment index identifies.

    (T5)               X is a group index. The target is the 0th (first) byte
                        in the LSEG in the specified group located (eventually)
                        lowest in MAS.

    (T6)               X is an external index. The target is the byte whose
                        address is the external name that the external index
    ──────────────────────────────────────────────────────────────────────────

    The following nomenclature describes a target:

    Nomenclature                                     Method
    ──────────────────────────────────────────────────────────────────────────
    Target: SI(segment name), displacement           [T0]
    Target: GI(group name), displacement             [T1]
    Target: EI(symbol name), displacement            [T2]
    Target: SI (segment name)                        [T4]
    Target: GI (group name)                          [T5]
    Target: EI (symbol name)                         [T6]
    ──────────────────────────────────────────────────────────────────────────

    The following examples illustrate how this notation is used:

    Sample Nomenclature                  Explanation
    ──────────────────────────────────────────────────────────────────────────
    Target: SI(CODE), 1024               The 1025th byte in the segment CODE.

    Target: GI(DATAAREA)                 The location in MAS of a group called
                                        DATAAREA.

    Target: EI(SIN)                      The address of the external
                                        subroutine SIN.

    Target: EI(PAYSCHEDULE), 24          The 24th byte following the location
                                        of an external data structure called
    ──────────────────────────────────────────────────────────────────────────

    Frame ── Every 8086 memory reference is to a location contained within a
    frame. This frame is designated by the contents of a segment register. For
    the linker to form a correct, usable memory reference, it must know what
    the target is, and to which frame the reference is being made. Thus, every
    fixup specifies such a frame, in one of five methods. Some methods use
    data, X, which is in the index number. Other methods require no data.

    The five methods of specifying frames are as follows:

    Method             Explanation
    ──────────────────────────────────────────────────────────────────────────
    (F0)               X is a segment index. The frame is the canonic frame of
                        the LSEG that the segment index defines.

    (F1)               X is a group index. The frame is the canonic frame
                        defined by the group (that is, the canonic frame
                        defined by the LSEG in the group located (eventually)
                        lowest in MAS).

    (F2)               X is an external index. The frame is determined when
                        the linker finds the external name's public definition.
                        There are two cases:

                        Case              Explanation
    ──────────────────────────────────────────────────────────────────────────
                        (F2a)             The linker defines the symbol
                                        relative to some LSEG, and there is
                                        no associated group. The linker also
                                        specifies the LSEG's canonic frame.

                        (F2c)             Regardless of how the linker defines
                                        the symbol, there is an associated
                                        group. And the linker specifies the
                                        canonic frame of the group. (The
                                        Group Index field of the PUBDEF
    ──────────────────────────────────────────────────────────────────────────

    Method             Explanation
    ──────────────────────────────────────────────────────────────────────────
    (F4)               No X. The frame is the canonic frame of the LSEG that
                        contains the location.

    (F5)               No X. The target determines the frame. There are three
    ──────────────────────────────────────────────────────────────────────────

                        Case              Explanation
    ──────────────────────────────────────────────────────────────────────────
                        (F5a)             The target specifies a segment index:
                                        in this case, the frame is determined
                                        as in (F0).

                        (F5b)             The target specifies a group index:
                                        in this case, the frame is determined
                                        as in (F1).

                        (F5c)             The target specifies an external
                                        index: in this case, the frame is
    ──────────────────────────────────────────────────────────────────────────

    The nomenclature that describes frames is similar to the above
    nomenclature for targets.

    Nomenclature                                     Method
    ──────────────────────────────────────────────────────────────────────────
    Frame: SI (segment name)                         [F0]
    Frame: GI (group name)                           [F1]
    Frame: EI (symbol name)                          [F2]
    Frame: Location                                  [F4]
    Frame: target                                    [F5]
    Frame: None                                      [F6]
    ──────────────────────────────────────────────────────────────────────────

    For an 8086 memory reference, the frame specified by a self-relative
    reference is usually the canonic frame of the LSEG that contains the
    location. Also, the frame specified by a segment-relative reference is the
    canonic frame of the LSEG that contains the target.

7.3.1  Self-Relative Fixup

    A self-relative fixup works as follows: the location implicitly defines a
    memory address──namely, the address of the byte following the location
    (because at the time of a self-relative reference, the 8086 IP
    (Instruction Pointer) is pointing to the byte following the reference).

    For 8086 self-relative references, if either the location or the target is
    outside the specified frame, the linker gives a warning. Otherwise, there
    is a unique l6-bit displacement that, when added to the address implicitly
    defined by the location, yields the relative position of the target in the
    frame.

    ■  If the location is an offset, the linker adds the displacement to the
        location (modulo 65,536) and reports no errors.

    ■  If the location is a lobyte, the displacement must be within the range
        {-128:127}; otherwise, the linker gives a warning. The linker adds the
        displacement to the location (modulo 256).

    ■  If the location is a base, pointer, or hibyte, it is unclear what the
        translator intended, so the linker's action is undefined.

7.3.2  Segment-Relative Fixup

    A segment-relative fixup operates as follows: a nonnegative 16-bit number,
    FBVAL, is defined as the frame number of the frame or selector value that
    the fixup specifies. A signed 20-bit number, FOVAL, is defined as the
    distance from the base of the frame to the target. If this signed 20-bit
    number is less than 0 or greater than 65,535, the linker reports an error.
    Otherwise, the linker uses FBVAL and FOVAL to fix up the location in the
    following fashion:

    ■  If the location is a pointer, the linker adds FBVAL (modulo 65,536) to
        the high-order word of the pointer, and adds FOVAL (modulo 65,536) to
        the low-order word of the pointer.

    ■  If the location is a base, the linker adds FBVAL (modulo 65,536) to the
        base and ignores FOVAL.

    ■  If the location is an offset, the linker adds FOVAL (modulo 65,536) to
        the offset and ignores FBVAL.

    ■  If the location is a hibyte, the linker adds (FOVAL/256) (modulo 256)
        to the hibyte and ignores FBVAL. (The division indicated is integer
        division; that is, the linker discards the remainder.)

    ■  If the location is a lobyte, the linker adds (FOVAL modulo 256) (modulo
        256) to the lobyte and ignores FBVAL.


7.4  Record Sequence

    An object code file must contain a sequence of one or more modules, or a
    library containing zero or more modules. The following syntax shows the
    valid record ordering necessary to form a module. In addition, the given
    semantic rules provide information about how to interpret the record
    sequence.

    ──────────────────────────────────────────────────────────────────────────
    Note
    The description language used in the following syntax is defined in
    WIRTH: CACM, November 1977, vol. 20, no. 11, pp. 822-823. The character
    strings represented by capital letters are not literals but identifiers,
    and are further defined in the record format section.
    ──────────────────────────────────────────────────────────────────────────



    object file  = tmodule

    tmodule      = {THEADR | LHEADR} seg-grp {component} modtail

    seg_grp      = {LNAMES} {SEGDEF} {EXTDEF | GRPDEF}

    component    = data | debug_record

    data         = content_def | thread_def |
                        PUBDEF | EXTDEF | COMDEF | LOCSYM

    debug_record = LINNUM

    content_def  = data_record {FIXUPP}

    thread_def   = FIXUPP (containing only Thread fields)

    data_record  = LIDATA | LEDATA

    modtail      = MODEND

    The following rules apply:

    ■  A FIXUPP record always refers to the previous data record.

    ■  All LNAMES, SEGDEF, GRPDEF, and EXTDEF records must precede all records
        that refer to them.

    ■  Comment records may appear anywhere in a file, except as the first or
        last record in a file or module, or within a content_def.


7.5  Introducing the Record Formats

    The following pages present diagrams of record formats in schematic form.
    Here is a sample record format that illustrates the various conventions:

7.5.1  Sample Record Format (SAMREC)

    ┌─────┬──────────┬─────///────┬────||||────┬─────┐
    │     │          │            │            │     │
    │ REC │  Record  │    Name    │   Number   │ CHK │
    │ TYP │  Length  │            │            │ SUM │
    │ xxH │          │            │            │     │
    │     │          │            │            │     │
    └─────┴──────────┼────///─────┼────||||────┴─────┘
                    │            │
                    └────rpt─────┘

    The Title and Official Abbreviation

    At the top of the figure is the name of the record format described, with
    its official abbreviation. To promote uniformity among various programs,
    including translators and debuggers, use the abbreviation in both code and
    documentation. The abbreviation of the record format is always six
    letters.

    The Boxes

    Each format is drawn with boxes of two sizes. A narrow box represents a
    single byte. A plain wide box represents two bytes. The wide boxes with
    three slashes in the top and bottom represent a variable number of bytes,
    one or more, depending upon the content. The wide boxes with four vertical
    bars in the top and bottom represent four-byte fields.

    RECTYP

    The first byte in each record contains a value between 0 and 255,
    indicating the record type.

    Record Length

    The second field in each record contains the number of bytes in the
    record, exclusive of the first two fields, where a field is a 16-bit
    number──a low byte followed by a high byte.

    Name

    Any field that indicates a name has the following internal structure: the
    first byte contains a number between 0 and 127, inclusive, indicating the
    number of remaining bytes in the field. The remaining bytes are
    interpreted as a byte string.

    Most translators constrain the character set to a subset of the ASCII
    character set.

    Number

    A four-byte number field represents a 32-bit unsigned integer, where the
    first eight bits (least-significant) are stored in the first byte (lowest
    address), the next eight bits are stored in the second byte, and so on.

    Repeated or Conditional Fields

    Some portions of a record format contain a field or series of fields that
    may be repeated one or more times. Such portions are indicated by the
    "repeated" or "rpt" brackets below the boxes.

    Similarly, some portions of a record format are present only if some given
    condition is true; these fields are indicated by similar "conditional" or
    "cond" brackets below the boxes.

    CHKSUM

    The last field in each record is a check sum, which contains the two's
    complement of the sum (modulo 256) of all other bytes in the record.
    Therefore, the sum (modulo 256) of all bytes in the record is zero.

    Bit Fields

    Sometimes descriptions of the contents of fields are at the bit level.
    Boxes with vertical lines drawn through them represent bytes or words; the
    vertical lines indicate bit boundaries. Thus, the following byte
    representation has three bit fields of three, one, and four bits,
    respectively.

    ┌───────────┬───┬───────────────┐
    │           │   │               │
    │           │   │               │
    │           │   │               │
    └───────────┴───┴───────────────┘
        3       1         4

7.5.2  T-Module Header Record (THEADR)

    ┌─────┬──────────┬─────///────┬─────┐
    │     │          │            │     │
    │ REC │  Record  │    T-      │ CHK │
    │ TYP │  Length  │    Module  │ SUM │
    │ 80H │          │    Name    │     │
    │     │          │            │     │
    └─────┴──────────┴────///─────┴─────┘

    T-Module Name

    The T-Module Name field contains the name for the T-module.

7.5.3  L-Module Header Record (LHEADR)

    ┌─────┬──────────┬─────///────┬─────┐
    │     │          │            │     │
    │ REC │  Record  │    L-      │ CHK │
    │ TYP │  Length  │    Module  │ SUM │
    │ 82H │          │    Name    │     │
    │     │          │            │     │
    └─────┴──────────┴────///─────┴─────┘

    L-Module Name

    The L-Module Name field contains the name for the L-module.

    Every module output from a translator must have a T-module or L-module
    header record. The linker requires a THEADR or LHEADR record to come first
    in the module and ignores any others. The LHEADR record is identical to
    the THEADR record, except it has a record type of 82H.

7.5.4  List of Names Record (LNAMES)

    ┌─────┬──────────┬─────///────┬─────┐
    │     │          │            │     │
    │ REC │  Record  │    Name    │ CHK │
    │ TYP │  Length  │            │ SUM │
    │ 96H │          │            │     │
    │     │          │            │     │
    └─────┴──────────┼────///─────┼─────┘
                    │            │
                    └────rpt─────┘

    The LNAMES record contains a list of names that the following SEGDEF and
    GRPDEF records may use as the names of segments, classes, and/or groups.

    The order of LNAMES records in a module and the order of names within each
    LNAMES record imply a mapping of these names to numbers: 1, 2, 3, etc.
    These numbers are used as "Name Indices" in the Segment Name Index, Class
    Name Index, and Group Name Index fields of the SEGDEF and GRPDEF records.

    Name

    This repeatable field provides a name, which may have zero length.

7.5.5  Segment Definition Record (SEGDEF)

    ┌───┬────────┬───///───┬─────────┬───///───┬─///─┬─///─┬───┐
    │   │        │         │         │         │     │     │   │
    │REC│ Record │ Segment │ Segment │ Segment │Class│Over │CHK│
    │TYP│ Length │   ATTR  │ Length  │   Name  │Name │Lay  │SUM│
    │98H│        │         │         │  Index  │Index│Name │   │
    │   │        │         │         │         │     │Index│   │
    └───┴────────┴───///───┴─────────┴───///───┴─///─┴─///─┴───┘

    Segment index values 1 through 32,767, which are used in other record
    types to refer to specific LSEGs, are defined implicitly by the sequence
    in which SEGDEF records appear in the object file.

    Segment ATTR

    The Segment ATTR field provides information on various attributes of a
    segment, and has the following format:

    ┌─────┬────────┬───────┐
    │     │        │       │
    │ ACB │ Frame  │  Off- │
    │  P  │ Number │  Set  │
    │     │        │       │
    │     │        │       │
    └─────┼────────┴───────┤
        │                │
        └───conditional──┘

    The ACBP byte contains four numbers──the A, C, B, and P attribute
    specifications. This byte has the following format:

    ┌───────────┬───────────┬───┬───┐
    │           │           │   │   │
    │     A     │     C     │ B │ P │
    │           │           │   │   │
    └───────────┴───────────┴───┴───┘

    A (Alignment) is a 3-bit subfield that specifies the alignment attribute
    of the LSEG. The semantics are defined as follows:

    Attribute      Explanation
    ──────────────────────────────────────────────────────────────────────────
    A=0            SEGDEF describes an absolute LSEG.
    A=1            SEGDEF describes a relocatable, byte-aligned LSEG.
    A=2            SEGDEF describes a relocatable, word-aligned LSEG.
    A=3            SEGDEF describes a relocatable, paragraph-aligned LSEG.
    A=4            SEGDEF describes a relocatable, page(256-byte)-aligned
                    LSEG.
    ──────────────────────────────────────────────────────────────────────────

    If A=0, the Frame Number and Offset fields are present. With the Microsoft
    linker, you may use absolute segments for addressing only; for example, to
    define the starting address of a ROM and to define symbolic names for
    addresses within the ROM. The linker ignores any data that belongs to an
    absolute LSEG and issues a warning if absolute segments are defined for a
    program that runs in protected mode. C (Combination) is a 3-bit subfield
    that specifies the combination attribute of the LSEG. Absolute segments
    (A=0) must have combination zero (C=0). For relocatable segments, the C
    field encodes a number (0,1,2,3,4,5,6, or 7) that indicates how the
    segment can be combined. One way to interpret this attribute is to
    consider how two LSEGs are combined.

    For example, suppose that X and Y are LSEGs, and that Z is the LSEG
    resulting from the combination of X and Y. Let LX and LY be the lengths of
    X and Y, and let MXY denote the maximum of LX, LY. Now, to accommodate the
    alignment attribute of Y, let G be the length of any gap required between
    the X and Y components of Z. Then, let LZ denote the length of the
    (combined) LSEG, Z; let dx (0(<=dx<LX) be the offset in X of a byte and,
    similarly, let dy be the offset (of a byte) in Y.

    The following table gives the length LZ of the combined LSEG, Z, and the
    offsets dx' and dy' in Z for the bytes corresponding to dx in X and to dy
    in Y.

    Table 7.2
    Combination Attribute Example
    C              LZ             dx'           dy'
    ──────────────────────────────────────────────────────────────────────────
    2              LX+LY+G        dx            dy+LX+G        "Public"
    5              LX+LY+G        dx            dy+LX+G        "Stack"
    6              MXY            dx            dy             "Common"
    ──────────────────────────────────────────────────────────────────────────

    Table 7.2 has no lines for C=0, 1, 3, 4, or 7. C=0 indicates that the
    relocatable LSEG may not be combined. C=1 and C=3 are undefined. C=4 and
    C=7 are treated the same as C=2.

    B (Big) is a 1-bit subfield that, if set to 1, indicates that the segment
    length is exactly 64K (65,536 bytes). In this case, the Segment Length
    field must contain zero.

    The P field must always be zero.

    The Frame Number and Offset fields (present only for absolute segments,
    A=0) specify the placement in MAS of the absolute segment. Offset is in
    the range between 0 and 15, inclusive. If you want an offset value larger
    than 15, you should adjust the frame number.

    Segment Length

    The Segment Length field gives a segment's length in bytes. This length
    may be zero. If it is, the linker does not delete the segment from the
    module. The Segment Length field is only large enough to hold numbers from
    0 to 64K-1, inclusive. To give the segment a length of 64K, you must use
    the B attribute bit in the ACBP field (see Segment ATTR in this section).

    Segment Name Index

    The segment name is a name that a programmer or translator assigns to the
    segment; for example, CODE, DATA, MODULENAME_CODE, TAXDATA, or STACK. The
    Segment Name Index field provides the segment name by indexing into the
    list of names provided by the LNAMES record.

    Class Name Index

    The class name is a name that a programmer or translator assigns to a
    segment. If none is assigned, the name is null and has a length of zero.
    The purpose of a class name is to let the programmer define a "handle" to
    order the LSEGs in MAS; for example, RED, WHITE, BLUE; or ROM, FASTRAM,
    DISPLAYRAM. The Class Name Index field provides the class name by indexing
    into the list of names provided by the LNAMES record.

    Overlay Name Index

    The overlay name is a name that the translator and/or the linker, at the
    programmer's request, applies to a segment. The overlay name, like the
    class name, may be null. The Overlay Name Index field provides the overlay
    name by indexing into the list of names provided by the LNAMES record.

    ──────────────────────────────────────────────────────────────────────────
    Note
    Microsoft language linkers (versions 3.00 and later) ignore the Overlay
    Name Index field, but the standard MS-DOS linker supports it.
    ──────────────────────────────────────────────────────────────────────────

7.5.6  Group Definition Record (GRPDEF)

    ┌─────┬──────────┬─────///────┬────///─────┬─────┐
    │     │          │            │            │     │
    │ REC │  Record  │    Group   │   Group    │ CHK │
    │ TYP │  Length  │    Name    │  Component │ SUM │
    │ 9AH │          │    Index   │ Descriptor │     │
    │     │          │            │            │     │
    └─────┴──────────┴──///───────┤──///───────┼─────┘
                                │            │
                                └──repeated──┘

    Group Name Index

    The linker may reference a collection of LSEGs with the group name. Most
    importantly, when the LSEGs are eventually fixed in MAS, a frame must
    exist that "covers" every LSEG of the group.

    The Group Name Index field provides the group name by indexing into the
    list of names provided by the LNAMES record.

    Group Component Descriptor

    This repeatable field has the following format:

    ┌─────┬────///────┐
    │     │           │
    │ FFH │  Segment  │
    │     │   Index   │
    │     │           │
    │     │           │
    └─────┴────///────┘

    The first byte of the Group Component Descriptor field contains 0FFH; the
    descriptor contains one field, which is a segment index that selects the
    LSEG described by a preceding SEGDEF record.

7.5.7  Public Names Definition Record (PUBDEF)

    ┌────┬──────────┬────///──┬───///───┬───────┬──///──┬─────┐
    │    │          │         │         │       │       │     │
    │ REC│  Record  │  Public │ Public  │Public │ Type  │ CHK │
    │ TYP│  Length  │   Base  │  Name   │Offset │ Index │ SUM │
    │ 90H│          │         │         │       │       │     │
    │    │          │         │         │       │       │     │
    └────┴──────────┴────///──┼──///────┴───────┴─///───┼─────┘
                            │                         │
                            └─────────repeated────────┘

    The PUBDEF record provides a list of one or more public names. For each
    name, three kinds of data are provided: (1) a base value for the name, (2)
    the offset value of the name, and (3) the type of entity represented by
    the name.

    Public Base

    The Public Base field has the following format:

    ┌────///────┬────///────┬───────────┐
    │           │           │           │
    │   Group   │  Segment  │   Frame   │
    │   Index   │   Index   │   Number  │
    │           │           │           │
    └────///────┴────///────┼───────────┤
                            │           │
                            └conditional┘

    The Group Index field has a format, which was given earlier, and contains
    a number between 0 and 32,767, inclusive. A nonzero value in the Group
    Index field associates a group with the public symbol and is used as
    described in case (F2c) of Section 7.3, "Conceptual Framework for
    Fixups." A zero value in the Group Index field indicates that there is no
    associated group.

    The Segment Index field has a format, which was given earlier, and
    contains a number between 0 and 32,767, inclusive.

    A nonzero value in the Segment Index field selects an LSEG. In this case,
    the location of each public symbol defined in the record is taken as a
    nonnegative displacement (given by a Public Offset field) from the first
    byte of the selected LSEG. Also, the Frame Number field must be absent.

    A zero value in the Segment Index field means that the defined symbols are
    absolute, and that the absolute addresses of the symbols are the values in
    the Public Offset field. The Group Index field is ignored. The Frame
    Number field contains a frame number only if the value of the Segment
    Index field is zero. The linker ignores this field.

    A nonzero value in the Group Index field selects some group. This group is
    taken as the "frame of reference" for references to all public symbols
    defined in this record. That is, the linker performs the following
    actions:

    ■  The linker converts any fixup of the form:

        Target: EI(P)

        Frame: Target

        (where P is a public symbol in this PUBDEF record) to a fixup of the
        form:

        Target: SI(L),d

        Frame: GI(G)

        where SI(L) and d are provided by the Segment Index and Public Offset
        fields. (The "normal" action would have the frame specifier in the new
        fixup be the same as in the old fixup: Frame:Target.)

    ■  When the linker converts the value of a public symbol, as defined by
        the Segment Index, Public Offset, and (optionally) Frame Number fields,
        to a {base,offset} pair, the base part is the base of the indicated
        group.

    A zero value in the Group Index field selects no group. The linker does
    not alter the frame specification of fixups referencing the symbol, and
    takes, as the base part of the absolute value of the public symbol, the
    canonic frame of the segment (either LSEG or PSEG) determined by the
    Segment Index field.

    Public Name

    The Public Name field gives the name of the object whose location in MAS
    is made available to other modules by the linker. The name must contain
    one or more characters.

    Public Offset

    The Public Offset field is a 16-bit value. It is either the offset of the
    public symbol with respect to an LSEG (if the segment index is greater
    than zero), or the offset of the public symbol with respect to the
    specified frame (if the segment index is equal to zero).

    Type Index

    The Type Index field identifies a single preceding TYPDEF (Type Definition
    Record), which contains a descriptor for the type of entity represented by
    the public symbol. The linker ignores this field.

7.5.8  Communal Names Definition Record (COMDEF)

    ┌─────┬─────┬────///────┬────///────┬──────┬──────────┬─────┐
    │     │     │           │           │      │          │     │
    │ REC │ REC │ Communal  │   Type    │ Data │ Communal │ CHK │
    │ TYP │ LEN │   Name    │   Index   │ Seg  │ Length   │ SUM │
    │ B0H │     │           │           │ Type │          │     │
    │     │     │           │           │      │          │     │
    └─────┴─────┼────///────┴────///────┴──────┴──────────┼─────┴
                │                                         │
                └───────repeated──────────────────────────┘

    The COMDEF record provides a list of one or more Communal Names, which
    define communal variables. A communal variable is an uninitialized public
    variable whose final size and location are not fixed during compiling.

    Communal variables are similar to FORTRAN common blocks in that if you are
    linking object modules that each declare a communal variable, then the
    size of that variable is the largest of the declared variables. In the C
    language, all uninitialized public variables are communal. The following
    example shows three different declarations of the same C communal
    variable:

    char    foo[4];          /* In file a.c */
    char    foo[1];          /* In file b.c */
    char    foo[1024];       /* In file c.c */

    If the objects produced from a.c, b.c, and c.c are linked together, the
    linker allocates 1024 bytes for the character array "foo."

    ──────────────────────────────────────────────────────────────────────────
    Note
    This record requires that a COMENT record from the Microsoft Extensions
    class appear before it in the object module.
    ──────────────────────────────────────────────────────────────────────────

    Communal Name

    The Communal Name field gives the communal variable name and must contain
    one or more characters.

    Communal names are treated as external names when an external name is
    requested elsewhere in the module. Communal names are ordered together
    with external names for the purpose of referring to an external name by
    its index. (See the description of the EXTDEF record later in this
    section for more details on external names.)

    Type Index

    The Type Index field is ignored by the Microsoft linker.

    Data Segment Type

    The Data Segment Type field is a single byte that describes the data
    segment in which the communal variable resides. It can contain the
    following values:

        62H (NEAR) = the communal variable is in the default data segment.
        61H (FAR) = the communal variable is not in the default data segment.


    Communal Length

    The Communal Length field describes the length of the communal variable
    according to its data segment type.

    If its value is NEAR (62H), the field represents the length in bytes.

    If its value is FAR (61H), the field represents:

    ┌─────//────┬─────///──────┐
    │ Number of │ Element size │
    │ elements  │ in bytes     │
    └────///────┴─────///──────┘


    The format of all the length fields is as follows:

    ┌─────┐
    │     │
    │  0  │
    │ to  │
    │ 127 │
    │     │
    └─────┘

    ┌─────┬───────────┐
    │     │           │
    │     │     0     │
    │ 129 │    to     │
    │(81H)│   64K-1   │
    │     │           │
    └─────┴───────────┘

    ┌─────┬─────────────────┐
    │     │                 │
    │     │        0        │
    │ 132 │       to        │
    │(84H)│      16M-1      │
    │     │                 │
    └─────┴─────────────────┘

    ┌─────┬─────────────────┐
    │     │                 │
    │     │     -2G-1       │
    │ 136 │       to        │
    │(88H)│      2G-1       │
    │     │                 │
    └─────┴─────────────────┘

    The first format (single byte), containing a value between 0 and 127,
    represents the number given.

    The second format, with a leading byte containing 129, represents the
    number contained in the following two bytes.

    The third format, with a leading byte containing 132, represents the
    number contained in the following three bytes.

    The fourth format, with a leading byte containing 136, represents the
    number contained in the following four bytes with its sign extended if
    necessary.

    Link-Time Semantics

    A PUBDEF matching a communal variable definition overrides the communal
    variable definition. Two communal variable definitions match if the names
    in their definitions match. If two matching definitions disagree on
    whether a communal variable is NEAR or FAR, the linker assumes the
    variable is NEAR.

    If the variable is NEAR, then its size is the largest of the sizes
    specified for it. If the variable is FAR, the linker issues a warning if
    the array element sizes conflict. If these sizes don't conflict, the
    variable's size is the element size multiplied by the largest number of
    elements specified. In addition, the sum of the sizes of all NEAR
    variables must not exceed 64 kilobytes, and the sum of the sizes of all
    FAR variables must not exceed the size of the machine's addressable memory
    space.

    HUGE Communal Variables:

    A FAR communal variable that is larger than 64 kilobytes (a HUGE communal
    variable) resides in segments that are contiguous (on an 8086) or that
    have consecutive selectors (on an 80286). No other data items reside in
    the segments occupied by a HUGE communal variable.

    If the linker finds matching HUGE and NEAR communal variable definitions,
    it issues a warning message, since it is impossible for a NEAR variable to
    be larger than 64 kilobytes.

7.5.9  Local Symbols Record (LOCSYM)

    ┌────┬──────────┬────///──┬───///───┬───────┬──///──┬─────┐
    │    │          │         │         │       │       │     │
    │ REC│  Record  │  Local  │ Local   │ Local │ Type  │ CHK │
    │ TYP│  Length  │   Base  │  Name   │Offset │ Index │ SUM │
    │ 92H│          │         │         │       │       │     │
    │    │          │         │         │       │       │     │
    └────┴──────────┴────///──┼──///────┴───────┴─///───┼─────┘
                            │                         │
                            └─────────repeated────────┘

    The LOCSYM record provides information for the definition of a local
    symbol, one that is visible only within the module in which it is defined.

    The form and meaning of each of the fields is identical to those in the
    PUBDEF record.

    ──────────────────────────────────────────────────────────────────────────
    Note
    The LOCSYM record requires that a COMENT record from the Microsoft
    Extensions class appear before it in the object module. Also, it is only
    recognized by Microsoft language linkers later than version 3.07.
    ──────────────────────────────────────────────────────────────────────────

7.5.10  External Names Definition Record (EXTDEF)

    ┌─────┬───────────┬────///────┬────///────┬─────┐
    │     │           │           │           │     │
    │ REC │  Record   │ External  │   Type    │ CHK │
    │ TYP │  Length   │   Name    │   Index   │ SUM │
    │ 8CH │           │           │           │     │
    │     │           │           │           │     │
    └─────┴───────────┼────///────┴────///────┼─────┘
                    │                       │
                    └───────repeated────────┘

    The EXTDEF record provides a list of external names and, for each name,
    the type of object it represents. The linker assigns to each external name
    the value provided by an identical public name or local name (if such a
    name is found).

    External Name

    The External Name field provides the external object name, which must have
    a nonzero length.

    Including a name in an EXTDEF record is an implicit request to link the
    object file to a module containing the same name declared as a public
    symbol, unless the name is defined as a local symbol within the same
    module as the EXTDEF. This request determines whether or not the external
    name is referenced within some FIXUPP record in the module.

    The order of EXTDEF records in a module and the order of external names
    within each EXTDEF record, together with COMDEF records and communal
    names, imply a mapping on the set of all external names requested by the
    module; for example: 1, 2, 3, etc. So to refer to a particular external
    name, the linker uses these numbers as "external indices" in the Target
    Datum and/or Frame Datum fields of FIXUPP records.

    External indices may not reference forward. For example, an EXTDEF
    defining the kth object must precede any record referring to that object
    with index k.

    Type Index

    The Type Index field is ignored by the Microsoft linker, except for linker
    versions earlier than 3.05, and for object modules lacking the COMENT
    record from the Microsoft Extensions class. For that case, refer to
    Section 7.6, "Microsoft Type Representations for Communal Variables."

7.5.11  Line Numbers Record (LINNUM)

    ┌─────┬──────────┬─────///───┬───────────┬───────────┬─────┐
    │     │          │           │           │           │     │
    │ REC │  Record  │   Line    │   Line    │   Line    │ CHK │
    │ TYP │  Length  │  Number   │  Number   │  Number   │ SUM │
    │ 94H │          │   Base    │           │  Offset   │     │
    │     │          │           │           │           │     │
    └─────┴──────────┴────///────┼───────────┴───────────┼─────┘
                                │                       │
                                └────────repeated───────┘

    The LINNUM record allows a translator to relate a line number in source
    code to the corresponding line in translated code.

    Line Number Base

    The Line Number Base field has the following format:

    ┌────///────┬────///────┐
    │           │           │
    │   Group   │  Segment  │
    │   Index   │   Index   │
    │           │           │
    └────///────┴────///────┘

    The Group Index field is ignored by the Microsoft linker.

    The Segment Index field determines the location of the first byte of code
    corresponding to some source line number.

    Line Number

    The Line Number field provides a binary line number between 0 and 32,767,
    inclusive. If the high-order bit is not zero, the number is considered
    undefined.

    Line Number Offset

    The Line Number Offset field is a 16-bit value, which is the offset of the
    line number with respect to an LSEG (if the segment index is greater than
    zero).

7.5.12  Logical Enumerated Data Record (LEDATA)

    ┌─────┬───────────┬────///────┬───────────┬─────┬─────┐
    │     │           │           │           │     │     │
    │ REC │  Record   │  Segment  │ Enumerated│     │ CHK │
    │ TYP │  Length   │   Index   │   Data    │ DAT │ SUM │
    │ A0H │           │           │  offset   │     │     │
    │     │           │           │           │     │     │
    └─────┴───────────┴────///────┴───────────┼─────┼─────┘
                                            │     │
                                            └─rpt─┘

    The LEDATA record provides contiguous data from which the linker may
    construct a portion of an 8086 memory image.

    Segment Index

    The Segment Index field, which must be nonzero, specifies an index
    relative to the SEGDEF records that precede the LEDATA record.

    Enumerated Data Offset

    The Enumerated Data Offset field specifies an offset that is relative to
    the base of the LSEG specified by the segment index. The field also
    defines the relative location of the first byte of the DAT field.
    Successive data bytes in the DAT field occupy successively higher
    locations of memory.

    DAT

    The DAT field provides up to 1024 consecutive bytes of relocatable or
    absolute data.

7.5.13  Logical Iterated Data Record (LIDATA)

    ┌─────┬──────────┬────///────┬───────────┬────///────┬─────┐
    │     │          │           │           │           │     │
    │ REC │  Record  │  Segment  │  Iterated │  Iterated │ CHK │
    │ TYP │  Length  │   Index   │    Data   │   Data    │ SUM │
    │ A2H │          │           │   Offset  │   Block   │     │
    │     │          │           │           │           │     │
    └─────┴──────────┴────///────┴───────────┼────///────┼─────┘
                                            │           │
                                            └─repeated──┘

    The LIDATA record provides contiguous data from which the linker may
    construct a portion of an 8086 memory image.

    Segment Index

    The Segment Index field, which must be nonzero, specifies an index that is
    relative to the SEGDEF records that precede the LIDATA record.

    Iterated Data Offset

    The Iterated Data Offset field specifies an offset that is relative to the
    base of the LSEG specified by the segment index. It also defines the
    relative location of the first byte in the iterated data block. Successive
    data bytes in the iterated data block occupy successively higher locations
    of memory.

    Iterated Data Block

    This repeated field is a structure specifying the repeated data bytes. It
    has the following format:

    ┌──────────┬────────────┬────///────┐
    │          │            │           │
    │  Repeat  │   Block    │           │
    │   Count  │   Count    │  Content  │
    │          │            │           │
    │          │            │           │
    └──────────┴────────────┴────///────┘

    ──────────────────────────────────────────────────────────────────────────
    Note
    The linker cannot handle LIDATA records whose iterated data blocks are
    larger than 512 bytes.
    ──────────────────────────────────────────────────────────────────────────

    Repeat Count:

    The Repeat Count field specifies the number of times to repeat the Content
    portion of this iterated data block. The value of the Repeat Count field
    must be nonzero.

    Block Count:

    The Block Count field specifies the number of iterated data blocks in the
    Content portion of this iterated data block. If the Block Count field has
    a value of zero, the Content portion of the iterated data block is
    interpreted as data bytes. If the field is nonzero, the Content portion is
    interpreted as that number of iterated data blocks.

    Content:

    The Content field may be interpreted in one of two ways, depending on the
    value of the previous Block Count field.

    If the Block Count field is zero, this field is a 1-byte count followed by
    the indicated number of data bytes. But if the Block Count field is
    nonzero, the Content field is interpreted as the first byte of another
    iterated data block.

7.5.14  Fixup Record (FIXUPP)

    ┌─────┬───────────┬────///────┬─────┐
    │     │           │           │     │
    │ REC │  Record   │  Thread   │ CHK │
    │ TYP │  Length   │    or     │ SUM │
    │ 9CH │           │   Fixup   │     │
    │     │           │           │     │
    └─────┴───────────┼────///────┼─────┘
                    │           │
                    └────rpt────┘

    The FIXUPP record specifies zero or more fixups. Each fixup requests a
    modification (fixup) to a location within the previous data record. A data
    record may be followed by more than one FIXUPP record that refers to it.
    Each fixup is specified by a Fixup field that specifies four kinds of
    data: a location, a mode, a target, and a frame. The frame and target may
    be specified completely within the Fixup field, or by reference to a
    preceding Thread field.

    A Thread field specifies a default target or frame that subsequently may
    be referred to. Eight threads are provided──four for frame specification
    and four for target specification. Once a thread has specified a target or
    frame, any Fixup fields that follow (in the same or following FIXUPP
    records) may refer to that target or frame until another Thread field with
    the same type (target or frame) and thread number (0-3) appears (in the
    same or in another FIXUPP record).

    Thread

    The Thread field has the following format:

    ┌─────┬────///────┐
    │     │           │
    │ TRD │   Index   │
    │ DAT │           │
    │     │           │
    └─────┼────///────│
        │           │
        └condiional─┘

    The TRD DAT (Thread Data) field is a byte with the following internal
    structure:

    ┌───┬───┬───┬───────────┬───────┐
    │   │   │   │           │       │
    │ 0 │ D │ 0 │  Method   │ THRED │
    │   │   │   │           │       │
    └───┴───┴───┴───────────┴───────┘

    The D field is one bit and defines the type of thread being used. If D=0,
    this bit defines a target thread, and if D=1, it defines a frame thread.

    Method is a 3-bit field containing a number between 0 and 3 (if D=0) or a
    number between 0 and 6 (if D=1).

    If D=0, then Method = (0, 1, 2, 4, 5, 6)mod 4, where 0, 1, 2, 4, 5, 6
    indicate methods T0, T1, T2, T4, T5, and T6 of specifying a target. Thus,
    Method indicates the kind of index or frame number required to specify the
    target, without indicating whether the target is specified by a primary or
    secondary method.

    If D=1, then Method = 0, 1, 2, 4, 5, corresponding to methods F0, F1, F2,
    F4, F5 of specifying a frame. Here, Method indicates the kind (if any) of
    index required to specify the frame.

    The THRED field contains a number between 0 and 3, inclusive, and
    associates a thread number to the frame or target defined by the Thread
    field.

    The Index field contains a segment index, group index, or external index
    depending on the specification in the Method field. If Method specifies F4
    or F5, this field will not be present.

    Fixup

    The Fixup field has the following format:

    ┌───────────┬─────┬────///────┬────///────┬────///────┐
    │           │     │           │           │           │
    │   LOCAT   │ FIX │   Frame   │  Target   │  Target   │
    │           │ DAT │   Datum   │   Datum   │   Dis-    │
    │           │     │           │           │ Placement │
    │           │     │           │           │           │
    └───────────┴─────┼────///────┼────///────┼────///────│
                    │           │           │           │
                    └────────────conditional────────────┘

    The LOCAT field is a byte pair with the following format:

    ┌───┬───┬───┬───────────┬───────────────────────────────────────┐
    │   │   │   │           │                                       │
    │ 1 │ M │ 0 │    LOC    │  D a t a   R e c o r d   O f f s e t  │
    │   │   │   │           │                                       │
    ├───┴───┴───┴───────────┴───────┬───────────────────────────────┤
    │                               │                               │
    └────────────lo byte────────────┴────────────hi byte────────────┘

    M is a 1-bit field that specifies the mode of the fixups: self-relative
    (if M=0) or segment-relative (if M=1).

    ──────────────────────────────────────────────────────────────────────────
    Note
    Self-relative fixups may not be applied to LIDATA records.
    ──────────────────────────────────────────────────────────────────────────

    LOC is a 3-bit field indicating that the byte(s) in the preceding data
    record to be fixed up are a lobyte (if LOC=0), an offset (if LOC=1), a
    base (if LOC=2), a pointer (if LOC=3), a hibyte (if LOC=4), or a
    "loader-resolved" offset (if LOC=5). Any other values in LOC are invalid.

    The Data Record Offset field contains a number between 0 and 1023,
    inclusive, that gives the relative position of the lowest order byte of
    location (the actual bytes being fixed up) within the preceding data
    record. The Data Record Offset value is relative to the first byte in the
    data fields in the data records.

    ──────────────────────────────────────────────────────────────────────────
    Note
    If the preceding data record is an LIDATA record, it is possible for the
    Data Record Offset value to designate a location within a Repeat Count
    field or a Block Count field of the Iterated Data Block field. Such a
    reference is an error. The linker's action on such a malformed record is
    undefined.
    ──────────────────────────────────────────────────────────────────────────

    The FIX DAT field is a byte with the following format:

    ┌───┬───────────┬───┬───┬───────┐
    │   │           │   │   │       │
    │ F │   Frame   │ T │ P │ TARGT │
    │   │           │   │   │       │
    └───┴───────────┴───┴───┴───────┘

    F is a 1-bit subfield that specifies whether the frame for this fixup is
    specified by a thread (if F=1) or explicitly (if F=0).

    The Frame field contains a number that is interpreted in one of two ways,
    as indicated by the F bit. If F is zero, the Frame field contains a number
    between 0 and 5, inclusive, corresponding to methods F0,...,F5 for
    specifying a frame. If F=1, then the Frame field contains a thread number
    (0-3). It specifies the frame most recently defined by a Thread field that
    defined a frame thread with the same thread number. (Notice that the
    Thread field may appear in the same FIXUPP record, or in an earlier one.)

    T is a 1-bit field that specifies whether the target specified for this
    fixup is defined by reference to a thread (T=1), or is given explicitly in
    the Fixup field (T=0).

    P is a 1-bit field that indicates whether the Target is specified by a
    primary method (requires a target displacement, if P=0) or by a secondary
    method (requires no target displacement, if P=1). Since a target thread
    does not have a primary/secondary attribute, the P bit is the only field
    that contains the target specification attribute.

    TARGT is interpreted as a 2-bit field. When T=0, it provides a number
    between 0 and 3, inclusive, corresponding to methods T0, T1, T2 or T4, T5,
    T6, depending on the value of P (where P is interpreted as the high-order
    bit of T0, T1, T2, T4, T5, or T6). When a thread specifies the target (if
    T=1), then the TARGET field specifies a thread number (0-3).

    The Frame Datum field is the "referent" portion of a frame specification,
    and is a segment index, group index, or external index. The Frame Datum
    field is present only when the frame is not specified by a thread (if F=0)
    or explicitly by methods F4, F5, or F6.

    The Target Datum field is the "referent" portion of a target
    specification, and is a segment index, group index, or external index. The
    Target Datum field is present only when a thread does not specify the
    target (if T=0).

    The Target Displacement field is the 2-byte displacement required by
    primary methods of specifying targets. This field is present if P=0.

    ──────────────────────────────────────────────────────────────────────────
    Note
    All these methods are described in Section 7.3, "Conceptual Framework
    for Fixups."
    ──────────────────────────────────────────────────────────────────────────

7.5.15  Module End Record (MODEND)

    ┌─────┬───────────┬─────┬────///────┬─────┐
    │     │           │     │           │     │
    │ REC │  Record   │ MOD │   START   │ CHK │
    │ TYP │  Length   │ TYP │   ADDRS   │ SUM │
    │ 8AH │           │     │           │     │
    │     │           │     │           │     │
    └─────┴───────────┴─────┼────///────┼─────┘
                            │           │
                            └conditional┘

    The MODEND record serves two purposes. It denotes the end of a module and
    indicates whether or not the module that just ended specifies an entry
    point to begin execution. If it does not, the linker specifies the
    execution address.

    MOD TYP

    The MOD TYP field specifies the attributes of the module. The bit
    allocation and associated meanings are as follows:

    ┌───────┬───┬───┬───┬───┬───┬───┐
    │       │   │   │   │   │   │   │
    │ MATTR │ 0 │ 0 │ 0 │ 0 │ 0 │ 1 │
    │       │   │   │   │   │   │   │
    └───────┴───┴───┴───┴───┴───┴───┘

    MATTR is a 2-bit field that specifies the following module attributes:

    MATTR              Module Attribute
    ──────────────────────────────────────────────────────────────────────────
    0                  Non-main module with no START ADDRS
    1                  Non-main module with START ADDRS
    2                  Main module with no START ADDRS
    3                  Main module with START ADDRS
    ──────────────────────────────────────────────────────────────────────────

    The START ADDRS field (present only if MATTR is 1 or 3) has the following
    format:

    ┌─────┬────///────┬────///────┬───────────┐
    │     │           │           │           │
    │ END │   Frame   │  Target   │  Target   │
    │ DAT │   Datum   │   Datum   │   Dis-    │
    │     │           │           │ Placement │
    │     │           │           │           │
    └─────┼────///────┴────///────┴───────────│
        │                                   │
        └────────────conditional────────────┘

    The starting address of a module has all the attributes of any other
    logical reference found in a module. The mapping of a logical starting
    address to a physical starting address is done in the same manner as
    mapping any other logical address to a physical address, as specified in
    the discussion of fixups and the FIXUPP record. The fields of the START
    ADDRS field have the same semantics as the FIX DAT, Frame Datum, Target
    Datum, and Target Displacement fields in the FIXUPP record. Only primary
    fixup methods are allowed. Frame method F4 is not allowed.

7.5.16  Comment Record (COMENT)

    ┌─────┬───────────┬───────────┬────///────┬─────┐
    │     │           │           │           │     │
    │ REC │  Record   │  Comment  │           │ CHK │
    │ TYP │  Length   │   Type    │  Comment  │ SUM │
    │ 88H │           │           │           │     │
    │     │           │           │           │     │
    └─────┴───────────┴───────────┴────///────┴─────┘

    The COMENT record allows translators to include comments in object text.

    Comment Type

    The Comment Type field indicates the type of comment that this record
    carries, allowing you to structure comments for processes that selectively
    act on comments.

    The format of the Comment Type field is as follows:

    ┌───┬───┬───┬───┬───┬───┬───┬───┬──────────────────────┐
    │ N │ N │   │   │   │   │   │   │          Comment     │
    │ P │ L │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │           Class      │
    └───┴───┴───┴───┴───┴───┴───┴───┴──────────────────────┘

    The NP (NOPURGE) bit, if set to 1, indicates that this comment cannot be
    purged by object file utility programs that can delete Comment records.

    The NL (NOLIST) bit, if set to 1, indicates that the text in the Comment
    field should not appear in the listing file of object file utility
    programs that can list object Comment records.

    The Comment Class field is a byte defined as follows:

╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
    Value              Meaning
    ──────────────────────────────────────────────────────────────────────────
    0                  Language Translator Comment (obsolete)

                        If the Comment field contains one of the strings "MS
                        PASCAL" or "FORTRAN 77," then the Comment record
                        enables the dsallocation switch on the Microsoft
                        linker.
    Value              Meaning
    ──────────────────────────────────────────────────────────────────────────
                        linker.

    156(9CH)           DOS Version

                        The Comment field contains a 2-byte integer that
                        specifies the DOS level number.

    157(9DH)           Memory Model

                        Indicates the memory model of the object module. The
                        Comment field contains a single byte with the values S,
                        M, L, or H, for SMALL, MEDIUM, LARGE, or HUGE,
                        respectively. This Comment record is used only by the
                        Microsoft XENIX linker.

    158(9EH)           Force Segment Ordering

                        Causes the linker to use a special segment ordering for
                        executable files. This Comment record has the same
    Value              Meaning
    ──────────────────────────────────────────────────────────────────────────
                        executable files. This Comment record has the same
                        effect as giving the dosseg switch to Microsoft
                        language versions of the linker.

    159(9FH)           Library Specifier

    129(81H)           Library Specifier (obsolete)

                        Specifies a library to add to the Microsoft linker's
                        library search list. The Comment field contains the
                        name of the library. Notice that, unlike all other name
                        specifications, the library name is not prefixed with
                        its length but is determined by the record length. The
                        nodefaultlibrarysearch switch causes the linker to
                        ignore these Comment records. The 159(9FH) class record
                        is ignored by XENIX versions of the Microsoft linker.

    161(A1H)           Microsoft Extensions

    Value              Meaning
    ──────────────────────────────────────────────────────────────────────────

                        Indicates that the object module contains extensions to
                        the original Microsoft adaptation of the Intel
                        Relocatable Object Module Format, such as the COMDEF
    ──────────────────────────────────────────────────────────────────────────


    Comment

    The Comment field provides the commentary information.


7.6  Microsoft Type Representations for Communal Variables

    ──────────────────────────────────────────────────────────────────────────
    Note
    Object modules not containing the COMENT record from the Microsoft
    Extensions class can represent communal variable definitions only with
    the obsolete method described here. Also, Microsoft language linkers
    earlier than version 3.05 can recognize this method only. The newer
    method uses the Communal Variable Definitions (COMDEF) record.
    ──────────────────────────────────────────────────────────────────────────

    A communal variable is defined in the object text by an EXTDEF record and
    the TYPDEF record to which it refers.

    The TYPDEF record of a communal variable has the following format:

    ┌─────┬────────┬───┬─────///────┬─────┐
    │ REC │ Record │   │    Eight   │ CHK │
    │ TYP │ Length │ 0 │    Leaf    │ SUM │
    │ 8EH │        │   │ Descriptor │     │
    └─────┴────────┴───┴─────///────┴─────┘

    The Eight Leaf Descriptor field has the following format:

    ┌───┬────///─────┐
    │ E │    Leaf    │
    │ N │ Descriptor │
    └───┴────///─────┘

    The EN bitfield specifies whether the next eight leaves in the Leaf
    Descriptor field are EASY (if EN = 0) or NICE (if EN = l). This byte is
    always zero for TYPDEFs of communal variables. The Leaf Descriptor field
    has one of the following two formats. The format for communal variables in
    the default data segment (NEAR variables) is as follows:

    ┌──────┬─────┬────///───┬─///────┐
    │ NEAR │ VAR │  Length  │  VAR   │
    │ 62H  │ TYP │    In    │ SUBTYP │
    │      │     │   Bits   │        │
    └──────┴─────┴────///───┼──///───┤
                            │        │
                            └────────┘
                            (optional)

    The VARTYP (Variable Type) field may be either SCALAR (7BH), STRUCT (79H),
    or ARRAY (77H). The linker ignores the VAR SUBTYP field (if one exists).
    The format for communal variables not in the default data segment (FAR
    variables) is as follows:

    ┌─────┬─────┬────///───┬───///───┐
    │ FAR │ VAR │  Number  │ Element │
    │ 61H │ TYP │    of    │  Type   │
    │     │ 77H │ Elements │  Index  │
    └─────┴─────┴────///───┴───///───┘

    The VARTYP field must be ARRAY (77H). The Length in Bits field specifies
    the Number of Elements, and the Element Type Index is an index to a
    previously defined TYPDEF record whose format is that of a NEAR communal
    variable.

    The format for the Length in Bits or Number of Elements fields is the same
    as the format of the Communal Length field of the COMDEF record.

    Link-Time Semantics

    All EXTDEF records referencing a TYPDEF record of one of the previously
    described formats are treated as communal variables. All others are
    treated as externally defined symbols for which a matching PUBDEF record
    is expected.

    For more information, see "Link-Time Semantics" in Section 7.5.8,
    "Communal Names Definition Record (COMDEF)."



────────────────────────────────────────────────────────────────────────────
Chapter 8  Programming Hints

    8.1   Introduction
    8.2   Interrupts
    8.3   System Calls
    8.4   Device Management
    8.5   Memory Management
    8.6   Process Management
    8.7   File and Directory Management
            8.7.1   Locking Files
    8.8   Miscellaneous


8.1  Introduction

    This chapter describes recommended MS-DOS 4.0 programming procedures. By
    using these programming hints, you can ensure compatibility with future
    versions of MS-DOS.

    The hints are organized in the following categories:

    ■  Interrupts

    ■  System Calls

    ■  Device Management

    ■  Memory Management

    ■  Process Management

    ■  File and Directory Management

    ■  Miscellaneous


8.2  Interrupts

    ■  Never explicitly issue Interrupt 22H (Terminate Process Exit Address).

        Only the DOS should do this. To change the terminate address, use
        Function 35H (Get Interrupt Vector) to get the current address and
        save it, then use Function 25H (Set Interrupt Vector) to change the
        Interrupt 22H entry in the vector table to point to the new terminate
        address.

    ■  Use Interrupt 24H (Critical-Error-Handler Address) with care. The
        Interrupt 24H handler must preserve the ES register.

        An Interrupt 24H handler can issue only the system calls 01H-0CH.
        Making any other calls destroys the MS-DOS stack and prevents
        successful use of the Retry or Ignore options.

        When using the Retry or Ignore options, you must preserve the SS, SP,
        DS, BX, CX, and DX registers.

    ■  When an Interrupt 24H (Critical-Error-Handler Address) is received,
        always IRET back to MS-DOS with one of the standard responses.

        Programs that do not IRET from Interrupt 24H leave the system in an
        unpredictable state until a function call other than 01H-0CH is made.
        The Ignore option may leave incorrect or invalid data in internal
        system buffers.

    ■  Avoid trapping Interrupt 23H (CONTROL+C Handler Address) and Interrupt
        24H (Critical-Error-Handler Address). Don't rely on trapping errors
        via Interrupt 24H as part of a copy protection scheme.

        These methods might not be included in future releases of MS-DOS.

    ■  A user program must never issue Interrupt 23H (CONTROL+C Handler
        Address).

        Only MS-DOS may issue Interrupt 23H.

    ■  Save any registers that your program uses before issuing Interrupt
        25H (Absolute Disk Read) or Interrupt 26H (Absolute Disk Write).

        These interrupts destroy all registers except for the segment
        registers.

        Avoid writing or reading an interrupt vector directly to or from
        memory.

    ■  Use Functions 25H and 35H (Set Interrupt Vector and Get Interrupt
        Vector) to set and get values in the interrupt table.


8.3  System Calls

    ■  Use new system calls.

        Avoid using system calls that have been superseded by new calls unless
        the program must maintain backward compatibility with MS-DOS versions
        before 2.0. See Section 1.9, "Old System Calls," for a list of these
        new calls.

    ■  Avoid using functions 01H-0CH and 26H (Create New PSP).

        Use the new "tools" approach for reading and writing on standard input
        and output. Use Function 4B00H or 4B03H (Load and Execute Program or
        Overlay) instead of 26H to execute a child process.

    ■  Use file-sharing calls if more than one process is in effect.

        For more information, see "File Sharing" in Section 1.5.2,
        "File-Related Function Requests."

    ■  Use networking calls where appropriate.

        Some forms of IOCtl can only be used with Microsoft Networks. For more
        information and a list of these calls, see Section 1.6, "Microsoft
        Networks."

    ■  When selecting a disk with Function 0EH (Select Disk), treat the value
        returned in AL with care.

        The value in AL specifies the maximum number of logical drives; it does
        not specify which drives are valid.


8.4  Device Management

    ■  Use installable device drivers.

        MS-DOS provides a modular device driver structure for the BIOS,
        allowing you to configure and install device drivers at boot time.
        Block device drivers transmit a block of data at a time, while
        character device drivers transmit a byte of data at a time.

        Examples of both types of device drivers are given in Chapter 2,
        "MS-DOS Device Drivers."

    ■  Use buffered I/O.

        The device drivers can handle streams of data up to 64 kilobytes. To
        improve performance when sending a large amount of output to the
        screen, you can send it with one system call.

    ■  Programs that use direct console I/O via Function 06H and 07H (Direct
        Console I/O and Direct Console Input) and that want to read CONTROL+C
        as data should ensure that CONTROL+C checking is off.

        The program should ensure that CONTROL+C checking is off by using
        Function 33H (CONTROL+C Check).

    ■  Be compatible with international support.

        To provide support for international character sets, MS-DOS recognizes
        all possible byte values as significant characters in filenames and
        data streams. MS-DOS versions earlier than 2.0 ignored the high bit in
        the MS-DOS filename.


8.5  Memory Management

    ■  Use memory management.

        MS-DOS keeps track of allocated memory by writing a memory control
        block at the beginning of each area of memory. Programs should use
        Functions 48H (Allocate Memory), 49H (Free Allocated Memory), and
        4AH (Set Block) to release unneeded memory.

        This allows for future compatibility. For more information, see Section
        1.3, "Memory Management."

    ■  Use only allocated memory.

        Don't directly access memory that was not provided as a result of a
        system call. Do not use fixed addressing, use only relative references.

        A program that uses memory that has not been allocated to it may
        destroy other memory control blocks or cause other applications to
        fail.


8.6  Process Management

    ■  Use Function 4B00H or 4B03H (Load and Execute Program or Overlay,
        also known as EXEC) to load and execute programs.

        EXEC is the preferred call to use when loading programs and program
        overlays. Using the EXEC call instead of hard-coding information about
        how to load a .exe file (or always assuming that your file is a .com
        file) isolates your program from changes in .exe file formats and
        future releases of MS-DOS.

    ■  Use Function 31H (Keep Process), instead of Interrupt 27H (Terminate
        But Stay Resident).

        Function 31H allows programs that are greater than 64 kilobytes to
        terminate and stay resident.

    ■  Programs should terminate using Function 4CH (End Process).

        Programs that terminate by one of the following must ensure that the CS
        register contains the segment address of the PSP:

        ■  A long jump to offset 0 in the PSP

        ■  Issuing an Interrupt 20H with CS:0 pointing at the PSP

        ■  Issuing an Interrupt 21H with AH=0, CS:0 pointing at the PSP

        ■  A long call to location 50H in the PSP with AH=0


8.7  File and Directory Management

    ■  Use the MS-DOS file management system.

        Using the MS-DOS file system ensures program compatibility with future
        MS-DOS versions through compatible disk formats and consistent internal
        storage.

    ■  Use file handles instead of FCBs.

        A handle is a 16-bit number that MS-DOS returns when a file is opened
        or created using Functions 3CH, 3DH, 5AH, or 5BH (Create Handle,
        Open Handle, Create Temporary File, or Create New File). The MS-DOS
        file-related function requests that use handles are listed in Table
        1.5 in Chapter 1, "System Calls."

        Although the default maximum number of open files is 20, this limit can
        be raised to 64K by Function 67H (Set Handle Count). For more
        information on this system call, see Chapter 1, "System Calls."

        You should use these calls instead of the old file-related functions
        that use FCBs (file control blocks). This is because a file operation
        can simply pass its handle rather than maintaining FCB information. If
        you must use FCBs, be sure the program closes them and does not move
        them around in memory.

    ■  Close files that have changed in length before issuing an Interrupt
        20H (Program Terminate), Function 00H (Terminate Program), Function
        4CH (End Process), or Function 0DH (Reset Disk).

        If you do not close a changed file, its length will not be recorded
        correctly in the directory.

    ■  Close files when they are no longer needed.

        Closing unneeded files increases efficiency in a networking
        environment.

    ■  If a program does use FCBs, that program should not close an FCB file
        and then continue writing to it. This practice will not work in a
        network environment and is not recommended under any circumstances.

    ■  Change disks only if all files on the disk are closed.

        If you don't close all the files, any information in internal system
        buffers may be written incorrectly to a changed disk.

8.7.1  Locking Files

    ■  Programs should not rely on being denied access to a locked region.

        To determine the status of a region, first attempt to lock it, then
        examine its error code.

    ■  Programs should not close a file with a locked region or terminate with
        an open file that contains a locked region.

        The result of this procedure is undefined. Programs that might be
        terminated by an Interrupt 23H or Interrupt 24H (CONTROL+C Handler
        Address or Critical-Error-Handler Address) should trap these interrupts
        and unlock any locked regions before exiting.


8.8  Miscellaneous

    ■  Avoid timing dependencies.

        Various machines use CPUs of different speeds. Also, programs that rely
        upon the speed of the clock for timing are not dependable in a
        networking environment.

    ■  Use the documented interface to the operating system. If either the
        hardware or media change, the operating system can use the features
        without modification.

        Don't use the ROM support provided by the OEM (Original Equipment
        Manufacturer).

        Don't directly address the video memory.

        Don't use undocumented function calls, interrupts, or features.

        These items may change or may not exist in future MS-DOS versions. If
        you do use these features, you will make your program highly
        non-portable.

    ■  Use the .exe format rather than the .com format.

        .Exe files are relocatable; .com files are direct memory images that
        load at a specific place and have no room for additional control
        information. .Exe files have headers that can be expanded for
        compatibility with future MS-DOS versions.

    ■  Use the environment to pass information to applications.

        The environment allows a parent process to pass information to a child
        process. The command.com file is usually the parent process to every
        application, so it can easily pass default drive and path information
        to the application.



────────────────────────────────────────────────────────────────────────────
Index

Numbers

80186 microprocessor
80286 microprocessor
8086 microprocessor
8086 object language
8086 object module format. See Object Module Formats (OMF)

A

Absolute Disk Read (Interrupt 25H)
Absolute Disk Write (Interrupt 26H)
Absolute segment, LSEG
ACBP byte, SEG ATTR
Address mode
Alignment attribute
Alignment subfield, SEG ATTR
Allocate Memory (Function 48H)
Application
    Frame Number, protected-mode
Archive bit
Array, character
ASCII character set
ASCIZ string
Assign list
Attribute
    primary
    secondary
    Target
Attribute byte
Attribute field
Attribute, LSEG
    Alignment
    Combination
    SEG ATTR field, SEGDEF
AUTOEXEC file
Auxiliary Input (Function 03H)
Auxiliary Output (Function 04H)

B

base, definition
Big subfield, SEG ATTR
BIN format file
Binary line number
BIOS Parameter ^Block (BPB)
BIOS Parameter Block (BPB)
Bit 8
Bit 9
Bitfield
Block Count subfield, Iterated Data Block
Block devices
    device drivers
    disk drives
    example
    installation
Boot sector
BPB pointer
Buffered Keyboard Input (Function 0AH)
BUILD BPB
Build BPB
Busy bit
Byte, representation

C

C language
CALL instruction
Cancel Assign-List Entry (Function 5FH, Code 04H)
Canonic Frame
Canonic Frame, definition
Carry flag
Case-Mapping Call
Change Current Directory (Function 3BH)
Change Directory Entry (Function 56H)
Character array
Character device driver, example
Character devices
Character set
    definition
Check Keyboard Status (Function 0BH)
CHKSUM field, SAMREC
Class Name, definition
CLOCK device
Close File (Function 10H)
Close Handle (Function 3EH)
Cluster
Code page
    definition
Code segment, CS
Combination attribute
Combination subfield, SEG ATTR
COMDEF record
    field
    CHKSUM
    Communal Length, repeated
    Communal Name, repeated
    Data Segment Type, repeated
    Record Length
    RECTYP
    Type Index, repeated
    length fields, format
    order with respect to COMENT
    purpose
    schematic
COMENT record
    field
    CHKSUM
    Comment
    Comment Type
    Record Length
    RECTYP
    order with respect to COMDEF
    order with respect to LOCSYM
    purpose
    schematic
    subfield of Comment Type
    Comment Class
Command code field
Command processor
Command.com
Commands, utility, NLS
Comment class subfield, Comment Type
Comment field, COMENT
Comment record
Comment Record (COMENT)
Comment Type field, COMENT
    bit settings
    definition
    format
Commit File (Function 68H)
Common blocks, FORTRAN
Communal Length field, COMDEF
Communal Name field, COMDEF
Communal Name, ordering with External Name
Communal names definition record. See COMDEF record
Communal Names Definition Record (COMDEF)
Communal variable
    FAR
    HUGE
    NEAR
    similarity to FORTRAN common block
    uninitialized public variable
Compatibility, ensuring
Complete Name, definition
Computer language
    C
    FORTRAN
COMSPEC
Con device
config.sys
config.sys file
Content subfield, Iterated Data Block
Control blocks
Control information
CONTROL+C Address (Interrupt 23H)
CONTROL+C Check (Function 33H)
CONTROL+C Handler Address (Interrupt 23H)
Country code
    definition
Country code, current
Country-dependent information
    case conversion tables
    collating sequence, character sorting
    currency
    date
    DBCS environmental vector
    keyboard support
    time
    valid single-byte characters
Country-dependent information, NLS
Country.sys file
Create Directory (Function 39H)
Create File (Function 16H)
Create Handle (Function 3CH)
Create New File (Function 5BH)
Create New PSP (Function 26H)
Create Temporary File (Function 5AH)
Critical Error Handler Address (Interrupt 24H)
CS register

D

DAT field, LEDATA
Data Record
Data segment
    DS
    ES
    FAR
    NEAR
Data Segment Type field, COMDEF
Delete Directory Entry (Function 41H)
Delete File (Function 13H)
Descriptor, Group Component, GRPDEF
Device control
Device drivers
    block
    creating
    example
    installable
    installing
    non-resident
    preserving registers
    resident
Device Handles
Device header
Device interrupt routine
Device management, programming hints
Device strategy routine
Device-related function requests
Direct Console Input (Function 07H)
Direct Console I/O (Function 06H)
Directory Entry
Directory-Related Function Requests
Directory-related function requests
Disk allocation
Disk Directory
Disk formats
    IBM
    standard MS-DOS
Disk Transfer Address (DTA)
Dispatch table
Display Character (Function 02H)
Display String (Function 09H)
Done bit
DS register
Duplicate File Handle (Function 45H)

E

Eight Leaf Descriptor field, TYPDEF
    format
    subfield
    EN
    Leaf Descriptor
Element Type Index subfield, Leaf Descriptor
EN subfield, Eight Leaf Descriptor
End address
End Process (Function 4CH)
Enumerated Data Offset field, LEDATA
Error bit
Error codes
Error Handling
Error handling
ES register
EXE device drivers
.exe files
exe files
EXE format file
EXE loader
EXTDEF record
    field
    CHKSUM
    External Name, repeated
    Record Length
    RECTYP
    Type Index, repeated
    purpose
    schematic
Extended error codes
Extended FCB
Extensions class, Microsoft
External Index
External indices
External Name
    mapping
    referenced in FIXUPP record
External Name field, EXTDEF
External names definition record. See EXTDEF record
External Names Definition Record (EXTDEF)

F

FAR data segment
FAR subfield, Leaf Descriptor
FAR variable format, Leaf Descriptor
FAT
FAT ID byte
FAT ID byte
FBVAL, definition
FCB
File Allocation Table
File and directory management, programming hints
File attributes
File Control Block
    definition
    extended
    fields
    format
    opened
    unopened
File locking, programming hints
Filename separators
Filename terminators
File-related function requests
File-sharing function requests
Find First File (Function 4EH)
Find Next File (Function 4FH)
FIX DAT subfield, Fixup
    internal structure
    schematic
Fixup
    definition
    Frame
    Location
    segment-relative
    self-relative
    Target
Fixup field, FIXUPP
    data type
    Frame
    Location
    Mode
    Target
    definition
    schematic
    subfield
    LOCAT
Fixup mode
    segment-relative
    self-relative
FIXUP record
FIXUPP record
    External Name referenced in
    field
    CHKSUM
    Fixup, repeated
    Frame Datum, conditional
    Record Length
    RECTYP
    Target Datum, conditional
    Thread, repeated
    purpose
    schematic
    subfield of FIX DAT, Fixup
    F
    Frame
    P
    T
    TARGT
    subfield of Fixup
    Frame Datum, conditional
    Target Datum, conditional
    Target Displacement, conditional
    subfield of LOCAT, Fixup
    Data Record Offset
    LOC
    M (mode)
    subfield of Thread
    Index, conditional
    TRD DAT
    subfield of TRD DAT, Thread
    D
    Method
    THRED
FIXUPP Record (FIXUPP)
Flush
Flush Buffer, Read Keyboard (Function 0CH)
Force Duplicate File Handle (Function 46H)
Format
FORTRAN, common blocks
FORTRAN language
FOVAL, definition
Frame
    definition
    Frame Number
    nomenclature
    specifying
    specifying, FIXUPP
Frame Datum field, FIXUPP
Frame Datum subfield, Fixup
    External Index
    Group Index
    Segment Index
Frame Datum subfield, START ADDRS
Frame Number
Frame Number, conditional
Frame Number, definition
Frame Number subfield, Public Base
Frame Number subfield, SEG ATTR
Frames
    Thread Number, FIXUPP
Free Allocated Memory (Function 49H)
Function Requests
    alphabetic order
    calling
    definition
    device-related
    Directory-related
    directory-related
    file-related
    file-sharing
    Function 00H
    Function 01H
    Function 02H
    Function 03H
    Function 04H
    Function 05H
    Function 06H
    Function 07H
    Function 08H
    Function 09H
    Function 0AH
    Function 0BH
    Function 0CH
    Function 0DH
    Function 0EH
    Function 0FH
    Function 10H
    Function 11H
    Function 12H
    Function 13H
    Function 14H
    Function 15H
    Function 16H
    Function 17H
    Function 19H
    Function 1AH
    Function 1BH
    Function 1CH
    Function 21H
    Function 22H
    Function 23H
    Function 24H
    Function 25H
    Function 26H
    Function 27H
    Function 28H
    Function 29H
    Function 2AH
    Function 2BH
    Function 2CH
    Function 2DH
    Function 2EH
    Function 2FH
    Function 30H
    Function 31H
    Function 33H
    Function 35H
    Function 36H
    Function 38H
    Function 39H
    Function 3AH
    Function 3BH
    Function 3CH
    Function 3DH
    Function 3EH
    Function 3FH
    Function 40H
    Function 41H
    Function 42H
    Function 43H
    Function 44H, Code 08H
    Function 44H, Code 09H
    Function 44H, Code 0AH
    Function 44H, Code 0BH
    Function 44H, Code 0CH
    Function 44H, Code 0DH
    Function 44H, Codes 00H and 01H
    Function 44H, Codes 02H and 03H
    Function 44H, Codes 04H and 05H
    Function 44H, Codes 06H and 07H
    Function 44H, Codes 0EH and 0FH
    Function 45H
    Function 47H
    Function 48H
    Function 49H
    Function 4AH
    Function 4BH, Code 00H
    Function 4BH, Code 03H
    Function 4CH
    Function 4DH
    Function 4EH
    Function 4FH
    Function 54H
    Function 56H
    Function 57H
    Function 58H
    Function 59H
    Function 5AH
    Function 5BH
    Function 5CH, Code 00H
    Function 5CH, Code 01H
    Function 5EH, Code 00H
    Function 5EH, Code 02H
    Function 5FH, Code 02H
    Function 5FH, Code 03H
    Function 5FH, Code 04H
    Function 62H
    Function 65H
    Function 66H
    Function 67H
    Function 68H
    Handling errors
    memory management
    National Language Support
    Network-related
    network-related
    numeric order
    process management
    standard character I/O
    system-management
Function requests
    definition
    Function 25H
    Function 35H
    Function 46H

G

Generic IOCtl for devices (Function 44H, Code 0DH)
Generic IOCtl for handles (Function 44H, Code 0CH)
Generic IOCtl Function
Get Assign List Entry (Function 5FH, Code 02H)
Get Country Data (Function 38H)
Get Current Directory (Function 47H)
Get Current Disk (Function 19H)
Get Date (Function 2AH)
Get Default Drive Data (Function 1BH)
Get Disk Free Space (Function 36H)
Get Disk Transfer Address (Function 2FH)
Get Drive Data (Function 1CH)
Get Extended Country Information (Function 65H)
Get Extended Error (Function 59H)
Get File Size (Function 23H)
Get Interrupt Vector (Function 35H)
Get Machine Name (Function 5EH, Code 00H)
Get MS-DOS Version Number (Function 30H)
Get PSP (Function 62H)
Get Return Code Child Process (Function 4DH)
Get Time (Function 2CH)
Get Verify State (Function 54H)
Get/Set Allocation Strategy (Function 58H)
Get/Set Date/Time of File (Function 57H)
Get/Set File Attributes (Function 43H)
Get/Set Global Code Page (Function 66H)
Get/Set IOCtl Drive Map (Function 44H, Codes 0EH and 0FH)
Get/Set Logical Drive Map Function
Group Component Descriptor field, GRPDEF
Group, definition
Group definition record. See GRPDEF record
Group Definition Record (GRPDEF)
Group Index
Group Index subfield, Line Number Base
Group Index subfield, Public Base
Group Name Index field, GRPDEF
GRPDEF record
    definition
    field
    CHKSUM
    Group Component Descriptor, repeated
    Group Name Index
    Record Length
    RECTYP
    schematic

H

Handles
    definition
    device
Handling errors
Header
hibyte, definition
Hidden files
High-Level Language
HUGE communal variable

I

IBM disk format
Index
    definition
Index fields
Index Number
Index subfield, Thread
    External Index
    Group Index
    Segment Index
Indices
Indices, external
Init
INIT code
Installable device drivers
Instruction
    CALL
    JUMP
    SHORT-JUMP
Instruction Pointer (IP)
Internal stack
Interrupt entry point
Interrupt handlers
Interrupt routines
Interrupt-handling routine
Interrupts
    21H
    Address of handlers
    alphabetic order
    definition
    Interrupt 20H
    Interrupt 21H
    Interrupt 22H
    Interrupt 23H
    Interrupt 24H
    Interrupt 25H
    Interrupt 26H
    Interrupt 27H
    issuing
    numeric order
    programming hints
    Vector table
I/O Control for Devices (Function 44H)
IOCtl
IOCtl bit
IOCtl Block (Function 44H, Codes 4 and 5)
IOCtl Character (Function 44H, Codes 2 and 3)
IOCtl Data (Function 44H, Codes 0 and 1)
IOCtl Is Changeable (Function 44H, Code 08H)
IOCtl Is Redirected Block (Function 44H, Code 09H)
IOCtl Is Redirected Handle (Function 44H, Code 0AH)
IOCtl Retry (Function 44H, Code 0BH)
IOCtl Status (Function 44H, Codes 6 and 7)
io.sys file
IP. See Instruction Pointer (IP)
Iterated Data Block field, LIDATA
Iterated Data Offset field, LIDATA

J

JUMP instruction

K

Keep Process (Function 31H)
Keyboard layouts, national, NLS

L

Language
    C
    FORTRAN
Leaf Descriptor subfield, Eight Leaf Descriptor
    format
    FAR variable
    NEAR variable
    subfield
    NEAR
    VARTYP
LEDATA
    field
    CHKSUM
    Record Length
    RECTYP
    purpose
LEDATA record
    field
    DAT, repeated
    Enumerated Data Offset
    Segment Index
    schematic
Length fields, COMDEF, format
Length in Bits subfield, Leaf Descriptor
Length of Record Field
LHEADR record
    field
    CHKSUM
    L-module Name
    Record Length
    RECTYP
    schematic
LIDATA record
    field
    CHKSUM
    Iterated Data Block, repeated
    Iterated Data Offset
    Record Length
    RECTYP
    Segment Index
    purpose
    schematic
    subfield of Iterated Data Block
    Block Count
    Content
    Repeat Count
Line Number Base field, LINNUM
Line number, binary
Line Number field, LINNUM
Line Number Offset field, LINNUM
Line Numbers Record (LINNUM)
LINK
Linker, Microsoft
Linker, Microsoft
Link-time semantics
LINNUM record
    field
    CHKSUM
    Line Number Base
    Line Number Offset, repeated
    Line Number, repeated
    Record Length
    RECTYP
    purpose
    schematic
    subfield of Line Number Base
    Group Index
    Segment Index
List of Names Record (LNAMES)
L-module Header Record (LHEADR)
L-module Name
LNAMES record
    field
    CHKSUM
    Name, repeated
    Record Length
    RECTYP
    schematic
Load and Execute Program (Function 4BH, Code 00H)
Load module
Load Overlay (Function 4BH, Code 03H)
Loadsize
lobyte, definition
Local Base field, LOCSYM
Local buffering
Local Name field, LOCSYM
Local Offset field, LOCSYM
Local symbol
Local Symbols Record (LOCSYM)
LOCAT subfield, Fixup
    internal structure
    schematic
Location
    types
    base
    hibyte
    lobyte
    offset
    pointer
Lock (Function 5CH, Code 00H)
LOCSYM record
    field
    CHKSUM
    Local Base
    Local Name, repeated
    Local Offset, repeated
    Record Length
    RECTYP
    Type Index, repeated
    order with respect to COMENT
    purpose
    schematic
Logical Enumerated Data Record (LEDATA)
Logical Iterated Data Record (LIDATA)
Logical sector
Logical sector numbers
Logical Segment. See LSEG
LSEG
    absolute
    Combination attribute
    Alignment attribute
    absolute segment
    relocatable segment
    canonic Frame
    Class name
    Combination attribute
    absolute segment
    relocatable segment
    combining
    Complete name
    definition
    Overlay Name
    relocatable
    byte-aligned
    Combination attribute
    page-aligned
    paragraph-aligned
    word-aligned
    Segment Name

M

Make Assign-List Entry (Function 5FH, Code 03H)
Mapping
    logical to physical starting address
MAS. See Memory Address Space (MAS)
MATTR subfield, MOD TYP
Maxalloc
Media Check
Media descriptor byte
Media, determining
Memory address
Memory Address Space (MAS)
Memory control block
Memory image, 8086
Memory image, LSEGs in
Memory image, relocatable
Memory management function requests
Memory management, programming hints
Memory model
    huge
    large
    medium
    small
Microprocessor
    80186
    80286
    8086
Microsoft Extensions class
Microsoft linker
Microsoft linker
Microsoft Networks
Microsoft Networks Manager's Guide
Microsoft Networks User's Guide
Minalloc
MOD TYP field, MODEND
    MATTR subfield
    module attributes
Mode
    fixup
    segment-relative
    self-relative
Mode, address
MODEND record
    field
    CHKSUM
    MOD TYP
    Record Length
    RECTYP
    START ADDRS, conditional
    purpose
    schematic
    subfield of START ADDRS
    Frame Datum, conditional
    Target Datum, conditional
    Target Displacement, conditional
Module
    record ordering in
Module, definition
Module End Record (MODEND)
Module header record, definition
Move File Pointer (Function 42H)
MS-DOS, 8086 object language
MS-DOS initialization
MS-DOS memory map
MS-DOS User's Reference
msdos.sys file
Multitasking

N

Name field
Name field, SAMREC
Name Indices
National keyboard layouts, NLS
National Language Support Function Requests
National Language Support (NLS)
    restrictions
    unsupported features
National Language Support system calls
NEAR data segment
NEAR subfield, Leaf Descriptor
NEAR variable format, Leaf Descriptor
Network-related Function Requests
Network-related function requests
Non IBM format bit
Non-destructive Read No Wait
NUL device
Number field, SAMREC
Number of Elements subfield, Leaf Descriptor

O

Object language, 8086
Object module
    restrictions
Object Module Formats
Object Module Formats (OMF)
offset, definition
Offset subfield, SEG ATTR
Old system calls
OMF. See Object Module Formats (OMF)
Open File (Function 0FH)
Open Handle (Function 3DH)
Opened FCB
Operating system
    MS-DOS
    PC-DOS
    XENIX
Overlay Name, definition

P

Parameter block
Parse File Name (Function 29H)
Path command
PC-DOS, 8086 object language
Physical Segment. See PSEG
Pointer to Next Device field
Predefined device handles
Print Character (Function 05H)
Printer Setup (Function 5EH, Code 02H
Process management function requests
Process management, programming hints
Processor. See Microprocessor
Program End Process (Interrupt 20H)
Program segment
Program Segment Prefix
Programming hints
    device management
    file and directory management
    file locking
    interrupts
    memory management
    miscellaneous
    process management
    recommendations
    system calls
Programming interfaces, NLS
Prompt command
Protected-mode
    application Frame Number
PSEG
    definition
PUBDEF record
    field
    Public Base, repeated
    Public Name, repeated
    Public Offset, repeated
    Record Length
    RECTYP
    Type Index, repeated
    purpose
    schematic
    subfield of Public Base
    Frame Number, conditional
    subfield, Public Base
    Frame Number, conditional
    Group Index
    Segment Index
Public Base field, PUBDEF
Public Name field, PUBDEF
Public names definition record. See PUBDEF record
Public Names Definition Record (PUBDEF)
Public Offset field, PUBDEF
Public symbol
Public variable

R

Random Block Read (Function 27H)
Random Block Write (Function 28H)
Random Read (Function 21H)
Random Write (Function 22H)
Read Handle (Function 3FH)
Read Keyboard and Echo (Function 01H)
Read Keyboard (Function 08H)
Read Only Memory
Read or Write
Record
    COMDEF
    COMENT
    comment
    Data
    EXTDEF
    FIXUP
    FIXUPP
    GRPDEF
    LEDATA
    LHEADR
    LIDATA
    LINNUM
    LNAMES
    LOCSYM
    MODEND
    PUBDEF
    RECTYP(record type)
    SAMREC (sample record)
    SEGDEF
    symbol definition
    COMDEF
    EXTDEF
    PUBDEF
    THEADR
    TYPDEF
Record format
    abbreviation
    bitfields
    conditional field
    repeated field
    sample (SAMREC)
    SAMREC (sample record)
    CHKSUM field
    Name field
    Number field
    Record Length field
    SAMREC(sample record)
    RECTYP field
    title
Record formats
Record Length field, SAMREC
Record order
    definition
    semantic rules
    syntax
Record Size
RECTYP(record type)field
Reference
    segment-relative
Reference self-relative
References
    WIRTH
    CACM, Nov. 1977)
Register
    CS
    DS
    ES
    SS
Registers, treatment of
Relocatable memory image
Relocatable segment, LSEG
Relocation information
Relocation item offset value to a word in the load
Relocation table
Remove Directory (Function 3AH)
Rename File (Function 17H)
Repeat Count subfield, Iterated Data Block
request header
Request packet
Reset Disk (Function 0DH)
Resident device drivers
ROM
Root directory

S

SAMREC (sample record)
    schematic
Search for First Entry (Function 11H)
Search for Next Entry (Function 12H)
Sector count
SEG ATTR field, SEGDEF
SEGDEF record
    definition
    field
    CHKSUM
    Class Name Index
    Overlay Name Index
    Record Length
    RECTYP
    SEG ATTR
    Segment Name Index
    schematic
    subfield of SEG ATTR
    Alignment
    Big
    Combination
    Offset, conditional
SEGDEF recrod
    field
    Segment Length
Segment
    absolute, LSEG
    attribute
    Alignment
    Combination
    logical (LSEG)
    physical (PSEG)
    relocatable, LSEG
Segment addressing
Segment definition record. See SEGDEF record
Segment Definition Record (SEGDEF)
Segment Index
Segment Index field, LEDATA
Segment Index field, LIDATA
Segment Index subfield, Group Component Descriptor
Segment Index subfield, Line Number Base
Segment Index subfield, Public Base
Segment Length field, SEGDEF
Segment Name, definition
Segment Name Index field, SEGDEF
Segment-relative fixup
Segment-relative reference
Select Disk (Function 0EH)
Self-relative fixup
Self-relative reference
Semantic rules, record ordering
Semantics, link-time
Sequential Read (Function 14H)
Sequential Write (Function 15H)
Set Block (Function 4AH)
Set command
Set Country Data (Function 38H)
Set Date (Function 2BH)
Set Disk Transfer Address (Function 1AH)
Set Handle Count(Function 67H)
Set Interrupt Vector (Function 25H)
Set Relative Record (Function 24H)
Set Time (Function 2DH)
Set/Reset Verify Flag (Function 2EH)
SHORT-JUMP instruction
SS register
Stack segment, SS
Standard character I/O function requests
START ADDRS field, MODEND
    Format
Start sector
Start segment value the relocation item offset
static request header
Status
Status field
Strategy entry point
Strategy routines
Subfield
    Comment Type
    Comment Class
    Eight Leaf Descriptor
    EN
    Leaf Descriptor
    Fixup
    Frame Datum
    Target Datum
    Target Displacement
    Frame Number, conditional
    Group Component Descriptor
    Segment Index
    Iterated Data Block
    Block Count
    Content
    Repeat Count
    Leaf Descriptor
    Element Type Index
    FAR
    Length in Bits
    NEAR
    Number of Elements
    VAR SUBTYP
    VARTYP
    Line Number Base, LINNUM
    Group Index
    Segment Index
    MOD TYP
    MATTR
    Public Base
    Group INdex
    SEG ATTR
    Alignment
    Big
    Combination
    Segment Index
    START ADDRS
    Frame Datum
    Target Datum
    Target Displacement
    Thread
    Index
    TRD DAT
Subfield OFFH
Superseded system calls
Symbol
    local
    public
Symbol definition
Symbol definition record
    COMDEF
    EXTDEF
    PUBDEF
Syntax, record ordering
Sysinit
System call
    National Language Support:
System Calls
    superseded calls
System calls
    definition
    programming hints
    replacements for old
    types of
System files
System prompt
System-management function requests

T

Target
    definition
    nomenclature
    specification attribute
    specifying
    specifying, FIXUPP
    Thread Number, FIXUPP
Target Datum field, FIXUPP
Target Datum subfield, Fixup
    External Index
    Group Index
    Segment Index
Target Datum subfield, START ADDRS
Target Displacement subfield, Fixup
Target Displacment subfield, START ADDRS
Terminate But Stay Resident (Interrupt 27H)
Terminate Process Exit Address (Interrupt 22H)
Terminate Program (Function 00H)
THEADR record
    field
    CHKSUM
    Record Length
    RECTYP
    T-module Name
    schematic
Thread Data subfield. See TRD DAT subfield, Thread
Thread field, FIXUPP
    data type
    Frame
    Target
    definition
Thread Number, THRED
T-module, definition
T-module Header Record (THEADR)
T-Module Name
Transfer address
TRD DAT subfield, Thread
    D subfield
    internal structure
    Method subfield
    schematic
    THRED subfield
TYPDEF record
    communal variable
    field
    CHKSUM
    Eight Leaf Descriptor
    Record Length
    RECTYP
    subfield of Eight Leaf Descriptor
    EN
    subfield of Leaf Descriptor
    Element Type Index
    FAR
    Length in Bits
    NEAR
    Number of Elements
    VAR SUBTYP, optional
    VARTYP
Type Index field, COMDEF
Type Index field, EXTDEF
Type Index field, LOCSYM
Type Index field, PUBDEF
Type-ahead buffer

U

Unit code field
Unlock (Function 5CH, Code 01H)
Unopened FCB
User Stack
User stack
Utility commands, NLS

V

VAR SUBTYP subfield, Leaf Descriptor
Variable
    communal
    FAR
    HUGE"
    NEAR
    public
VARTYP subfield, Leaf Descriptor
    ARRAY
    SCALAR
    STRUCT
Vector table
Volume ID
Volume label

W

Wildcard characters
Write Handle (Function 40H)

X

XENIX, 8086 object language