home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft_Programmers_Library.7z / MPL / msdos / dosadvnc.txt < prev    next >
Encoding:
Text File  |  2013-11-08  |  1.4 MB  |  35,719 lines

Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
  1.  Advanced MS-DOS Programming
  2.  
  3.  
  4.  ════════════════════════════════════════════════════════════════════════════
  5.  
  6.  
  7.  Advanced MS-DOS Programming
  8.  
  9.  The Microsoft(R) Guide for Assembly Language and C Programmers
  10.  
  11.  By Ray Duncan
  12.  
  13.  
  14.  ════════════════════════════════════════════════════════════════════════════
  15.  
  16.  
  17.    PUBLISHED BY
  18.    Microsoft Press
  19.    A Division of Microsoft Corporation
  20.    16011 NE 36th Way, Box 97017, Redmond, Washington 98073-9717
  21.    Copyright (C) 1986, 1988 by Ray Duncan
  22.    Published 1986. Second edition 1988.
  23.    All rights reserved. No part of the contents of this book may be
  24.    reproduced or transmitted in any form or by any means without the written
  25.    permission of the publisher.
  26.    Library of Congress Cataloging in Publication Data
  27.  
  28.    Duncan, Ray, 1952-
  29.    Advanced MS-DOS programming.
  30.    Rev. ed. of: Advanced MS-DOS. (C)1986.
  31.    Includes index.
  32.    1. MS-DOS (Computer operating system)  2. Assembler language
  33.    (Computer program language)  3. C (Computer program language)
  34.    I. Duncan, Ray, 1952-    Advanced MS-DOS.    II. Title.
  35.    QA76.76.063D858      1988      005.4'46      88-1251
  36.    ISBN 1-55615-157-8
  37.    Printed and bound in the United States of America.
  38.  
  39.    1 2 3 4 5 6 7 8 9    FGFG    3 2 1 0 9 8
  40.  
  41.    Distributed to the book trade in the United States by Harper & Row.
  42.  
  43.    Distributed to the book trade in Canada by General Publishing Company,
  44.    Ltd.
  45.  
  46.    Penguin Books Ltd., Harmondworth, Middlesex, England
  47.    Penguin Books Australia Ltd., Ringwood, Victoria, Australia
  48.    Penguin Books N.Z. Ltd., 182-190 Wairu Road, Auckland 10, New Zealand
  49.  
  50.    British Cataloging in Publication Data available
  51.  
  52.    IBM(R), PC/AT(R), and PS/2(R) are registered trademarks of International
  53.    Business Machines Corporation. CodeView(R), Microsoft(R), MS-DOS(R), and
  54.    XENIX(R) are registered trademarks and InPort TM is a trademark of
  55.    Microsoft Corporation.
  56.  
  57.    ──────────────────────────────────────────────────────────────────────────
  58.        Technical Editor: Mike Halvorson  Production Editor: Mary Ann Jones
  59.    ──────────────────────────────────────────────────────────────────────────
  60.  
  61.  
  62.  
  63.                                    Dedication
  64.  
  65.                                    For Carolyn
  66.  
  67.  
  68.  
  69.  ────────────────────────────────────────────────────────────────────────────
  70.  Contents
  71.  
  72.    Road Map to Figures and Tables
  73.  
  74.    Acknowledgments
  75.  
  76.    Introduction
  77.  
  78.    SECTION 1   PROGRAMMING FOR MS-DOS
  79.  
  80.    Chapter 1   Genealogy of MS-DOS
  81.  
  82.    Chapter 2   MS-DOS in Operation
  83.  
  84.    Chapter 3   Structure of MS-DOS Application Programs
  85.  
  86.    Chapter 4   MS-DOS Programming Tools
  87.  
  88.    Chapter 5   Keyboard and Mouse Input
  89.  
  90.    Chapter 6   Video Display
  91.  
  92.    Chapter 7   Printer and Serial Port
  93.  
  94.    Chapter 8   File Management
  95.  
  96.    Chapter 9   Volumes and Directories
  97.  
  98.    Chapter 10  Disk Internals
  99.  
  100.    Chapter 11  Memory Management
  101.  
  102.    Chapter 12  The EXEC Function
  103.  
  104.    Chapter 13  Interrupt Handlers
  105.  
  106.    Chapter 14  Installable Device Drivers
  107.  
  108.    Chapter 15  Filters
  109.  
  110.    Chapter 16  Compatibility and Portability
  111.  
  112.    SECTION 2   MS-DOS FUNCTIONS REFERENCE
  113.  
  114.    SECTION 3   IBM ROM BIOS AND MOUSE FUNCTIONS REFERENCE
  115.  
  116.    SECTION 4   LOTUS/INTEL/MICROSOFT EMS FUNCTIONS REFERENCE
  117.  
  118.    Index
  119.  
  120.  
  121.  
  122.  
  123.  ────────────────────────────────────────────────────────────────────────────
  124.  Road Map to Figures and Tables
  125.  
  126.    MS-DOS versions and release dates
  127.  
  128.    MS-DOS memory map
  129.  
  130.    Structure of program segment prefix (PSP)
  131.  
  132.    Structure of .EXE load module
  133.  
  134.    Register conditions at program entry
  135.  
  136.    Segments, groups, and classes
  137.  
  138.    Macro Assembler switches
  139.  
  140.    C Compiler switches
  141.  
  142.    Linker switches
  143.  
  144.    MAKE switches
  145.  
  146.    ANSI escape sequences
  147.  
  148.    Video attributes
  149.  
  150.    Structure of normal file control block (FCB)
  151.  
  152.    Structure of extended file control block
  153.  
  154.    MS-DOS error codes
  155.  
  156.    Structure of boot sector
  157.  
  158.    Structure of directory entry
  159.  
  160.    Structure of fixed-disk master block
  161.  
  162.    LIM EMS error codes
  163.  
  164.    Intel 80x86 internal interrupts (faults)
  165.  
  166.    Intel 80x86, MS-DOS, and ROM BIOS interrupts
  167.  
  168.    Device-driver attribute word
  169.  
  170.    Device-driver command codes
  171.  
  172.    Structure of BIOS parameter block (BPB)
  173.  
  174.    Media descriptor byte
  175.  
  176.  
  177.  
  178.  ────────────────────────────────────────────────────────────────────────────
  179.  Acknowledgments
  180.  
  181.    My renewed thanks to the outstanding editors and production staff at
  182.    Microsoft Press, who make beautiful books happen, and to the talented
  183.    Microsoft developers, who create great programs to write books about.
  184.    Special thanks to Mike Halvorson, Jeff Hinsch, Mary Ann Jones, Claudette
  185.    Moore, Dori Shattuck, and Mark Zbikowski; if this book has anything unique
  186.    to offer, these people deserve most of the credit.
  187.  
  188.  
  189.  
  190.  ────────────────────────────────────────────────────────────────────────────
  191.  Introduction
  192.  
  193.    Advanced MS-DOS Programming is written for the experienced C or
  194.    assembly-language programmer. It provides all the information you need to
  195.    write robust, high-performance applications under the MS-DOS operating
  196.    system. Because I believe that working, well-documented programs are
  197.    unbeatable learning tools, I have included detailed programming examples
  198.    throughout──including complete utility programs that you can adapt to your
  199.    own needs.
  200.  
  201.    This book is both a tutorial and a reference and is divided into four
  202.    sections, so that you can find information more easily. Section 1
  203.    discusses MS-DOS capabilities and services by functional group in the
  204.    context of common programming issues, such as user input, control of the
  205.    display, memory management, and file handling. Special classes of
  206.    programs, such as interrupt handlers, device drivers, and filters, have
  207.    their own chapters.
  208.  
  209.    Section 2 provides a complete reference guide to MS-DOS function calls,
  210.    organized so that you can see the calling sequence, results, and version
  211.    dependencies of each function at a glance. I have also included notes,
  212.    where relevant, about quirks and special uses of functions as well as
  213.    cross-references to related functions. An assembly-language example is
  214.    included for each entry in Section 2.
  215.  
  216.    Sections 3 and 4 are references to IBM ROM BIOS, Microsoft Mouse driver,
  217.    and Lotus/Intel/Microsoft Expanded Memory Specification functions. The
  218.    entries in these two sections have the same form as in Section 2, except
  219.    that individual programming examples have been omitted.
  220.  
  221.    The programs in this book were written with the marvelous Brief editor
  222.    from Solution Systems and assembled or compiled with Microsoft Macro
  223.    Assembler version 5.1 and Microsoft C Compiler version 5.1. They have been
  224.    tested under MS-DOS versions 2.1, 3.1, 3.3, and 4.0 on an 8088-based IBM
  225.    PC, an 80286-based IBM PC/AT, and an 80386-based IBM PS/2 Model 80. As far
  226.    as I am aware, they do not contain any software or hardware dependencies
  227.    that will prevent them from running properly on any IBM PC─compatible
  228.    machine running MS-DOS version 2.0 or later.
  229.  
  230.  Changes from the First Edition
  231.  
  232.    Readers who are familiar with the first edition will find many changes in
  233.    the second edition, but the general structure of the book remains the
  234.    same. Most of the material comparing MS-DOS to CP/M and UNIX/XENIX has
  235.    been removed; although these comparisons were helpful a few years ago,
  236.    MS-DOS has become its own universe and deserves to be considered on its
  237.    own terms.
  238.  
  239.    The previously monolithic chapter on character devices has been broken
  240.    into three more manageable chapters focusing on the keyboard and mouse,
  241.    the display, and the serial port and printer. Hardware-dependent video
  242.    techniques have been de-emphasized; although this topic is more important
  243.    than ever, it has grown so complex that it requires a book of its own. A
  244.    new chapter discusses compatibility and portability of MS-DOS applications
  245.    and also contains a brief introduction to Microsoft OS/2, the new
  246.    multitasking, protected-mode operating system.
  247.  
  248.    A road map to vital figures and tables has been added, following the Table
  249.    of Contents, to help you quickly locate the layouts of the program segment
  250.    prefix, file control block, and the like.
  251.  
  252.    The reference sections at the back of the book have been extensively
  253.    updated and enlarged and are now complete through MS-DOS version 4.0, the
  254.    IBM PS/2 Model 80 ROM BIOS and the VGA video adapter, the Microsoft Mouse
  255.    driver version 6.0, and the Lotus/Intel/Microsoft Expanded Memory
  256.    Specification version 4.0.
  257.  
  258.    In the two years since Advanced MS-DOS Programming was first published,
  259.    hundreds of readers have been kind enough to send me their comments, and I
  260.    have tried to incorporate many of their suggestions in this new edition.
  261.    As before, please feel free to contact me via MCI Mail (user name LMI),
  262.    CompuServe (user ID 72406,1577), or BIX (user name rduncan).
  263.  
  264.    Ray Duncan  Los Angeles, California  September 1988
  265.  
  266.  
  267.  
  268.  ────────────────────────────────────────────────────────────────────────────
  269.  SECTION 1  PROGRAMMING FOR MS-DOS
  270.  ────────────────────────────────────────────────────────────────────────────
  271.  
  272.  
  273.  
  274.  ────────────────────────────────────────────────────────────────────────────
  275.  Chapter 1  Genealogy of MS-DOS
  276.  
  277.    In only seven years, MS-DOS has evolved from a simple program loader into
  278.    a sophisticated, stable operating system for personal computers that are
  279.    based on the Intel 8086 family of microprocessors (Figure 1-1). MS-DOS
  280.    supports networking, graphical user interfaces, and storage devices of
  281.    every description; it serves as the platform for thousands of application
  282.    programs; and it has over 10 million licensed users──dwarfing the combined
  283.    user bases of all of its competitors.
  284.  
  285.    The progenitor of MS-DOS was an operating system called 86-DOS, which was
  286.    written by Tim Paterson for Seattle Computer Products in mid-1980. At that
  287.    time, Digital Research's CP/M-80 was the operating system most commonly
  288.    used on microcomputers based on the Intel 8080 and Zilog Z-80
  289.    microprocessors, and a wide range of application software (word
  290.    processors, database managers, and so forth) was available for use with
  291.    CP/M-80.
  292.  
  293.    To ease the process of porting 8-bit CP/M-80 applications into the new
  294.    16-bit environment, 86-DOS was originally designed to mimic CP/M-80 in
  295.    both available functions and style of operation. Consequently, the
  296.    structures of 86-DOS's file control blocks, program segment prefixes, and
  297.    executable files were nearly identical to those of CP/M-80. Existing
  298.    CP/M-80 programs could be converted mechanically (by processing their
  299.    source-code files through a special translator program) and, after
  300.    conversion, would run under 86-DOS either immediately or with very little
  301.    hand editing.
  302.  
  303.    Because 86-DOS was marketed as a proprietary operating system for Seattle
  304.    Computer Products' line of S-100 bus, 8086-based microcomputers, it made
  305.    very little impact on the microcomputer world in general. Other vendors of
  306.    8086-based microcomputers were understandably reluctant to adopt a
  307.    competitor's operating system and continued to wait impatiently for the
  308.    release of Digital Research's CP/M-86.
  309.  
  310.    In October 1980, IBM approached the major microcomputer-software houses in
  311.    search of an operating system for the new line of personal computers it
  312.    was designing. Microsoft had no operating system of its own to offer
  313.    (other than a stand-alone version of Microsoft BASIC) but paid a fee to
  314.    Seattle Computer Products for the right to sell Paterson's 86-DOS. (At
  315.    that time, Seattle Computer Products received a license to use and sell
  316.    Microsoft's languages and all 8086 versions of Microsoft's operating
  317.    system.) In July 1981, Microsoft purchased all rights to 86-DOS, made
  318.    substantial alterations to it, and renamed it MS-DOS. When the first IBM
  319.    PC was released in the fall of 1981, IBM offered MS-DOS (referred to as
  320.    PC-DOS 1.0) as its primary operating system.
  321.  
  322.    IBM also selected Digital Research's CP/M-86 and Softech's P-system as
  323.    alternative operating systems for the PC. However, they were both very
  324.    slow to appear at IBM PC dealers and suffered the additional disadvantages
  325.    of higher prices and lack of available programming languages. IBM threw
  326.    its considerable weight behind PC-DOS by releasing all the IBM-logo PC
  327.    application software and development tools to run under it. Consequently,
  328.    most third-party software developers targeted their products for PC-DOS
  329.    from the start, and CP/M-86 and P-system never became significant factors
  330.    in the IBM PC─compatible market.
  331.  
  332.    In spite of some superficial similarities to its ancestor CP/M-80, MS-DOS
  333.    version 1.0 contained a number of improvements over CP/M-80, including the
  334.    following:
  335.  
  336.    ■  An improved disk-directory structure that included information about a
  337.       file's attributes (such as whether it was a system or a hidden file),
  338.       its exact size in bytes, and the date that the file was created or last
  339.       modified
  340.  
  341.    ■  A superior disk-space allocation and management method, allowing
  342.       extremely fast sequential or random record access and program loading
  343.  
  344.    ■  An expanded set of operating-system services, including
  345.       hardware-independent function calls to set or read the date and time, a
  346.       filename parser, multiple-block record I/O, and variable record sizes
  347.  
  348.    ■  An AUTOEXEC.BAT batch file to perform a user-defined series of commands
  349.       when the system was started or reset
  350.  
  351.    IBM was the only major computer manufacturer (sometimes referred to as
  352.    OEM, for original equipment manufacturer) to ship MS-DOS version 1.0 (as
  353.    PC-DOS 1.0) with its products. MS-DOS version 1.25 (equivalent to IBM
  354.    PC-DOS 1.1) was released in June 1982 to fix a number of bugs and also to
  355.    support double-sided disks and improved hardware independence in the DOS
  356.    kernel. This version was shipped by several vendors besides IBM, including
  357.    Texas Instruments, COMPAQ, and Columbia, who all entered the personal
  358.    computer market early. Due to rapid decreases in the prices of RAM and
  359.    fixed disks, MS-DOS version 1 is no longer in common use.
  360.  
  361.    MS-DOS version 2.0 (equivalent to PC-DOS 2.0) was first released in March
  362.    1983. It was, in retrospect, a new operating system (though great care was
  363.    taken to maintain compatibility with MS-DOS version 1). It contained many
  364.    significant innovations and enhanced features, including those listed on
  365.    the following page.
  366.  
  367.    ■  Support for both larger-capacity floppy disks and hard disks
  368.  
  369.    ■  Many UNIX/XENIX-like features, including a hierarchical file structure,
  370.       file handles, I/O redirection, pipes, and filters
  371.  
  372.    ■  Background printing (print spooling)
  373.  
  374.    ■  Volume labels, plus additional file attributes
  375.  
  376.    ■  Installable device drivers
  377.  
  378.    ■  A user-customizable system-configuration file that controlled the
  379.       loading of additional device drivers, the number of system disk
  380.       buffers, and so forth
  381.  
  382.    ■  Maintenance of environment blocks that could be used to pass
  383.       information between programs
  384.  
  385.    ■  An optional ANSI display driver that allowed programs to position the
  386.       cursor and control display characteristics in a hardware-independent
  387.       manner
  388.  
  389.    ■  Support for the dynamic allocation, modification, and release of memory
  390.       by application programs
  391.  
  392.    ■  Support for customized user command interpreters (shells)
  393.  
  394.    ■  System tables to assist application software in modifying its currency,
  395.       time, and date formats (known as international support)
  396.  
  397.    MS-DOS version 2.11 was subsequently released to improve international
  398.    support (table-driven currency symbols, date formats, decimal-point
  399.    symbols, currency separators, and so forth), to add support for 16-bit
  400.    Kanji characters throughout, and to fix a few minor bugs. Version 2.11
  401.    rapidly became the base version shipped for 8086/8088-based personal
  402.    computers by every major OEM, including Hewlett-Packard, Wang, Digital
  403.    Equipment Corporation, Texas Instruments, COMPAQ, and Tandy.
  404.  
  405.    MS-DOS version 2.25, released in October 1985, was distributed in the Far
  406.    East but was never shipped by OEMs in the United States and Europe. In
  407.    this version, the international support for Japanese and Korean character
  408.    sets was extended even further, additional bugs were repaired, and many of
  409.    the system utilities were made compatible with MS-DOS version 3.0.
  410.  
  411.    MS-DOS version 3.0 was introduced by IBM in August 1984 with the release
  412.    of the 80286-based PC/AT machines. It represented another major rewrite of
  413.    the entire operating system and included the important new features listed
  414.    on the following page.
  415.  
  416.    ■  Direct control of the print spooler by application software
  417.  
  418.    ■  Further expansion of international support for currency formats
  419.  
  420.    ■  Extended error reporting, including a code that suggests a recovery
  421.       strategy to the application program
  422.  
  423.    ■  Support for file and record locking and sharing
  424.  
  425.    ■  Support for larger fixed disks
  426.  
  427.    MS-DOS version 3.1, which was released in November 1984, added support for
  428.    the sharing of files and printers across a network. Beginning with version
  429.    3.1, a new operating-system module called the redirector intercepts an
  430.    application program's requests for I/O and filters out the requests that
  431.    are directed to network devices, passing these requests to another machine
  432.    for processing.
  433.  
  434.    Since version 3.1, the changes to MS-DOS have been evolutionary rather
  435.    than revolutionary. Version 3.2, which appeared in 1986, generalized the
  436.    definition of device drivers so that new media types (such as 3.5-inch
  437.    floppy disks) could be supported more easily. Version 3.3 was released in
  438.    1987, concurrently with the new IBM line of PS/2 personal computers, and
  439.    drastically expanded MS-DOS's multilanguage support for keyboard mappings,
  440.    printer character sets, and display fonts. Version 4.0, delivered in 1988,
  441.    was enhanced with a visual shell as well as support for very large file
  442.    systems.
  443.  
  444.    While MS-DOS has been evolving, Microsoft has also put intense efforts
  445.    into the areas of user interfaces and multitasking operating systems.
  446.    Microsoft Windows, first shipped in 1985, provides a multitasking,
  447.    graphical user "desktop" for MS-DOS systems. Windows has won widespread
  448.    support among developers of complex graphics applications such as desktop
  449.    publishing and computer-aided design because it allows their programs to
  450.    take full advantage of whatever output devices are available without
  451.    introducing any hardware dependence.
  452.  
  453.    Microsoft Operating System/2 (MS OS/2), released in 1987, represents a new
  454.    standard for application developers: a protected-mode, multitasking,
  455.    virtual-memory system specifically designed for applications requiring
  456.    high-performance graphics, networking, and interprocess communications.
  457.    Although MS OS/2 is a new product and is not a derivative of MS-DOS, its
  458.    user interface and file system are compatible with MS-DOS and Microsoft
  459.    Windows, and it offers the ability to run one real-mode (MS-DOS)
  460.    application alongside MS OS/2 protected-mode applications. This
  461.    compatibility allows users to move between the MS-DOS and OS/2
  462.    environments with a minimum of difficulty.
  463.  
  464.    ┌─────────────┐
  465.    │ MS-DOS 1.0  │ 1981: First operating system on IBM PC
  466.    │ PC-DOS 1.0  │
  467.    └──────┬──────┘
  468.           │
  469.    ┌────────────┐
  470.    │ MS-DOS 1.25 │ Double-sided disk support and bug fixes added:
  471.    │ PC-DOS 1.1  │ widely distributed by OEMs other than IBM
  472.    └──────┬──────┘
  473.           │
  474.    ┌────────────┐ 1983: Introduced with IBM PC/XT;
  475.    │ MS-DOS 2.0  │ support for UNIX/XENIX-like hierarchical
  476.    │ PC-DOS 2.0  │ file structure and hard disks added
  477.    └──────┬──────┘
  478.           ├──────────────────────────────────────┐
  479.    ┌────────────┐                        ┌────────────┐
  480.    │ MS-DOS 2.01 │ 2.0 with international │ PC-DOS 2.1  │ Introduced with PCjr
  481.    └──────┬──────┘ support                └─────────────┘ 2.0 with bug fixes
  482.           │
  483.    ┌────────────┐
  484.    │ MS-DOS 2.11 │ 2.01 with bug fixes
  485.    └──────┬──────┘
  486.           ├──────────────────────────────────────┐
  487.    ┌────────────┐ 1984: Introduced with  ┌────────────┐ 1985: Far East OEMs;
  488.    │ MS-DOS 3.0  │ PC/AT; support for     │ MS-DOS 2.25 │ support for extended
  489.    │ PC-DOS 3.0  │ 1.2 MB floppy disk,    └─────────────┘ character sets
  490.    └──────┬──────┘ larger hard disk added
  491.           │
  492.    ┌────────────┐
  493.    │ MS-DOS 3.1  │ Support for Microsoft  ┌─────────────┐ 1985: Graphical
  494.    │ PC-DOS 3.1  │ Networks added         │   Windows   │ user interface
  495.    └──────┬──────┘                        │     1.0     │ for MS-DOS
  496.           │                               └──────┬──────┘
  497.    ┌────────────┐                               │
  498.    │ MS-DOS 3.2  │ 1986: Support for 3.5-        │
  499.    │ PC-DOS 3.2  │ inch disks added              │
  500.    └──────┬──────┘                               │
  501.           │                               ┌────────────┐ 1987: Compatibility
  502.    ┌────────────┐ 1987: Introduced with  │   Windows   │ with OS/2
  503.    │ MS-DOS 3.3  │ IBM PS/2; generalized  │     2.0     │ Presentation Manager
  504.    │ PC-DOS 3.3  │ code-page (font)       └─────────────┘
  505.    └──────┬──────┘ support
  506.           │
  507.    ┌────────────┐ 1988: Support for
  508.    │ MS-DOS 4.0  │ logical volumes larger
  509.    │ PC-DOS 4.0  │ than 32 MB; visual shell
  510.    └─────────────┘
  511.  
  512.    Figure 1-1.  The evolution of MS-DOS.
  513.  
  514.    What does the future hold for MS-DOS? Only the long-range planning teams
  515.    at Microsoft and IBM know for sure. But it seems safe to assume that
  516.    MS-DOS, with its relatively small memory requirements, adaptability to
  517.    diverse hardware configurations, and enormous base of users, will remain
  518.    important to programmers and software publishers for years to come.
  519.  
  520.  
  521.  
  522.  ────────────────────────────────────────────────────────────────────────────
  523.  Chapter 2  MS-DOS in Operation
  524.  
  525.    It is unlikely that you will ever be called upon to configure the MS-DOS
  526.    software for a new model of computer. Still, an acquaintance with the
  527.    general structure of MS-DOS can often be very helpful in understanding the
  528.    behavior of the system as a whole. In this chapter, we will discuss how
  529.    MS-DOS is organized and how it is loaded into memory when the computer is
  530.    turned on.
  531.  
  532.  
  533.  The Structure of MS-DOS
  534.  
  535.    MS-DOS is partitioned into several layers that serve to isolate the kernel
  536.    logic of the operating system, and the user's perception of the system,
  537.    from the hardware it is running on. These layers are
  538.  
  539.    ■  The BIOS (Basic Input/Output System)
  540.  
  541.    ■  The DOS kernel
  542.  
  543.    ■  The command processor (shell)
  544.  
  545.    We'll discuss the functions of each of these layers separately.
  546.  
  547.  The BIOS Module
  548.  
  549.    The BIOS is specific to the individual computer system and is provided by
  550.    the manufacturer of the system. It contains the default resident
  551.    hardware-dependent drivers for the following devices:
  552.  
  553.    ■  Console display and keyboard (CON)
  554.  
  555.    ■  Line printer (PRN)
  556.  
  557.    ■  Auxiliary device (AUX)
  558.  
  559.    ■  Date and time (CLOCK$)
  560.  
  561.    ■  Boot disk device (block device)
  562.  
  563.    The MS-DOS kernel communicates with these device drivers through I/O
  564.    request packets; the drivers then translate these requests into the proper
  565.    commands for the various hardware controllers. In many MS-DOS systems,
  566.    including the IBM PC, the most primitive parts of the hardware drivers are
  567.    located in read-only memory (ROM) so that they can be used by stand-alone
  568.    applications, diagnostics, and the system startup program.
  569.  
  570.    The terms resident and installable are used to distinguish between the
  571.    drivers built into the BIOS and the drivers installed during system
  572.    initialization by DEVICE commands in the CONFIG.SYS file. (Installable
  573.    drivers will be discussed in more detail later in this chapter and in
  574.    Chapter 14.)
  575.  
  576.    The BIOS is read into random-access memory (RAM) during system
  577.    initialization as part of a file named IO.SYS. (In PC-DOS, the file is
  578.    called IBMBIO.COM.) This file is marked with the special attributes hidden
  579.    and system.
  580.  
  581.  The DOS Kernel
  582.  
  583.    The DOS kernel implements MS-DOS as it is seen by application programs.
  584.    The kernel is a proprietary program supplied by Microsoft Corporation and
  585.    provides a collection of hardware-independent services called system
  586.    functions. These functions include the following:
  587.  
  588.    ■  File and record management
  589.  
  590.    ■  Memory management
  591.  
  592.    ■  Character-device input/output
  593.  
  594.    ■  Spawning of other programs
  595.  
  596.    ■  Access to the real-time clock
  597.  
  598.    Programs can access system functions by loading registers with
  599.    function-specific parameters and then transferring to the operating system
  600.    by means of a software interrupt.
  601.  
  602.    The DOS kernel is read into memory during system initialization from the
  603.    MSDOS.SYS file on the boot disk. (The file is called IBMDOS.COM in
  604.    PC-DOS.) This file is marked with the attributes hidden and system.
  605.  
  606.  The Command Processor
  607.  
  608.    The command processor, or shell, is the user's interface to the operating
  609.    system. It is responsible for parsing and carrying out user commands,
  610.    including the loading and execution of other programs from a disk or other
  611.    mass-storage device.
  612.  
  613.    The default shell that is provided with MS-DOS is found in a file called
  614.    COMMAND.COM. Although COMMAND.COM prompts and responses constitute the
  615.    ordinary user's complete perception of MS-DOS, it is important to realize
  616.    that COMMAND.COM is not the operating system, but simply a special class
  617.    of program running under the control of MS-DOS.
  618.  
  619.    COMMAND.COM can be replaced with a shell of the programmer's own design by
  620.    simply adding a SHELL directive to the system-configuration file
  621.    (CONFIG.SYS) on the system startup disk. The product COMMAND-PLUS from ESP
  622.    Systems is an example of such an alternative shell.
  623.  
  624.    More about COMMAND.COM
  625.  
  626.    The default MS-DOS shell, COMMAND.COM, is divided into three parts:
  627.  
  628.    ■  A resident portion
  629.  
  630.    ■  An initialization section
  631.  
  632.    ■  A transient module
  633.  
  634.    The resident portion is loaded in lower memory, above the DOS kernel and
  635.    its buffers and tables. It contains the routines to process Ctrl-C and
  636.    Ctrl-Break, critical errors, and the termination (final exit) of other
  637.    transient programs. This part of COMMAND.COM issues error messages and is
  638.    responsible for the familiar prompt
  639.  
  640.    Abort, Retry, Ignore?
  641.  
  642.    The resident portion also contains the code required to reload the
  643.    transient portion of COMMAND.COM when necessary.
  644.  
  645.    The initialization section of COMMAND.COM is loaded above the resident
  646.    portion when the system is started. It processes the AUTOEXEC.BAT batch
  647.    file (the user's list of commands to execute at system startup), if one is
  648.    present, and is then discarded.
  649.  
  650.    The transient portion of COMMAND.COM is loaded at the high end of memory,
  651.    and its memory can also be used for other purposes by application
  652.    programs. The transient module issues the user prompt, reads the commands
  653.    from the keyboard or batch file, and causes them to be executed. When an
  654.    application program terminates, the resident portion of COMMAND.COM does a
  655.    checksum of the transient module to determine whether it has been
  656.    destroyed and fetches a fresh copy from the disk if necessary.
  657.  
  658.    The user commands that are accepted by COMMAND.COM fall into three
  659.    categories:
  660.  
  661.    ■  Internal commands
  662.  
  663.    ■  External commands
  664.  
  665.    ■  Batch files
  666.  
  667.    Internal commands, sometimes called intrinsic commands, are those carried
  668.    out by code embedded in COMMAND.COM itself. Commands in this category
  669.    include COPY, REN(AME), DIR(ECTORY), and DEL(ETE). The routines for the
  670.    internal commands are included in the transient part of COMMAND.COM.
  671.  
  672.    External commands, sometimes called extrinsic commands or transient
  673.    programs, are the names of programs stored in disk files. Before these
  674.    programs can be executed, they must be loaded from the disk into the
  675.    transient program area (TPA) of memory. (See "How MS-DOS Is Loaded" in
  676.    this chapter.) Familiar examples of external commands are CHKDSK, BACKUP,
  677.    and RESTORE. As soon as an external command has completed its work, it is
  678.    discarded from memory; hence, it must be reloaded from disk each time it
  679.    is invoked.
  680.  
  681.    Batch files are text files that contain lists of other intrinsic,
  682.    extrinsic, or batch commands. These files are processed by a special
  683.    interpreter that is built into the transient portion of COMMAND.COM. The
  684.    interpreter reads the batch file one line at a time and carries out each
  685.    of the specified operations in order.
  686.  
  687.    In order to interpret a user's command, COMMAND.COM first looks to see if
  688.    the user typed the name of a built-in (intrinsic) command that it can
  689.    carry out directly. If not, it searches for an external command
  690.    (executable program file) or batch file by the same name. The search is
  691.    carried out first in the current directory of the current disk drive and
  692.    then in each of the directories specified in the most recent PATH command.
  693.    In each directory inspected, COMMAND.COM first tries to find a file with
  694.    the extension .COM, then .EXE, and finally .BAT. If the search fails for
  695.    all three file types in all of the possible locations, COMMAND.COM
  696.    displays the familiar message
  697.  
  698.    Bad command or file name
  699.  
  700.    If a .COM file or a .EXE file is found, COMMAND.COM uses the MS-DOS EXEC
  701.    function to load and execute it. The EXEC function builds a special data
  702.    structure called a program segment prefix (PSP) above the resident portion
  703.    of COMMAND.COM in the transient program area. The PSP contains various
  704.    linkages and pointers needed by the application program. Next, the EXEC
  705.    function loads the program itself, just above the PSP, and performs any
  706.    relocation that may be necessary. Finally, it sets up the registers
  707.    appropriately and transfers control to the entry point for the program.
  708.    (Both the PSP and the EXEC function will be discussed in more detail in
  709.    Chapters 3 and 12.) When the transient program has finished its job, it
  710.    calls a special MS-DOS termination function that releases the transient
  711.    program's memory and returns control to the program that caused the
  712.    transient program to be loaded (COMMAND.COM, in this case).
  713.  
  714.    A transient program has nearly complete control of the system's resources
  715.    while it is executing. The only other tasks that are accomplished are
  716.    those performed by interrupt handlers (such as the keyboard input driver
  717.    and the real-time clock) and operations that the transient program
  718.    requests from the operating system. MS-DOS does not support sharing of the
  719.    central processor among several tasks executing concurrently, nor can it
  720.    wrest control away from a program when it crashes or executes for too
  721.    long. Such capabilities are the province of MS OS/2, which is a
  722.    protected-mode system with preemptive multitasking (time-slicing).
  723.  
  724.  
  725.  How MS-DOS Is Loaded
  726.  
  727.    When the system is started or reset, program execution begins at address
  728.    0FFFF0H. This is a feature of the 8086/8088 family of microprocessors and
  729.    has nothing to do with MS-DOS. Systems based on these processors are
  730.    designed so that address 0FFFF0H lies within an area of ROM and contains a
  731.    jump machine instruction to transfer control to system test code and the
  732.    ROM bootstrap routine (Figure 2-1).
  733.  
  734.    The ROM bootstrap routine reads the disk bootstrap routine from the first
  735.    sector of the system startup disk (the boot sector) into memory at some
  736.    arbitrary address and then transfers control to it (Figure 2-2). (The
  737.    boot sector also contains a table of information about the disk format.)
  738.  
  739.    The disk bootstrap routine checks to see if the disk contains a copy of
  740.    MS-DOS. It does this by reading the first sector of the root directory and
  741.    determining whether the first two files are IO.SYS and MSDOS.SYS (or
  742.    IBMBIO.COM and IBMDOS.COM), in that order. If these files are not present,
  743.    the user is prompted to change disks and strike any key to try again.
  744.  
  745.           ┌───────────────────────────────────────────────┐
  746.           │            ROM bootstrap routine              │
  747.           ├───────────────────────────────────────────────┤
  748.           │                                               │
  749.           ├───────────────────────────────────────────────┤  Top of RAM
  750.           │                                               │
  751.           │                                               │
  752.           └──────────────────────┐                        │
  753.           ┌────────────────────┐ └────────────────────────┘
  754.           │                    └──────────────────────────┐
  755.           │                                               │
  756.           │                                               │
  757.           │                                               │
  758.    00400H ├───────────────────────────────────────────────┤
  759.           │             Interrupt vectors                 │
  760.    00000H └───────────────────────────────────────────────┘
  761.  
  762.    Figure 2-1.  A typical 8086/8088-based computer system immediately after
  763.    system startup or reset. Execution begins at location 0FFFF0H, which
  764.    contains a jump instruction that directs program control to the ROM
  765.    bootstrap routine.
  766.  
  767.           ┌───────────────────────────────────────────────┐
  768.           │            ROM bootstrap routine              │
  769.           ├───────────────────────────────────────────────┤
  770.           │                                               │
  771.           ├───────────────────────────────────────────────┤  Top of RAM
  772.           │                                               │
  773.           ├───────────────────────────────────────────────┤
  774.           │           Disk bootstrap routine              │
  775.           ├───────────────────────────────────────────────┤  Arbitrary
  776.           │                                               │   load location
  777.           │                                               │
  778.           └──────────────────────┐                        │
  779.           ┌────────────────────┐ └────────────────────────┘
  780.           │                    └──────────────────────────┐
  781.           │                                               │
  782.           │                                               │
  783.    00400H ├───────────────────────────────────────────────┤
  784.           │             Interrupt vectors                 │
  785.    00000H └───────────────────────────────────────────────┘
  786.  
  787.    Figure 2-2.  The ROM bootstrap routine loads the disk bootstrap routine
  788.    into memory from the first sector of the system startup disk and then
  789.    transfers control to it.
  790.  
  791.    If the two system files are found, the disk bootstrap reads them into
  792.    memory and transfers control to the initial entry point of IO.SYS (Figure
  793.    2-3). (In some implementations, the disk bootstrap reads only IO.SYS into
  794.    memory, and IO.SYS in turn loads the MSDOS.SYS file.)
  795.  
  796.    The IO.SYS file that is loaded from the disk actually consists of two
  797.    separate modules. The first is the BIOS, which contains the linked set of
  798.    resident device drivers for the console, auxiliary port, printer, block,
  799.    and clock devices, plus some hardware-specific initialization code that is
  800.    run only at system startup. The second module, SYSINIT, is supplied by
  801.    Microsoft and linked into the IO.SYS file, along with the BIOS, by the
  802.    computer manufacturer.
  803.  
  804.    SYSINIT is called by the manufacturer's BIOS initialization code. It
  805.    determines the amount of contiguous memory present in the system and then
  806.    relocates itself to high memory. Then it moves the DOS kernel, MSDOS.SYS,
  807.    from its original load location to its final memory location, overlaying
  808.    the original SYSINIT code and any other expendable initialization code
  809.    that was contained in the IO.SYS file (Figure 2-4).
  810.  
  811.    Next, SYSINIT calls the initialization code in MSDOS.SYS. The DOS kernel
  812.    initializes its internal tables and work areas, sets up the interrupt
  813.    vectors 20H through 2FH, and traces through the linked list of resident
  814.    device drivers, calling the initialization function for each. (See Chapter
  815.    14.)
  816.  
  817.           ┌───────────────────────────────────────────────┐
  818.           │             ROM bootstrap routine             │
  819.           ├───────────────────────────────────────────────┤
  820.           │                                               │
  821.           ├───────────────────────────────────────────────┤  Top of RAM
  822.           │                                               │
  823.           ├───────────────────────────────────────────────┤
  824.           │            Disk bootstrap routine             │
  825.           ├───────────────────────────────────────────────┤
  826.           │                                               │
  827.           └──────────────────────┐                        │
  828.           ┌────────────────────┐ └────────────────────────┘
  829.           │                    └──────────────────────────┐
  830.           │                                               │
  831.           ├───────────────────────────────────────────────┤
  832.           │          DOS kernel (from MSDOS.SYS)          │
  833.           ├───────────────────────────────────────────────┤  In temporary
  834.           │             SYSINIT (from IO.SYS)             │   location
  835.           ├───────────────────────────────────────────────┤
  836.           │              BIOS (from IO.SYS)               │
  837.           ├───────────────────────────────────────────────┤
  838.           │                                               │
  839.    00400H ├───────────────────────────────────────────────┤
  840.           │               Interrupt vectors               │
  841.    00000H └───────────────────────────────────────────────┘
  842.  
  843.    Figure 2-3.  The disk bootstrap reads the file IO.SYS into memory. This
  844.    file contains the MS-DOS BIOS (resident device drivers) and the SYSINIT
  845.    module. Either the disk bootstrap or the BIOS (depending upon the
  846.    manufacturer's implementation) then reads the DOS kernel into memory from
  847.    the MSDOS.SYS file.
  848.  
  849.    These driver functions determine the equipment status, perform any
  850.    necessary hardware initialization, and set up the vectors for any external
  851.    hardware interrupts the drivers will service.
  852.  
  853.    As part of the initialization sequence, the DOS kernel examines the
  854.    disk-parameter blocks returned by the resident block-device drivers,
  855.    determines the largest sector size that will be used in the system, builds
  856.    some drive-parameter blocks, and allocates a disk sector buffer. Control
  857.    then returns to SYSINIT.
  858.  
  859.    When the DOS kernel has been initialized and all resident device drivers
  860.    are available, SYSINIT can call on the normal MS-DOS file services to open
  861.    the CONFIG.SYS file. This optional file can contain a variety of commands
  862.    that enable the user to customize the MS-DOS environment. For instance,
  863.    the user can specify additional hardware device drivers, the number of
  864.    disk buffers, the maximum number of files that can be open at one time,
  865.    and the filename of the command processor (shell).
  866.  
  867.    If it is found, the entire CONFIG.SYS file is loaded into memory for
  868.    processing. All lowercase characters are converted to uppercase, and the
  869.    file is interpreted one line at a time to process the commands. Memory is
  870.    allocated for the disk buffer cache and the internal file control blocks
  871.    used by the handle file and record system functions. (See Chapter 8.) Any
  872.    device drivers indicated in the CONFIG.SYS file are sequentially loaded
  873.    into memory, initialized by calls to their init modules, and linked into
  874.    the device-driver list. The init function of each driver tells SYSINIT how
  875.    much memory to reserve for that driver.
  876.  
  877.           ┌───────────────────────────────────────────────┐
  878.           │            ROM bootstrap routine              │
  879.           ├───────────────────────────────────────────────┤
  880.           │                                               │
  881.           ├───────────────────────────────────────────────┤  Top of RAM
  882.           │               SYSINIT module                  │
  883.           ├───────────────────────────────────────────────┤
  884.           │                                               │
  885.           └──────────────────────┐                        │
  886.           ┌────────────────────┐ └────────────────────────┘
  887.           │                    └──────────────────────────┐
  888.           │                                               │
  889.           ├───────────────────────────────────────────────┤
  890.           │              Installable drivers              │
  891.           ├───────────────────────────────────────────────┤
  892.           │              File control blocks              │
  893.           ├───────────────────────────────────────────────┤
  894.           │               Disk buffer cache               │
  895.           ├───────────────────────────────────────────────┤
  896.           │                  DOS kernel                   │
  897.           ├───────────────────────────────────────────────┤  In final
  898.           │                     BIOS                      │   location
  899.           ├───────────────────────────────────────────────┤
  900.           │                                               │
  901.           ├───────────────────────────────────────────────┤
  902.    00400H ├───────────────────────────────────────────────┤
  903.           │             Interrupt vectors                 │
  904.    00000H └───────────────────────────────────────────────┘
  905.  
  906.    Figure 2-4.  SYSINIT moves itself to high memory and relocates the DOS
  907.    kernel, MSDOS.SYS, downward to its final address. The MS-DOS disk buffer
  908.    cache and file control block areas are allocated, and then the installable
  909.    device drivers specified in the CONFIG.SYS file are loaded and linked into
  910.    the system.
  911.  
  912.    After all installable device drivers have been loaded, SYSINIT closes all
  913.    file handles and reopens the console (CON), printer (PRN), and auxiliary
  914.    (AUX) devices as the standard input, standard output, standard error,
  915.    standard list, and standard auxiliary devices. This allows a
  916.    user-installed character-device driver to override the BIOS's resident
  917.    drivers for the standard devices.
  918.  
  919.    Finally, SYSINIT calls the MS-DOS EXEC function to load the command
  920.    interpreter, or shell. (The default shell is COMMAND.COM, but another
  921.    shell can be substituted by means of the CONFIG.SYS file.) Once the shell
  922.    is loaded, it displays a prompt and waits for the user to enter a command.
  923.    MS-DOS is now ready for business, and the SYSINIT module is discarded
  924.    (Figure 2-5).
  925.  
  926.           ┌───────────────────────────────────────────────┐
  927.           │            ROM bootstrap routine              │
  928.           ├───────────────────────────────────────────────┤
  929.           │                                               │
  930.           ├───────────────────────────────────────────────┤  Top of RAM
  931.           │         Transient part of COMMAND.COM         │
  932.           ├───────────────────────────────────────────────┤
  933.           └──────────────────────┐                        │
  934.           ┌────────────────────┐ └────────────────────────┘
  935.           │                    └──────────────────────────┐
  936.           │            Transient program area             │
  937.           ├───────────────────────────────────────────────┤
  938.           │         Resident part of COMMAND.COM          │
  939.           ├───────────────────────────────────────────────┤
  940.           │              Installable drivers              │
  941.           ├───────────────────────────────────────────────┤
  942.           │              File control blocks              │
  943.           ├───────────────────────────────────────────────┤
  944.           │               Disk buffer cache               │
  945.           ├───────────────────────────────────────────────┤
  946.           │                  DOS kernel                   │
  947.           ├───────────────────────────────────────────────┤
  948.           │                     BIOS                      │
  949.           ├───────────────────────────────────────────────┤
  950.           │                                               │
  951.    00400H ├───────────────────────────────────────────────┤
  952.           │             Interrupt vectors                 │
  953.    00000H └───────────────────────────────────────────────┘
  954.  
  955.    Figure 2-5.  The final result of the MS-DOS startup process for a typical
  956.    system. The resident portion of COMMAND.COM lies in low memory, above the
  957.    DOS kernel. The transient portion containing the batch-file interpreter
  958.    and intrinsic commands is placed in high memory, where it can be overlaid
  959.    by extrinsic commands and application programs running in the transient
  960.    program area.
  961.  
  962.  
  963.  
  964.  ────────────────────────────────────────────────────────────────────────────
  965.  Chapter 3  Structure of MS-DOS Application Programs
  966.  
  967.    Programs that run under MS-DOS come in two basic flavors: .COM programs,
  968.    which have a maximum size of approximately 64 KB, and .EXE programs, which
  969.    can be as large as available memory. In Intel 8086 parlance, .COM programs
  970.    fit the tiny model, in which all segment registers contain the same value;
  971.    that is, the code and data are mixed together. In contrast, .EXE programs
  972.    fit the small, medium, or large model, in which the segment registers
  973.    contain different values; that is, the code, data, and stack reside in
  974.    separate segments. .EXE programs can have multiple code and data segments,
  975.    which are respectively addressed by long calls and by manipulation of the
  976.    data segment (DS) register.
  977.  
  978.    A .COM-type program resides on the disk as an absolute memory image, in a
  979.    file with the extension .COM. The file does not have a header or any other
  980.    internal identifying information. A .EXE program, on the other hand,
  981.    resides on the disk in a special type of file with a unique header, a
  982.    relocation map, a checksum, and other information that is (or can be) used
  983.    by MS-DOS.
  984.  
  985.    Both .COM and .EXE programs are brought into memory for execution by the
  986.    same mechanism: the EXEC function, which constitutes the MS-DOS loader.
  987.    EXEC can be called with the filename of a program to be loaded by
  988.    COMMAND.COM (the normal MS-DOS command interpreter), by other shells or
  989.    user interfaces, or by another program that was previously loaded by EXEC.
  990.    If there is sufficient free memory in the transient program area, EXEC
  991.    allocates a block of memory to hold the new program, builds the program
  992.    segment prefix (PSP) at its base, and then reads the program into memory
  993.    immediately above the PSP. Finally, EXEC sets up the segment registers and
  994.    the stack and transfers control to the program.
  995.  
  996.    When it is invoked, EXEC can be given the addresses of additional
  997.    information, such as a command tail, file control blocks, and an
  998.    environment block; if supplied, this information will be passed on to the
  999.    new program. (The exact procedure for using the EXEC function in your own
  1000.    programs is discussed, with examples, in Chapter 12.)
  1001.  
  1002.    .COM and .EXE programs are often referred to as transient programs. A
  1003.    transient program "owns" the memory block it has been allocated and has
  1004.    nearly total control of the system's resources while it is executing. When
  1005.    the program terminates, either because it is aborted by the operating
  1006.    system or because it has completed its work and systematically performed a
  1007.    final exit back to MS-DOS, the memory block is then freed (hence the term
  1008.    transient) and can be used by the next program in line to be loaded.
  1009.  
  1010.  
  1011.  The Program Segment Prefix
  1012.  
  1013.    A thorough understanding of the program segment prefix is vital to
  1014.    successful programming under MS-DOS. It is a reserved area, 256 bytes
  1015.    long, that is set up by MS-DOS at the base of the memory block allocated
  1016.    to a transient program. The PSP contains some linkages to MS-DOS that can
  1017.    be used by the transient program, some information MS-DOS saves for its
  1018.    own purposes, and some information MS-DOS passes to the transient
  1019.    program──to be used or not, as the program requires (Figure 3-1).
  1020.  
  1021.    Offset
  1022.    0000H ┌────────────────────────────────────────────────────────┐
  1023.          │                        Int 20H                         │
  1024.    0002H ├────────────────────────────────────────────────────────┤
  1025.          │            Segment, end of allocation block            │
  1026.    0004H ├────────────────────────────────────────────────────────┤
  1027.          │                        Reserved                        │
  1028.    0005H ├────────────────────────────────────────────────────────┤
  1029.          │        Long call to MS-DOS function dispatcher         │
  1030.    000AH ├────────────────────────────────────────────────────────┤
  1031.          │        Previous contents of termination handler        │
  1032.          │               interrupt vector (Int 22H)               │
  1033.    000EH ├────────────────────────────────────────────────────────┤
  1034.          │ Previous contents of Ctrl-C interrupt vector (Int 23H) │
  1035.    0012H ├────────────────────────────────────────────────────────┤
  1036.          │      Previous contents of critical-error handler       │
  1037.          │               interrupt vector (Int 24H)               │
  1038.    0016H ├────────────────────────────────────────────────────────┤
  1039.          │                        Reserved                        │
  1040.    002CH ├────────────────────────────────────────────────────────┤
  1041.          │          Segment address of environment block          │
  1042.    002EH ├────────────────────────────────────────────────────────┤
  1043.          │                        Reserved                        │
  1044.    005CH ├────────────────────────────────────────────────────────┤
  1045.          │             Default file control block #1              │
  1046.    006CH ├────────────────────────────────────────────────────────┤
  1047.          │             Default file control block #2              │
  1048.          │              (overlaid if FCB #1 opened)               │
  1049.    008OH ├────────────────────────────────────────────────────────┤
  1050.          └──────────────────────────┐                             │
  1051.          ┌────────────────────────┐ └─────────────────────────────┘
  1052.          │                        └───────────────────────────────┐
  1053.          │  Command tail and default disk transfer area (buffer)  │
  1054.    OOFFH └────────────────────────────────────────────────────────┘
  1055.  
  1056.    Figure 3-1.  The structure of the program segment prefix.
  1057.  
  1058.    In the first versions of MS-DOS, the PSP was designed to be compatible
  1059.    with a control area that was built beneath transient programs under
  1060.    Digital Research's venerable CP/M operating system, so that programs could
  1061.    be ported to MS-DOS without extensive logical changes. Although MS-DOS has
  1062.    evolved considerably since those early days, the structure of the PSP is
  1063.    still recognizably similar to its CP/M equivalent. For example, offset
  1064.    0000H in the PSP contains a linkage to the MS-DOS process-termination
  1065.    handler, which cleans up after the program has finished its job and
  1066.    performs a final exit. Similarly, offset 0005H in the PSP contains a
  1067.    linkage to the MS-DOS function dispatcher, which performs disk operations,
  1068.    console input/output, and other such services at the request of the
  1069.    transient program. Thus, calls to PSP:0000 and PSP:0005 have the same
  1070.    effect as CALL 0000 and CALL 0005 under CP/M. (These linkages are not the
  1071.    "approved" means of obtaining these services, however.)
  1072.  
  1073.    The word at offset 0002H in the PSP contains the segment address of the
  1074.    top of the transient program's allocated memory block. The program can use
  1075.    this value to determine whether it should request more memory to do its
  1076.    job or whether it has extra memory that it can release for use by other
  1077.    processes.
  1078.  
  1079.    Offsets 000AH through 0015H in the PSP contain the previous contents of
  1080.    the interrupt vectors for the termination, Ctrl-C, and critical-error
  1081.    handlers. If the transient program alters these vectors for its own
  1082.    purposes, MS-DOS restores the original values saved in the PSP when the
  1083.    program terminates.
  1084.  
  1085.    The word at PSP offset 002CH holds the segment address of the environment
  1086.    block, which contains a series of ASCIIZ strings (sequences of ASCII
  1087.    characters terminated by a null, or zero, byte). The environment block is
  1088.    inherited from the program that called the EXEC function to load the
  1089.    currently executing program. It contains such information as the current
  1090.    search path used by COMMAND.COM to find executable programs, the location
  1091.    on the disk of COMMAND.COM itself, and the format of the user prompt used
  1092.    by COMMAND.COM.
  1093.  
  1094.    The command tail──the remainder of the command line that invoked the
  1095.    transient program, after the program's name──is copied into the PSP
  1096.    starting at offset 0081H. The length of the command tail, not including
  1097.    the return character at its end, is placed in the byte at offset 0080H.
  1098.    Redirection or piping parameters and their associated filenames do not
  1099.    appear in the portion of the command line (the command tail) that is
  1100.    passed to the transient program, because redirection is transparent to
  1101.    applications.
  1102.  
  1103.    To provide compatibility with CP/M, MS-DOS parses the first two parameters
  1104.    in the command tail into two default file control blocks (FCBs) at
  1105.    PSP:005CH and PSP:006CH, under the assumption that they may be filenames.
  1106.    However, if the parameters are filenames that include a path
  1107.    specification, only the drive code will be valid in these default FCBs,
  1108.    because FCB-type file- and record-access functions do not support
  1109.    hierarchical file structures. Although the default FCBs were an aid in
  1110.    earlier years, when compatibility with CP/M was more of a concern, they
  1111.    are essentially useless in modern MS-DOS application programs that must
  1112.    provide full path support. (File control blocks are discussed in detail in
  1113.    Chapter 8 and hierarchical file structures are discussed in Chapter 9.)
  1114.  
  1115.    The 128-byte area from 0080H through 00FFH in the PSP also serves as the
  1116.    default disk transfer area (DTA), which is set by MS-DOS before passing
  1117.    control to the transient program. If the program does not explicitly
  1118.    change the DTA, any file read or write operations requested with the FCB
  1119.    group of function calls automatically use this area as a data buffer. This
  1120.    is rarely useful and is another facet of MS-DOS's handling of the PSP that
  1121.    is present only for compatibility with CP/M.
  1122.  
  1123.    ──────────────────────────────────────────────────────────────────────────
  1124.    WARNING
  1125.      Programs must not alter any part of the PSP below offset 005CH.
  1126.    ──────────────────────────────────────────────────────────────────────────
  1127.  
  1128.  
  1129.  Introduction to .COM Programs
  1130.  
  1131.    Programs of the .COM persuasion are stored in disk files that hold an
  1132.    absolute image of the machine instructions to be executed. Because the
  1133.    files contain no relocation information, they are more compact, and are
  1134.    loaded for execution slightly faster, than equivalent .EXE files. Note
  1135.    that MS-DOS does not attempt to ascertain whether a .COM file actually
  1136.    contains executable code (there is no signature or checksum, as in the
  1137.    case of a .EXE file); it simply brings any file with the .COM extension
  1138.    into memory and jumps to it.
  1139.  
  1140.    Because .COM programs are loaded immediately above the program segment
  1141.    prefix and do not have a header that can specify another entry point, they
  1142.    must always have an origin of 0100H, which is the length of the PSP.
  1143.    Location 0100H must contain an executable instruction. The maximum length
  1144.    of a .COM program is 65,536 bytes, minus the length of the PSP (256 bytes)
  1145.    and a mandatory word of stack (2 bytes).
  1146.  
  1147.    When control is transferred to the .COM program from MS-DOS, all of the
  1148.    segment registers point to the PSP (Figure 3-2). The stack pointer
  1149.    register contains 0FFFEH if memory allows; otherwise, it is set as high as
  1150.    possible in memory minus 2 bytes. (MS-DOS pushes a zero word on the stack
  1151.    before entry.)
  1152.  
  1153.       SS:SP  ┌────────────────────────────────────────────────────────┐
  1154.              │                                                        │
  1155.              │       Stack grows downward from top of segment         │
  1156.              │                           │                            │
  1157.              │                                                       │
  1158.              │                                                       │
  1159.              │                           │                            │
  1160.              │                 Program code and data                  │
  1161.              │                                                        │
  1162.    CS:0100H  ├────────────────────────────────────────────────────────┤
  1163.              │                 Program segment prefix                 │
  1164.    CS:0000H  └────────────────────────────────────────────────────────┘
  1165.    DS:0000H
  1166.    ES:0000H
  1167.    SS:0000H
  1168.  
  1169.    Figure 3-2.  A memory image of a typical .COM-type program after loading.
  1170.    The contents of the .COM file are brought into memory just above the
  1171.    program segment prefix. Program, code, and data are mixed together in the
  1172.    same segment, and all segment registers contain the same value.
  1173.  
  1174.    Although the size of an executable .COM file can't exceed 64 KB, the
  1175.    current versions of MS-DOS allocate all of the transient program area to
  1176.    .COM programs when they are loaded. Because many such programs date from
  1177.    the early days of MS-DOS and are not necessarily "well-behaved" in their
  1178.    approach to memory management, the operating system simply makes the
  1179.    worst-case assumption and gives .COM programs everything that is
  1180.    available. If a .COM program wants to use the EXEC function to invoke
  1181.    another process, it must first shrink down its memory allocation to the
  1182.    minimum memory it needs in order to continue, taking care to protect its
  1183.    stack. (This is discussed in more detail in Chapter 12.)
  1184.  
  1185.    When a .COM program finishes executing, it can return control to MS-DOS by
  1186.    several means. The preferred method is Int 21H Function 4CH, which allows
  1187.    the program to pass a return code back to the program, shell, or batch
  1188.    file that invoked it. However, if the program is running under MS-DOS
  1189.    version 1, it must exit by means of Int 20H, Int 21H Function 0, or a
  1190.    NEAR RETURN. (Because a word of zero was pushed onto the stack at entry, a
  1191.    NEAR RETURN causes a transfer to PSP:0000, which contains an Int 20H
  1192.    instruction.)
  1193.  
  1194.    A .COM-type application can be linked together from many separate object
  1195.    modules. All of the modules must use the same code-segment name and class
  1196.    name, and the module with the entry point at offset 0100H within the
  1197.    segment must be linked first. In addition, all of the procedures within a
  1198.    .COM program should have the NEAR attribute, because all executable code
  1199.    resides in one segment.
  1200.  
  1201.    When linking a .COM program, the linker will display the message
  1202.  
  1203.    Warning: no stack segment
  1204.  
  1205.    This message can be ignored. The linker output is a .EXE file, which must
  1206.    be converted into a .COM file with the MS-DOS EXE2BIN utility before
  1207.    execution. You can then delete the .EXE file. (An example of this process
  1208.    is provided in Chapter 4.)
  1209.  
  1210.  An Example .COM Program
  1211.  
  1212.    The HELLO.COM program listed in Figure 3-3 demonstrates the structure of
  1213.    a simple assembly-language program that is destined to become a .COM file.
  1214.    (You may find it helpful to compare this listing with the HELLO.EXE
  1215.    program later in this chapter.) Because this program is so short and
  1216.    simple, a relatively high proportion of the source code is actually
  1217.    assembler directives that do not result in any executable code.
  1218.  
  1219.    The NAME statement simply provides a module name for use during the
  1220.    linkage process. This aids understanding of the map that the linker
  1221.    produces. In MASM versions 5.0 and later, the module name is always the
  1222.    same as the filename, and the NAME statement is ignored.
  1223.  
  1224.    The PAGE command, when used with two operands, as in line 2, defines the
  1225.    length and width of the page. These default respectively to 66 lines and
  1226.    80 characters. If you use the PAGE command without any operands, a
  1227.    formfeed is sent to the printer and a heading is printed. In larger
  1228.    programs, use the PAGE command liberally to place each of your subroutines
  1229.    on separate pages for easy reading.
  1230.  
  1231.    The TITLE command, in line 3, specifies the text string (limited to 60
  1232.    characters) that is to be printed at the upper left corner of each page.
  1233.    The TITLE command is optional and cannot be used more than once in each
  1234.    assembly-language source file.
  1235.  
  1236.    ──────────────────────────────────────────────────────────────────────────
  1237.     1:          name    hello
  1238.     2:          page    55,132
  1239.     3:          title   HELLO.COM--print hello on terminal
  1240.     4:
  1241.     5:  ;
  1242.     6:  ; HELLO.COM:    demonstrates various components
  1243.     7:  ;               of a functional .COM-type assembly-
  1244.     8:  ;               language program, and an MS-DOS
  1245.     9:  ;               function call.
  1246.    10:  ;
  1247.    11:  ; Ray Duncan, May 1988
  1248.    12:  ;
  1249.    13:
  1250.    14:  stdin   equ     0               ; standard input handle
  1251.    15:  stdout  equ     1               ; standard output handle
  1252.    16:  stderr  equ     2               ; standard error handle
  1253.    17:
  1254.    18:  cr      equ     0dh             ; ASCII carriage return
  1255.    19:  lf      equ     0ah             ; ASCII linefeed
  1256.    20:
  1257.    21:
  1258.    22:  _TEXT   segment word public 'CODE'
  1259.    23:
  1260.    24:          org     100h            ; .COM files always have
  1261.    25:                                  ; an origin of 100h
  1262.    26:
  1263.    27:          assume  cs:_TEXT,ds:_TEXT,es:_TEXT,ss:_TEXT
  1264.    28:
  1265.    29:  print   proc    near            ; entry point from MS-DOS
  1266.    30:
  1267.    31:          mov     ah,40h          ; function 40h = write
  1268.    32:          mov     bx,stdout       ; handle for standard output
  1269.    33:          mov     cx,msg_len      ; length of message
  1270.    34:          mov     dx,offset msg   ; address of message
  1271.    35:          int     21h             ; transfer to MS-DOS
  1272.    36:
  1273.    37:          mov     ax,4c00h        ; exit, return code = 0
  1274.    38:          int     21h             ; transfer to MS-DOS
  1275.    39:
  1276.    40:  print   endp
  1277.    41:
  1278.    42:
  1279.    43:  msg     db      cr,lf           ; message to display
  1280.    44:          db      'Hello World!',cr,lf
  1281.    45:
  1282.    46:  msg_len equ     $-msg           ; length of message
  1283.    47:
  1284.    48:
  1285.    49:  _TEXT   ends
  1286.    50:
  1287.    51:          end     print           ; defines entry point
  1288.    ──────────────────────────────────────────────────────────────────────────
  1289.  
  1290.    Figure 3-3.  The HELLO.COM program listing.
  1291.  
  1292.    Dropping down past a few comments and EQU statements, we come to a
  1293.    declaration of a code segment that begins in line 22 with a SEGMENT
  1294.    command and ends in line 49 with an ENDS command. The label in the
  1295.    leftmost field of line 22 gives the code segment the name _TEXT. The
  1296.    operand fields at the right end of the line give the segment the
  1297.    attributes WORD, PUBLIC, and `CODE'. (You might find it helpful to read
  1298.    the Microsoft Macro Assembler manual for detailed explanations of each
  1299.    possible segment attribute.)
  1300.  
  1301.    Because this program is going to be converted into a .COM file, all of its
  1302.    executable code and data areas must lie within one code segment. The
  1303.    program must also have its origin at offset 0100H (immediately above the
  1304.    program segment prefix), which is taken care of by the ORG statement
  1305.    in line 24.
  1306.  
  1307.    Following the ORG instruction, we encounter an ASSUME statement on line
  1308.    27. The concept of ASSUME often baffles new assembly-language programmers.
  1309.    In a way, ASSUME doesn't "do" anything; it simply tells the assembler
  1310.    which segment registers you are going to use to point to the various
  1311.    segments of your program, so that the assembler can provide segment
  1312.    overrides when they are necessary. It's important to notice that the
  1313.    ASSUME statement doesn't take care of loading the segment registers with
  1314.    the proper values; it merely notifies the assembler of your intent to do
  1315.    that within the program. (Remember that, in the case of a .COM program,
  1316.    MS-DOS initializes all the segment registers before entry to point to the
  1317.    PSP.)
  1318.  
  1319.    Within the code segment, we come to another type of block declaration that
  1320.    begins with the PROC command on line 29 and closes with ENDP on line 40.
  1321.    These two instructions declare the beginning and end of a procedure, a
  1322.    block of executable code that performs a single distinct function. The
  1323.    label in the leftmost field of the PROC statement (in this case, print)
  1324.    gives the procedure a name. The operand field gives it an attribute. If
  1325.    the procedure carries the NEAR attribute, only other code in the same
  1326.    segment can call it, whereas if it carries the FAR attribute, code located
  1327.    anywhere in the CPU's memory-addressing space can call it. In .COM
  1328.    programs, all procedures carry the NEAR attribute.
  1329.  
  1330.    For the purposes of this example program, I have kept the print procedure
  1331.    ridiculously simple. It calls MS-DOS Int 21H Function 40H to send the
  1332.    message Hello World! to the video screen, and calls Int 21H Function 4CH
  1333.    to terminate the program.
  1334.  
  1335.    The END statement in line 51 tells the assembler that it has reached the
  1336.    end of the source file and also specifies the entry point for the program.
  1337.    If the entry point is not a label located at offset 0100H, the .EXE file
  1338.    resulting from the assembly and linkage of this source program cannot be
  1339.    converted into a .COM file.
  1340.  
  1341.  
  1342.  Introduction to .EXE Programs
  1343.  
  1344.    We have just discussed a program that was written in such a way that it
  1345.    could be assembled into a .COM file. Such a program is simple in
  1346.    structure, so a programmer who needs to put together this kind of quick
  1347.    utility can concentrate on the program logic and do a minimum amount of
  1348.    worrying about control of the assembler. However, .COM-type programs have
  1349.    some definite disadvantages, and so most serious assembly-language efforts
  1350.    for MS-DOS are written to be converted into .EXE files.
  1351.  
  1352.    Although .COM programs are effectively restricted to a total size of 64 KB
  1353.    for machine code, data, and stack combined, .EXE programs can be
  1354.    practically unlimited in size (up to the limit of the computer's available
  1355.    memory). .EXE programs also place the code, data, and stack in separate
  1356.    parts of the file. Although the normal MS-DOS program loader does not take
  1357.    advantage of this feature of .EXE files, the ability to load different
  1358.    parts of large programs into several separate memory fragments, as well as
  1359.    the opportunity to designate a "pure" code portion of your program that
  1360.    can be shared by several tasks, is very significant in multitasking
  1361.    environments such as Microsoft Windows.
  1362.  
  1363.    The MS-DOS loader always brings a .EXE program into memory immediately
  1364.    above the program segment prefix, although the order of the code, data,
  1365.    and stack segments may vary (Figure 3-4). The .EXE file has a header, or
  1366.    block of control information, with a characteristic format (Figures 3-5
  1367.    and 3-6). The size of this header varies according to the number of
  1368.    program instructions that need to be relocated at load time, but it is
  1369.    always a multiple of 512 bytes.
  1370.  
  1371.    Before MS-DOS transfers control to the program, the initial values of the
  1372.    code segment (CS) register and instruction pointer (IP) register are
  1373.    calculated from the entry-point information in the .EXE file header and
  1374.    the program's load address. This information derives from an END statement
  1375.    in the source code for one of the program's modules. The data segment (DS)
  1376.    and extra segment (ES) registers are made to point to the PSP so that the
  1377.    program can access the environment-block pointer, command tail, and other
  1378.    useful information contained there.
  1379.  
  1380.       SS:SP ┌────────────────────────────────────────────────────────┐
  1381.             │                                                        │
  1382.             │                     Stack segment:                     │
  1383.             │        stack grows downward from top of segment        │
  1384.             │                           │                            │
  1385.             │                                                       │
  1386.    SS:0000H ├────────────────────────────────────────────────────────┤
  1387.             │                      Data segment                      │
  1388.             ├────────────────────────────────────────────────────────┤
  1389.             │                      Program code                      │
  1390.    CS:0000H ├────────────────────────────────────────────────────────┤
  1391.             │                 Program segment prefix                 │
  1392.    DS:0000H └────────────────────────────────────────────────────────┘
  1393.    ES:0000H
  1394.  
  1395.    Figure 3-4.  A memory image of a typical .EXE-type program immediately
  1396.    after loading. The contents of the .EXE file are relocated and brought
  1397.    into memory above the program segment prefix. Code, data, and stack reside
  1398.    in separate segments and need not be in the order shown here. The entry
  1399.    point can be anywhere in the code segment and is specified by the END
  1400.    statement in the main module of the program. When the program receives
  1401.    control, the DS (data segment) and ES (extra segment) registers point to
  1402.    the program segment prefix; the program usually saves this value and then
  1403.    resets the DS and ES registers to point to its data area.
  1404.  
  1405.    The initial contents of the stack segment (SS) and stack pointer (SP)
  1406.    registers come from the header. This information derives from the
  1407.    declaration of a segment with the attribute STACK somewhere in the
  1408.    program's source code. The memory space allocated for the stack may be
  1409.    initialized or uninitialized, depending on the stack-segment definition;
  1410.    many programmers like to initialize the stack memory with a recognizable
  1411.    data pattern so that they can inspect memory dumps and determine how much
  1412.    stack space is actually used by the program.
  1413.  
  1414.    When a .EXE program finishes processing, it should return control to
  1415.    MS-DOS through Int 21H Function 4CH. Other methods are available, but
  1416.    they offer no advantages and are considerably less convenient (because
  1417.    they usually require the CS register to point to the PSP).
  1418.  
  1419.    Byte
  1420.    offset
  1421.    0000H ┌────────────────────────────────────────────────────────┐
  1422.          │           First of .EXE file signature (4DH)           │
  1423.    0001H ├────────────────────────────────────────────────────────┤
  1424.          │        Second part of .EXE file signature (5AH)        │
  1425.    0002H ├────────────────────────────────────────────────────────┤
  1426.          │                 Length of file MOD 512                 │
  1427.    0004H ├────────────────────────────────────────────────────────┤
  1428.          │    Size of file in 512-byte pages, including header    │
  1429.    0006H ├────────────────────────────────────────────────────────┤
  1430.          │            Number of relocation-table items            │
  1431.    0008H ├────────────────────────────────────────────────────────┤
  1432.          │      Size of header in paragraphs (16-byte units)      │
  1433.    000AH ├────────────────────────────────────────────────────────┤
  1434.          │   Minimum number of paragraphs needed above program    │
  1435.    000CH ├────────────────────────────────────────────────────────┤
  1436.          │   Maximum number of paragraphs desired above program   │
  1437.    000EH ├────────────────────────────────────────────────────────┤
  1438.          │          Segment displacement of stack module          │
  1439.    0010H ├────────────────────────────────────────────────────────┤
  1440.          │            Contents of SP register at entry            │
  1441.    0012H ├────────────────────────────────────────────────────────┤
  1442.          │                     Word checksum                      │
  1443.    0014H ├────────────────────────────────────────────────────────┤
  1444.          │            Contents of IP register at entry            │
  1445.    0016H ├────────────────────────────────────────────────────────┤
  1446.          │          Segment displacement of code module           │
  1447.    0018H ├────────────────────────────────────────────────────────┤
  1448.          │        Offset of first relocation item in file         │
  1449.    001AH ├────────────────────────────────────────────────────────┤
  1450.          │    Overlay number (0 for resident part of program)     │
  1451.    001BH ├────────────────────────────────────────────────────────┤
  1452.          │                Variable reserved space                 │
  1453.          ├────────────────────────────────────────────────────────┤
  1454.          │                    Relocation table                    │
  1455.          ├────────────────────────────────────────────────────────┤
  1456.          │                Variable reserved space                 │
  1457.          ├────────────────────────────────────────────────────────┤
  1458.          │               Program and data segments                │
  1459.          ├────────────────────────────────────────────────────────┤
  1460.          │                     Stack segment                      │
  1461.          └────────────────────────────────────────────────────────┘
  1462.  
  1463.    Figure 3-5.  The format of a .EXE load module.
  1464.  
  1465.    The input to the linker for a .EXE-type program can be many separate
  1466.    object modules. Each module can use a unique code-segment name, and the
  1467.    procedures can carry either the NEAR or the FAR attribute, depending on
  1468.    naming conventions and the size of the executable code. The programmer
  1469.    must take care that the modules linked together contain only one segment
  1470.    with the STACK attribute and only one entry point defined with an END
  1471.    assembler directive. The output from the linker is a file with a .EXE
  1472.    extension. This file can be executed immediately.
  1473.  
  1474.    ──────────────────────────────────────────────────────────────────────────
  1475.    C>DUMP HELLO.EXE
  1476.           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
  1477.    0000  4D 5A 28 00 02 00 01 00 20 00 09 00 FF FF 03 00  MZ(..... .......
  1478.    0010  80 00 20 05 00 00 00 00 1E 00 00 00 01 00 01 00  .. .............
  1479.    0020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  1480.    0030  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  1481.    0040  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  1482.    0050  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  1483.          .
  1484.          .
  1485.          .
  1486.    0200  B8 01 00 8E D8 B4 40 BB 01 00 B9 10 00 90 BA 08  ......@.........
  1487.    0210  00 CD 21 B8 00 4C CD 21 0D 0A 48 65 6C 6C 6F 20  ..!..L.!..Hello
  1488.    0220  57 6F 72 6C 64 21 0D 0A                          World!..
  1489.    ──────────────────────────────────────────────────────────────────────────
  1490.  
  1491.    Figure 3-6.  A hex dump of the HELLO.EXE program, demonstrating the
  1492.    contents of a simple .EXE load module. Note the following interesting
  1493.    values: the .EXE signature in bytes 0000H and 0001H, the number of
  1494.    relocation-table items in bytes 0006H and 0007H, the minimum extra memory
  1495.    allocation (MIN_ALLOC) in bytes 000AH and 000BH, the maximum extra memory
  1496.    allocation (MAX_ALLOC) in bytes 000CH and 000DH, and the initial IP
  1497.    (instruction pointer) register value in bytes 0014H and 0015H. See also
  1498.    Figure 3-5.
  1499.  
  1500.  An Example .EXE Program
  1501.  
  1502.    The HELLO.EXE program in Figure 3-7 demonstrates the fundamental
  1503.    structure of an assembly-language program that is destined to become a
  1504.    .EXE file. At minimum, it should have a module name, a code segment, a
  1505.    stack segment, and a primary procedure that receives control of the
  1506.    computer from MS-DOS after the program is loaded. The HELLO.EXE program
  1507.    also contains a data segment to provide a more complete example.
  1508.  
  1509.    The NAME, TITLE, and PAGE directives were covered in the HELLO.COM example
  1510.    program and are used in the same manner here, so we'll move to the first
  1511.    new item of interest. After a few comments and EQU statements, we come to
  1512.    a declaration of a code segment that begins on line 21 with a SEGMENT
  1513.    command and ends on line 41 with an ENDS command. As in the HELLO.COM
  1514.    example program, the label in the leftmost field of the line gives the
  1515.    code segment the name _TEXT. The operand fields at the right end of the
  1516.    line give the attributes WORD, PUBLIC, and `CODE'.
  1517.  
  1518.    Following the code-segment instruction, we find an ASSUME statement on
  1519.    line 23. Notice that, unlike the equivalent statement in the HELLO.COM
  1520.    program, the ASSUME statement in this program specifies several different
  1521.    segment names. Again, remember that this statement has no direct effect on
  1522.    the contents of the segment registers but affects only the operation of
  1523.    the assembler itself.
  1524.  
  1525.    ──────────────────────────────────────────────────────────────────────────
  1526.     1:          name    hello
  1527.     2:          page    55,132
  1528.     3:          title   HELLO.EXE--print Hello on terminal
  1529.     4:  ;
  1530.     5:  ; HELLO.EXE:    demonstrates various components
  1531.     6:  ;               of a functional .EXE-type assembly-
  1532.     7:  ;               language program, use of segments,
  1533.     8:  ;               and an MS-DOS function call.
  1534.     9:  ;
  1535.    10:  ; Ray Duncan, May 1988
  1536.    11:  ;
  1537.    12:
  1538.    13:  stdin   equ     0               ; standard input handle
  1539.    14:  stdout  equ     1               ; standard output handle
  1540.    15:  stderr  equ     2               ; standard error handle
  1541.    16:
  1542.    17:  cr      equ     0dh             ; ASCII carriage return
  1543.    18:  lf      equ     0ah             ; ASCII linefeed
  1544.    19:
  1545.    20:
  1546.    21:  _TEXT   segment word public 'CODE'
  1547.    22:
  1548.    23:          assume  cs:_TEXT,ds:_DATA,ss:STACK
  1549.    24:
  1550.    25:  print   proc    far             ; entry point from MS-DOS
  1551.    26:
  1552.    27:          mov     ax,_DATA        ; make our data segment
  1553.    28:          mov     ds,ax           ; addressable...
  1554.    29:
  1555.    30:          mov     ah,40h          ; function 40h = write
  1556.    31:          mov     bx,stdout       ; standard output handle
  1557.    32:          mov     cx,msg_len      ; length of message
  1558.    33:          mov     dx,offset msg   ; address of message
  1559.    34:          int     21h             ; transfer to MS-DOS
  1560.    35:
  1561.    36:          mov     ax,4c00h        ; exit, return code = 0
  1562.    37:          int     21h             ; transfer to MS-DOS
  1563.    38:
  1564.    39:  print   endp
  1565.    40:
  1566.    41:  _TEXT   ends
  1567.    42:
  1568.    43:
  1569.    44:  _DATA   segment word public 'DATA'
  1570.    45:
  1571.    46:  msg     db      cr,lf           ; message to display
  1572.    47:          db      'Hello World!',cr,lf
  1573.    48:
  1574.    49:  msg_len equ     $-msg           ; length of message
  1575.    50:
  1576.    51:  _DATA   ends
  1577.    52:
  1578.    53:
  1579.    54:  STACK   segment para stack `STACK'
  1580.    55:
  1581.    56:          db      128 dup (?)
  1582.    57:
  1583.    58:  STACK   ends
  1584.    59:
  1585.    60:          end     print           ; defines entry point
  1586.    ──────────────────────────────────────────────────────────────────────────
  1587.  
  1588.    Figure 3-7.  The HELLO.EXE program listing.
  1589.  
  1590.    Within the code segment, the main print procedure is declared by the PROC
  1591.    command on line 25 and closed with ENDP on line 39. Because the procedure
  1592.    resides in a .EXE file, we have given it the FAR attribute as an example,
  1593.    but the attribute is really irrelevant because the program is so small and
  1594.    the procedure is not called by anything else in the same program.
  1595.  
  1596.    The print procedure first initializes the DS register, as indicated in the
  1597.    earlier ASSUME statement, loading it with a value that causes it to point
  1598.    to the base of the data area. (MS-DOS automatically sets up the CS and SS
  1599.    registers.) Next, the procedure uses MS-DOS Int 21H Function 40H to
  1600.    display the message Hello World! on the screen, just as in the HELLO.COM
  1601.    program. Finally, the procedure exits back to MS-DOS with an Int 21H
  1602.    Function 4CH on lines 36 and 37, passing a return code of zero (which by
  1603.    convention means a success).
  1604.  
  1605.    Lines 44 through 51 declare a data segment named _DATA, which contains the
  1606.    variables and constants the program will use. If the various modules of a
  1607.    program contain multiple data segments with the same name, the linker will
  1608.    collect them and place them in the same physical memory segment.
  1609.  
  1610.    Lines 54 through 58 establish a stack segment; PUSH and POP instructions
  1611.    will access this area of scratch memory. Before MS-DOS transfers control
  1612.    to a .EXE program, it sets up the SS and SP registers according to the
  1613.    declared size and location of the stack segment. Be sure to allow enough
  1614.    room for the maximum stack depth that can occur at runtime, plus a safe
  1615.    number of extra words for registers pushed onto the stack during an MS-DOS
  1616.    service call. If the stack overflows, it may damage your other code and
  1617.    data segments and cause your program to behave strangely or even to crash
  1618.    altogether!
  1619.  
  1620.    The END statement on line 60 winds up our brief HELLO.EXE program, telling
  1621.    the assembler that it has reached the end of the source file and providing
  1622.    the label of the program's point of entry from MS-DOS.
  1623.  
  1624.    The differences between .COM and .EXE programs are summarized in Figure
  1625.    3-8.
  1626.  
  1627. ╓┌─┌──────────────────┌──────────────────────────┌───────────────────────────╖
  1628.                       .COM program               .EXE program
  1629.    ──────────────────────────────────────────────────────────────────────────
  1630.    Maximum size       65,536 bytes minus 256     No limit
  1631.                       bytes for PSP and 2 bytes
  1632.                       for stack
  1633.  
  1634.    Entry point        PSP:0100H                  Defined by END statement
  1635.  
  1636.    AL at entry        00H if default FCB #1 has  Same
  1637.                       valid drive, 0FFH if
  1638.                       invalid drive
  1639.  
  1640.                       .COM program               .EXE program
  1641.    ──────────────────────────────────────────────────────────────────────────
  1642. 
  1643.    AH at entry        00H if default FCB #2 has  Same
  1644.                       valid drive, 0FFH if
  1645.                       invalid drive
  1646.  
  1647.    CS at entry        PSP                        Segment containing module
  1648.                                                  with entry point
  1649.  
  1650.    IP at entry        0100H                      Offset of entry point within
  1651.                                                  its segment
  1652.  
  1653.    DS at entry        PSP                        PSP
  1654.  
  1655.    ES at entry        PSP                        PSP
  1656.  
  1657.    SS at entry        PSP                        Segment with STACK attribute
  1658.  
  1659.    SP at entry        0FFFEH or top word in      Size of segment defined with
  1660.                       available memory,          STACK attribute
  1661.                       .COM program               .EXE program
  1662.    ──────────────────────────────────────────────────────────────────────────
  1663.                      available memory,          STACK attribute
  1664.                       whichever is lower
  1665.  
  1666.    Stack at entry     Zero word                  Initialized or uninitialized
  1667.  
  1668.    Stack size         65,536 bytes minus 256     Defined in segment with
  1669.                       bytes for PSP and size of  STACK attribute
  1670.                       executable code and data
  1671.  
  1672.    Subroutine calls   Usually NEAR               NEAR or FAR
  1673.  
  1674.    Exit method        Int 21H Function 4CH      Int 21H Function 4CH
  1675.                       preferred, NEAR RET if     preferred
  1676.                       MS-DOS version 1
  1677.  
  1678.    Size of file       Exact size of program      Size of program plus header
  1679.                                                  (multiple of 512 bytes)
  1680.    ──────────────────────────────────────────────────────────────────────────
  1681.  
  1682.                       .COM program               .EXE program
  1683.    ──────────────────────────────────────────────────────────────────────────
  1684. 
  1685.  
  1686.    Figure 3-8.  Summary of the differences between .COM and .EXE programs,
  1687.    including their entry conditions.
  1688.  
  1689.  
  1690.  More About Assembly-Language Programs
  1691.  
  1692.    Now that we've looked at working examples of .COM and .EXE
  1693.    assembly-language programs, let's backtrack and discuss their elements a
  1694.    little more formally. The following discussion is based on the Microsoft
  1695.    Macro Assembler, hereafter referred to as MASM. If you are familiar with
  1696.    MASM and are an experienced assembly-language programmer, you may want to
  1697.    skip this section.
  1698.  
  1699.    MASM programs can be thought of as having three structural levels:
  1700.  
  1701.    ■  The module level
  1702.  
  1703.    ■  The segment level
  1704.  
  1705.    ■  The procedure level
  1706.  
  1707.    Modules are simply chunks of source code that can be independently
  1708.    maintained and assembled. Segments are physical groupings of like items
  1709.    (machine code or data) within a program and a corresponding segregation of
  1710.    dissimilar items. Procedures are functional subdivisions of an executable
  1711.    program──routines that carry out a particular task.
  1712.  
  1713.  Program Modules
  1714.  
  1715.    Under MS-DOS, the module-level structure consists of files containing the
  1716.    source code for individual routines. Each source file is translated by the
  1717.    assembler into a relocatable object module. An object module can reside
  1718.    alone in an individual file or with many other object modules in an
  1719.    object-module library of frequently used or related routines. The
  1720.    Microsoft Object Linker (LINK) combines object-module files, often with
  1721.    additional object modules extracted from libraries, into an executable
  1722.    program file.
  1723.  
  1724.    Using modules and object-module libraries reduces the size of your
  1725.    application source files (and vastly increases your productivity), because
  1726.    these files need not contain the source code for routines they have in
  1727.    common with other programs. This technique also allows you to maintain the
  1728.    routines more easily, because you need to alter only one copy of their
  1729.    source code stored in one place, instead of many copies stored in
  1730.    different applications. When you improve (or fix) one of these routines,
  1731.    you can simply reassemble it, put its object module back into the library,
  1732.    relink all of the programs that use the routine, and voilga: instant
  1733.    upgrade.
  1734.  
  1735.  Program Segments
  1736.  
  1737.    The term segments refers to two discrete programming concepts: physical
  1738.    segments and logical segments.
  1739.  
  1740.    Physical segments are 64 KB blocks of memory. The Intel 8086/8088 and
  1741.    80286 microprocessors have four segment registers, which are essentially
  1742.    used as pointers to these blocks. (The 80386 has six segment registers,
  1743.    which are a superset of those found on the 8086/8088 and 80286.) Each
  1744.    segment register can point to the bottom of a different 64 KB area of
  1745.    memory. Thus, a program can address any location in memory by appropriate
  1746.    manipulation of the segment registers, but the maximum amount of memory
  1747.    that it can address simultaneously is 256 KB.
  1748.  
  1749.    As we discussed earlier in the chapter, .COM programs assume that all four
  1750.    segment registers always point to the same place──the bottom of the
  1751.    program. Thus, they are limited to a maximum size of 64 KB. .EXE programs,
  1752.    on the other hand, can address many different physical segments and can
  1753.    reset the segment registers to point to each segment as it is needed.
  1754.    Consequently, the only practical limit on the size of a .EXE program is
  1755.    the amount of available memory. The example programs throughout the
  1756.    remainder of this book focus on .EXE programs.
  1757.  
  1758.    Logical segments are the program components. A minimum of three logical
  1759.    segments must be declared in any .EXE program: a code segment, a data
  1760.    segment, and a stack segment. Programs with more than 64 KB of code or
  1761.    data have more than one code or data segment. The routines or data that
  1762.    are used most frequently are put into the primary code and data segments
  1763.    for speed, and routines or data that are used less frequently are put into
  1764.    secondary code and data segments.
  1765.  
  1766.    Segments are declared with the SEGMENT and ENDS directives in the
  1767.    following form:
  1768.  
  1769.    name   SEGMENT attributes
  1770.    .
  1771.    .
  1772.    .
  1773.    name   ENDS
  1774.  
  1775.    The attributes of a segment include its align type (BYTE, WORD, or PARA),
  1776.    combine type (PUBLIC, PRIVATE, COMMON, or STACK), and class type. The
  1777.    segment attributes are used by the linker when it is combining logical
  1778.    segments to create the physical segments of an executable program. Most of
  1779.    the time, you can get by just fine using a small selection of attributes
  1780.    in a rather stereotypical way. However, if you want to use the full range
  1781.    of attributes, you might want to read the detailed explanation in the MASM
  1782.    manual.
  1783.  
  1784.    Programs are classified into one memory model or another based on the
  1785.    number of their code and data segments. The most commonly used memory
  1786.    model for assembly-language programs is the small model, which has one
  1787.    code and one data segment, but you can also use the medium, compact, and
  1788.    large models (Figure 3-9). (Two additional models exist with which we
  1789.    will not be concerning ourselves further: the tiny model, which consists
  1790.    of intermixed code and data in a single segment── for example, a .COM file
  1791.    under MS-DOS; and the huge model, which is supported by the Microsoft C
  1792.    Optimizing Compiler and which allows use of data structures larger than 64
  1793.    KB.)
  1794.  
  1795.    Model                    Code segments           Data segments
  1796.    ──────────────────────────────────────────────────────────────────────────
  1797.    Small                    One                     One
  1798.    Medium                   Multiple                One
  1799.    Compact                  One                     Multiple
  1800.    Large                    Multiple                Multiple
  1801.    ──────────────────────────────────────────────────────────────────────────
  1802.  
  1803.    Figure 3-9.  Memory models commonly used in assembly-language and C
  1804.    programs.
  1805.  
  1806.    For each memory model, Microsoft has established certain segment and class
  1807.    names that are used by all its high-level-language compilers (Figure
  1808.    3-10). Because segment names are arbitrary, you may as well adopt the
  1809.    Microsoft conventions. Their use will make it easier for you to integrate
  1810.    your assembly-language routines into programs written in languages such as
  1811.    C, or to use routines from high-level-language libraries in your
  1812.    assembly-language programs.
  1813.  
  1814.    Another important Microsoft high-level-language convention is to use the
  1815.    GROUP directive to name the near data segment (the segment the program
  1816.    expects to address with offsets from the DS register) and the stack
  1817.    segment as members of DGROUP (the automatic data group), a special name
  1818.    recognized by the linker and also by the program loaders in Microsoft
  1819.    Windows and Microsoft OS/2. The GROUP directive causes logical segments
  1820.    with different names to be combined into a single physical segment so that
  1821.    they can be addressed using the same segment base address. In C programs,
  1822.    DGROUP also contains the local heap, which is used by the C runtime
  1823.    library for dynamic allocation of small amounts of memory.
  1824.  
  1825. ╓┌─┌───────────┌────────────┌───────────┌───────────┌────────────┌───────────╖
  1826.    Memory      Segment      Align       Combine     Class        Group
  1827.    model       name         type        type        type
  1828.    ──────────────────────────────────────────────────────────────────────────
  1829.    Memory      Segment      Align       Combine     Class        Group
  1830.    model       name         type        type        type
  1831.    ──────────────────────────────────────────────────────────────────────────
  1832.    Small       _TEXT        WORD        PUBLIC      CODE
  1833.                _DATA        WORD        PUBLIC      DATA         DGROUP
  1834.                STACK        PARA        STACK       STACK        DGROUP
  1835.  
  1836.    Medium      module_TEXT  WORD        PUBLIC      CODE
  1837.                .            WORD        PUBLIC      DATA         DGROUP
  1838.                .
  1839.                .
  1840.                _DATA
  1841.                STACK        PARA        STACK       STACK        DGROUP
  1842.  
  1843.    Compact     _TEXT        WORD        PUBLIC      CODE
  1844.                data         PARA        PRIVATE     FAR_DATA
  1845.                .            WORD        PUBLIC      DATA         DGROUP
  1846.                .
  1847.                .
  1848.                _DATA
  1849.                STACK        PARA        STACK       STACK        DGROUP
  1850.    Memory      Segment      Align       Combine     Class        Group
  1851.    model       name         type        type        type
  1852.    ──────────────────────────────────────────────────────────────────────────
  1853.               STACK        PARA        STACK       STACK        DGROUP
  1854.  
  1855.    Large       module_TEXT  WORD        PUBLIC      CODE
  1856.                .
  1857.                .
  1858.                .
  1859.                data         PARA        PRIVATE     FAR_DATA
  1860.                .
  1861.                .
  1862.                .
  1863.                _DATA        WORD        PUBLIC      DATA         DGROUP
  1864.                STACK        PARA        STACK       STACK        DGROUP
  1865.    ──────────────────────────────────────────────────────────────────────────
  1866.  
  1867.  
  1868.    Figure 3-10.  Segments, groups, and classes for the standard memory models
  1869.    as used with assembly-language programs. The Microsoft C Optimizing
  1870.    Compiler and other high-level-language compilers use a superset of these
  1871.    segments and classes.
  1872.  
  1873.    For pure assembly-language programs that will run under MS-DOS, you can
  1874.    ignore DGROUP. However, if you plan to integrate assembly-language
  1875.    routines and programs written in high-level languages, you'll want to
  1876.    follow the Microsoft DGROUP convention. For example, if you are planning
  1877.    to link routines from a C library into an assembly-language program, you
  1878.    should include the line
  1879.  
  1880.    DGROUP group _DATA,STACK
  1881.  
  1882.    near the beginning of the program.
  1883.  
  1884.    The final Microsoft convention of interest in creating .EXE programs is
  1885.    segment order. The high-level compilers assume that code segments always
  1886.    come first, followed by far data segments, followed by the near data
  1887.    segment, with the stack and heap last. This order won't concern you much
  1888.    until you begin integrating assembly-language code with routines from
  1889.    high-level-language libraries, but it is easiest to learn to use the
  1890.    convention right from the start.
  1891.  
  1892.  Program Procedures
  1893.  
  1894.    The procedure level of program structure is partly real and partly
  1895.    conceptual. Procedures are basically just a fancy guise for subroutines.
  1896.  
  1897.    Procedures within a program are declared with the PROC and ENDP directives
  1898.    in the following form:
  1899.  
  1900.    name   PROC attribute
  1901.    .
  1902.    .
  1903.    .
  1904.           RET
  1905.    name   ENDP
  1906.  
  1907.    The attribute carried by a PROC declaration, which is either NEAR or FAR,
  1908.    tells the assembler what type of call you expect to use to enter the
  1909.    procedure──that is, whether the procedure will be called from other
  1910.    routines in the same segment or from routines in other segments. When the
  1911.    assembler encounters a RET instruction within the procedure, it uses the
  1912.    attribute information to generate the correct opcode for either a near
  1913.    (intra-segment) or far (inter-segment) return.
  1914.  
  1915.    Each program should have a main procedure that receives control from
  1916.    MS-DOS. You specify the entry point for the program by including the name
  1917.    of the main procedure in the END statement in one of the program's source
  1918.    files. The main procedure's attribute (NEAR or FAR) is really not too
  1919.    important, because the program returns control to MS-DOS with a function
  1920.    call rather than a RET instruction. However, by convention, most
  1921.    programmers assign the main procedure the FAR attribute anyway.
  1922.  
  1923.    You should break the remainder of the program into procedures in an
  1924.    orderly way, with each procedure performing a well-defined single
  1925.    function, returning its results to its caller, and avoiding actions that
  1926.    have global effects within the program. Ideally procedures invoke each
  1927.    other only by CALL instructions, have only one entry point and one exit
  1928.    point, and always exit by means of a RET instruction, never by jumping to
  1929.    some other location within the program.
  1930.  
  1931.    For ease of understanding and maintenance, a procedure should not exceed
  1932.    one page (about 60 lines); if it is longer than a page, it is probably too
  1933.    complex and you should delegate some of its function to one or more
  1934.    subsidiary procedures. You should preface the source code for each
  1935.    procedure with a detailed comment that states the procedure's calling
  1936.    sequence, results returned, registers affected, and any data items
  1937.    accessed or modified. The effort invested in making your procedures
  1938.    compact, clean, flexible, and well-documented will be repaid many times
  1939.    over when you reuse the procedures in other programs.
  1940.  
  1941.  
  1942.  
  1943.  ────────────────────────────────────────────────────────────────────────────
  1944.  Chapter 4  MS-DOS Programming Tools
  1945.  
  1946.    Preparing a new program to run under MS-DOS is an iterative process with
  1947.    four basic steps:
  1948.  
  1949.    ■  Use of a text editor to create or modify an ASCII source-code file
  1950.  
  1951.    ■  Use of an assembler or high-level-language compiler (such as the
  1952.       Microsoft Macro Assembler or the Microsoft C Optimizing Compiler) to
  1953.       translate the source file into relocatable object code
  1954.  
  1955.    ■  Use of a linker to transform the relocatable object code into an
  1956.       executable MS-DOS load module
  1957.  
  1958.    ■  Use of a debugger to methodically test and debug the program
  1959.  
  1960.    Additional utilities the MS-DOS software developer may find necessary or
  1961.    helpful include the following:
  1962.  
  1963.    ■  LIB, which creates and maintains object-module libraries
  1964.  
  1965.    ■  CREF, which generates a cross-reference listing
  1966.  
  1967.    ■  EXE2BIN, which converts .EXE files to .COM files
  1968.  
  1969.    ■  MAKE, which compares dates of files and carries out operations based on
  1970.       the result of the comparison
  1971.  
  1972.    This chapter gives an operational overview of the Microsoft programming
  1973.    tools for MS-DOS, including the assembler, the C compiler, the linker, and
  1974.    the librarian. In general, the information provided here also applies to
  1975.    the IBM programming tools for MS-DOS, which are really the Microsoft
  1976.    products with minor variations and different version numbers. Even if your
  1977.    preferred programming language is not C or assembly language, you will
  1978.    need at least a passing familiarity with these tools because all of the
  1979.    examples in the IBM and Microsoft DOS reference manuals are written in one
  1980.    of these languages.
  1981.  
  1982.    The survey in this chapter, together with the example programs and
  1983.    reference section elsewhere in the book, should provide the experienced
  1984.    programmer with sufficient information to immediately begin writing useful
  1985.    programs. Readers who do not have a background in C, assembly language, or
  1986.    the Intel 80x86 microprocessor architecture should refer to the tutorial
  1987.    and reference works listed at the end of this chapter.
  1988.  
  1989.  
  1990.  File Types
  1991.  
  1992.    The MS-DOS programming tools can create and process many different file
  1993.    types. The following extensions are used by convention for these files:
  1994.  
  1995. ╓┌─┌──────────┌──────────────────────────────────────────────────────────────╖
  1996.    Extension  File type
  1997.    Extension  File type
  1998.    ──────────────────────────────────────────────────────────────────────────
  1999.    .ASM       Assembly-language source file
  2000.  
  2001.    .C         C source file
  2002.  
  2003.    .COM       MS-DOS executable load module that does not require relocation
  2004.               at runtime
  2005.  
  2006.    .CRF       Cross-reference information file produced by the assembler for
  2007.               processing by CREF.EXE
  2008.  
  2009.    .DEF       Module-definition file describing a program's segment behavior
  2010.               (MS OS/2 and Microsoft Windows programs only; not relevant to
  2011.               normal MS-DOS applications)
  2012.  
  2013.    .EXE       MS-DOS executable load module that requires relocation at
  2014.               runtime
  2015.  
  2016.    .H         C header file containing C source code for constants, macros,
  2017.               and functions; merged into another C program with the #include
  2018.    Extension  File type
  2019.    ──────────────────────────────────────────────────────────────────────────
  2020.              and functions; merged into another C program with the #include
  2021.               directive
  2022.  
  2023.    .INC       Include file for assembly-language programs, typically
  2024.               containing macros and/or equates for systemwide values such as
  2025.               error codes
  2026.  
  2027.    .LIB       Object-module library file made up of one or more .OBJ files;
  2028.               indexed and manipulated by LIB.EXE
  2029.  
  2030.    .LST       Program listing, produced by the assembler, that includes
  2031.               memory locations, machine code, the original program text, and
  2032.               error messages
  2033.  
  2034.    .MAP       Listing of symbols and their locations within a load module;
  2035.               produced by the linker
  2036.  
  2037.    .OBJ       Relocatable-object-code file produced by an assembler or
  2038.               compiler
  2039.    Extension  File type
  2040.    ──────────────────────────────────────────────────────────────────────────
  2041.              compiler
  2042.  
  2043.    .REF       Cross-reference listing produced by CREF.EXE from the
  2044.               information in a .CRF file
  2045.    ──────────────────────────────────────────────────────────────────────────
  2046.  
  2047.  
  2048.  
  2049.  The Microsoft Macro Assembler
  2050.  
  2051.    The Microsoft Macro Assembler (MASM) is distributed as the file MASM.EXE.
  2052.    When beginning a program translation, MASM needs the following
  2053.    information:
  2054.  
  2055.    ■  The name of the file containing the source program
  2056.  
  2057.    ■  The filename for the object program to be created
  2058.  
  2059.    ■  The destination of the program listing
  2060.  
  2061.    ■  The filename for the information that is later processed by the
  2062.       cross-reference utility (CREF.EXE)
  2063.  
  2064.    You can invoke MASM in two ways. If you enter the name of the assembler
  2065.    alone, it prompts you for the names of each of the various input and
  2066.    output files. The assembler supplies reasonable defaults for all the
  2067.    responses except the source filename, as shown in the following example:
  2068.  
  2069.    C>MASM  <Enter>
  2070.  
  2071.    Microsoft (R) Macro Assembler Version 5.10
  2072.    Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.
  2073.  
  2074.    Source filename [.ASM]: HELLO  <Enter>
  2075.    Object filename [HELLO.OBJ]:  <Enter>
  2076.    Source listing  [NUL.LST]:  <Enter>
  2077.    Cross-reference [NUL.CRF]:  <Enter>
  2078.  
  2079.      49006 Bytes symbol space free
  2080.  
  2081.          0 Warning Errors
  2082.          0 Severe Errors
  2083.  
  2084.    C>
  2085.  
  2086.    You can use a logical device name (such as PRN or COM1) at any of the MASM
  2087.    prompts to send that output of the assembler to a character device rather
  2088.    than a file. Note that the default for the listing and cross-reference
  2089.    files is the NUL device──that is, no file is created. If you end any
  2090.    response with a semicolon, MASM assumes that the remaining responses are
  2091.    all to be the default.
  2092.  
  2093.    A more efficient way to use MASM is to supply all parameters in the
  2094.    command line, as follows:
  2095.  
  2096.      MASM [options] source,[object],[listing],[crossref]
  2097.  
  2098.    For example, the following command lines are equivalent to the preceding
  2099.    interactive session:
  2100.  
  2101.    C>MASM HELLO,,NUL,NUL  <Enter>
  2102.  
  2103.    or
  2104.  
  2105.    C>MASM HELLO;  <Enter>
  2106.  
  2107.    These commands use the file HELLO.ASM as the source, generate the
  2108.    object-code file HELLO.OBJ, and send the listing and cross-reference files
  2109.    to the bit bucket.
  2110.  
  2111.    MASM accepts several optional switches in the command line, to control
  2112.    code generation and output files. Figure 4-1 lists the switches accepted
  2113.    by MASM version 5.1. As shown in the following example, you can put
  2114.    frequently used options in a MASM environment variable, where they will be
  2115.    found automatically by the assembler:
  2116.  
  2117.    C>SET MASM=/T /Zi  <Enter>
  2118.  
  2119.    The switches in the environment variable will be overridden by any that
  2120.    you enter in the command line.
  2121.  
  2122.    In other versions of the Microsoft Macro Assembler, additional or fewer
  2123.    switches may be available. For exact instructions, see the manual for the
  2124.    version of MASM that you are using.
  2125.  
  2126. ╓┌─┌──────────┌──────────────────────────────────────────────────────────────╖
  2127.    Switch     Meaning
  2128.    ──────────────────────────────────────────────────────────────────────────
  2129.    /A         Arrange segments in alphabetic order.
  2130.    /Bn        Set size of source-file buffer (in KB).
  2131.    /C         Force creation of a cross-reference (.CRF) file.
  2132.    /D         Produce listing on both passes (to find phase errors).
  2133.    /Dsymbol   Define symbol as a null text string (symbol can be referenced
  2134.               by conditional assembly directives in file).
  2135.    /E         Assemble for 80x87 numeric coprocessor emulator using IEEE
  2136.               real-number format.
  2137.    /Ipath     Set search path for include files.
  2138.    /L         Force creation of a program-listing file.
  2139.    /LA        Force listing of all generated code.
  2140.    /ML        Preserve case sensitivity in all names (uppercase names
  2141.               distinct from their lowercase equivalents).
  2142.    /MX        Preserve lowercase in external names only (names defined with
  2143.               PUBLIC or EXTRN directives).
  2144.    Switch     Meaning
  2145.    ──────────────────────────────────────────────────────────────────────────
  2146.              PUBLIC or EXTRN directives).
  2147.    /MU        Convert all lowercase names to uppercase.
  2148.    /N         Suppress generation of tables of macros, structures, records,
  2149.               segments, groups, and symbols at the end of the listing.
  2150.    /P         Check for impure code in 80286/80386 protected mode.
  2151.    /S         Arrange segments in order of occurrence (default).
  2152.    /T         "Terse" mode; suppress all messages unless errors are
  2153.               encountered during the assembly.
  2154.    /V         "Verbose" mode; report number of lines and symbols at end of
  2155.               assembly.
  2156.    /Wn        Set error display (warning) level; n=0─2.
  2157.    /X         Force listing of false conditionals.
  2158.    /Z         Display source lines containing errors on the screen.
  2159.    /Zd        Include line-number information in .OBJ file.
  2160.    /Zi        Include line-number and symbol information in .OBJ file.
  2161.    ──────────────────────────────────────────────────────────────────────────
  2162.  
  2163.  
  2164.    Figure 4-1.  Microsoft Macro Assembler version 5.1 switches.
  2165.  
  2166.    MASM allows you to override the default extensions on any file──a feature
  2167.    that can be rather dangerous. For example, if in the preceding example you
  2168.    had responded to the Object filename prompt with HELLO.ASM, the assembler
  2169.    would have accepted the entry without comment and destroyed your source
  2170.    file. This is not too likely to happen in the interactive command mode,
  2171.    but you must be very careful with file extensions when MASM is used in a
  2172.    batch file.
  2173.  
  2174.  
  2175.  The Microsoft C Optimizing Compiler
  2176.  
  2177.    The Microsoft C Optimizing Compiler consists of three executable files──
  2178.    C1.EXE, C2.EXE, and C3.EXE──that implement the C preprocessor, language
  2179.    translator, code generator, and code optimizer. An additional control
  2180.    program, CL.EXE, executes the three compiler files in order, passing each
  2181.    the necessary information about filenames and compilation options.
  2182.  
  2183.    Before using the C compiler and the linker, you need to set up four
  2184.    environment variables:
  2185.  
  2186.    Variable                 Action
  2187.    ──────────────────────────────────────────────────────────────────────────
  2188.    PATH=path                Specifies the location of the three executable C
  2189.                             compiler files (C1, C2, and C3) if they are not
  2190.                             in the current directory; used by CL.EXE.
  2191.  
  2192.    INCLUDE=path             Specifies the location of #include files (default
  2193.                             extension .H) that are not found in the current
  2194.                             directory.
  2195.  
  2196.    LIB=path                 Specifies the location(s) for object-code
  2197.                             libraries that are not found in the current
  2198.                             directory.
  2199.  
  2200.    TMP=path                 Specifies the location for temporary working
  2201.                             files created by the C compiler and linker.
  2202.    ──────────────────────────────────────────────────────────────────────────
  2203.  
  2204.    CL.EXE does not support an interactive mode or response files. You always
  2205.    invoke it with a command line of the following form:
  2206.  
  2207.      CL [options] file [file ...]
  2208.  
  2209.    You may list any number of files──if a file has a .C extension, it will be
  2210.    compiled into a relocatable-object-module (.OBJ) file. Ordinarily, if the
  2211.    compiler encounters no errors, it automatically passes all resulting .OBJ
  2212.    files and any additional .OBJ files specified in the command line to the
  2213.    linker, along with the names of the appropriate runtime libraries.
  2214.  
  2215.    The C compiler has many optional switches controlling its memory models,
  2216.    output files, code generation, and code optimization. These are summarized
  2217.    in Figure 4-2. The C compiler's arcane switch syntax is derived largely
  2218.    from UNIX/XENIX, so don't expect it to make any sense.
  2219.  
  2220. ╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
  2221.    Switch                   Meaning
  2222.    ──────────────────────────────────────────────────────────────────────────
  2223.    /Ax                      Select memory model:
  2224.                             C = compact model
  2225.                             H = huge model
  2226.                             L = large model
  2227.                             M = medium model
  2228.    Switch                   Meaning
  2229.    ──────────────────────────────────────────────────────────────────────────
  2230.                            M = medium model
  2231.                             S = small model (default)
  2232.    /c                       Compile only; do not invoke linker.
  2233.    /C                       Do not strip comments.
  2234.    /D<name>[=text]          Define macro.
  2235.    /E                       Send preprocessor output to standard output.
  2236.    /EP                      Send preprocessor output to standard output
  2237.                             without line numbers.
  2238.    /F<n>                    Set stack size (in hexadecimal bytes).
  2239.    /Fa [filename]           Generate assembly listing.
  2240.    /Fc [filename]           Generate mixed source/object listing.
  2241.    /Fe [filename]           Force executable filename.
  2242.    /Fl [filename]           Generate object listing.
  2243.    /Fm [filename]           Generate map file.
  2244.    /Fo [filename]           Force object-module filename.
  2245.    /FPx                     Select floating-point control:
  2246.                             a = calls with alternate math library
  2247.                             c = calls with emulator library
  2248.                             c87 = calls with 8087 library
  2249.    Switch                   Meaning
  2250.    ──────────────────────────────────────────────────────────────────────────
  2251.                            c87 = calls with 8087 library
  2252.                             i = in-line with emulator (default)
  2253.                             i87 = in-line with 8087
  2254.    /Fs [filename]           Generate source listing.
  2255.    /Gx                      Select code generation:
  2256.                             0 = 8086 instructions (default)
  2257.                             1 = 186 instructions
  2258.                             2 = 286 instructions
  2259.                             c = Pascal style function calls
  2260.                             s = no stack checking
  2261.                             t[n] = data size threshold
  2262.    /H<n>                    Specify external name length.
  2263.    /I<path>                 Specify additional #include path.
  2264.    /J                       Specify default char type as unsigned.
  2265.    /link [options]          Pass switches and library names to linker.
  2266.    /Ox                      Select optimization:
  2267.                             a = ignore aliasing
  2268.                             d = disable optimizations
  2269.                             i = enable intrinsic functions
  2270.    Switch                   Meaning
  2271.    ──────────────────────────────────────────────────────────────────────────
  2272.                            i = enable intrinsic functions
  2273.                             l = enable loop optimizations
  2274.                             n = disable "unsafe" optimizations
  2275.                             p = enable precision optimizations
  2276.                             r = disable in-line return
  2277.                             s = optimize for space
  2278.    /Ox                      t = optimize for speed (default)
  2279.                             w = ignore aliasing except across function
  2280.                             calls
  2281.                             x = enable maximum optimization (equivalent to
  2282.                             /Oailt /Gs)
  2283.    /P                       Send preprocessor output to file.
  2284.    /Sx                      Select source-listing control:
  2285.                             l<columns> = set line width
  2286.                             p<lines> = set page length
  2287.                             s<string> = set subtitle string
  2288.                             t<string> = set title string
  2289.    /Tc<file>                Compile file without .C extension.
  2290.    /u                       Remove all predefined macros.
  2291.    Switch                   Meaning
  2292.    ──────────────────────────────────────────────────────────────────────────
  2293.   /u                       Remove all predefined macros.
  2294.    /U<name>                 Remove specified predefined macro.
  2295.    /V<string>               Set version string.
  2296.    /W<n>                    Set warning level (0─3).
  2297.    /X                       Ignore "standard places" for include files.
  2298.    /Zx                      Select miscellaneous compilation control:
  2299.                             a = disable extensions
  2300.                             c = make Pascal functions case-insensitive
  2301.                             d = include line-number information
  2302.                             e = enable extensions (default)
  2303.                             g = generate declarations
  2304.                             i = include symbolic debugging information
  2305.                             l = remove default library info
  2306.                             p<n> = pack structures on n-byte boundary
  2307.                             s = check syntax only
  2308.    ──────────────────────────────────────────────────────────────────────────
  2309.  
  2310.  
  2311.    Figure 4-2.  Microsoft C Optimizing Compiler version 5.1 switches.
  2312.  
  2313.  
  2314.  The Microsoft Object Linker
  2315.  
  2316.    The object module produced by MASM from a source file is in a form that
  2317.    contains relocation information and may also contain unresolved references
  2318.    to external locations or subroutines. It is written in a common format
  2319.    that is also produced by the various high-level compilers (such as FORTRAN
  2320.    and C) that run under MS-DOS. The computer cannot execute object modules
  2321.    without further processing.
  2322.  
  2323.    The Microsoft Object Linker (LINK), distributed as the file LINK.EXE,
  2324.    accepts one or more of these object modules, resolves external references,
  2325.    includes any necessary routines from designated libraries, performs any
  2326.    necessary offset relocations, and writes a file that can be loaded and
  2327.    executed by MS-DOS. The output of LINK is always in .EXE load-module
  2328.    format. (See Chapter 3.)
  2329.  
  2330.    As with MASM, you can give LINK its parameters interactively or by
  2331.    entering all the required information in a single command line. If you
  2332.    enter the name of the linker alone, the following type of dialog ensues:
  2333.  
  2334.    C>LINK  <Enter>
  2335.  
  2336.    Microsoft (R) Overlay Linker  Version 3.61
  2337.    Copyright (C) Microsoft Corp 1983-1987. All rights reserved.
  2338.  
  2339.    Object Modules [.OBJ]: HELLO  <Enter>
  2340.    Run File [HELLO.EXE]:  <Enter>
  2341.    List File [NUL.MAP]: HELLO  <Enter>
  2342.    Libraries [.LIB]:  <Enter>
  2343.  
  2344.    C>
  2345.  
  2346.    If you are using LINK version 4.0 or later, the linker also asks for the
  2347.    name of a module-definition (.DEF) file. Simply press the Enter key in
  2348.    response to such a prompt. Module-definition files are used when building
  2349.    Microsoft Windows or MS OS/2 "new .EXE" executable files but are not
  2350.    relevant in normal MS-DOS applications.
  2351.  
  2352.    The input file for this example was HELLO.OBJ; the output files were
  2353.    HELLO.EXE (the executable program) and HELLO.MAP (the load map produced by
  2354.    the linker after all references and addresses were resolved). Figure 4-3
  2355.    shows the load map.
  2356.  
  2357.    ──────────────────────────────────────────────────────────────────────────
  2358.     Start  Stop   Length Name                   Class
  2359.     00000H 00017H 00018H _TEXT                  CODE
  2360.     00018H 00027H 00010H _DATA                  DATA
  2361.     00030H 000AFH 00080H STACK                  STACK
  2362.     000B0H 000BBH 0000CH $$TYPES                DEBTYP
  2363.     000C0H 000D6H 00017H $$SYMBOLS              DEBSYM
  2364.  
  2365.      Address         Publics by Name
  2366.  
  2367.      Address         Publics by Value
  2368.  
  2369.    Program entry point at 0000:0000
  2370.    ──────────────────────────────────────────────────────────────────────────
  2371.  
  2372.    Figure 4-3.  Map produced by the Microsoft Object Linker (LINK) during the
  2373.    generation of the HELLO.EXE program from Chapter 3. The program contains
  2374.    one CODE, one DATA, and one STACK segment. The first instruction to be
  2375.    executed lies in the first byte of the CODE segment. The $$TYPES and
  2376.    $$SYMBOLS segments contain information for the CodeView debugger and are
  2377.    not part of the program; these segments are ignored by the normal MS-DOS
  2378.    loader.
  2379.  
  2380.    You can obtain the same result more quickly by entering all parameters in
  2381.    the command line, in the following form:
  2382.  
  2383.      LINK options objectfile, [exefile], [mapfile], [libraries]
  2384.  
  2385.    Thus, the command-line equivalent to the preceding interactive session is
  2386.  
  2387.    C>LINK HELLO,HELLO,HELLO,,  <Enter>
  2388.  
  2389.    or
  2390.  
  2391.    C>LINK HELLO,,HELLO;  <Enter>
  2392.  
  2393.    If you enter a semicolon as the last character in the command line, LINK
  2394.    assumes the default values for all further parameters.
  2395.  
  2396.    A third method of commanding LINK is with a response file. A response file
  2397.    contains lines of text that correspond to the responses you would give the
  2398.    linker interactively. You specify the name of the response file in the
  2399.    command line with a leading @ character, as follows:
  2400.  
  2401.      LINK @filename
  2402.  
  2403.    You can also enter the name of a response file at any prompt. If the
  2404.    response file is not complete, LINK will prompt you for the missing
  2405.    information.
  2406.  
  2407.    When entering linker commands, you can specify multiple object files with
  2408.    the + operator or with spaces, as in the following example:
  2409.  
  2410.    C>LINK HELLO+VMODE+DOSINT,MYPROG,,GRAPHICS;  <Enter>
  2411.  
  2412.    This command would link the files HELLO.OBJ, VMODE.OBJ, and DOSINT.OBJ,
  2413.    searching the library file GRAPHICS.LIB to resolve any references to
  2414.    symbols not defined in the specified object files, and would produce a
  2415.    file named MYPROG.EXE. LINK uses the current drive and directory when they
  2416.    are not explicitly included in a filename; it will not automatically use
  2417.    the same drive and directory you specified for a previous file in the same
  2418.    command line.
  2419.  
  2420.    By using the + operator or space characters in the libraries field, you
  2421.    can specify up to 32 library files to be searched. Each high-level-
  2422.    language compiler provides default libraries that are searched
  2423.    automatically during the linkage process if the linker can find them
  2424.    (unless they are explicitly excluded with the /NOD switch). LINK looks for
  2425.    libraries first in the current directory of the default disk drive, then
  2426.    along any paths that were provided in the command line, and finally along
  2427.    the path(s) specified by the LIB variable if it is present in the
  2428.    environment.
  2429.  
  2430.    LINK accepts several optional switches as part of the command line or at
  2431.    the end of any interactive prompt. Figure 4-4 lists these switches. The
  2432.    number of switches available and their actions vary among different
  2433.    versions of LINK. See your Microsoft Object Linker instruction manual for
  2434.    detailed information about your particular version.
  2435.  
  2436. ╓┌─┌────────┌───────────────────────────┌────────────────────────────────────╖
  2437.    Switch   Full form                   Meaning
  2438.    Switch   Full form                   Meaning
  2439.    ──────────────────────────────────────────────────────────────────────────
  2440.    /A:n     /ALIGNMENT:n                Set segment sector alignment factor.
  2441.                                         N must be a power of 2 (default =
  2442.                                         512). Not related to logical-segment
  2443.                                         alignment (BYTE, WORD, PARA, PAGE,
  2444.                                         and so forth). Relevant to segmented
  2445.                                         executable files (Microsoft Windows
  2446.                                         and MS OS/2) only.
  2447.  
  2448.    /B       /BATCH                      Suppress linker prompt if a library
  2449.                                         cannot be found in the current
  2450.                                         directory or in the locations
  2451.                                         specified by the LIB environment
  2452.                                         variable.
  2453.  
  2454.    /CO      /CODEVIEW                   Include symbolic debugging
  2455.                                         information in the .EXE file for use
  2456.                                         by CodeView.
  2457.  
  2458.    /CP      /CPARMAXALLOC               Set the field in the .EXE file header
  2459.    Switch   Full form                   Meaning
  2460.    ──────────────────────────────────────────────────────────────────────────
  2461.   /CP      /CPARMAXALLOC               Set the field in the .EXE file header
  2462.                                         controlling the amount of memory
  2463.                                         allocated to the program in addition
  2464.                                         to the memory required for the
  2465.                                         program's code, stack, and
  2466.                                         initialized data.
  2467.  
  2468.    /DO      /DOSSEG                     Use standard Microsoft segment naming
  2469.                                         and ordering conventions.
  2470.  
  2471.    /DS      /DSALLOCATE                 Load data at high end of the data
  2472.                                         segment. Relevant to real-mode
  2473.                                         programs only.
  2474.  
  2475.    /E       /EXEPACK                    Pack executable file by removing
  2476.                                         sequences of repeated bytes and
  2477.                                         optimizing relocation table.
  2478.  
  2479.    /F       /FARCALLTRANSLATION         Optimize far calls to labels within
  2480.    Switch   Full form                   Meaning
  2481.    ──────────────────────────────────────────────────────────────────────────
  2482.   /F       /FARCALLTRANSLATION         Optimize far calls to labels within
  2483.                                         the same physical segment for speed
  2484.                                         by replacing them with near calls and
  2485.                                         NOPs.
  2486.  
  2487.    /HE      /HELP                       Display information about available
  2488.                                         options.
  2489.  
  2490.    /HI      /HIGH                       Load program as high in memory as
  2491.                                         possible.
  2492.  
  2493.    /I       /INFORMATION                Display information about progress of
  2494.                                         linking, including pass numbers and
  2495.                                         the names of object files being
  2496.                                         linked.
  2497.  
  2498.    /INC     /INCREMENTAL                Force production of .SYM and .ILK
  2499.                                         files for subsequent use by ILINK
  2500.                                         (incremental linker). May not be used
  2501.    Switch   Full form                   Meaning
  2502.    ──────────────────────────────────────────────────────────────────────────
  2503.                                        (incremental linker). May not be used
  2504.                                         with /EXEPACK. Relevant to segmented
  2505.                                         executable files (Microsoft Windows
  2506.                                         and MS OS/2) only.
  2507.  
  2508.    /LI      /LINENUMBERS                Write address of the first
  2509.                                         instruction that corresponds to each
  2510.                                         source-code line to the map file. Has
  2511.                                         no effect if the compiler does not
  2512.                                         include line-number information in
  2513.                                         the object module. Force creation of
  2514.                                         a map file.
  2515.  
  2516.    /M[:n]   /MAP[:n]                    Force creation of a .MAP file listing
  2517.                                         all public symbols, sorted by name
  2518.                                         and by location. The optional value n
  2519.                                         is the maximum number of symbols that
  2520.                                         can be sorted (default = 2048); when
  2521.                                         n is supplied, the alphabetically
  2522.    Switch   Full form                   Meaning
  2523.    ──────────────────────────────────────────────────────────────────────────
  2524.                                        n is supplied, the alphabetically
  2525.                                         sorted list is omitted.
  2526.  
  2527.    /NOD     /NODEFAULTLIBRARYSEARCH     Skip search of any default compiler
  2528.                                         libraries specified in the .OBJ file.
  2529.  
  2530.    /NOE     /NOEXTENDEDDICTSEARCH       Ignore extended library dictionary
  2531.                                         (if it is present). The extended
  2532.                                         dictionary ordinarily provides the
  2533.                                         linker with information about
  2534.                                         inter-module dependencies, to speed
  2535.                                         up linking.
  2536.  
  2537.    /NOF     /NOFARCALLTRANSLATION       Disable optimization of far calls to
  2538.                                         labels within the same segment.
  2539.  
  2540.    /NOG     /NOGROUPASSOCIATION         Ignore group associations when
  2541.                                         assigning addresses to data and code
  2542.                                         items.
  2543.    Switch   Full form                   Meaning
  2544.    ──────────────────────────────────────────────────────────────────────────
  2545.                                        items.
  2546.  
  2547.    /NOI     /NOIGNORECASE               Do not ignore case in names during
  2548.                                         linking.
  2549.  
  2550.    /NON     /NONULLSDOSSEG              Arrange segments as for /DOSSEG but
  2551.                                         do not insert 16 null bytes at start
  2552.                                         of _TEXT segment.
  2553.  
  2554.    /NOP     /NOPACKCODE                 Do not pack contiguous logical code
  2555.                                         segments into a single physical
  2556.                                         segment.
  2557.  
  2558.    /O:n     /OVERLAYINTERRUPT:n         Use interrupt number n with the
  2559.                                         overlay manager supplied with some
  2560.                                         Microsoft high-level languages.
  2561.  
  2562.    /PAC[:n] /PACKCODE[:n]               Pack contiguous logical code segments
  2563.                                         into a single physical code segment.
  2564.    Switch   Full form                   Meaning
  2565.    ──────────────────────────────────────────────────────────────────────────
  2566.                                        into a single physical code segment.
  2567.                                         The optional value n is the maximum
  2568.                                         size for each packed physical code
  2569.                                         segment (default = 65,536 bytes).
  2570.                                         Segments in different groups are not
  2571.                                         packed.
  2572.  
  2573.    /PADC:n  /PADCODE:n                  Add n filler bytes to end of each
  2574.                                         code module so that a larger module
  2575.                                         can be inserted later with ILINK.
  2576.                                         Relevant to segmented executable
  2577.                                         files (Windows and MS OS/2) only.
  2578.  
  2579.    /PADD:n  /PADDATA:n                  Add n filler bytes to end of each
  2580.                                         data module so that a larger module
  2581.                                         can be inserted later with ILINK.
  2582.                                         Relevant to segmented executable
  2583.                                         files (Microsoft Windows and MS OS/2)
  2584.                                         only.
  2585.    Switch   Full form                   Meaning
  2586.    ──────────────────────────────────────────────────────────────────────────
  2587.                                        only.
  2588.  
  2589.    /PAU     /PAUSE                      Pause during linking, allowing a
  2590.                                         change of disks before .EXE file is
  2591.                                         written.
  2592.  
  2593.    /SE:n    /SEGMENTS:n                 Set maximum number of segments in
  2594.                                         linked program (default = 128).
  2595.  
  2596.    /ST:n    /STACK:n                    Set stack size of program in bytes;
  2597.                                         ignore stack segment size
  2598.                                         declarations within object modules
  2599.                                         and definition file.
  2600.  
  2601.    /W       /WARNFIXUP                  Display warning messages for offsets
  2602.                                         relative to a segment base that is
  2603.                                         not the same as the group base.
  2604.                                         Relevant to segmented executable
  2605.                                         files (Microsoft Windows and MS OS/2)
  2606.    Switch   Full form                   Meaning
  2607.    ──────────────────────────────────────────────────────────────────────────
  2608.                                        files (Microsoft Windows and MS OS/2)
  2609.                                         only.
  2610.    ──────────────────────────────────────────────────────────────────────────
  2611.  
  2612.  
  2613.    Figure 4-4.  Switches accepted by the Microsoft Object Linker (LINK)
  2614.    version 5.0. Earlier versions use a subset of these switches. Note that
  2615.    any abbreviation for a switch is acceptable as long as it is sufficient to
  2616.    specify the switch uniquely.
  2617.  
  2618.  
  2619.  The EXE2BIN Utility
  2620.  
  2621.    The EXE2BIN utility (EXE2BIN.EXE) transforms a .EXE file created by LINK
  2622.    into an executable .COM file, if the program meets the following
  2623.    prerequisites:
  2624.  
  2625.    ■  It cannot contain more than one declared segment and cannot
  2626.       define a stack.
  2627.  
  2628.    ■  It must be less than 64 KB in length.
  2629.  
  2630.    ■  It must have an origin at 0100H.
  2631.  
  2632.    ■  The first location in the file must be specified as the entry point
  2633.       in the source code's END directive.
  2634.  
  2635.    Although .COM files are somewhat more compact than .EXE files, you should
  2636.    avoid using them. Programs that use separate segments for code, data, and
  2637.    stack are much easier to port to protected-mode environments such as MS
  2638.    OS/2; in addition, .COM files do not support the symbolic debugging
  2639.    information used by CodeView.
  2640.  
  2641.    Another use for the EXE2BIN utility is to convert an installable device
  2642.    driver──after it is assembled and linked into a .EXE file──into a
  2643.    memory-image .BIN or .SYS file with an origin of zero. This conversion is
  2644.    required in MS-DOS version 2, which cannot load device drivers as .EXE
  2645.    files. The process of writing an installable device driver is discussed in
  2646.    more detail in Chapter 14.
  2647.  
  2648.    Unlike most of the other programming utilities, EXE2BIN does not have an
  2649.    interactive mode. It always takes its source and destination filenames,
  2650.    separated by spaces, from the MS-DOS command line, as follows:
  2651.  
  2652.      EXE2BIN sourcefile [destinationfile]
  2653.  
  2654.    If you do not supply the source-file extension, it defaults to .EXE; the
  2655.    destination-file extension defaults to .BIN. If you do not specify a name
  2656.    for the destination file, EXE2BIN gives it the same name as the source
  2657.    file, with a .BIN extension.
  2658.  
  2659.    For example, to convert the file HELLO.EXE into HELLO.COM, you would use
  2660.    the following command line:
  2661.  
  2662.    C>EXE2BIN HELLO.EXE HELLO.COM  <Enter>
  2663.  
  2664.    The EXE2BIN program also has other capabilities, such as pure binary
  2665.    conversion with segment fixup for creating program images to be placed in
  2666.    ROM; but because these features are rarely used during MS-DOS application
  2667.    development, they will not be discussed here.
  2668.  
  2669.  
  2670.  The CREF Utility
  2671.  
  2672.    The CREF cross-reference utility CREF.EXE processes a .CRF file produced
  2673.    by MASM, creating an ASCII text file with the default extension .REF. The
  2674.    file contains a cross-reference listing of all symbols declared in the
  2675.    program and the line numbers in which they are referenced. (See Figure
  2676.    4-5.) Such a listing is very useful when debugging large
  2677.    assembly-language programs with many interdependent procedures and
  2678.    variables.
  2679.  
  2680.    CREF may be supplied with its parameters interactively or in a single
  2681.    command line. If you enter the utility name alone, CREF prompts you for
  2682.    the input and output filenames, as shown in the following example:
  2683.  
  2684.    C>CREF  <Enter>
  2685.  
  2686.    Microsoft (R) Cross-Reference Utility  Version 5.10
  2687.    Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved.
  2688.  
  2689.    Cross-reference [.CRF]: HELLO  <Enter>
  2690.    Listing [HELLO.REF]:
  2691.  
  2692.    15 Symbols
  2693.  
  2694.    C>
  2695.  
  2696.    ──────────────────────────────────────────────────────────────────────────
  2697.    Microsoft Cross-Reference  Version 5.10       Thu May 26 11:09:34 1988
  2698.    HELLO.EXE --- print Hello on terminal
  2699.  
  2700.      Symbol Cross-Reference    (# definition, + modification)Cref-1
  2701.  
  2702.    @CPU . . . . . . . . . . . . . .   1#
  2703.    @VERSION . . . . . . . . . . . .   1#
  2704.  
  2705.    CODE . . . . . . . . . . . . . .  21
  2706.    CR . . . . . . . . . . . . . . .  17#    46     47
  2707.  
  2708.    DATA . . . . . . . . . . . . . .  44
  2709.  
  2710.    LF . . . . . . . . . . . . . . .  18#    46     47
  2711.  
  2712.    MSG. . . . . . . . . . . . . . .  33     46#
  2713.    MSG_LEN. . . . . . . . . . . . .  32     49#
  2714.  
  2715.    PRINT. . . . . . . . . . . . . .  25#    39     60
  2716.  
  2717.    STACK. . . . . . . . . . . . . .  23     54#    54     58
  2718.    STDERR . . . . . . . . . . . . .  15#
  2719.    STDIN. . . . . . . . . . . . . .  13#
  2720.    STDOUT . . . . . . . . . . . . .  14#    31
  2721.  
  2722.    _DATA. . . . . . . . . . . . . .  23     27     44#    51
  2723.    _TEXT. . . . . . . . . . . . . .  21#    23     41
  2724.  
  2725.     15 Symbols
  2726.    ──────────────────────────────────────────────────────────────────────────
  2727.  
  2728.    Figure 4-5.  Cross-reference listing HELLO.REF produced by the CREF
  2729.    utility from the file HELLO.CRF, for the HELLO.EXE program example from
  2730.    Chapter 3. The symbols declared in the program are listed on the left in
  2731.    alphabetic order. To the right of each symbol is a list of all the lines
  2732.    where that symbol is referenced. The number with a # sign after it denotes
  2733.    the line where the symbol is declared. Numbers followed by a + sign
  2734.    indicate that the symbol is modified at the specified line. The line
  2735.    numbers given in the cross-reference listing correspond to the line
  2736.    numbers generated by the assembler in the program-listing (.LST) file, not
  2737.    to any physical line count in the original source file.
  2738.  
  2739.    The parameters may also be entered in the command line in the following
  2740.    form:
  2741.  
  2742.      CREF CRF_file, listing_file
  2743.  
  2744.    For example, the command-line equivalent to the preceding interactive
  2745.    session is:
  2746.  
  2747.    C>CREF HELLO,HELLO  <Enter>
  2748.  
  2749.    If CREF cannot find the specified .CRF file, it displays an error message.
  2750.    Otherwise, it leaves the cross-reference listing in the specified file on
  2751.    the disk. You can send the file to the printer with the COPY command, in
  2752.    the following form:
  2753.  
  2754.      COPY listing_file PRN:
  2755.  
  2756.    You can also send the cross-reference listing directly to a character
  2757.    device as it is generated by responding to the Listing prompt with the
  2758.    name of the device.
  2759.  
  2760.  
  2761.  The Microsoft Library Manager
  2762.  
  2763.    Although the object modules that are produced by MASM or by high-level-
  2764.    language compilers can be linked directly into executable load modules,
  2765.    they can also be collected into special files called object-module
  2766.    libraries. The modules in a library are indexed by name and by the public
  2767.    symbols they contain, so that they can be extracted by the linker to
  2768.    satisfy external references in a program.
  2769.  
  2770.    The Microsoft Library Manager (LIB) is distributed as the file LIB.EXE.
  2771.    LIB creates and maintains program libraries, adding, updating, and
  2772.    deleting object files as necessary. LIB can also check a library file for
  2773.    internal consistency or print a table of its contents (Figure 4-6).
  2774.  
  2775.    LIB follows the command conventions of most other Microsoft programming
  2776.    tools. You must supply it with the name of a library file to work on, one
  2777.    or more operations to perform, the name of a listing file or device, and
  2778.    (optionally) the name of the output library. If you do not specify a name
  2779.    for the output library, LIB gives it the same name as the input library
  2780.    and changes the extension of the input library to .BAK.
  2781.  
  2782.    The LIB operations are simply the names of object files, with a prefix
  2783.    character that specifies the action to be taken:
  2784.  
  2785.    Prefix     Meaning
  2786.    ──────────────────────────────────────────────────────────────────────────
  2787.    -          Delete an object module from the library.
  2788.    *          Extract a module and place it in a separate .OBJ file.
  2789.    +          Add an object module or the entire contents of another library
  2790.               to the library.
  2791.    ──────────────────────────────────────────────────────────────────────────
  2792.  
  2793.    You can combine command prefixes. For example, -+ replaces a module, and
  2794.    *- extracts a module into a new file and then deletes it from the library.
  2795.  
  2796.    ──────────────────────────────────────────────────────────────────────────
  2797.    _abort............abort             _abs..............abs
  2798.    _access...........access            _asctime..........asctime
  2799.    _atof.............atof              _atoi.............atoi
  2800.    _atol.............atol              _bdos.............bdos
  2801.    _brk..............brk               _brkctl...........brkctl
  2802.    _bsearch..........bsearch           _calloc...........calloc
  2803.    _cgets............cgets             _chdir............dir
  2804.    _chmod............chmod             _chsize...........chsize
  2805.         .
  2806.         .
  2807.         .
  2808.    _exit             Offset: 00000010H  Code and data size: 44H
  2809.      __exit
  2810.  
  2811.    _filbuf           Offset: 00000160H  Code and data size: BBH
  2812.      __filbuf
  2813.  
  2814.    _file             Offset: 00000300H  Code and data size: CAH
  2815.      __iob             __iob2            __lastiob
  2816.         .
  2817.         .
  2818.         .
  2819.    ──────────────────────────────────────────────────────────────────────────
  2820.  
  2821.    Figure 4-6.  Extract from the table-of-contents listing produced by the
  2822.    Microsoft Library Manager (LIB) for the Microsoft C library SLIBC.LIB. The
  2823.    first part of the listing is an alphabetic list of all public names
  2824.    declared in all of the modules in the library. Each name is associated
  2825.    with the object module to which it belongs. The second part of the listing
  2826.    is an alphabetic list of the object-module names in the library, each
  2827.    followed by its offset within the library file and the actual size of the
  2828.    module in bytes. The entry for each module is followed by a summary of the
  2829.    public names that are declared within it.
  2830.  
  2831.    When you invoke LIB with its name alone, it requests the other information
  2832.    it needs interactively, as shown in the following example:
  2833.  
  2834.    C>LIB  <Enter>
  2835.  
  2836.    Microsoft (R) Library Manager  Version 3.08
  2837.    Copyright (C) Microsoft Corp 1983-1987. All rights reserved.
  2838.  
  2839.    Library name:  SLIBC  <Enter>
  2840.    Operations: +VIDEO  <Enter>
  2841.    List file:  SLIBC.LST  <Enter>
  2842.    Output library:  SLIBC2  <Enter>
  2843.  
  2844.    C>
  2845.  
  2846.    In this example, LIB added the object module VIDEO.OBJ to the library
  2847.    SLIBC.LIB, wrote a library table of contents into the file SLIBC.LST, and
  2848.    named the resulting new library SLIBC2.LIB.
  2849.  
  2850.    The Library Manager can also be run with a command line of the following
  2851.    form:
  2852.  
  2853.      LIB library [commands],[list],[newlibrary]
  2854.  
  2855.    For example, the following command line is equivalent to the preceding
  2856.    interactive session:
  2857.  
  2858.    C>LIB SLIBC +VIDEO,SLIBC.LST,SLIBC2;  <Enter>
  2859.  
  2860.    As with the other Microsoft utilities, a semicolon at the end of the
  2861.    command line causes LIB to use the default responses for any parameters
  2862.    that are omitted.
  2863.  
  2864.    Like LINK, LIB can also accept its commands from a response file. The
  2865.    contents of the file are lines of text that correspond exactly to the
  2866.    responses you would give LIB interactively. You specify the name of the
  2867.    response file in the command line with a leading @ character, as follows:
  2868.  
  2869.      LIB @filename
  2870.  
  2871.    LIB has only three switches: /I (/IGNORECASE), /N (/NOIGNORECASE), and
  2872.    /PAGESIZE:number. The /IGNORECASE switch is the default. The /NOIGNORECASE
  2873.    switch causes LIB to regard as distinct any symbols that differ only in
  2874.    the case of their component letters. You should place the /PAGESIZE
  2875.    switch, which defines the size of a unit of allocation space for a given
  2876.    library, immediately after the library filename. The library page size is
  2877.    in bytes and must be a power of 2 between 16 and 32,768 (16, 32, 64, and
  2878.    so forth); the default is 16 bytes. Because the index to a library is
  2879.    always a fixed number of pages, setting a larger page size allows you to
  2880.    store more object modules in that library; on the other hand, it will
  2881.    result in more wasted space within the file.
  2882.  
  2883.  
  2884.  The MAKE Utility
  2885.  
  2886.    The MAKE utility (MAKE.EXE) compares dates of files and carries out
  2887.    commands based on the result of that comparison. Because of this single,
  2888.    rather basic capability, MAKE can be used to maintain complex programs
  2889.    built from many modules. The dates of source, object, and executable files
  2890.    are simply compared in a logical sequence; the assembler, compiler,
  2891.    linker, and other programming tools are invoked as appropriate.
  2892.  
  2893.    The MAKE utility processes a plain ASCII text file called, as you might
  2894.    expect, a make file. You start the utility with a command-line entry in
  2895.    the following form:
  2896.  
  2897.      MAKE makefile [options]
  2898.  
  2899.    By convention, a make file has the same name as the executable file that
  2900.    is being maintained, but without an extension. The available MAKE switches
  2901.    are listed in Figure 4-7.
  2902.  
  2903.    A simple make file contains one or more dependency statements separated by
  2904.    blank lines. Each dependency statement can be followed by a list of MS-DOS
  2905.    commands, in the following form:
  2906.  
  2907.      targetfile : sourcefile ...
  2908.  
  2909.        command
  2910.  
  2911.        command
  2912.  
  2913.        .
  2914.  
  2915.        .
  2916.  
  2917.        .
  2918.  
  2919.    If the date and time of any source file are later than those of the target
  2920.    file, the accompanying list of commands is carried out. You may use
  2921.    comment lines, which begin with a # character, freely in a make file. MAKE
  2922.    can also process inference rules and macro definitions. For further
  2923.    details on these advanced capabilities, see the Microsoft or IBM
  2924.    documentation.
  2925.  
  2926.    Switch     Meaning
  2927.    ──────────────────────────────────────────────────────────────────────────
  2928.    /D         Display last modification date of each file as it is processed.
  2929.    /I         Ignore exit (return) codes returned by commands and programs
  2930.               executed as a result of dependency statements.
  2931.    /N         Display commands that would be executed as a result of
  2932.               dependency statements but do not execute those commands.
  2933.    /S         Do not display commands as they are executed.
  2934.    /X         Direct error messages from MAKE, or any program that MAKE runs,
  2935.    <filename> to the specified file. If filename is a hyphen (-), direct
  2936.               error messages to the standard output.
  2937.    ──────────────────────────────────────────────────────────────────────────
  2938.  
  2939.    Figure 4-7.  Switches for the MAKE utility.
  2940.  
  2941.  
  2942.  A Complete Example
  2943.  
  2944.    Let's put together everything we've learned about using the MS-DOS
  2945.    programming tools so far. Figure 4-8 shows a sketch of the overall
  2946.    process of building an executable program.
  2947.  
  2948.    Assume that we have the source code for the HELLO.EXE program from Chapter
  2949.    3 in the file HELLO.ASM. To assemble the source program into the
  2950.    relocatable object module HELLO.OBJ with symbolic debugging information
  2951.    included, also producing a program listing in the file HELLO.LST and a
  2952.    cross-reference data file HELLO.CRF, we would enter
  2953.  
  2954.    C>MASM /C /L /Zi /T HELLO;  <Enter>
  2955.  
  2956.    To convert the cross-reference raw-data file HELLO.CRF into a
  2957.    cross-reference listing in the file HELLO.REF, we would enter
  2958.  
  2959.    C>CREF HELLO,HELLO  <Enter>
  2960.  
  2961.    ┌───────────────┐             ┌───────────────┐
  2962.    │     MASM      │             │  C or other   │
  2963.    │  source-code  │             │  HLL source-  │
  2964.    │     file      │             │   code file   │
  2965.    └───┬───────────┘             └───┬───────────┘
  2966.        │       ┌─────────────────────┘  Compiler
  2967.    ┌─────────────┐
  2968.    │  Relocatable  │
  2969.    │ object-module ├────┐
  2970.    │  file (.OBJ)  │    │
  2971.    └───┬───────────┘    │
  2972.        │ LIB            │
  2973.    ┌──────────────┐    │        ┌───────────────┐
  2974.    │ Object-module │      LINK  │  Executable   │
  2975.    │   libraries   ├─────────────   program     │
  2976.    │    (.LIB)     │            │    (.EXE)     │
  2977.    └───────────────┘      │      └───┬───────────┘
  2978.                           │          │ EXE2BIN
  2979.    ┌───────────────┐      │      ┌──────────────┐
  2980.    │     HLL       │      │      │   Executable  │
  2981.    │   runtime     ├──────┘      │    program    │
  2982.    │  libraries    │             │     (.COM)    │
  2983.    └───────────────┘             └───────────────┘
  2984.  
  2985.    Figure 4-8.  Creation of an MS-DOS application program, from source code
  2986.    to executable file.
  2987.  
  2988.    To convert the relocatable object file HELLO.OBJ into the executable file
  2989.    HELLO.EXE, creating a load map in the file HELLO.MAP and appending
  2990.    symbolic debugging information to the executable file, we would enter
  2991.  
  2992.    C>LINK /MAP /CODEVIEW HELLO;  <Enter>
  2993.  
  2994.    We could also automate the entire process just described by creating a
  2995.    make file named HELLO (with no extension) and including the following
  2996.    instructions:
  2997.  
  2998.    hello.obj : hello.asm
  2999.     masm /C /L /Zi /T hello;
  3000.     cref hello,hello
  3001.  
  3002.    hello.exe : hello.obj
  3003.     link /MAP /CODEVIEW hello;
  3004.  
  3005.    Then, when we have made some change to HELLO.ASM and want to rebuild the
  3006.    executable HELLO.EXE file, we need only enter
  3007.  
  3008.    C>MAKE HELLO  <Enter>
  3009.  
  3010.  
  3011.  Programming Resources and References
  3012.  
  3013.    The literature on IBM PC─compatible personal computers, the Intel 80x86
  3014.    microprocessor family, and assembly-language and C programming is vast.
  3015.    The list below contains a selection of those books that I have found to be
  3016.    useful and reliable. The list should not be construed as an endorsement by
  3017.    Microsoft Corporation.
  3018.  
  3019.  MASM Tutorials
  3020.  
  3021.    Assembly Language Primer for the IBM PC and XT, by Robert Lafore. New
  3022.    American Library, New York, NY, 1984. ISBN 0-452-25711-5.
  3023.  
  3024.    8086/8088/80286 Assembly Language, by Leo Scanlon. Brady Books, Simon and
  3025.    Schuster, New York, NY, 1988. ISBN 0-13-246919-7.
  3026.  
  3027.  C Tutorials
  3028.  
  3029.    Microsoft C Programming for the IBM, by Robert Lafore. Howard K. Sams &
  3030.    Co., Indianapolis, IN, 1987. ISBN 0-672-22515-8.
  3031.  
  3032.    Proficient C, by Augie Hansen. Microsoft Press, Redmond, WA, 1987. ISBN
  3033.    1-55615-007-5.
  3034.  
  3035.  Intel 80x86 Microprocessor References
  3036.  
  3037.    iAPX 88 Book. Intel Corporation, Literature Department SV3-3, 3065 Bowers
  3038.    Ave., Santa Clara, CA 95051. Order no. 210200.
  3039.  
  3040.    iAPX 286 Programmer's Reference Manual. Intel Corporation, Literature
  3041.    Department SV3-3, 3065 Bowers Ave., Santa Clara, CA 95051. Order no.
  3042.    210498.
  3043.  
  3044.    iAPX 386 Programmer's Reference Manual. Intel Corporation, Literature
  3045.    Department SV3-3, 3065 Bowers Ave., Santa Clara, CA 95051. Order no.
  3046.    230985.
  3047.  
  3048.  PC, PC/AT, and PS/2 Architecture
  3049.  
  3050.    The IBM Personal Computer from the Inside Out (Revised Edition), by Murray
  3051.    Sargent and Richard L. Shoemaker. Addison-Wesley Publishing Company,
  3052.    Reading, MA, 1986. ISBN 0-201-06918-0.
  3053.  
  3054.    Programmer's Guide to PC & PS/2 Video Systems, by Richard Wilton.
  3055.    Microsoft Press, Redmond, WA, 1987. ISBN 1-55615-103-9.
  3056.  
  3057.    Personal Computer Technical Reference. IBM Corporation, IBM Technical
  3058.    Directory, P. O. Box 2009, Racine, WI 53404. Part no. 6322507.
  3059.  
  3060.    Personal Computer AT Technical Reference. IBM Corporation, IBM Technical
  3061.    Directory, P. O. Box 2009, Racine, WI 53404. Part no. 6280070.
  3062.  
  3063.    Options and Adapters Technical Reference. IBM Corporation, IBM Technical
  3064.    Directory, P. O. Box 2009, Racine, WI 53404. Part no. 6322509.
  3065.  
  3066.    Personal System/2 Model 30 Technical Reference. IBM Corporation, IBM
  3067.    Technical Directory, P. O. Box 2009, Racine, WI 53404. Part no. 68X2201.
  3068.  
  3069.    Personal System/2 Model 50/60 Technical Reference. IBM Corporation, IBM
  3070.    Technical Directory, P. O. Box 2009, Racine, WI 53404. Part no. 68X2224.
  3071.  
  3072.    Personal System/2 Model 80 Technical Reference. IBM Corporation, IBM
  3073.    Technical Directory, P. O. Box 2009, Racine, WI 53404. Part no. 68X2256.
  3074.  
  3075.  
  3076.  
  3077.  ────────────────────────────────────────────────────────────────────────────
  3078.  Chapter 5  Keyboard and Mouse Input
  3079.  
  3080.    The fundamental means of user input under MS-DOS is the keyboard. This
  3081.    follows naturally from the MS-DOS command-line interface, whose lineage
  3082.    can be traced directly to minicomputer operating systems with Teletype
  3083.    consoles. During the first few years of MS-DOS's existence, when
  3084.    8088/8086-based machines were the norm, nearly every popular application
  3085.    program used key-driven menus and text-mode displays.
  3086.  
  3087.    However, as high-resolution graphics adapters (and 80286/80386-based
  3088.    machines with enough power to drive them) have become less expensive,
  3089.    programs that support windows and a graphical user interface have steadily
  3090.    grown more popular. Such programs typically rely on a pointing device such
  3091.    as a mouse, stylus, joystick, or light pen to let the user navigate in a
  3092.    "point-and-shoot" manner, reducing keyboard entry to a minimum. As a
  3093.    result, support for pointing devices has become an important consideration
  3094.    for all software developers.
  3095.  
  3096.  
  3097.  Keyboard Input Methods
  3098.  
  3099.    Applications running under MS-DOS on IBM PC─compatible machines can use
  3100.    several methods to obtain keyboard input:
  3101.  
  3102.    ■  MS-DOS handle-oriented functions
  3103.  
  3104.    ■  MS-DOS traditional character functions
  3105.  
  3106.    ■  IBM ROM BIOS keyboard-driver functions
  3107.  
  3108.    These methods offer different degrees of flexibility, portability, and
  3109.    hardware independence.
  3110.  
  3111.    The handle, or stream-oriented, functions are philosophically derived from
  3112.    UNIX/XENIX and were first introduced in MS-DOS version 2.0. A program uses
  3113.    these functions by supplying a handle, or token, for the desired device,
  3114.    plus the address and length of a buffer.
  3115.  
  3116.    When a program begins executing, MS-DOS supplies it with predefined
  3117.    handles for certain commonly used character devices, including the
  3118.    keyboard:
  3119.  
  3120.    Handle             Device name                          Opened to
  3121.    ──────────────────────────────────────────────────────────────────────────
  3122.    0                  Standard input (stdin)               CON
  3123.    1                  Standard output (stdout)             CON
  3124.    2                  Standard error (stderr)              CON
  3125.    3                  Standard auxiliary (stdaux)          AUX
  3126.    4                  Standard printer (stdprn)            PRN
  3127.    ──────────────────────────────────────────────────────────────────────────
  3128.  
  3129.    These handles can be used for read and write operations without further
  3130.    preliminaries. A program can also obtain a handle for a character device
  3131.    by explicitly opening the device for input or output using its logical
  3132.    name (as though it were a file). The handle functions support I/O
  3133.    redirection, allowing a program to take its input from another device or
  3134.    file instead of the keyboard, for example. Redirection is discussed in
  3135.    detail in Chapter 15.
  3136.  
  3137.    The traditional character-input functions are a superset of the character
  3138.    I/O functions that were present in CP/M. Originally included in MS-DOS
  3139.    simply to facilitate the porting of existing applications from CP/M, they
  3140.    are still widely used. In MS-DOS versions 2.0 and later, most of the
  3141.    traditional functions also support I/O redirection (although not as well
  3142.    as the handle functions do).
  3143.  
  3144.    Use of the IBM ROM BIOS keyboard functions presupposes that the program is
  3145.    running on an IBM PC─compatible machine. The ROM BIOS keyboard driver
  3146.    operates at a much more primitive level than the MS-DOS functions and
  3147.    allows a program to circumvent I/O redirection or MS-DOS's special
  3148.    handling of certain control characters. Programs that use the ROM BIOS
  3149.    keyboard driver are inherently less portable than those that use the
  3150.    MS-DOS functions and may interfere with the proper operation of other
  3151.    programs; many of the popular terminate-and-stay-resident (TSR) utilities
  3152.    fall into this category.
  3153.  
  3154.  Keyboard Input with Handles
  3155.  
  3156.    The principal MS-DOS function for keyboard input using handles is Int 21H
  3157.    Function 3FH (Read File or Device). The parameters for this function are
  3158.    a handle, the segment and offset of a buffer, and the length of the
  3159.    buffer. (For a more detailed explanation of this function, see Section
  3160.    II of this book, "MS-DOS Functions Reference.")
  3161.  
  3162.    As an example, let's use the predefined standard input handle (0) and Int
  3163.    21H Function 3FH to read a line from the keyboard:
  3164.  
  3165.    ──────────────────────────────────────────────────────────────────────────
  3166.    buffer  db   80 dup (?)     ; keyboard input buffer
  3167.            .
  3168.            .
  3169.            .
  3170.            mov  ah,3fh         ; function 3fh = read file or device
  3171.            mov  bx,0           ; handle for standard input
  3172.            mov  cx,80          ; maximum bytes to read
  3173.            mov  dx,seg buffer  ; DS:DX = buffer address
  3174.            mov  ds,dx
  3175.            mov  dx,offset buffer
  3176.            int  21h            ; transfer to MS-DOS
  3177.            jc   error          ; jump if error detected
  3178.            .
  3179.            .
  3180.            .
  3181.    ──────────────────────────────────────────────────────────────────────────
  3182.  
  3183.    When control returns from Int 21H Function 3FH, the carry flag is clear if
  3184.    the function was successful, and AX contains the number of characters
  3185.    read. If there was an error, the carry flag is set and AX contains an
  3186.    error code; however, this should never occur when reading the keyboard.
  3187.  
  3188.    The standard input is redirectable, so the code just shown is not a
  3189.    foolproof way of obtaining input from the keyboard. Depending upon whether
  3190.    a redirection parameter was included in the command line by the user,
  3191.    program input might be coming from the keyboard, a file, another character
  3192.    device, or even the bit bucket (NUL device). To bypass redirection and be
  3193.    absolutely certain where your input is coming from, you can ignore the
  3194.    predefined standard input handle and open the console as though it were a
  3195.    file, using the handle obtained from that open operation to perform your
  3196.    keyboard input, as in the following example:
  3197.  
  3198.    ──────────────────────────────────────────────────────────────────────────
  3199.    buffer  db     80 dup (?)   ; keyboard input buffer
  3200.    fname   db     'CON',0      ; keyboard device name
  3201.    handle  dw     0            ; keyboard device handle
  3202.            .
  3203.            .
  3204.            .
  3205.            mov    ah,3dh       ; function 3dh = open
  3206.            mov    al,0         ; mode = read
  3207.            mov    dx,seg fname ; DS:DX = device name
  3208.            mov    ds,dx
  3209.            mov    dx,offset fname
  3210.            int    21h          ; transfer to MS-DOS
  3211.            jc     error        ; jump if open failed
  3212.            mov    handle,ax    ; save handle for CON
  3213.            .
  3214.            .
  3215.            .
  3216.            mov    ah,3fh       ; function 3fh = read file or device
  3217.            mov    bx,handle    ; BX = handle for CON
  3218.            mov    cx,80        ; maximum bytes to read
  3219.            mov    dx,offset buffer ; DS:DX = buffer address
  3220.            int    21h          ; transfer to MS-DOS
  3221.            jc     error        ; jump if error detected
  3222.            .
  3223.            .
  3224.            .
  3225.    ──────────────────────────────────────────────────────────────────────────
  3226.  
  3227.    When a programmer uses Int 21H Function 3FH to read from the keyboard, the
  3228.    exact result depends on whether MS-DOS regards the handle to be in ASCII
  3229.    mode or binary mode (sometimes known as cooked mode and raw mode). ASCII
  3230.    mode is the default, although binary mode can be selected with Int 21H
  3231.    Function 44H (IOCTL) when necessary.
  3232.  
  3233.    In ASCII mode, MS-DOS initially places characters obtained from the
  3234.    keyboard in a 128-byte internal buffer, and the user can edit the input
  3235.    with the Backspace key and the special function keys. MS-DOS automatically
  3236.    echoes the characters to the standard output, expanding tab characters to
  3237.    spaces (although they are left as the ASCII code 09H in the buffer). The
  3238.    Ctrl-C, Ctrl-S, and Ctrl-P key combinations receive special handling, and
  3239.    the Enter key is translated to a carriage return─linefeed pair. When the
  3240.    user presses Enter or Ctrl-Z, MS-DOS copies the requested number of
  3241.    characters (or the actual number of characters entered, if less than the
  3242.    number requested) out of the internal buffer into the calling program's
  3243.    buffer.
  3244.  
  3245.    In binary mode, MS-DOS never echoes input characters. It passes the
  3246.    Ctrl-C, Ctrl-S, Ctrl-P, and Ctrl-Z key combinations and the Enter key
  3247.    through to the application unchanged, and Int 21H Function 3FH does not
  3248.    return control to the application until the exact number of characters
  3249.    requested has been received.
  3250.  
  3251.    Ctrl-C checking is discussed in more detail at the end of this chapter.
  3252.    For now, simply note that the application programmer can substitute a
  3253.    custom handler for the default MS-DOS Ctrl-C handler and thereby avoid
  3254.    having the application program lose control of the machine when the user
  3255.    enters a Ctrl-C or Ctrl-Break.
  3256.  
  3257.  Keyboard Input with Traditional Calls
  3258.  
  3259.    The MS-DOS traditional keyboard functions offer a variety of character and
  3260.    line-oriented services with or without echo and Ctrl-C detection. These
  3261.    functions are summarized on the following page.
  3262.  
  3263.    Int 21H Function   Action                               Ctrl-C checking
  3264.    ──────────────────────────────────────────────────────────────────────────
  3265.    01H               Keyboard input with echo             Yes
  3266.    06H               Direct console I/O                   No
  3267.    07H               Keyboard input without echo          No
  3268.    08H               Keyboard input without echo          Yes
  3269.    0AH               Buffered keyboard input              Yes
  3270.    0BH               Input-status check                   Yes
  3271.    0CH               Input-buffer reset and input         Varies
  3272.    ──────────────────────────────────────────────────────────────────────────
  3273.  
  3274.    In MS-DOS versions 2.0 and later, redirection of the standard input
  3275.    affects all these functions. In other words, they act as though they were
  3276.    special cases of an Int 21H Function 3FH call using the predefined
  3277.    standard input handle (0).
  3278.  
  3279.    The character-input functions (01H, 06H, 07H, and 08H) all return a
  3280.    character in the AL register. For example, the following sequence waits
  3281.    until a key is pressed and then returns it in AL:
  3282.  
  3283.    ──────────────────────────────────────────────────────────────────────────
  3284.            mov     ah,1        ; function 01h = read keyboard
  3285.            int     21h         ; transfer to MS-DOS
  3286.    ──────────────────────────────────────────────────────────────────────────
  3287.  
  3288.    The character-input functions differ in whether the input is echoed to the
  3289.    screen and whether they are sensitive to Ctrl-C interrupts. Although
  3290.    MS-DOS provides no pure keyboard-status function that is immune to Ctrl-C,
  3291.    a program can read keyboard status (somewhat circuitously) without
  3292.    interference by using Int 21H Function 06H. Extended keys, such as the
  3293.    IBM PC keyboard's special function keys, require two calls to a
  3294.    character-input function.
  3295.  
  3296.    As an alternative to single-character input, a program can use
  3297.    buffered-line input (Int 21H Function 0AH) to obtain an entire line from
  3298.    the keyboard in one operation. MS-DOS builds up buffered lines in an
  3299.    internal buffer and does not pass them to the calling program until the
  3300.    user presses the Enter key. While the line is being entered, all the usual
  3301.    editing keys are active and are handled by the MS-DOS keyboard driver. You
  3302.    use Int 21H Function 0AH as follows:
  3303.  
  3304.    ──────────────────────────────────────────────────────────────────────────
  3305.    buff    db      81          ; maximum length of input
  3306.            db      0           ; actual length (from MS-DOS)
  3307.            db      81 dup (0)  ; receives keyboard input
  3308.            .
  3309.            .
  3310.            .
  3311.            mov     ah,0ah      ; function 0ah = read buffered line
  3312.            mov     dx,seg buff ; DS:DX = buffer address
  3313.            mov     ds,dx
  3314.            mov     dx,offset buff
  3315.            int     21h         ; transfer to MS-DOS
  3316.            .
  3317.            .
  3318.            .
  3319.    ──────────────────────────────────────────────────────────────────────────
  3320.  
  3321.    Int 21H Function 0AH differs from Int 21H Function 3FH in several
  3322.    important ways. First, the maximum length is passed in the first byte of
  3323.    the buffer, rather than in the CX register. Second, the actual length is
  3324.    returned in the second byte of the structure, rather than in the AX
  3325.    register. Finally, when the user has entered one less than the specified
  3326.    maximum number of characters, MS-DOS ignores all subsequent characters and
  3327.    sounds a warning beep until the Enter key is pressed.
  3328.  
  3329.    For detailed information about each of the traditional keyboard-input
  3330.    functions, see Section II of this book, "MS-DOS Functions Reference."
  3331.  
  3332.  Keyboard Input with ROM BIOS Functions
  3333.  
  3334.    Programmers writing applications for IBM PC compatibles can bypass the
  3335.    MS-DOS keyboard functions and choose from two hardware-dependent
  3336.    techniques for keyboard input.
  3337.  
  3338.    The first method is to call the ROM BIOS keyboard driver using Int 16H.
  3339.    For example, the following sequence reads a single character from the
  3340.    keyboard input buffer and returns it in the AL register:
  3341.  
  3342.    ──────────────────────────────────────────────────────────────────────────
  3343.            mov    ah,0         ; function 0=read keyboard
  3344.            int    16h          ; transfer to ROM BIOS
  3345.    ──────────────────────────────────────────────────────────────────────────
  3346.  
  3347.    Int 16H Function 00H also returns the keyboard scan code in the AH
  3348.    register, allowing the program to detect key codes that are not ordinarily
  3349.    returned by MS-DOS. Other Int 16H services return the keyboard status
  3350.    (that is, whether a character is waiting) or the keyboard shift state
  3351.    (from the ROM BIOS data area 0000:0417H). For a more detailed explanation
  3352.    of ROM BIOS keyboard functions, see Section III of this book, "IBM ROM
  3353.    BIOS and Mouse Functions Reference."
  3354.  
  3355.    You should consider carefully before building ROM BIOS dependence into an
  3356.    application. Although this technique allows you to bypass any I/O
  3357.    redirection that may be in effect, ways exist to do this without
  3358.    introducing dependence on the ROM BIOS. And there are real disadvantages
  3359.    to calling the ROM BIOS keyboard driver:
  3360.  
  3361.    ■  It always bypasses I/O redirection, which sometimes may not be
  3362.       desirable.
  3363.  
  3364.    ■  It is dependent on IBM PC compatibility and does not work correctly,
  3365.       unchanged, on some older machines such as the Hewlett-Packard
  3366.       TouchScreen or the Wang Professional Computer.
  3367.  
  3368.    ■  It may introduce complicated interactions with TSR utilities.
  3369.  
  3370.    The other and more hardware-dependent method of keyboard input on an IBM
  3371.    PC is to write a new handler for ROM BIOS Int 09H and service the keyboard
  3372.    controller's interrupts directly. This involves translation of scan codes
  3373.    to ASCII characters and maintenance of the type-ahead buffer. In ordinary
  3374.    PC applications, there is no reason to take over keyboard I/O at this
  3375.    level; therefore, I will not discuss this method further here. If you are
  3376.    curious about the techniques that would be required, the best reference is
  3377.    the listing for the ROM BIOS Int 09H handler in the IBM PC or PC/AT
  3378.    technical reference manual.
  3379.  
  3380.  
  3381.  Ctrl-C and Ctrl-Break Handlers
  3382.  
  3383.    In the discussion of keyboard input with the MS-DOS handle and traditional
  3384.    functions, I made some passing references to the fact that Ctrl-C entries
  3385.    can interfere with the expected behavior of those functions. Let's look at
  3386.    this subject in more detail now.
  3387.  
  3388.    During most character I/O operations, MS-DOS checks for a Ctrl-C (ASCII
  3389.    code 03H) waiting at the keyboard and executes an Int 23H if one is
  3390.    detected. If the system break flag is on, MS-DOS also checks for a Ctrl-C
  3391.    entry during certain other operations (such as file reads and writes).
  3392.    Ordinarily, the Int 23H vector points to a routine that simply terminates
  3393.    the currently active process and returns control to the parent process──
  3394.    usually the MS-DOS command interpreter.
  3395.  
  3396.    In other words, if your program is executing and you enter a Ctrl-C,
  3397.    accidentally or intentionally, MS-DOS simply aborts the program. Any files
  3398.    the program has opened using file control blocks will not be closed
  3399.    properly, any interrupt vectors it has altered may not be restored
  3400.    correctly, and if it is performing any direct I/O operations (for example,
  3401.    if it contains an interrupt driver for the serial port), all kinds of
  3402.    unexpected events may occur.
  3403.  
  3404.    Although you can use a number of partially effective methods to defeat
  3405.    Ctrl-C checking, such as performing keyboard input with Int 21H Functions
  3406.    06H and 07H, placing all character devices into binary mode, or turning
  3407.    off the system break flag with Int 21H Function 33H, none of these is
  3408.    completely foolproof. The simplest and most elegant way to defeat Ctrl-C
  3409.    checking is simply to substitute your own Int 23H handler, which can take
  3410.    some action appropriate to your program. When the program terminates,
  3411.    MS-DOS automatically restores the previous contents of the Int 23H vector
  3412.    from information saved in the program segment prefix. The following
  3413.    example shows how to install your own Ctrl-C handler (which in this case
  3414.    does nothing at all):
  3415.  
  3416.    ──────────────────────────────────────────────────────────────────────────
  3417.            push    ds          ; save data segment
  3418.                                ; set int 23h vector...
  3419.            mov     ax,2523h    ; function 25h = set interrupt
  3420.                                ; int 23h = vector for
  3421.                                ; Ctrl-C handler
  3422.            mov     dx,seg handler ; DS:DX = handler address
  3423.            mov     ds,dx
  3424.            mov     dx,offset handler
  3425.            int     21h         ; transfer to MS-DOS
  3426.  
  3427.            pop     ds          ; restore data segment
  3428.            .
  3429.            .
  3430.            .
  3431.    handler:                    ; a Ctrl-C handler
  3432.            iret                ; that does nothing
  3433.    ──────────────────────────────────────────────────────────────────────────
  3434.  
  3435.    The first part of the code (which alters the contents of the Int 23H
  3436.    vector) would be executed in the initialization part of the application.
  3437.    The handler receives control whenever MS-DOS detects a Ctrl-C at the
  3438.    keyboard. (Because this handler consists only of an interrupt return, the
  3439.    Ctrl-C will remain in the keyboard input stream and will be passed to the
  3440.    application when it requests a character from the keyboard, appearing on
  3441.    the screen as ^C.)
  3442.  
  3443.    When an Int 23H handler is called, MS-DOS is in a stable state. Thus, the
  3444.    handler can call any MS-DOS function. It can also reset the segment
  3445.    registers and the stack pointer and transfer control to some other point
  3446.    in the application without ever returning control to MS-DOS with an IRET.
  3447.  
  3448.    On IBM PC compatibles, an additional interrupt handler must be taken into
  3449.    consideration. Whenever the ROM BIOS keyboard driver detects the key
  3450.    combination Ctrl-Break, it calls a handler whose address is stored in the
  3451.    vector for Int 1BH. The default ROM BIOS Int 1BH handler does nothing.
  3452.    MS-DOS alters the Int 1BH vector to point to its own handler, which sets a
  3453.    flag and returns; the net effect is to remap the Ctrl-Break into a Ctrl-C
  3454.    that is forced ahead of any other characters waiting in the keyboard
  3455.    buffer.
  3456.  
  3457.    Taking over the Int 1BH vector in an application is somewhat tricky but
  3458.    extremely useful. Because the keyboard is interrupt driven, a press of
  3459.    Ctrl-Break lets the application regain control under almost any
  3460.    circumstance──often, even if the program has crashed or is in an endless
  3461.    loop.
  3462.  
  3463.    You cannot, in general, use the same handler for Int 1BH that you use for
  3464.    Int 23H. The Int 1BH handler is more limited in what it can do, because it
  3465.    has been called as a result of a hardware interrupt and MS-DOS may have
  3466.    been executing a critical section of code at the time the interrupt was
  3467.    serviced. Thus, all registers except CS:IP are in an unknown state; they
  3468.    may have to be saved and then modified before your interrupt handler can
  3469.    execute. Similarly, the depth of the stack in use when the Int 1BH handler
  3470.    is called is unknown, and if the handler is to perform stack-intensive
  3471.    operations, it may have to save the stack segment and the stack pointer
  3472.    and switch to a new stack that is known to have sufficient depth.
  3473.  
  3474.    In normal application programs, you should probably avoid retaining
  3475.    control in an Int 1BH handler, rather than performing an IRET. Because of
  3476.    subtle differences among non-IBM ROM BIOSes, it is difficult to predict
  3477.    the state of the keyboard controller and the 8259 Programmable Interrupt
  3478.    Controller (PIC) when the Int 1BH handler begins executing. Also, MS-DOS
  3479.    itself may not be in a stable state at the point of interrupt, a situation
  3480.    that can manifest itself in unexpected critical errors during subsequent
  3481.    I/O operations. Finally, MS-DOS versions 3.2 and later allocate a stack
  3482.    from an internal pool for use by the Int 09H handler. If the Int 1BH
  3483.    handler never returns, the Int 09H handler never returns either, and
  3484.    repeated entries of Ctrl-Break will eventually exhaust the stack pool,
  3485.    halting the system.
  3486.  
  3487.    Because Int 1BH is a ROM BIOS interrupt and not an MS-DOS interrupt,
  3488.    MS-DOS does not restore the previous contents of the Int 1BH vector when a
  3489.    program exits. If your program modifies this vector, it must save the
  3490.    original value and restore it before terminating. Otherwise, the vector
  3491.    will be left pointing to some random area in the next program that runs,
  3492.    and the next time the user presses Ctrl-Break a system crash is the best
  3493.    you can hope for.
  3494.  
  3495.  Ctrl-C and Ctrl-Break Handlers and High-Level Languages
  3496.  
  3497.    Capturing the Ctrl-C and Ctrl-Break interrupts is straightforward when you
  3498.    are programming in assembly language. The process is only slightly more
  3499.    difficult with high-level languages, as long as you have enough
  3500.    information about the language's calling conventions that you can link in
  3501.    a small assembly-language routine as part of the program.
  3502.  
  3503.    The BREAK.ASM listing in Figure 5-1 contains source code for a Ctrl-Break
  3504.    handler that can be linked with small-model Microsoft C programs running
  3505.    on an IBM PC compatible. The short C program in Figure 5-2 demonstrates
  3506.    use of the handler. (This code should be readily portable to other C
  3507.    compilers.)
  3508.  
  3509.    ──────────────────────────────────────────────────────────────────────────
  3510.            page    55,132
  3511.            title   Ctrl-C & Ctrl-Break Handlers
  3512.            name    break
  3513.  
  3514.    ;
  3515.    ; Ctrl-C and Ctrl-Break handler for Microsoft C
  3516.    ; programs running on IBM PC compatibles
  3517.    ;
  3518.    ; by Ray Duncan
  3519.    ;
  3520.    ; Assemble with:  C>MASM /Mx BREAK;
  3521.    ;
  3522.    ; This module allows C programs to retain control
  3523.    ; when the user enters a Ctrl-Break or Ctrl-C.
  3524.    ; It uses Microsoft C parameter-passing conventions
  3525.    ; and assumes the C small memory model.
  3526.    ;
  3527.    ; The procedure _capture is called to install
  3528.    ; a new handler for the Ctrl-C and Ctrl-Break
  3529.    ; interrupts (1bh and 23h).  _capture is passed
  3530.    ; the address of a static variable, which will be
  3531.    ; set to true by the handler whenever a Ctrl-C
  3532.    ; or Ctrl-Break is detected.  The C syntax is:
  3533.    ;
  3534.    ;               static int flag;
  3535.    ;               capture(&flag);
  3536.    ;
  3537.    ; The procedure _release is called by the C program
  3538.    ; to restore the original Ctrl-Break and Ctrl-C
  3539.    ; handler. The C syntax is:
  3540.    ;               release();
  3541.    ;
  3542.    ; The procedure ctrlbrk is the actual interrupt
  3543.    ; handler.  It receives control when a software
  3544.    ; int 1bh is executed by the ROM BIOS or int 23h
  3545.    ; is executed by MS-DOS.  It simply sets the C
  3546.    ; program's variable to true (1) and returns.
  3547.    ;
  3548.  
  3549.    args    equ     4               ; stack offset of arguments,
  3550.                                    ; C small memory model
  3551.  
  3552.    cr      equ     0dh             ; ASCII carriage return
  3553.    lf      equ     0ah             ; ASCII linefeed
  3554.  
  3555.    _TEXT   segment word public 'CODE'
  3556.  
  3557.            assume cs:_TEXT
  3558.  
  3559.  
  3560.            public  _capture
  3561.    _capture proc   near            ; take over Ctrl-Break
  3562.                                    ; and Ctrl-C interrupt vectors
  3563.  
  3564.            push    bp              ; set up stack frame
  3565.            mov     bp,sp
  3566.  
  3567.            push    ds              ; save registers
  3568.            push    di
  3569.            push    si
  3570.  
  3571.                                    ; save address of
  3572.                                    ; calling program's "flag"
  3573.            mov     ax,word ptr [bp+args]
  3574.            mov     word ptr cs:flag,ax
  3575.            mov     word ptr cs:flag+2,ds
  3576.  
  3577.                                    ; save address of original
  3578.            mov     ax,3523h        ; int 23h handler
  3579.            int     21h
  3580.            mov     word ptr cs:int23,bx
  3581.            mov     word ptr cs:int23+2,es
  3582.            mov     ax,351bh        ; save address of original
  3583.            int     21h             ; int 1bh handler
  3584.            mov     word ptr cs:int1b,bx
  3585.            mov     word ptr cs:int1b+2,es
  3586.            push    cs              ; set DS:DX = address
  3587.            pop     ds              ; of new handler
  3588.            mov     dx,offset _TEXT:ctrlbrk
  3589.  
  3590.            mov     ax,02523h       ; set int 23h vector
  3591.            int     21h
  3592.  
  3593.            mov     ax,0251bh       ; set int 1bh vector
  3594.            int     21h
  3595.  
  3596.            pop     si              ; restore registers
  3597.            pop     di
  3598.            pop     ds
  3599.  
  3600.            pop     bp              ; discard stack frame
  3601.            ret                     ; and return to caller
  3602.  
  3603.    _capture endp
  3604.  
  3605.  
  3606.            public  _release
  3607.    _release proc   near            ; restore original Ctrl-C
  3608.                                    ; and Ctrl-Break handlers
  3609.  
  3610.            push    bp              ; save registers
  3611.            push    ds
  3612.            push    di
  3613.            push    si
  3614.  
  3615.            lds     dx,cs:int1b     ; get address of previous
  3616.                                    ; int 1bh handler
  3617.  
  3618.            mov     ax,251bh        ; set int 1bh vector
  3619.            int     21h
  3620.  
  3621.            lds     dx,cs:int23     ; get address of previous
  3622.                                    ; int 23h handler
  3623.  
  3624.            mov     ax,2523h        ; set int 23h vector
  3625.            int     21h
  3626.  
  3627.            pop     si              ; restore registers
  3628.            pop     di              ; and return to caller
  3629.            pop     ds
  3630.            pop     bp
  3631.            ret
  3632.    release endp
  3633.  
  3634.    ctrlbrk proc    far             ; Ctrl-C and Ctrl-Break
  3635.                                    ; interrupt handler
  3636.  
  3637.            push    bx              ; save registers
  3638.            push    ds
  3639.  
  3640.            lds     bx,cs:flag      ; get address of C program's
  3641.                                    ; "flag variable"
  3642.  
  3643.                                    ; and set the flag "true"
  3644.            mov     word ptr ds:[bx],1
  3645.  
  3646.            pop     ds              ; restore registers
  3647.            pop     bx
  3648.  
  3649.            iret                    ; return from handler
  3650.  
  3651.    ctrlbrk endp
  3652.  
  3653.    flag    dd      0               ; far pointer to caller's
  3654.                                    ; Ctrl-Break or Ctrl-C flag
  3655.  
  3656.    int23   dd      0               ; address of original
  3657.                                    ; Ctrl-C handler
  3658.  
  3659.    int1b   dd      0               ; address of original
  3660.                                    ; Ctrl-Break handler
  3661.  
  3662.    _TEXT   ends
  3663.  
  3664.            end
  3665.    ──────────────────────────────────────────────────────────────────────────
  3666.  
  3667.    Figure 5-1.  BREAK.ASM: A Ctrl-C and Ctrl-Break interrupt handler that can
  3668.    be linked with Microsoft C programs.
  3669.  
  3670.    ──────────────────────────────────────────────────────────────────────────
  3671.    /*
  3672.        TRYBREAK.C
  3673.  
  3674.        Demo of BREAK.ASM Ctrl-Break and Ctrl-C
  3675.        interrupt handler, by Ray Duncan
  3676.  
  3677.        To create the executable file TRYBREAK.EXE, enter:
  3678.  
  3679.        MASM /Mx BREAK;
  3680.        CL TRYBREAK.C BREAK.OBJ
  3681.    */
  3682.  
  3683.    #include <stdio.h>
  3684.  
  3685.    main(int argc, char *argv[])
  3686.    {
  3687.        int hit = 0;                     /* flag for key press      */
  3688.        int c = 0;                       /* character from keyboard */
  3689.        static int flag = 0;             /* true if Ctrl-Break
  3690.                                            or Ctrl-C detected      */
  3691.  
  3692.        puts("\n*** TRYBREAK.C running ***\n");
  3693.        puts("Press Ctrl-C or Ctrl-Break to test handler,");
  3694.        puts("Press the Esc key to exit TRYBREAK.\n");
  3695.  
  3696.        capture(&flag);                  /* install new Ctrl-C and
  3697.                                            Ctrl-Break handler and
  3698.                                            pass address of flag    */
  3699.  
  3700.        puts("TRYBREAK has captured interrupt vectors.\n");
  3701.  
  3702.        while(1)
  3703.        {
  3704.            hit = kbhit();               /* check for key press     */
  3705.                                         /* (MS-DOS sees Ctrl-C
  3706.                                             when keyboard polled)  */
  3707.  
  3708.            if(flag != 0)                /* if flag is true, an     */
  3709.            {                            /* interrupt has occurred  */
  3710.                puts("\nControl-Break detected.\n");
  3711.                flag = 0;                /* reset interrupt flag    */
  3712.            }
  3713.            if(hit != 0)                 /* if any key waiting      */
  3714.            {
  3715.                c = getch();             /* read key, exit if Esc   */
  3716.                if( (c & 0x7f) == 0x1b) break;
  3717.                putch(c);                /* otherwise display it    */
  3718.            }
  3719.        }
  3720.        release();                       /* restore original Ctrl-C
  3721.                                            and Ctrl-Break handlers */
  3722.  
  3723.        puts("\n\nTRYBREAK has released interrupt vectors.");
  3724.    }
  3725.    ──────────────────────────────────────────────────────────────────────────
  3726.  
  3727.    Figure 5-2.  TRYBREAK.C: A simple Microsoft C program that demonstrates
  3728.    use of the interrupt handler BREAK.ASM from Figure 5-1.
  3729.  
  3730.    In the example handler, the procedure named capture is called with the
  3731.    address of an integer variable within the C program. It saves the address
  3732.    of the variable, points the Int 1BH and Int 23H vectors to its own
  3733.    interrupt handler, and then returns.
  3734.  
  3735.    When MS-DOS detects a Ctrl-C or Ctrl-Break, the interrupt handler sets the
  3736.    integer variable within the C program to true (1) and returns. The C
  3737.    program can then poll this variable at its leisure. Of course, to detect
  3738.    more than one Ctrl-C, the program must reset the variable to zero again.
  3739.  
  3740.    The procedure named release simply restores the Int 1BH and Int 23H
  3741.    vectors to their original values, thereby disabling the interrupt handler.
  3742.    Although it is not strictly necessary for release to do anything about Int
  3743.    23H, this action does give the C program the option of restoring the
  3744.    default handler for Int 23H without terminating.
  3745.  
  3746.  
  3747.  Pointing Devices
  3748.  
  3749.    Device drivers for pointing devices are supplied by the hardware
  3750.    manufacturer and are loaded with a DEVICE statement in the CONFIG.SYS
  3751.    file. Although the hardware characteristics of the available pointing
  3752.    devices differ greatly, nearly all of their drivers present the same
  3753.    software interface to application programs: the Int 33H protocol used by
  3754.    the Microsoft Mouse driver. Version 6 of the Microsoft Mouse driver (which
  3755.    was current as this was written) offers the following functions:
  3756.  
  3757. ╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
  3758.    Function           Meaning
  3759.    ──────────────────────────────────────────────────────────────────────────
  3760.    00H               Reset mouse and get status.
  3761.    Function           Meaning
  3762.    ──────────────────────────────────────────────────────────────────────────
  3763.    00H               Reset mouse and get status.
  3764.    01H               Show mouse pointer.
  3765.    02H               Hide mouse pointer.
  3766.    03H               Get button status and pointer position.
  3767.    04H               Set pointer position.
  3768.    05H               Get button-press information.
  3769.    06H               Get button-release information.
  3770.    07H               Set horizontal limits for pointer.
  3771.    08H               Set vertical limits for pointer.
  3772.    09H               Set graphics pointer type.
  3773.    0AH               Set text pointer type.
  3774.    0BH               Read mouse-motion counters.
  3775.    0CH               Install interrupt handler for mouse events.
  3776.    0DH               Turn on light pen emulation.
  3777.    0EH               Turn off light pen emulation.
  3778.    0FH               Set mickeys to pixel ratio.
  3779.    10H               Set pointer exclusion area.
  3780.    13H               Set double-speed threshold.
  3781.    14H               Swap mouse-event interrupt routines.
  3782.    Function           Meaning
  3783.    ──────────────────────────────────────────────────────────────────────────
  3784.   14H               Swap mouse-event interrupt routines.
  3785.    15H               Get buffer size for mouse-driver state.
  3786.    16H               Save mouse-driver state.
  3787.    17H               Restore mouse-driver state.
  3788.    18H               Install alternate handler for mouse events.
  3789.    19H               Get address of alternate handler.
  3790.    1AH               Set mouse sensitivity.
  3791.    1BH               Get mouse sensitivity.
  3792.    1CH               Set mouse interrupt rate.
  3793.    1DH               Select display page for pointer.
  3794.    1EH               Get display page for pointer.
  3795.    1FH               Disable mouse driver.
  3796.    20H               Enable mouse driver.
  3797.    21H               Reset mouse driver.
  3798.    22H               Set language for mouse-driver messages.
  3799.    23H               Get language number.
  3800.    24H               Get driver version, mouse type, and IRQ number.
  3801.    ──────────────────────────────────────────────────────────────────────────
  3802.  
  3803.    Function           Meaning
  3804.    ──────────────────────────────────────────────────────────────────────────
  3805. 
  3806.  
  3807.    Although this list of mouse functions may appear intimidating, the average
  3808.    application will only need a few of them.
  3809.  
  3810.    A program first calls Int 33H Function 00H to initialize the mouse driver
  3811.    for the current display mode and to check its status. At this point, the
  3812.    mouse is "alive" and the application can obtain its state and position;
  3813.    however, the pointer does not become visible until the process calls Int
  3814.    33H Function 01H.
  3815.  
  3816.    The program can then call Int 33H Functions 03H, 05H, and 06H to
  3817.    monitor the mouse position and the status of the mouse buttons.
  3818.    Alternatively, the program can register an interrupt handler for mouse
  3819.    events, using Int 33H Function 0CH. This latter technique eliminates the
  3820.    need to poll the mouse driver; the driver will notify the program by
  3821.    calling the interrupt handler whenever the mouse is moved or a button is
  3822.    pressed or released.
  3823.  
  3824.    When the application is finished with the mouse, it can call Int 33H
  3825.    Function 02H to hide the mouse pointer. If the program has registered an
  3826.    interrupt handler for mouse events, it should disable further calls to the
  3827.    handler by resetting the mouse driver again with Int 33H Function 00H.
  3828.  
  3829.    For a complete description of the mouse-driver functions, see Section
  3830.    III of this book, "IBM ROM BIOS and Mouse Functions Reference." Figure
  3831.    5-3 shows a small demonstration program that polls the mouse continually,
  3832.    to display its position and status.
  3833.  
  3834.    ──────────────────────────────────────────────────────────────────────────
  3835.    /*
  3836.        Simple Demo of Int 33H Mouse Driver
  3837.        (C) 1988 Ray Duncan
  3838.  
  3839.        Compile with: CL MOUDEMO.C
  3840.    */
  3841.  
  3842.    #include <stdio.h>
  3843.    #include <dos.h>
  3844.  
  3845.    union REGS regs;
  3846.  
  3847.    void cls(void);                     /* function prototypes       */
  3848.    void gotoxy(int, int);
  3849.  
  3850.    main(int argc, char *argv[])
  3851.    {
  3852.        int x,y,buttons;                /* some scratch variables    */
  3853.                                        /* for the mouse state       */
  3854.  
  3855.        regs.x.ax = 0;                  /* reset mouse driver        */
  3856.        int86(0x33, ®s, ®s);      /* and check status          */
  3857.  
  3858.        if(regs.x.ax == 0)              /* exit if no mouse          */
  3859.        {   printf("\nMouse not available\n");
  3860.            exit(1);
  3861.        }
  3862.  
  3863.        cls();                          /* clear the screen          */
  3864.        gotoxy(45,0);                   /* and show help info        */
  3865.        puts("Press Both Mouse Buttons To Exit");
  3866.  
  3867.        regs.x.ax = 1;                  /* display mouse cursor      */
  3868.        int86(0x33, ®s, ®s);
  3869.  
  3870.        do {
  3871.            regs.x.ax = 3;              /* get mouse position        */
  3872.            int86(0x33, ®s, ®s);  /* and button status         */
  3873.            buttons = regs.x.bx & 3;
  3874.            x = regs.x.cx;
  3875.            y = regs.x.dx;
  3876.            gotoxy(0,0);                 /* display mouse position    */
  3877.            printf("X = %3d  Y = %3d", x, y);
  3878.  
  3879.        } while(buttons != 3);           /* exit if both buttons down */
  3880.  
  3881.        regs.x.ax = 2;                   /* hide mouse cursor         */
  3882.        int86(0x33, ®s, ®s);
  3883.  
  3884.        cls();                           /* display message and exit  */
  3885.        gotoxy(0,0);
  3886.        puts("Have a Mice Day!");
  3887.    }
  3888.  
  3889.    /*
  3890.        Clear the screen
  3891.    */
  3892.    void cls(void)
  3893.    {
  3894.        regs.x.ax = 0x0600;              /* ROM BIOS video driver     */
  3895.        regs.h.bh = 7;                   /* int 10h function 06h      */
  3896.        regs.x.cx = 0;                   /* initializes a window      */
  3897.        regs.h.dh = 24;
  3898.        regs.h.dl = 79;
  3899.        int86(0x10, ®s, ®s);
  3900.    }
  3901.  
  3902.    /*
  3903.        Position cursor to (x,y)
  3904.    */
  3905.    void gotoxy(int x, int y)
  3906.    {
  3907.        regs.h.dl = x;                   /* ROM BIOS video driver     */
  3908.        regs.h.dh = y;                   /* int 10h function 02h      */
  3909.        regs.h.bh = 0;                   /* positions the cursor      */
  3910.        regs.h.ah = 2;
  3911.        int86(0x10, ®s, ®s);
  3912.    }
  3913.    ──────────────────────────────────────────────────────────────────────────
  3914.  
  3915.    Figure 5-3.  MOUDEMO.C: A simple Microsoft C program that polls the mouse
  3916.    and continually displays the coordinates of the mouse pointer in the upper
  3917.    left corner of the screen. The program uses the ROM BIOS video driver,
  3918.    which is discussed in Chapter 6, to clear the screen and position the
  3919.    text cursor.
  3920.  
  3921.  
  3922.  
  3923.  ────────────────────────────────────────────────────────────────────────────
  3924.  Chapter 6  Video Display
  3925.  
  3926.    The visual presentation of an application program is one of its most
  3927.    important elements. Users frequently base their conclusions about a
  3928.    program's performance and "polish" on the speed and attractiveness of its
  3929.    displays. Therefore, a feel for the computer system's display facilities
  3930.    and capabilities at all levels, from MS-DOS down to the bare hardware, is
  3931.    important to you as a programmer.
  3932.  
  3933.  
  3934.  Video Display Adapters
  3935.  
  3936.    The video display adapters found in IBM PC─compatible computers have a
  3937.    hybrid interface to the central processor. The overall display
  3938.    characteristics, such as vertical and horizontal resolution, background
  3939.    color, and palette, are controlled by values written to I/O ports whose
  3940.    addresses are hardwired on the adapter, whereas the appearance of each
  3941.    individual character or graphics pixel on the display is controlled by a
  3942.    specific location within an area of memory called the regen buffer or
  3943.    refresh buffer. Both the CPU and the video controller access this memory;
  3944.    the software updates the display by simply writing character codes or bit
  3945.    patterns directly into the regen buffer. (This is called memory-mapped
  3946.    I/O.)
  3947.  
  3948.    The following adapters are in common use as this book is being written:
  3949.  
  3950.    ■  Monochrome/Printer Display Adapter (MDA). Introduced with the original
  3951.       IBM PC in 1981, this adapter supports 80-by-25 text display on a green
  3952.       (monochrome) screen and has no graphics capabilities at all.
  3953.  
  3954.    ■  Color/Graphics Adapter (CGA). Also introduced by IBM in 1981, this
  3955.       adapter supports 40-by-25 and 80-by-25 text modes and 320-by-200,
  3956.       4-color or 640-by-200, 2-color graphics (all-points-addressable, or
  3957.       APA) modes on composite or digital RGB monitors.
  3958.  
  3959.    ■  Enhanced Graphics Adapter (EGA). Introduced by IBM in 1985 and upwardly
  3960.       compatible from the CGA, this adapter adds support for 640-by-350,
  3961.       16-color graphics modes on digital RGB monitors. It also supports an
  3962.       MDA-compatible text mode.
  3963.  
  3964.    ■  Multi-Color Graphics Array (MCGA). Introduced by IBM in 1987 with the
  3965.       Personal System/2 (PS/2) models 25 and 30, this adapter is partially
  3966.       compatible with the CGA and EGA and supports 640-by-480, 2-color or
  3967.       320-by-200, 256-color graphics on analog RGB monitors.
  3968.  
  3969.    ■  Video Graphics Array (VGA). Introduced by IBM in 1987 with the PS/2
  3970.       models 50, 60, and 80, this adapter is upwardly compatible from the EGA
  3971.       and supports 640-by-480, 16-color or 320-by-200, 256-color graphics on
  3972.       analog RGB monitors. It also supports an MDA-compatible text mode.
  3973.  
  3974.    ■  Hercules Graphics Card, Graphics CardPlus, and InColor Cards. These are
  3975.       upwardly compatible from the MDA for text display but offer graphics
  3976.       capabilities that are incompatible with all of the IBM adapters.
  3977.  
  3978.    The locations of the regen buffers for the various IBM PC─compatible
  3979.    adapters are shown in Figure 6-1.
  3980.  
  3981.           ┌───────────────────────────────────────────────────────┐
  3982.           │                       ROM BIOS                        │
  3983.    FE000H ├───────────────────────────────────────────────────────┤
  3984.           │          System ROM, Stand-alone BASIC, etc.          │
  3985.    F4000H ├───────────────────────────────────────────────────────┤
  3986.           │             Reserved for BIOS extensions              │
  3987.           │             (hard-disk controller, etc.)              │
  3988.    C0000H ├───────────────────────────────────────────────────────┤
  3989.           │                       Reserved                        │
  3990.    BC000H ├───────────────────────────────────────────────────────┤
  3991.           │    16 KB regen buffer for CGA, EGA, MCGA, and VGA     │
  3992.           │       in text modes and 200-line graphics modes       │
  3993.    B8000H ├───────────────────────────────────────────────────────┤
  3994.           │                       Reserved                        │
  3995.    B1000H ├───────────────────────────────────────────────────────┤
  3996.           │         4 KB Monochrome Adapter regen buffer          │
  3997.    B0000H ├───────────────────────────────────────────────────────┤
  3998.           │       Regen buffer area for EGA, MCGA, and VGA        │
  3999.           │        in 350-line or 480-line graphics modes         │
  4000.    A0000H ├───────────────────────────────────────────────────────┤
  4001.           │             Transient part of COMMAND.COM             │
  4002.           ├───────────────────────────────────────────────────────┤
  4003.           │                Transient program area                 │
  4004.    varies ├───────────────────────────────────────────────────────┤
  4005.           │                MS-DOS and its buffers,                │
  4006.           │              tables, and device drivers               │
  4007.    00400H ├───────────────────────────────────────────────────────┤
  4008.           │                   Interrupt vectors                   │
  4009.    00000H └───────────────────────────────────────────────────────┘
  4010.  
  4011.    Figure 6-1.  Memory diagram of an IBM PC─compatible personal computer,
  4012.    showing the locations of the regen buffers for various adapters.
  4013.  
  4014.  
  4015.  Support Considerations
  4016.  
  4017.    MS-DOS offers several functions to transfer text to the display. Version 1
  4018.    supported only Teletype-like output capabilities; version 2 added an
  4019.    optional ANSI console driver to allow the programmer to clear the screen,
  4020.    position the cursor, and select colors and attributes with standard escape
  4021.    sequences embedded in the output. Programs that use only the MS-DOS
  4022.    functions will operate properly on any computer system that runs MS-DOS,
  4023.    regardless of the level of IBM hardware compatibility.
  4024.  
  4025.    On IBM PC─compatible machines, the ROM BIOS contains a video driver that
  4026.    programs can invoke directly, bypassing MS-DOS. The ROM BIOS functions
  4027.    allow a program to write text or individual pixels to the screen or to
  4028.    select display modes, video pages, palette, and foreground and background
  4029.    colors. These functions are relatively efficient (compared with the MS-DOS
  4030.    functions, at least), although the graphics support is primitive.
  4031.  
  4032.    Unfortunately, the display functions of both MS-DOS and the ROM BIOS were
  4033.    designed around the model of a cursor-addressable terminal and therefore
  4034.    do not fully exploit the capabilities of the memory-mapped, high-bandwidth
  4035.    display adapters used on IBM PC─compatible machines. As a result, nearly
  4036.    every popular interactive application with full-screen displays or
  4037.    graphics capability ignores both MS-DOS and the ROM BIOS and writes
  4038.    directly to the video controller's registers and regen buffer.
  4039.  
  4040.    Programs that control the hardware directly are sometimes called
  4041.    "ill-behaved," because they are performing operations that are normally
  4042.    reserved for operating-system device drivers. These programs are a severe
  4043.    management problem in multitasking real-mode environments such as DesqView
  4044.    and Microsoft Windows, and they are the main reason why such environments
  4045.    are not used more widely. It could be argued, however, that the blame for
  4046.    such problematic behavior lies not with the application programs but with
  4047.    the failure of MS-DOS and the ROM BIOS──even six years after the first
  4048.    appearance of the IBM PC──to provide display functions of adequate range
  4049.    and power.
  4050.  
  4051.  
  4052.  MS-DOS Display Functions
  4053.  
  4054.    Under MS-DOS versions 2.0 and later, the preferred method for sending text
  4055.    to the display is to use handle-based Int 21H Function 40H (Write File or
  4056.    Device). When an application program receives control, MS-DOS has already
  4057.    assigned it handles for the standard output (1) and standard error (2)
  4058.    devices, and these handles can be used immediately. For example, the
  4059.    sequence at the top of the following page writes the message hello to the
  4060.    display using the standard output handle.
  4061.  
  4062.    ──────────────────────────────────────────────────────────────────────────
  4063.    msg     db      'hello'     ; message to display
  4064.    msg_len equ     $-msg       ; length of message
  4065.            .
  4066.            .
  4067.            .
  4068.            mov     ah,40h      ; function 40h = write file or device
  4069.            mov     bx,1        ; BX = standard output handle
  4070.            mov     cx,msg_len  ; CX = message length
  4071.            mov     dx,seg msg  ; DS:DX = address of message
  4072.            mov     ds,dx
  4073.            mov     dx,offset msg
  4074.            int     21h         ; transfer to MS-DOS
  4075.            jc      error       ; jump if error detected
  4076.            .
  4077.            .
  4078.            .
  4079.    ──────────────────────────────────────────────────────────────────────────
  4080.  
  4081.    If there is no error, the function returns the carry flag cleared and the
  4082.    number of characters actually transferred in register AX. Unless a Ctrl-Z
  4083.    is embedded in the text or the standard output is redirected to a disk
  4084.    file and the disk is full, this number should equal the number of
  4085.    characters requested.
  4086.  
  4087.    As in the case of keyboard input, the user's ability to specify
  4088.    command-line redirection parameters that are invisible to the application
  4089.    means that if you use the predefined standard output handle, you can't
  4090.    always be sure where your output is going. However, to ensure that your
  4091.    output actually goes to the display, you can use the predefined standard
  4092.    error handle, which is always opened to the CON (logical console) device
  4093.    and is not redirectable.
  4094.  
  4095.    As an alternative to the standard output and standard error handles, you
  4096.    can bypass any output redirection and open a separate channel to CON,
  4097.    using the handle obtained from that open operation for character output.
  4098.    For example, the following code opens the console display for output and
  4099.    then writes the string hello to it:
  4100.  
  4101.    ──────────────────────────────────────────────────────────────────────────
  4102.    fname   db      'CON',0      ; name of CON device
  4103.    handle  dw      0            ; handle for CON device
  4104.    msg     db      'hello'      ; message to display
  4105.    msg_len equ     $-msg        ; length of message
  4106.            .
  4107.            .
  4108.            .
  4109.            mov     ax,3d02h     ; AH = function 3dh = open
  4110.                                 ; AL = mode = read/write
  4111.            mov     dx,seg fname ; DS:DX = device name
  4112.            mov     ds,dx
  4113.            mov     dx,offset fname
  4114.            int     21h          ; transfer to MS-DOS
  4115.            jc      error        ; jump if open failed
  4116.            mov     handle,ax    ; save handle for CON
  4117.            .
  4118.            .
  4119.            .
  4120.            mov     ah,40h       ; function 40h = write
  4121.            mov     cx,msg_len   ; CX = message length
  4122.            mov     dx,seg msg   ; DS:DX = address of message
  4123.            mov     ds,dx
  4124.            mov     dx,offset msg
  4125.            mov     bx,handle    ; BX = CON device handle
  4126.            int     21h          ; transfer to MS-DOS
  4127.            jc      error        ; jump if error detected
  4128.            .
  4129.            .
  4130.            .
  4131.    ──────────────────────────────────────────────────────────────────────────
  4132.  
  4133.    As with the keyboard input functions, MS-DOS also supports traditional
  4134.    display functions that are upwardly compatible from the corresponding CP/M
  4135.    output calls:
  4136.  
  4137.    ■  Int 21H Function 02H sends the character in the DL register to the
  4138.       standard output device. It is sensitive to Ctrl-C interrupts, and it
  4139.       handles carriage returns, linefeeds, bell codes, and backspaces
  4140.       appropriately.
  4141.  
  4142.    ■  Int 21H Function 06H transfers the character in the DL register to the
  4143.       standard output device, but it is not sensitive to Ctrl-C interrupts.
  4144.       You must take care when using this function, because it can also be
  4145.       used for input and for status requests.
  4146.  
  4147.    ■  Int 21H Function 09H sends a string to the standard output device. The
  4148.       string is terminated by the $ character.
  4149.  
  4150.    With MS-DOS version 2 or later, these three traditional functions are
  4151.    converted internally to handle-based writes to the standard output and
  4152.    thus are susceptible to output redirection.
  4153.  
  4154.    The sequence at the top of the following page sounds a warning beep by
  4155.    sending an ASCII bell code (07H) to the display driver using the
  4156.    traditional character-output call Int 21H Function 02H.
  4157.  
  4158.    ──────────────────────────────────────────────────────────────────────────
  4159.            .
  4160.            .
  4161.            .
  4162.            mov     dl,7        ; 07h = ASCII bell code
  4163.            mov     ah,2        ; function 02h = display character
  4164.            int     21h         ; transfer to MS-DOS
  4165.            .
  4166.            .
  4167.            .
  4168.    ──────────────────────────────────────────────────────────────────────────
  4169.  
  4170.    The following sequence uses the traditional string-output call Int 21H
  4171.    Function 09H to display a string:
  4172.  
  4173.    ──────────────────────────────────────────────────────────────────────────
  4174.    msg     db      'hello$'
  4175.            .
  4176.            .
  4177.            .
  4178.            mov     dx,seg msg  ; DS:DX = message address
  4179.            mov     ds,dx
  4180.            mov     dx,offset msg
  4181.            mov     ah,9        ; function 09h = write string
  4182.            int     21h         ; transfer to MS-DOS
  4183.            .
  4184.            .
  4185.            .
  4186.    ──────────────────────────────────────────────────────────────────────────
  4187.  
  4188.    Note that MS-DOS detects the $ character as a terminator and does not
  4189.    display it on the screen.
  4190.  
  4191.  Screen Control with MS-DOS Functions
  4192.  
  4193.    With version 2.0 or later, if MS-DOS loads the optional device driver
  4194.    ANSI.SYS in response to a DEVICE directive in the CONFIG.SYS file,
  4195.    programs can clear the screen, control the cursor position, and select
  4196.    foreground and background colors by embedding escape sequences in the text
  4197.    output. Escape sequences are so called because they begin with an escape
  4198.    character (1BH), which alerts the driver to intercept and interpret the
  4199.    subsequent characters in the sequence. When the ANSI driver is not loaded,
  4200.    MS-DOS simply passes the escape sequence to the display like any other
  4201.    text, usually resulting in a chaotic screen.
  4202.  
  4203.    The escape sequences that can be used with the ANSI driver for screen
  4204.    control are a subset of those defined in the ANSI 3.64─1979 Standard.
  4205.    These standard sequences are summarized in Figure 6-2. Note that case is
  4206.    significant for the last character in an escape sequence and that numbers
  4207.    must always be represented as ASCII digit strings, not as their binary
  4208.    values. (A separate set of escape sequences supported by ANSI.SYS, but not
  4209.    compatible with the ANSI standard, may be used for reprogramming and
  4210.    remapping the keyboard.)
  4211.  
  4212. ╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
  4213.    Escape sequence    Meaning
  4214.    ──────────────────────────────────────────────────────────────────────────
  4215.    Esc[2J             Clear screen; place cursor in upper left corner (home
  4216.                       position).
  4217.    Esc[K              Clear from cursor to end of line.
  4218.    Esc[row;colH       Position cursor. (Row is the y coordinate in the range
  4219.                       1─25 and col is the x coordinate in the range 1─80 for
  4220.                       80-by-25 text display modes.) Escape sequences
  4221.                       terminated with the letter f instead of H have the same
  4222.                       effect.
  4223.    Escape sequence    Meaning
  4224.    ──────────────────────────────────────────────────────────────────────────
  4225.                      effect.
  4226.    Esc[nA             Move cursor up n rows.
  4227.    Esc[nB             Move cursor down n rows.
  4228.    Esc[nC             Move cursor right n columns.
  4229.    Esc[nD             Move cursor left n columns.
  4230.    Esc[s              Save current cursor position.
  4231.    Esc[u              Restore cursor to saved position.
  4232.    Esc[6n             Return current cursor position on the standard input
  4233.                       handle in the format Esc[row;colR.
  4234.    Esc[nm             Select character attributes:
  4235.                        0 = no special attributes
  4236.                        1 = high intensity
  4237.                        2 = low intensity
  4238.                        3 = italic
  4239.                        4 = underline
  4240.                        5 = blink
  4241.                        6 = rapid blink
  4242.                        7 = reverse video
  4243.                        8 = concealed text (no display)
  4244.    Escape sequence    Meaning
  4245.    ──────────────────────────────────────────────────────────────────────────
  4246.                       8 = concealed text (no display)
  4247.                       30 = foreground black
  4248.                       31 = foreground red
  4249.                       32 = foreground green
  4250.                       33 = foreground yellow
  4251.                       34 = foreground blue
  4252.                       35 = foreground magenta
  4253.                       36 = foreground cyan
  4254.                       37 = foreground white
  4255.                       40 = background black
  4256.                       41 = background red
  4257.                       42 = background green
  4258.                       43 = background yellow
  4259.                       44 = background blue
  4260.                       45 = background magenta
  4261.                       46 = background cyan
  4262.                       47 = background white
  4263.    Esc[=nh            Select display mode:
  4264.                        0 = 40-by-25, 16-color text (color burst off)
  4265.    Escape sequence    Meaning
  4266.    ──────────────────────────────────────────────────────────────────────────
  4267.                       0 = 40-by-25, 16-color text (color burst off)
  4268.                        1 = 40-by-25, 16-color text
  4269.                        2 = 80-by-25, 16-color text (color burst off)
  4270.                        3 = 80-by-25, 16-color text
  4271.                        4 = 320-by-200, 4-color graphics
  4272.                        5 = 320-by-200, 4-color graphics (color burst off)
  4273.                        6 = 620-by-200, 2-color graphics
  4274.                       14 = 640-by-200, 16-color graphics (EGA and VGA,
  4275.                       MS-DOS 4.0)
  4276.                       15 = 640-by-350, 2-color graphics (EGA and VGA,
  4277.                       MS-DOS 4.0)
  4278.                       16 = 640-by-350, 16-color graphics (EGA and VGA,
  4279.                       MS-DOS 4.0)
  4280.                       17 = 640-by-480, 2-color graphics (MCGA and VGA,
  4281.                       MS-DOS 4.0)
  4282.                       18 = 640-by-480, 16-color graphics (VGA, MS-DOS 4.0)
  4283.                       19 = 320-by-200, 256-color graphics (MCGA and VGA,
  4284.                       MS-DOS 4.0)
  4285.                       Escape sequences terminated with l instead of h have
  4286.    Escape sequence    Meaning
  4287.    ──────────────────────────────────────────────────────────────────────────
  4288.                      Escape sequences terminated with l instead of h have
  4289.                       the same effect.
  4290.    Esc[=7h            Enable line wrap.
  4291.    Esc[=7l            Disable line wrap.
  4292.    ──────────────────────────────────────────────────────────────────────────
  4293.  
  4294.  
  4295.    Figure 6-2.  The ANSI escape sequences supported by the MS-DOS ANSI.SYS
  4296.    driver. Programs running under MS-DOS 2.0 or later may use these
  4297.    functions, if ANSI.SYS is loaded, to control the appearance of the display
  4298.    in a hardware-independent manner. The symbol Esc indicates an ASCII escape
  4299.    code──a character with the value 1BH. Note that cursor positions in ANSI
  4300.    escape sequences are one-based, unlike the cursor coordinates used by the
  4301.    IBM ROM BIOS, which are zero-based. Numbers embedded in an escape sequence
  4302.    must always be represented as a string of ASCII digits, not as their
  4303.    binary values.
  4304.  
  4305.  Binary Output Mode
  4306.  
  4307.    Under MS-DOS version 2 or later, you can substantially increase display
  4308.    speeds for well-behaved application programs without sacrificing hardware
  4309.    independence by selecting binary (raw) mode for the standard output. In
  4310.    binary mode, MS-DOS does not check between each character it transfers to
  4311.    the output device for a Ctrl-C waiting at the keyboard, nor does it filter
  4312.    the output string for certain characters such as Ctrl-Z.
  4313.  
  4314.    Bit 5 in the device information word associated with a device handle
  4315.    controls binary mode. Programs access the device information word by using
  4316.    Subfunctions 00H and 01H of the MS-DOS IOCTL function (I/O Control, Int
  4317.    21H Function 44H). For example, the sequence on the following page places
  4318.    the standard output handle into binary mode.
  4319.  
  4320.    ──────────────────────────────────────────────────────────────────────────
  4321.                                ; get device information...
  4322.            mov     bx,1        ; standard output handle
  4323.            mov     ax,4400h    ; function 44h subfunction 00h
  4324.            int     21h         ; transfer to MS-DOS
  4325.  
  4326.            mov     dh,0        ; set upper byte of DX = 0
  4327.            or      dl,20h      ; set binary mode bit in DL
  4328.  
  4329.                                ; write device information...
  4330.                                ; (BX still has handle)
  4331.            mov     ax,4401h    ; function 44h subfunction 01h
  4332.            int     21h         ; transfer to MS-DOS
  4333.    ──────────────────────────────────────────────────────────────────────────
  4334.  
  4335.    Note that if a program changes the mode of any of the standard handles, it
  4336.    should restore those handles to ASCII (cooked) mode before it exits.
  4337.    Otherwise, subsequent application programs may behave in unexpected ways.
  4338.    For more detailed information on the IOCTL function, see Section II of
  4339.    this book, "MS-DOS Functions Reference."
  4340.  
  4341.  
  4342.  The ROM BIOS Display Functions
  4343.  
  4344.    You can somewhat improve the display performance of programs that are
  4345.    intended for use only on IBM PC─compatible machines by using the ROM BIOS
  4346.    video driver instead of the MS-DOS output functions. Accessed by means of
  4347.    Int 10H, the ROM BIOS driver supports the following functions for all of
  4348.    the currently available IBM display adapters:
  4349.  
  4350. ╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
  4351.    Function           Action
  4352.    ──────────────────────────────────────────────────────────────────────────
  4353.    Display mode control
  4354.    00H               Set display mode.
  4355.    0FH               Get display mode.
  4356.  
  4357.    Cursor control
  4358.    01H               Set cursor size.
  4359.    02H               Set cursor position.
  4360.    03H               Get cursor position and size.
  4361.  
  4362.    Writing to the display
  4363.    09H               Write character and attribute at cursor.
  4364.    0AH               Write character-only at cursor.
  4365.    0EH               Write character in teletype mode.
  4366.  
  4367.    Reading from the display
  4368.    08H               Read character and attribute at cursor.
  4369.  
  4370.    Function           Action
  4371.    ──────────────────────────────────────────────────────────────────────────
  4372. 
  4373.    Graphics support
  4374.    0CH               Write pixel.
  4375.    0DH               Read pixel.
  4376.  
  4377.    Scroll or clear display
  4378.    06H               Scroll up or initialize window.
  4379.    07H               Scroll down or initialize window.
  4380.  
  4381.    Miscellaneous
  4382.    04H               Read light pen.
  4383.    05H               Select display page.
  4384.    0BH               Select palette/set border color.
  4385.    ──────────────────────────────────────────────────────────────────────────
  4386.  
  4387.  
  4388.    Additional ROM BIOS functions are available on the EGA, MCGA, VGA, and
  4389.    PCjr to support the enhanced features of these adapters, such as
  4390.    programmable palettes and character sets (fonts). Some of the functions
  4391.    are valid only in certain display modes.
  4392.  
  4393.    Each display mode is characterized by the number of colors it can display,
  4394.    its vertical resolution, its horizontal resolution, and whether it
  4395.    supports text or graphics memory mapping. The ROM BIOS identifies it with
  4396.    a unique number. Section III of this book, "IBM ROM BIOS and Mouse
  4397.    Functions Reference," documents all of the ROM BIOS Int 10H functions and
  4398.    display modes.
  4399.  
  4400.    As you can see from the preceding list, the ROM BIOS offers several
  4401.    desirable capabilities that are not available from MS-DOS, including
  4402.    initialization or scrolling of selected screen windows, modification of
  4403.    the cursor shape, and reading back the character being displayed at an
  4404.    arbitrary screen location. These functions can be used to isolate your
  4405.    program from the hardware on any IBM PC─compatible adapter. However, the
  4406.    ROM BIOS functions do not suffice for the needs of a high-performance,
  4407.    interactive, full-screen program such as a word processor. They do not
  4408.    support the rapid display of character strings at an arbitrary screen
  4409.    position, and they do not implement graphics operations at the level
  4410.    normally required by applications (for example, bit-block transfers and
  4411.    rapid drawing of lines, circles, and filled polygons). And, of course,
  4412.    they are of no use whatsoever in non-IBM display modes such as the
  4413.    monochrome graphics mode of the Hercules Graphics Card.
  4414.  
  4415.    Let's look at a simple example of a call to the ROM BIOS video driver. The
  4416.    following sequence writes the string hello to the screen:
  4417.  
  4418.    ──────────────────────────────────────────────────────────────────────────
  4419.    msg     db      'hello'
  4420.    msg_len equ     $-msg
  4421.            .
  4422.            .
  4423.            .
  4424.            mov     si,seg msg  ; DS:SI = message address
  4425.            mov     ds,si
  4426.            mov     si,offset msg
  4427.            mov     cx,msg_len  ; CX = message length
  4428.            cld
  4429.    next:   lodsb               ; get AL = next character
  4430.            push    si          ; save message pointer
  4431.            mov     ah,0eh      ; int 10h function 0eh = write
  4432.                                ; character in teletype mode
  4433.            mov     bh,0        ; assume video page 0
  4434.            mov     bl,color    ; (use in graphics modes only)
  4435.            int     10h         ; transfer to ROM BIOS
  4436.            pop     si          ; restore message pointer
  4437.            loop    next        ; loop until message done
  4438.            .
  4439.            .
  4440.            .
  4441.    ──────────────────────────────────────────────────────────────────────────
  4442.  
  4443.    (Note that the SI and DI registers are not necessarily preserved across a
  4444.    call to a ROM BIOS video function.)
  4445.  
  4446.  
  4447.  Memory-mapped Display Techniques
  4448.  
  4449.    Display performance is best when an application program takes over
  4450.    complete control of the video adapter and the refresh buffer. Because the
  4451.    display is memory-mapped, the speed at which characters can be put on the
  4452.    screen is limited only by the CPU's ability to copy bytes from one
  4453.    location in memory to another. The trade-off for this performance is that
  4454.    such programs are highly sensitive to hardware compatibility and do not
  4455.    always function properly on "clones" or even on new models of IBM video
  4456.    adapters.
  4457.  
  4458.  Text Mode
  4459.  
  4460.    Direct programming of the IBM PC─compatible video adapters in their text
  4461.    display modes (sometimes also called alphanumeric display modes) is
  4462.    straightforward. The character set is the same for all, and the cursor
  4463.    home position──(x,y) = (0,0)──is defined to be the upper left corner of
  4464.    the screen (Figure 6-3). The MDA uses 4 KB of memory starting at segment
  4465.    B000H as a regen buffer, and the various adapters with both text and
  4466.    graphics capabilities (CGA, EGA, MCGA, and VGA) use 16 KB of memory
  4467.    starting at segment B800H. (See Figure 6-1.) In the latter case, the 16
  4468.    KB is divided into "pages" that can be independently updated and
  4469.    displayed.
  4470.  
  4471.     (0,0)┌─────────────────────────────────┐(79,0)
  4472.          │                                 │
  4473.          │                                 │
  4474.          │                                 │
  4475.          │                                 │
  4476.          │                                 │
  4477.          │                                 │
  4478.          │                                 │
  4479.    (0,24)└─────────────────────────────────┘(79,24)
  4480.  
  4481.    Figure 6-3.  Cursor addressing for 80-by-25 text display modes (IBM ROM
  4482.    BIOS modes 2, 3, and 7).
  4483.  
  4484.    Each character-display position is allotted 2 bytes in the regen buffer.
  4485.    The first byte (even address) contains the ASCII code of the character,
  4486.    which is translated by a special hardware character generator into a
  4487.    dot-matrix pattern for the screen. The second byte (odd address) is the
  4488.    attribute byte. Several bit fields in this byte control such features as
  4489.    blinking, intensity (highlighting), and reverse video, depending on the
  4490.    adapter type and display mode (Figures 6-4 and 6-5). Figure 6-6 shows a
  4491.    hex and ASCII dump of part of the video map for the MDA.
  4492.  
  4493.    Display                  Background              Foreground
  4494.    ──────────────────────────────────────────────────────────────────────────
  4495.    No display (black)       000                     000
  4496.    No display (white)      111                     111
  4497.    Underline                000                     001
  4498.    Normal video             000                     111
  4499.    Reverse video            111                     000
  4500.    ──────────────────────────────────────────────────────────────────────────
  4501.  
  4502.    Figure 6-4.  Attribute byte for 80-by-25 monochrome text display mode on
  4503.    the MDA, Hercules cards, EGA, and VGA (IBM ROM BIOS mode 7).
  4504.  
  4505.    Value              Color
  4506.    ──────────────────────────────────────────────────────────────────────────
  4507.     0                 Black
  4508.     1                 Blue
  4509.     2                 Green
  4510.     3                 Cyan
  4511.     4                 Red
  4512.     5                 Magenta
  4513.     6                 Brown
  4514.     7                 White
  4515.     8                 Gray
  4516.     9                 Light blue
  4517.    10                 Light green
  4518.    11                 Light cyan
  4519.    12                 Light red
  4520.    13                 Light magenta
  4521.    14                 Yellow
  4522.    15                 Intense white
  4523.    ──────────────────────────────────────────────────────────────────────────
  4524.  
  4525.    Figure 6-5.  Attribute byte for the 40-by-25 and 80-by-25 text display
  4526.    modes on the CGA, EGA, MCGA, and VGA (IBM ROM BIOS modes 0─3). The table
  4527.    of color values assumes default palette programming and that the B or I
  4528.    bit controls intensity.
  4529.  
  4530.    ──────────────────────────────────────────────────────────────────────────
  4531.    B000:0000 3e 07 73 07 65 07 6c 07 65 07 63 07 74 07 20 07
  4532.    B000:0010 74 07 65 07 6d 07 70 07 20 07 20 07 20 07 20 07
  4533.    B000:0020 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
  4534.    B000:0030 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
  4535.    B000:0040 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
  4536.    B000:0050 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
  4537.    B000:0060 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
  4538.    B000:0070 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
  4539.    B000:0080 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
  4540.    B000:0090 20 07 20 07 20 07 20 07 20 07 20 07 20 07 20 07
  4541.    ──────────────────────────────────────────────────────────────────────────
  4542.  
  4543.    Figure 6-6.  Example dump of the first 160 bytes of the MDA's regen
  4544.    buffer. These bytes correspond to the first visible line on the screen.
  4545.    Note that ASCII character codes are stored in even bytes and their
  4546.    respective character attributes in odd bytes; all the characters in this
  4547.    example line have the attribute normal video.
  4548.  
  4549.    You can calculate the memory offset of any character on the display as the
  4550.    line number (y coordinate) times 80 characters per line times 2 bytes per
  4551.    character, plus the column number (x coordinate) times 2 bytes per
  4552.    character, plus (for the text/graphics adapters) the page number times the
  4553.    size of the page (4 KB per page in 80-by-25 modes; 2 KB per page in
  4554.    40-by-25 modes). In short, the formula for the offset of the
  4555.    character-attribute pair for a given screen position (x,y) in 80-by-25
  4556.    text modes is
  4557.  
  4558.      offset = ((y * 50H + x) * 2) + (page * 1000H)
  4559.  
  4560.    In 40-by-25 text modes, the formula is
  4561.  
  4562.      offset = ((y * 50H + x) * 2) + (page * 0800H)
  4563.  
  4564.    Of course, the segment register being used to address the video buffer
  4565.    must be set appropriately, depending on the type of display adapter.
  4566.  
  4567.    As a simple example, assume that the character to be displayed is in the
  4568.    AL register, the desired attribute byte for the character is in the AH
  4569.    register, the x coordinate (column) is in the BX register, and the y
  4570.    coordinate (row) is in the CX register. The following code stores the
  4571.    character and attribute byte into the MDA's video refresh buffer at the
  4572.    proper location:
  4573.  
  4574.    ──────────────────────────────────────────────────────────────────────────
  4575.            push    ax          ; save char and attribute
  4576.            mov     ax,160
  4577.            mul     cx          ; DX:AX = Y * 160
  4578.            shl     bx,1        ; multiply X by 2
  4579.            add     bx,ax       ; BX = (Y*160) + (X*2)
  4580.            mov     ax,0b000h   ; ES = segment of monochrome
  4581.            mov     es,ax       ; adapter refresh buffer
  4582.            pop     ax          ; restore char and attribute
  4583.            mov     es:[bx],ax  ; write them to video buffer
  4584.    ──────────────────────────────────────────────────────────────────────────
  4585.  
  4586.    More frequently, we wish to move entire strings into the refresh buffer,
  4587.    starting at a given coordinate. In the next example, assume that the DS:SI
  4588.    registers point to the source string, the ES:DI registers point to the
  4589.    starting position in the video buffer (calculated as shown in the previous
  4590.    example), the AH register contains the attribute byte to be assigned to
  4591.    every character in the string, and the CX register contains the length of
  4592.    the string. The following code moves the entire string into the refresh
  4593.    buffer:
  4594.  
  4595.    ──────────────────────────────────────────────────────────────────────────
  4596.    xfer:   lodsb               ; fetch next character
  4597.            stosw               ; store char + attribute
  4598.            loop    xfer        ; until all chars moved
  4599.    ──────────────────────────────────────────────────────────────────────────
  4600.  
  4601.    Of course, the video drivers written for actual application programs must
  4602.    take into account many additional factors, such as checking for special
  4603.    control codes (linefeeds, carriage returns, tabs), line wrap, and
  4604.    scrolling.
  4605.  
  4606.    Programs that write characters directly to the CGA regen buffer in text
  4607.    modes must deal with an additional complicating factor──they must examine
  4608.    the video controller's status port and access the refresh buffer only
  4609.    during the horizontal retrace or vertical retrace intervals. (A retrace
  4610.    interval is the period when the electron beam that illuminates the screen
  4611.    phosphors is being repositioned to the start of a new scan line.)
  4612.    Otherwise, the contention for memory between the CPU and the video
  4613.    controller is manifest as unsightly "snow" on the display. (If you are
  4614.    writing programs for any of the other IBM PC─compatible video adapters,
  4615.    such as the MDA, EGA, MCGA, or VGA, you can ignore the retrace intervals;
  4616.    snow is not a problem with these video controllers.)
  4617.  
  4618.    A program can detect the occurrence of a retrace interval by monitoring
  4619.    certain bits in the video controller's status register. For example,
  4620.    assume that the offset for the desired character position has been
  4621.    calculated as in the preceding example and placed in the BX register, the
  4622.    segment for the CGA's refresh buffer is in the ES register, and an ASCII
  4623.    character code to be displayed is in the CL register. The following code
  4624.    waits for the beginning of a new horizontal retrace interval and then
  4625.    writes the character into the buffer:
  4626.  
  4627.    ──────────────────────────────────────────────────────────────────────────
  4628.            mov     dx,03dah    ; DX = video controller's
  4629.                                ; status port address
  4630.            cli                 ; disable interrupts
  4631.  
  4632.                                ; if retrace is already
  4633.                                ; in progress, wait for
  4634.                                ; it to end...
  4635.    wait1:  in      al,dx       ; read status port
  4636.            and     al,1        ; check if retrace bit on
  4637.            jnz     wait1       ; yes, wait
  4638.  
  4639.                                ; wait for new retrace
  4640.                                ; interval to start...
  4641.    wait2:  in      al,dx       ; read status port
  4642.            and     al,1        ; retrace bit on yet?
  4643.            jz      wait2       ; jump if not yet on
  4644.  
  4645.            mov     es:[bx],cl  ; write character to
  4646.                                ; the regen buffer
  4647.            sti                 ; enable interrupts again
  4648.    ──────────────────────────────────────────────────────────────────────────
  4649.  
  4650.    The first wait loop "synchronizes" the code to the beginning of a
  4651.    horizontal retrace interval. If only the second wait loop were used (that
  4652.    is, if a character were written when a retrace interval was already in
  4653.    progress), the write would occasionally begin so close to the end of a
  4654.    horizontal retrace "window" that it would partially miss the retrace,
  4655.    resulting in scattered snow at the left edge of the display. Notice that
  4656.    the code also disables interrupts during accesses to the video buffer, so
  4657.    that service of a hardware interrupt won't disrupt the synchronization
  4658.    process.
  4659.  
  4660.    Because of the retrace-interval constraints just outlined, the rate at
  4661.    which you can update the CGA in text modes is severely limited when the
  4662.    updating is done one character at a time. You can obtain better results by
  4663.    calculating all the relevant addresses and setting up the appropriate
  4664.    registers, disabling the video controller by writing to register 3D8H,
  4665.    moving the entire string to the buffer with a REP MOVSW operation, and
  4666.    then reenabling the video controller. If the string is of reasonable
  4667.    length, the user won't even notice a flicker in the display. Of course,
  4668.    this procedure introduces additional hardware dependence into your code
  4669.    because it requires much greater knowledge of the 6845 controller.
  4670.    Luckily, snow is not a problem in CGA graphics modes.
  4671.  
  4672.  Graphics Mode
  4673.  
  4674.    Graphics-mode memory-mapped programming for IBM PC─compatible adapters is
  4675.    considerably more complicated than text-mode programming. Each bit or
  4676.    group of bits in the regen buffer corresponds to an addressable point, or
  4677.    pixel, on the screen. The mapping of bits to pixels differs for each of
  4678.    the available graphics modes, with their differences in resolution and
  4679.    number of supported colors. The newer adapters (EGA, MCGA, and VGA) also
  4680.    use the concept of bit planes, where bits of a pixel are segregated into
  4681.    multiple banks of memory mapped at the same address; you must manipulate
  4682.    these bit planes by a combination of memory-mapped I/O and port
  4683.    addressing.
  4684.  
  4685.    IBM-video-systems graphics programming is a subject large enough for a
  4686.    book of its own, but we can use the 640-by-200, 2-color graphics display
  4687.    mode of the CGA (which is also supported by all subsequent IBM
  4688.    text/graphics adapters) to illustrate a few of the techniques involved.
  4689.    This mode is simple to deal with because each pixel is represented by a
  4690.    single bit. The pixels are assigned (x,y) coordinates in the range (0,0)
  4691.    through (639,199), where x is the horizontal displacement, y is the
  4692.    vertical displacement, and the home position (0,0) is the upper left
  4693.    corner of the display. (See Figure 6-7.)
  4694.  
  4695.      (0,0)┌─────────────────────────────────┐(639,0)
  4696.           │                                 │
  4697.           │                                 │
  4698.           │                                 │
  4699.           │                                 │
  4700.           │                                 │
  4701.           │                                 │
  4702.           │                                 │
  4703.    (0,199)└─────────────────────────────────┘(639,199)
  4704.  
  4705.    Figure 6-7.  Point addressing for 640-by-200, 2-color graphics modes on
  4706.    the CGA, EGA, MCGA, and VGA (IBM ROM BIOS mode 6).
  4707.  
  4708.    Each successive group of 80 bytes (640 bits) represents one horizontal
  4709.    scan line. Within each byte, the bits map one-for-one onto pixels, with
  4710.    the most significant bit corresponding to the leftmost displayed pixel of
  4711.    a set of eight pixels and the least significant bit corresponding to the
  4712.    rightmost displayed pixel of the set. The memory map is set up so that all
  4713.    the even y coordinates are scanned as a set and all the odd y coordinates
  4714.    are scanned as a set; this mapping is referred to as the memory interlace.
  4715.  
  4716.    To find the regen buffer offset for a particular (x,y) coordinate, you
  4717.    would use the following formula:
  4718.  
  4719.      offset = ((y AND 1) * 2000H) + (y/2 * 50H) + (x/8)
  4720.  
  4721.    The assembly-language implementation of this formula is as follows:
  4722.  
  4723.    ──────────────────────────────────────────────────────────────────────────
  4724.                                ; assume AX = Y, BX = X
  4725.            shr     bx,1        ; divide X by 8
  4726.            shr     bx,1
  4727.            shr     bx,1
  4728.            push    ax          ; save copy of Y
  4729.            shr     ax,1        ; find (Y/2) * 50h
  4730.            mov     cx,50h      ; with product in DX:AX
  4731.            mul     cx
  4732.            add     bx,ax       ; add product to X/8
  4733.            pop     ax          ; add (Y AND 1) * 2000h
  4734.            and     ax,1
  4735.            jz      label1
  4736.            add     bx,2000h
  4737.    label1:                     ; now BX = offset into
  4738.                                ; video buffer
  4739.    ──────────────────────────────────────────────────────────────────────────
  4740.  
  4741.    After calculating the correct byte address, you can use the following
  4742.    formula to calculate the bit position for a given pixel coordinate:
  4743.  
  4744.      bit = 7 - (x MOD 8)
  4745.  
  4746.    where bit 7 is the most significant bit and bit 0 is the least significant
  4747.    bit. It is easiest to build an 8-byte table, or array of bit masks, and
  4748.    use the operation X AND 7 to extract the appropriate entry from the table:
  4749.  
  4750.    (X AND 7)          Bit mask          (X AND 7)          Bit mask
  4751.    ──────────────────────────────────────────────────────────────────────────
  4752.    0                  80H               4                  08H
  4753.    1                  40H               5                  04H
  4754.    2                  20H               6                  02H
  4755.    3                  10H               7                  01H
  4756.    ──────────────────────────────────────────────────────────────────────────
  4757.  
  4758.    The assembly-language implementation of this second calculation is as
  4759.    follows:
  4760.  
  4761.    ──────────────────────────────────────────────────────────────────────────
  4762.    table   db      80h         ; X AND 7 = offset 0
  4763.            db      40h         ; X AND 7 = offset 1
  4764.            db      20h         ; X AND 7 = offset 2
  4765.            db      10h         ; X AND 7 = offset 3
  4766.            db      08h         ; X AND 7 = offset 4
  4767.            db      04h         ; X AND 7 = offset 5
  4768.            db      02h         ; X AND 7 = offset 6
  4769.            db      01h         ; X AND 7 = offset 7
  4770.            .
  4771.            .
  4772.            .
  4773.                                ; assume BX = X coordinate
  4774.            and     bx,7        ; isolate 0─7 offset
  4775.            mov     al,[bx+table]
  4776.                                ; now AL = mask from table
  4777.            .
  4778.            .
  4779.            .
  4780.    ──────────────────────────────────────────────────────────────────────────
  4781.  
  4782.    The program can then use the mask, together with the byte offset
  4783.    previously calculated, to set or clear the appropriate bit in the video
  4784.    controller's regen buffer.
  4785.  
  4786.  
  4787.  
  4788.  ────────────────────────────────────────────────────────────────────────────
  4789.  Chapter 7  Printer and Serial Port
  4790.  
  4791.    MS-DOS supports printers, plotters, modems, and other hard-copy output or
  4792.    communication devices with device drivers for parallel ports and serial
  4793.    ports. Parallel ports are so named because they transfer a byte──8 bits──
  4794.    in parallel to the destination device over eight separate physical paths
  4795.    (plus additional status and handshaking signals). The serial port, on the
  4796.    other hand, communicates with the CPU with bytes but sends data to or
  4797.    receives data from its destination device serially──a bit at a time──over
  4798.    a single physical connection.
  4799.  
  4800.    Parallel ports are typically used for high-speed output devices, such as
  4801.    line printers, over relatively short distances (less than 50 feet). They
  4802.    are rarely used for devices that require two-way communication with the
  4803.    computer. Serial ports are used for lower-speed devices, such as modems
  4804.    and terminals, that require two-way communication (although some printers
  4805.    also have serial interfaces). A serial port can drive its device reliably
  4806.    over much greater distances (up to 1000 feet) over as few as three wires──
  4807.    transmit, receive, and ground.
  4808.  
  4809.    The most commonly used type of serial interface follows a standard called
  4810.    RS-232. This standard specifies a 25-wire interface with certain
  4811.    electrical characteristics, the use of various handshaking signals, and a
  4812.    standard DB-25 connector. Other serial-interface standards exist──for
  4813.    example, the RS-422, which is capable of considerably higher speeds than
  4814.    the RS-232── but these are rarely used in personal computers (except for
  4815.    the Apple Macintosh) at this time.
  4816.  
  4817.    MS-DOS has built-in device drivers for three parallel adapters, and for
  4818.    two serial adapters on the PC or PC/AT and three serial adapters on the
  4819.    PS/2. The logical names for these devices are LPT1, LPT2, LPT3, COM1,
  4820.    COM2, and COM3. The standard printer (PRN) and standard auxiliary (AUX)
  4821.    devices are normally aliased to LPT1 and COM1, but you can redirect PRN to
  4822.    one of the serial ports with the MS-DOS MODE command.
  4823.  
  4824.    As with keyboard and video display I/O, you can manage printer and
  4825.    serial-port I/O at several levels that offer different degrees of
  4826.    flexibility and hardware independence:
  4827.  
  4828.    ■  MS-DOS handle-oriented functions
  4829.  
  4830.    ■  MS-DOS traditional character functions
  4831.  
  4832.    ■  IBM ROM BIOS driver functions
  4833.  
  4834.    In the case of the serial port, direct control of the hardware by
  4835.    application programs is also common. I will discuss each of these I/O
  4836.    methods briefly, with examples, in the following pages.
  4837.  
  4838.  
  4839.  Printer Output
  4840.  
  4841.    The preferred method of printer output is to use the handle write function
  4842.    (Int 21H Function 40H) with the predefined standard printer handle (4).
  4843.    For example, you could write the string hello to the printer as follows:
  4844.  
  4845.    ──────────────────────────────────────────────────────────────────────────
  4846.    msg     db      'hello'     ; message for printer
  4847.    msg_len equ     $-msg       ; length of message
  4848.            .
  4849.            .
  4850.            .
  4851.            mov     ah,40h      ; function 40h = write file or device
  4852.            mov     bx,4        ; BX = standard printer handle
  4853.            mov     cx,msg_len  ; CX = length of string
  4854.            mov     dx,seg msg  ; DS:DX = string address
  4855.            mov     ds,dx
  4856.            mov     dx,offset msg
  4857.            int     21h         ; transfer to MS-DOS
  4858.            jc      error       ; jump if error
  4859.            .
  4860.            .
  4861.            .
  4862.    ──────────────────────────────────────────────────────────────────────────
  4863.  
  4864.    If there is no error, the function returns the carry flag cleared and the
  4865.    number of characters actually transferred to the list device in register
  4866.    AX. Under normal circumstances, this number should always be the same as
  4867.    the length requested and the carry flag indicating an error should never
  4868.    be set. However, the output will terminate early if your data contains an
  4869.    end-of-file mark (Ctrl-Z).
  4870.  
  4871.    You can write independently to several list devices (for example, LPT1,
  4872.    LPT2) by issuing a specific open request (Int 21H Function 3DH) for each
  4873.    device and using the handles returned to access the printers individually
  4874.    with Int 21H Function 40H. You have already seen this general approach in
  4875.    Chapters 5 and 6.
  4876.  
  4877.    An alternative method of printer output is to use the traditional Int 21H
  4878.    Function 05H, which transfers the character in the DL register to the
  4879.    printer. (This function is sensitive to Ctrl-C interrupts.) For example,
  4880.    the assembly-language code sequence at the top of the following page would
  4881.    write the the string hello to the line printer.
  4882.  
  4883.    ──────────────────────────────────────────────────────────────────────────
  4884.    msg     db      'hello'     ; message for printer
  4885.    msg_len equ     $-msg       ; length of message
  4886.            .
  4887.            .
  4888.            .
  4889.            mov     bx,seg msg  ; DS:BX = string address
  4890.            mov     ds,bx
  4891.            mov     bx,offset msg
  4892.            mov     cx,msg_len  ; CX = string length
  4893.  
  4894.    next:   mov     dl,[bx]     ; get next character
  4895.            mov     ah,5        ; function 05h = printer output
  4896.            int     21h         ; transfer to MS-DOS
  4897.            inc     bx          ; bump string pointer
  4898.            loop    next        ; loop until string done
  4899.            .
  4900.            .
  4901.            .
  4902.    ──────────────────────────────────────────────────────────────────────────
  4903.  
  4904.    Programs that run on IBM PC─compatible machines can obtain improved
  4905.    printer throughput by bypassing MS-DOS and calling the ROM BIOS printer
  4906.    driver directly by means of Int 17H. Section III of this book, "IBM ROM
  4907.    BIOS and Mouse Functions Reference," documents the Int 17H functions in
  4908.    detail. Use of the ROM BIOS functions also allows your program to test
  4909.    whether the printer is off line or out of paper, a capability that MS-DOS
  4910.    does not offer.
  4911.  
  4912.    For example, the following sequence of instructions calls the ROM BIOS
  4913.    printer driver to send the string hello to the line printer:
  4914.  
  4915.    ──────────────────────────────────────────────────────────────────────────
  4916.    msg     db      'hello'     ; message for printer
  4917.    msg_len equ     $-msg       ; length of message
  4918.            .
  4919.            .
  4920.            .
  4921.            mov     bx,seg msg  ; DS:BX = string address
  4922.            mov     ds,bx
  4923.            mov     bx,offset msg
  4924.            mov     cx,msg_len  ; CX = string length
  4925.            mov     dx,0        ; DX = printer number
  4926.  
  4927.    next:   mov     al,[bx]     ; AL = character to print
  4928.            mov     ah,0        ; function 00h = printer output
  4929.            int     17h         ; transfer to ROM BIOS
  4930.            inc     bx          ; bump string pointer
  4931.            loop    next        ; loop until string done
  4932.            .
  4933.            .
  4934.            .
  4935.    ──────────────────────────────────────────────────────────────────────────
  4936.  
  4937.    Note that the printer numbers used by the ROM BIOS are zero-based, whereas
  4938.    the printer numbers in MS-DOS logical-device names are one-based. For
  4939.    example, ROM BIOS printer 0 corresponds to LPT1.
  4940.  
  4941.    Finally, the most hardware-dependent technique of printer output is to
  4942.    access the printer controller directly. Considering the functionality
  4943.    already provided in MS-DOS and the IBM ROM BIOS, as well as the speeds of
  4944.    the devices involved, I cannot see any justification for using direct
  4945.    hardware control in this case. The disadvantage of introducing such
  4946.    extreme hardware dependence for such a low-speed device would far outweigh
  4947.    any small performance gains that might be obtained.
  4948.  
  4949.  
  4950.  The Serial Port
  4951.  
  4952.    MS-DOS support for serial ports (often referred to as the auxiliary device
  4953.    in MS-DOS manuals) is weak compared with its keyboard, video-display, and
  4954.    printer support. This is one area where the application programmer is
  4955.    justified in making programs hardware dependent to extract adequate
  4956.    performance.
  4957.  
  4958.    Programs that restrict themselves to MS-DOS functions to ensure
  4959.    portability can use the handle read and write functions (Int 21H Functions
  4960.    3FH and 40H), with the predefined standard auxiliary handle (3) to
  4961.    access the serial port. For example, the following code writes the string
  4962.    hello to the serial port that is currently defined as the AUX device:
  4963.  
  4964.    ──────────────────────────────────────────────────────────────────────────
  4965.    msg     db      'hello'     ; message for serial port
  4966.    msg_len equ     $-msg       ; length of message
  4967.            .
  4968.            .
  4969.            .
  4970.            mov     ah,40h      ; function 40h = write file or device
  4971.            mov     bx,3        ; BX = standard aux handle
  4972.            mov     cx,msg_len  ; CX = string length
  4973.            mov     dx,seg msg  ; DS:DX = string address
  4974.            mov     ds,dx
  4975.            mov     dx,offset msg
  4976.            int     21h         ; transfer to MS-DOS
  4977.            jc      error       ; jump if error
  4978.            .
  4979.            .
  4980.            .
  4981.    ──────────────────────────────────────────────────────────────────────────
  4982.  
  4983.    The standard auxiliary handle gives access to only the first serial port
  4984.    (COM1). If you want to read or write COM2 and COM3 using the handle calls,
  4985.    you must issue an open request (Int 21H Function 3DH) for the desired
  4986.    serial port and use the handle returned by that function with Int 21H
  4987.    Functions 3FH and 40H.
  4988.  
  4989.    Some versions of MS-DOS have a bug in character-device handling that
  4990.    manifests itself as follows: If you issue a read request with Int 21H
  4991.    Function 3FH for the exact number of characters that are waiting in the
  4992.    driver's buffer, the length returned in the AX register is the number of
  4993.    characters transferred minus one. You can circumvent this problem by
  4994.    always requesting more characters than you expect to receive or by placing
  4995.    the device handle into binary mode using Int 21H Function 44H.
  4996.  
  4997.    MS-DOS also supports two traditional functions for serial-port I/O. Int
  4998.    21H Function 03H inputs a character from COM1 and returns it in the AL
  4999.    register; Int 21H Function 04H transmits the character in the DL register
  5000.    to COM1. Like the other traditional calls, these two are direct
  5001.    descendants of the CP/M auxiliary-device functions.
  5002.  
  5003.    For example, the following code sends the string hello to COM1 using the
  5004.    traditional Int 21H Function 04H:
  5005.  
  5006.    ──────────────────────────────────────────────────────────────────────────
  5007.    msg     db      'hello'     ; message for serial port
  5008.    msg_len equ     $-msg       ; length of message
  5009.            .
  5010.            .
  5011.            .
  5012.            mov     bx,seg msg  ; DS:BX = string address
  5013.            mov     ds,bx
  5014.            mov     bx,offset msg
  5015.            mov     cx,msg_len  ; CX = length of string
  5016.      mov     dl,[bx]     ; get next character
  5017.            mov     ah,4        ; function 04h = aux output
  5018.            int     21h         ; transfer to MS-DOS
  5019.            inc     bx          ; bump pointer to string
  5020.            loop    next        ; loop until string done
  5021.            .
  5022.            .
  5023.            .
  5024.    ──────────────────────────────────────────────────────────────────────────
  5025.  
  5026.    MS-DOS translates the traditional auxiliary-device functions into calls on
  5027.    the same device driver used by the handle calls. Therefore, it is
  5028.    generally preferable to use the handle functions in the first place,
  5029.    because they allow very long strings to be read or written in one
  5030.    operation, they give access to serial ports other than COM1, and they are
  5031.    symmetrical with the handle video-display, keyboard, printer, and file I/O
  5032.    methods described elsewhere in this book.
  5033.  
  5034.    Although the handle or traditional serial-port functions allow you to
  5035.    write programs that are portable to any machine running MS-DOS, they have
  5036.    a number of disadvantages:
  5037.  
  5038.    ■  The built-in MS-DOS serial-port driver is slow and is not interrupt
  5039.       driven.
  5040.  
  5041.    ■  MS-DOS serial-port I/O is not buffered.
  5042.  
  5043.    ■  Determining the status of the auxiliary device requires a separate call
  5044.       to the IOCTL function (Int 21H Function 44H)──if you request input and
  5045.       no characters are ready, your program will simply hang.
  5046.  
  5047.    ■  MS-DOS offers no standardized function to configure the serial port
  5048.       from within a program.
  5049.  
  5050.    For programs that are going to run on the IBM PC or compatibles, a more
  5051.    flexible technique for serial-port I/O is to call the IBM ROM BIOS
  5052.    serial-port driver by means of Int 14H. You can use this driver to
  5053.    initialize the serial port to a desired configuration and baud rate,
  5054.    examine the status of the controller, and read or write characters.
  5055.    Section III of this book, "IBM ROM BIOS and Mouse Functions Reference,"
  5056.    documents the functions available from the ROM BIOS serial-port driver.
  5057.  
  5058.    For example, the following sequence sends the character X to the first
  5059.    serial port (COM1):
  5060.  
  5061.    ──────────────────────────────────────────────────────────────────────────
  5062.            .
  5063.            .
  5064.            .
  5065.            mov     ah,1        ; function 01h = send character
  5066.            mov     al,'X'      ; AL = character to transmit
  5067.            mov     dx,0        ; DX = serial-port number
  5068.            int     14h         ; transfer to ROM BIOS
  5069.            and     ah,80h      ; did transmit fail?
  5070.            jnz     error       ; jump if transmit error
  5071.            .
  5072.            .
  5073.            .
  5074.    ──────────────────────────────────────────────────────────────────────────
  5075.  
  5076.    As with the ROM BIOS printer driver, the serial-port numbers used by the
  5077.    ROM BIOS are zero-based, whereas the serial-port numbers in MS-DOS
  5078.    logical-device names are one-based. In this example, serial port 0
  5079.    corresponds to COM1.
  5080.  
  5081.    Unfortunately, like the MS-DOS auxiliary-device driver, the ROM BIOS
  5082.    serial-port driver is not interrupt driven. Although it will support
  5083.    higher transfer speeds than the MS-DOS functions, at rates greater than
  5084.    2400 baud it may still lose characters. Consequently, most programmers
  5085.    writing high-performance applications that use a serial port (such as
  5086.    telecommunications programs) take complete control of the serial-port
  5087.    controller and provide their own interrupt driver. The built-in functions
  5088.    provided by MS-DOS, and by the ROM BIOS in the case of the IBM PC, are
  5089.    simply not adequate.
  5090.  
  5091.    Writing such programs requires a good understanding of the hardware. In
  5092.    the case of the IBM PC, the chips to study are the INS8250 Asynchronous
  5093.    Communications Controller and the Intel 8259A Programmable Interrupt
  5094.    Controller. The IBM technical reference documentation for these chips is a
  5095.    bit disorganized, but most of the necessary information is there if you
  5096.    look for it.
  5097.  
  5098.  
  5099.  The TALK Program
  5100.  
  5101.    The simple terminal-emulator program TALK.ASM (Figure 7-1) is an example
  5102.    of a useful program that performs screen, keyboard, and serial-port I/O.
  5103.    This program recapitulates all of the topics discussed in Chapters 5
  5104.    through 7. TALK uses the IBM PC's ROM BIOS video driver to put characters
  5105.    on the screen, to clear the display, and to position the cursor; it uses
  5106.    the MS-DOS character-input calls to read the keyboard; and it contains its
  5107.    own interrupt driver for the serial-port controller.
  5108.  
  5109.    ──────────────────────────────────────────────────────────────────────────
  5110.            name      talk
  5111.            page      55,132
  5112.            .lfcond             ; List false conditionals too
  5113.            title     TALK--Simple terminal emulator
  5114.  
  5115.    ;
  5116.    ; TALK.ASM--Simple IBM PC terminal emulator
  5117.    ;
  5118.    ; Copyright (c) 1988 Ray Duncan
  5119.    ;
  5120.    ; To assemble and link this program into TALK.EXE:
  5121.    ;
  5122.    ;       C>MASM TALK;
  5123.    ;       C>LINK TALK;
  5124.    ;
  5125.  
  5126.    stdin   equ     0               ; standard input handle
  5127.    stdout  equ     1               ; standard output handle
  5128.    stderr  equ     2               ; standard error handle
  5129.  
  5130.    cr      equ     0dh             ; ASCII carriage return
  5131.    lf      equ     0ah             ; ASCII linefeed
  5132.    bsp     equ     08h             ; ASCII backspace
  5133.    escape  equ     1bh             ; ASCII escape code
  5134.  
  5135.    dattr   equ     07h             ; display attribute to use
  5136.                                    ; while in emulation mode
  5137.  
  5138.    bufsiz  equ     4096            ; size of serial-port buffer
  5139.  
  5140.    echo    equ     0               ; 0 = full-duplex, -1 = half-duplex
  5141.       equ     -1
  5142.    false   equ     0
  5143.  
  5144.    com1    equ     true            ; use COM1 if nonzero
  5145.    com2    equ     not com1        ; use COM2 if nonzero
  5146.  
  5147.    pic_mask  equ   21h             ; 8259 interrupt mask port
  5148.    pic_eoi   equ   20h             ; 8259 EOI port
  5149.  
  5150.            if      com1
  5151.    com_data equ    03f8h           ; port assignments for COM1
  5152.    com_ier  equ    03f9h
  5153.    com_mcr  equ    03fch
  5154.    com_sts  equ    03fdh
  5155.    com_int  equ    0ch             ; COM1 interrupt number
  5156.    int_mask equ    10h             ; IRQ4 mask for 8259
  5157.            endif
  5158.  
  5159.            if      com2
  5160.    com_data equ    02f8h           ; port assignments for COM2
  5161.    com_ier  equ    02f9h
  5162.    com_mcr  equ    02fch
  5163.    com_sts  equ    02fdh
  5164.    com_int  equ    0bh             ; COM2 interrupt number
  5165.    int_mask equ    08h             ; IRQ3 mask for 8259
  5166.            endif
  5167.  
  5168.    _TEXT   segment word public 'CODE'
  5169.  
  5170.            assume  cs:_TEXT,ds:_DATA,es:_DATA,ss:STACK
  5171.  
  5172.    talk    proc    far             ; entry point from MS-DOS
  5173.  
  5174.            mov     ax,_DATA        ; make data segment addressable
  5175.            mov     ds,ax
  5176.            mov     es,ax
  5177.                                    ; initialize display for
  5178.                                    ; terminal emulator mode...
  5179.  
  5180.            mov     ah,15           ; get display width and
  5181.            int     10h             ; current display mode
  5182.            dec     ah              ; save display width for use
  5183.            mov     columns,ah      ; by the screen-clear routine
  5184.  
  5185.            cmp     al,7            ; enforce text display mode
  5186.            je      talk2           ; mode 7 ok, proceed
  5187.           cmp     al,3
  5188.            jbe     talk2           ; modes 0-3 ok, proceed
  5189.  
  5190.            mov     dx,offset msg1
  5191.            mov     cx,msg1_len
  5192.            jmp     talk6           ; print error message and exit
  5193.  
  5194.    talk2:  mov     bh,dattr        ; clear screen and home cursor
  5195.            call    cls
  5196.  
  5197.            call    asc_enb         ; capture serial-port interrupt
  5198.                                    ; vector and enable interrupts
  5199.  
  5200.            mov     dx,offset msg2  ; display message
  5201.            mov     cx,msg2_len     ; 'terminal emulator running'
  5202.            mov     bx,stdout       ; BX = standard output handle
  5203.            mov     ah,40h          ; function 40h = write file or device
  5204.            int     21h             ; transfer to MS-DOS
  5205.  
  5206.    talk3:  call    pc_stat         ; keyboard character waiting?
  5207.            jz      talk4           ; nothing waiting, jump
  5208.  
  5209.            call    pc_in           ; read keyboard character
  5210.  
  5211.            cmp     al,0            ; is it a function key?
  5212.            jne     talk32          ; not function key, jump
  5213.  
  5214.            call    pc_in           ; function key, discard 2nd
  5215.                                    ; character of sequence
  5216.            jmp     talk5           ; then terminate program
  5217.  
  5218.    talk32:                         ; keyboard character received
  5219.            if      echo
  5220.            push    ax              ; if half-duplex, echo
  5221.            call    pc_out          ; character to PC display
  5222.            pop     ax
  5223.            endif
  5224.  
  5225.            call    com_out         ; write char to serial port
  5226.  
  5227.    talk4:  call    com_stat        ; serial-port character waiting?
  5228.            jz      talk3           ; nothing waiting, jump
  5229.  
  5230.            call    com_in          ; read serial-port character
  5231.  
  5232.            cmp     al,20h          ; is it control code?
  5233.            jae     talk45          ; jump if not
  5234.            call    ctrl_code       ; control code, process it
  5235.  
  5236.            jmp     talk3           ; check keyboard again
  5237.  
  5238.    talk45:                         ; noncontrol char received,
  5239.            call    pc_out          ; write it to PC display
  5240.  
  5241.            jmp     talk4           ; see if any more waiting
  5242.  
  5243.    talk5:                          ; function key detected,
  5244.                                    ; prepare to terminate...
  5245.  
  5246.            mov     bh,07h          ; clear screen and home cursor
  5247.            call    cls
  5248.  
  5249.            mov     dx,offset msg3  ; display farewell message
  5250.            mov     cx,msg3_len
  5251.  
  5252.    talk6:  push    dx              ; save message address
  5253.            push    cx              ; and message length
  5254.  
  5255.            call    asc_dsb         ; disable serial-port interrupts
  5256.                                    ; and release interrupt vector
  5257.  
  5258.            pop     cx              ; restore message length
  5259.            pop     dx              ; and address
  5260.  
  5261.            mov     bx,stdout       ; handle for standard output
  5262.            mov     ah,40h          ; function 40h = write device
  5263.            int     21h             ; transfer to MS-DOS
  5264.  
  5265.            mov     ax,4c00h        ; terminate program with
  5266.            int     21h             ; return code = 0
  5267.  
  5268.    talk    endp
  5269.  
  5270.    com_stat proc   near            ; check asynch status; returns
  5271.                                    ; Z = false if character ready
  5272.                                    ; Z = true if nothing waiting
  5273.            push    ax
  5274.            mov     ax,asc_in       ; compare ring buffer pointers
  5275.            cmp     ax,asc_out
  5276.            pop     ax
  5277.            ret                     ; return to caller
  5278.    stat endp
  5279.  
  5280.    com_in  proc    near            ; get character from serial-
  5281.                                    ; port buffer; returns
  5282.                                    ; new character in AL
  5283.  
  5284.            push    bx              ; save register BX
  5285.  
  5286.    com_in1:                        ; if no char waiting, wait
  5287.            mov     bx,asc_out      ; until one is received
  5288.            cmp     bx,asc_in
  5289.            je      com_in1         ; jump, nothing waiting
  5290.  
  5291.            mov     al,[bx+asc_buf] ; character is ready,
  5292.                                    ; extract it from buffer
  5293.  
  5294.            inc     bx              ; update buffer pointer
  5295.            cmp     bx,bufsiz
  5296.            jne     com_in2
  5297.            xor     bx,bx           ; reset pointer if wrapped
  5298.    com_in2:
  5299.            mov     asc_out,bx      ; store updated pointer
  5300.            pop     bx              ; restore register BX
  5301.            ret                     ; and return to caller
  5302.  
  5303.    com_in  endp
  5304.  
  5305.    com_out proc    near            ; write character in AL
  5306.                                    ; to serial port
  5307.  
  5308.            push    dx              ; save register DX
  5309.            push    ax              ; save character to send
  5310.            mov     dx,com_sts      ; DX = status port address
  5311.  
  5312.    com_out1:                       ; check if transmit buffer
  5313.            in      al,dx           ; is empty (TBE bit = set)
  5314.            and     al,20h
  5315.            jz      com_out1        ; no, must wait
  5316.  
  5317.            pop     ax              ; get character to send
  5318.            mov     dx,com_data     ; DX = data port address
  5319.            out     dx,al           ; transmit the character
  5320.            pop     dx              ; restore register DX
  5321.            ret                     ; and return to caller
  5322.  
  5323.    com_out endp
  5324.    pc_stat proc    near            ; read keyboard status; returns
  5325.                                    ; Z = false if character ready
  5326.                                    ; Z = true if nothing waiting
  5327.                                    ; register DX destroyed
  5328.  
  5329.            mov     al,in_flag      ; if character already
  5330.            or      al,al           ; waiting, return status
  5331.            jnz     pc_stat1
  5332.  
  5333.            mov     ah,6            ; otherwise call MS-DOS to
  5334.            mov     dl,0ffh         ; determine keyboard status
  5335.            int     21h
  5336.  
  5337.            jz      pc_stat1        ; jump if no key ready
  5338.  
  5339.            mov     in_char,al      ; got key, save it for
  5340.            mov     in_flag,0ffh    ; "pc_in" routine
  5341.  
  5342.    pc_stat1:                       ; return to caller with
  5343.            ret                     ; Z flag set appropriately
  5344.  
  5345.    pc_stat endp
  5346.  
  5347.    pc_in   proc    near            ; read keyboard character,
  5348.                                    ; return it in AL
  5349.                                    ; DX may be destroyed
  5350.  
  5351.            mov     al,in_flag      ; key already waiting?
  5352.            or      al,al
  5353.            jnz     pc_in1          ; yes, return it to caller
  5354.  
  5355.            call    pc_stat         ; try to read a character
  5356.            jmp     pc_in
  5357.  
  5358.    pc_in1: mov     in_flag,0       ; clear char-waiting flag
  5359.            mov     al,in_char      ; and return AL = character
  5360.            ret
  5361.  
  5362.    pc_in   endp
  5363.  
  5364.    pc_out  proc    near            ; write character in AL
  5365.                                    ; to the PC's display
  5366.  
  5367.            mov     ah,0eh          ; ROM BIOS function 0eh =
  5368.                                    ; "teletype output"
  5369.            push    bx              ; save register BX
  5370.            xor     bx,bx           ; assume page 0
  5371.            int     10h             ; transfer to ROM BIOS
  5372.            pop     bx              ; restore register BX
  5373.            ret                     ; and return to caller
  5374.  
  5375.    pc_out  endp
  5376.  
  5377.  
  5378.    cls     proc    near            ; clear display using
  5379.                                    ; char attribute in BH
  5380.                                    ; registers AX, CX,
  5381.                                    ; and DX destroyed
  5382.  
  5383.            mov     dl,columns      ; set DL,DH = X,Y of
  5384.            mov     dh,24           ; lower right corner
  5385.            mov     cx,0            ; set CL,CH = X,Y of
  5386.                                    ; upper left corner
  5387.            mov     ax,600h         ; ROM BIOS function 06h =
  5388.                                    ; "scroll or initialize
  5389.                                    ; window"
  5390.            int     10h             ; transfer to ROM BIOS
  5391.            call    home            ; set cursor at (0,0)
  5392.            ret                     ; and return to caller
  5393.  
  5394.    cls     endp
  5395.  
  5396.    clreol  proc    near            ; clear from cursor to end
  5397.                                    ; of line using attribute
  5398.                                    ; in BH, registers AX, CX,
  5399.                                    ; and DX destroyed
  5400.  
  5401.            call    getxy           ; get current cursor position
  5402.            mov     cx,dx           ; current position = "upper
  5403.                                    ; left corner" of window;
  5404.            mov     dl,columns      ; "lower right corner" X is
  5405.                                    ; max columns, Y is same
  5406.                                    ; as upper left corner
  5407.            mov     ax,600h         ; ROM BIOS function 06h =
  5408.                                    ; "scroll or initialize
  5409.                                    ; window"
  5410.            int     10h             ; transfer to ROM BIOS
  5411.            ret                     ; return to caller
  5412.  
  5413.    clreol  endp
  5414.    home    proc    near            ; put cursor at home position
  5415.  
  5416.            mov     dx,0            ; set (X,Y) = (0,0)
  5417.            call    gotoxy          ; position the cursor
  5418.            ret                     ; return to caller
  5419.  
  5420.    home    endp
  5421.  
  5422.    gotoxy  proc    near            ; position the cursor
  5423.                                    ; call with DL = X, DH = Y
  5424.  
  5425.            push    bx              ; save registers
  5426.            push    ax
  5427.  
  5428.            mov     bh,0            ; assume page 0
  5429.            mov     ah,2            ; ROM BIOS function 02h =
  5430.                                    ; set cursor position
  5431.            int     10h             ; transfer to ROM BIOS
  5432.  
  5433.            pop     ax              ; restore registers
  5434.            pop     bx
  5435.            ret                     ; and return to caller
  5436.  
  5437.    gotoxy  endp
  5438.  
  5439.  
  5440.    getxy   proc    near            ; get cursor position,
  5441.                                    ; returns DL = X, DH = Y
  5442.  
  5443.            push    ax              ; save registers
  5444.            push    bx
  5445.            push    cx
  5446.  
  5447.            mov     ah,3            ; ROM BIOS function 03h =
  5448.                                    ; get cursor position
  5449.            mov     bh,0            ; assume page 0
  5450.            int     10h             ; transfer to ROM BIOS
  5451.  
  5452.            pop     cx              ; restore registers
  5453.            pop     bx
  5454.            pop     ax
  5455.            ret                     ; and return to caller
  5456.  
  5457.    getxy   endp
  5458.    ctrl_code proc  near            ; process control code
  5459.                                    ; call with AL = char
  5460.  
  5461.            cmp     al,cr           ; if carriage return
  5462.            je      ctrl8           ; just send it
  5463.  
  5464.            cmp     al,lf           ; if linefeed
  5465.            je      ctrl8           ; just send it
  5466.  
  5467.            cmp     al,bsp          ; if backspace
  5468.            je      ctrl8           ; just send it
  5469.  
  5470.            cmp     al,26           ; is it cls control code?
  5471.            jne     ctrl7           ; no, jump
  5472.  
  5473.            mov     bh,dattr        ; cls control code, clear
  5474.            call    cls             ; screen and home cursor
  5475.  
  5476.            jmp     ctrl9
  5477.  
  5478.    ctrl7:
  5479.            cmp     al,escape       ; is it Escape character?
  5480.            jne     ctrl9           ; no, throw it away
  5481.  
  5482.            call    esc_seq         ; yes, emulate CRT terminal
  5483.            jmp     ctrl9
  5484.  
  5485.    ctrl8:  call    pc_out          ; send CR, LF, or backspace
  5486.                                    ; to the display
  5487.  
  5488.    ctrl9:  ret                     ; return to caller
  5489.  
  5490.    ctrl_code endp
  5491.  
  5492.  
  5493.    esc_seq proc    near            ; decode Televideo 950 escape
  5494.                                    ; sequence for screen control
  5495.  
  5496.            call    com_in          ; get next character
  5497.            cmp     al,84           ; is it clear to end of line?
  5498.            jne     esc_seq1        ; no, jump
  5499.  
  5500.            mov     bh,dattr        ; yes, clear to end of line
  5501.            call    clreol
  5502.            jmp     esc_seq2        ; then exit
  5503.    esc_seq1:
  5504.            cmp     al,61           ; is it cursor positioning?
  5505.            jne     esc_seq2        ; no jump
  5506.  
  5507.            call    com_in          ; yes, get Y parameter
  5508.            sub     al,33           ; and remove offset
  5509.            mov     dh,al
  5510.  
  5511.            call    com_in          ; get X parameter
  5512.            sub     al,33           ; and remove offset
  5513.            mov     dl,al
  5514.            call    gotoxy          ; position the cursor
  5515.  
  5516.    esc_seq2:                       ; return to caller
  5517.            ret
  5518.  
  5519.    esc_seq endp
  5520.  
  5521.  
  5522.    asc_enb proc    near            ; capture serial-port interrupt
  5523.                                    ; vector and enable interrupt
  5524.  
  5525.                                    ; save address of previous
  5526.                                    ; interrupt handler...
  5527.            mov     ax,3500h+com_int ; function 35h = get vector
  5528.            int     21h             ; transfer to MS-DOS
  5529.            mov     word ptr oldvec+2,es
  5530.            mov     word ptr oldvec,bx
  5531.  
  5532.                                    ; now install our handler...
  5533.            push    ds              ; save our data segment
  5534.            mov     ax,cs           ; set DS:DX = address
  5535.            mov     ds,ax           ; of our interrupt handler
  5536.            mov     dx,offset asc_int
  5537.            mov     ax,2500h+com_int ; function 25h = set vector
  5538.            int     21h             ; transfer to MS-DOS
  5539.            pop     ds              ; restore data segment
  5540.  
  5541.            mov     dx,com_mcr      ; set modem-control register
  5542.            mov     al,0bh          ; DTR and OUT2 bits
  5543.            out     dx,al
  5544.  
  5545.            mov     dx,com_ier      ; set interrupt-enable
  5546.            mov     al,1            ; register on serial-
  5547.            out     dx,al           ; port controller
  5548.            in      al,pic_mask     ; read current 8259 mask
  5549.            and     al,not int_mask ; set mask for COM port
  5550.            out     pic_mask,al     ; write new 8259 mask
  5551.  
  5552.            ret                     ; back to caller
  5553.  
  5554.    asc_enb endp
  5555.  
  5556.  
  5557.    asc_dsb proc    near            ; disable interrupt and
  5558.                                    ; release interrupt vector
  5559.  
  5560.            in      al,pic_mask     ; read current 8259 mask
  5561.            or      al,int_mask     ; reset mask for COM port
  5562.            out     pic_mask,al     ; write new 8259 mask
  5563.  
  5564.            push    ds              ; save our data segment
  5565.            lds     dx,oldvec       ; load address of
  5566.                                    ; previous interrupt handler
  5567.            mov     ax,2500h+com_int ; function 25h = set vector
  5568.            int     21h             ; transfer to MS-DOS
  5569.            pop     ds              ; restore data segment
  5570.  
  5571.            ret                     ; back to caller
  5572.  
  5573.    asc_dsb endp
  5574.  
  5575.  
  5576.    asc_int proc    far             ; interrupt service routine
  5577.                                    ; for serial port
  5578.  
  5579.            sti                     ; turn interrupts back on
  5580.  
  5581.            push    ax              ; save registers
  5582.            push    bx
  5583.            push    dx
  5584.            push    ds
  5585.  
  5586.            mov     ax,_DATA        ; make our data segment
  5587.            mov     ds,ax           ; addressable
  5588.  
  5589.            cli                     ; clear interrupts for
  5590.                                    ; pointer manipulation
  5591.  
  5592.            mov     dx,com_data     ; DX = data port address
  5593.            in      al,dx           ; read this character
  5594.            mov     bx,asc_in       ; get buffer pointer
  5595.            mov     [asc_buf+bx],al ; store this character
  5596.            inc     bx              ; bump pointer
  5597.            cmp     bx,bufsiz       ; time for wrap?
  5598.            jne     asc_int1        ; no, jump
  5599.            xor     bx,bx           ; yes, reset pointer
  5600.  
  5601.    asc_int1:                       ; store updated pointer
  5602.            mov     asc_in,bx
  5603.  
  5604.            sti                     ; turn interrupts back on
  5605.  
  5606.            mov     al,20h          ; send EOI to 8259
  5607.            out     pic_eoi,al
  5608.  
  5609.            pop     ds              ; restore all registers
  5610.            pop     dx
  5611.            pop     bx
  5612.            pop     ax
  5613.  
  5614.            iret                    ; return from interrupt
  5615.  
  5616.    asc_int endp
  5617.  
  5618.    _TEXT   ends
  5619.  
  5620.  
  5621.    _DATA   segment word public 'DATA'
  5622.  
  5623.    in_char db      0               ; PC keyboard input char
  5624.    in_flag db      0               ; <>0 if char waiting
  5625.  
  5626.    columns db      0               ; highest numbered column in
  5627.                                    ; current display mode (39 or 79)
  5628.  
  5629.    msg1    db      cr,lf
  5630.            db      'Display must be text mode.'
  5631.            db      cr,lf
  5632.    msg1_len equ $-msg1
  5633.  
  5634.    msg2    db      'Terminal emulator running...'
  5635.            db      cr,lf
  5636.    msg2_len equ $-msg2
  5637.  
  5638.    msg3    db      'Exit from terminal emulator.'
  5639.            db      cr,lf
  5640.    msg3_len equ $-msg3
  5641.    oldvec  dd      0               ; original contents of serial-
  5642.                                    ; port interrupt vector
  5643.  
  5644.    asc_in  dw      0               ; input pointer to ring buffer
  5645.    asc_out dw      0               ; output pointer to ring buffer
  5646.  
  5647.    asc_buf db      bufsiz dup (?)  ; communications buffer
  5648.  
  5649.    _DATA   ends
  5650.  
  5651.  
  5652.    STACK   segment para stack 'STACK'
  5653.  
  5654.            db      128 dup (?)
  5655.  
  5656.    STACK   ends
  5657.  
  5658.            end     talk            ;  defines entry point
  5659.    ──────────────────────────────────────────────────────────────────────────
  5660.  
  5661.    Figure 7-1.  TALK.ASM: A simple terminal-emulator program for IBM
  5662.    PC─compatible computers. This program demonstrates use of the MS-DOS and
  5663.    ROM BIOS video and keyboard functions and direct control of the
  5664.    serial-communications adapter.
  5665.  
  5666.    The TALK program illustrates the methods that an application should use to
  5667.    take over and service interrupts from the serial port without running
  5668.    afoul of MS-DOS conventions.
  5669.  
  5670.    The program begins with some equates and conditional assembly statements
  5671.    that configure the program for half- or full-duplex and for the desired
  5672.    serial port (COM1 or COM2). At entry from MS-DOS, the main routine of the
  5673.    program──the procedure named talk──checks the status of the serial port,
  5674.    initializes the display, and calls the asc_enb routine to take over the
  5675.    serial-port interrupt vector and enable interrupts. The talk procedure
  5676.    then enters a loop that reads the keyboard and sends the characters out
  5677.    the serial port and then reads the serial port and puts the characters on
  5678.    the display──in other words, it causes the PC to emulate a simple CRT
  5679.    terminal.
  5680.  
  5681.    The TALK program intercepts and handles control codes (carriage return,
  5682.    linefeed, and so forth) appropriately. It detects escape sequences and
  5683.    handles them as a subset of the Televideo 950 terminal capabilities. (You
  5684.    can easily modify the program to emulate any other cursor-addressable
  5685.    terminal.) When one of the PC's special function keys is pressed, the
  5686.    program disables serial-port interrupts, releases the serial-port
  5687.    interrupt vector, and exits back to MS-DOS.
  5688.  
  5689.    There are several TALK program procedures that are worth your attention
  5690.    because they can easily be incorporated into other programs. These are
  5691.    listed in the table on the following page.
  5692.  
  5693. ╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
  5694.    Procedure          Action
  5695.    ──────────────────────────────────────────────────────────────────────────
  5696.    asc_enb            Takes over the serial-port interrupt vector and enables
  5697.                       interrupts by writing to the modem-control register of
  5698.                       the INS8250 and the interrupt-mask register of the
  5699.                       8259A.
  5700.  
  5701.    asc_dsb            Restores the original state of the serial-port
  5702.                       interrupt vector and disables interrupts by writing to
  5703.                       the interrupt-mask register of the 8259A.
  5704.  
  5705.    asc_int            Services serial-port interrupts, placing received
  5706.                       characters into a ring buffer.
  5707.  
  5708.    com_stat           Tests whether characters from the serial port are
  5709.                       waiting in the ring buffer.
  5710.  
  5711.    com_in             Removes characters from the interrupt handler's ring
  5712.                       buffer and increments the buffer pointers
  5713.                       appropriately.
  5714.    Procedure          Action
  5715.    ──────────────────────────────────────────────────────────────────────────
  5716.                      appropriately.
  5717.  
  5718.    com_out            Sends one character to the serial port.
  5719.  
  5720.    cls                Calls the ROM BIOS video driver to clear the screen.
  5721.  
  5722.    clreol             Calls the ROM BIOS video driver to clear from the
  5723.                       current cursor position to the end of the line.
  5724.  
  5725.    home               Places the cursor in the upper left corner of the
  5726.                       screen.
  5727.  
  5728.    gotoxy             Positions the cursor at the desired position on the
  5729.                       display.
  5730.  
  5731.    getxy              Obtains the current cursor position.
  5732.  
  5733.    pc_out             Sends one character to the PC's display.
  5734.  
  5735.    Procedure          Action
  5736.    ──────────────────────────────────────────────────────────────────────────
  5737. 
  5738.    pc_stat            Gets status for the PC's keyboard.
  5739.  
  5740.    pc_in              Returns a character from the PC's keyboard.
  5741.    ──────────────────────────────────────────────────────────────────────────
  5742.  
  5743.  
  5744.  
  5745.  
  5746.  
  5747.  ────────────────────────────────────────────────────────────────────────────
  5748.  Chapter 8  File Management
  5749.  
  5750.    The dual heritage of MS-DOS──CP/M and UNIX/XENIX──is perhaps most clearly
  5751.    demonstrated in its file-management services. In general, MS-DOS provides
  5752.    at least two distinct operating-system calls for each major file or record
  5753.    operation. This chapter breaks this overlapping battery of functions into
  5754.    two groups and explains the usage, advantages, and disadvantages of each.
  5755.  
  5756.    I will refer to the set of file and record functions that are compatible
  5757.    with CP/M as FCB functions. These functions rely on a data structure
  5758.    called a file control block (hence, FCB) to maintain certain bookkeeping
  5759.    information about open files. This structure resides in the application
  5760.    program's memory space. The FCB functions allow the programmer to create,
  5761.    open, close, and delete files and to read or write records of any size at
  5762.    any record position within such files. These functions do not support the
  5763.    hierarchical (treelike) file structure that was first introduced in MS-DOS
  5764.    version 2.0, so they can be used only to access files in the current
  5765.    subdirectory for a given disk drive.
  5766.  
  5767.    I will refer to the set of file and record functions that provide
  5768.    compatibility with UNIX/XENIX as the handle functions. These functions
  5769.    allow the programmer to open or create files by passing MS-DOS a
  5770.    null-terminated string that describes the file's location in the
  5771.    hierarchical file structure (the drive and path), the file's name, and its
  5772.    extension. If the open or create operation is successful, MS-DOS returns a
  5773.    16-bit token, or handle, that is saved by the application program and used
  5774.    to specify the file in subsequent operations.
  5775.  
  5776.    When you use the handle functions, the operating system maintains the data
  5777.    structures that contain bookkeeping information about the file inside its
  5778.    own memory space, and these structures are not accessible to the
  5779.    application program. The handle functions fully support the hierarchical
  5780.    file structure, allowing the programmer to create, open, close, and delete
  5781.    files in any subdirectory on any disk drive and to read or write records
  5782.    of any size at any byte offset within such files.
  5783.  
  5784.    Although we are discussing the FCB functions first in this chapter for
  5785.    historical reasons, new MS-DOS applications should always be written using
  5786.    the more powerful handle functions. Use of the FCB functions in new
  5787.    programs should be avoided, unless compatibility with MS-DOS version 1.0
  5788.    is needed.
  5789.  
  5790.  
  5791.  Using the FCB Functions
  5792.  
  5793.    Understanding the structure of the file control block is the key to
  5794.    success with the FCB family of file and record functions. An FCB is a
  5795.    37-byte data structure allocated within the application program's memory
  5796.    space; it is divided into many fields (Figure 8-1). Typically, the
  5797.    program initializes an FCB with a drive code, a filename, and an extension
  5798.    (conveniently accomplished with the parse-filename service, Int 21H
  5799.    Function 29H) and then passes the address of the FCB to MS-DOS to open or
  5800.    create the file. If the file is successfully opened or created, MS-DOS
  5801.    fills in certain fields of the FCB with information from the file's entry
  5802.    in the disk directory. This information includes the file's exact size in
  5803.    bytes and the date and time the file was created or last updated. MS-DOS
  5804.    also places certain other information within a reserved area of the FCB;
  5805.    however, this area is used by the operating system for its own purposes
  5806.    and varies among different versions of MS-DOS. Application programs should
  5807.    never modify the reserved area.
  5808.  
  5809.    For compatibility with CP/M, MS-DOS automatically sets the record-size
  5810.    field of the FCB to 128 bytes. If the program does not want to use this
  5811.    default record size, it must place the desired size (in bytes) into the
  5812.    record-size field after the open or create operation. Subsequently, when
  5813.    the program needs to read or write records from the file, it must pass the
  5814.    address of the FCB to MS-DOS; MS-DOS, in turn, keeps the FCB updated with
  5815.    information about the current position of the file pointer and the size of
  5816.    the file. Data is always read to or written from the current disk transfer
  5817.    area (DTA), whose address is set with Int 21H Function 1AH. If the
  5818.    application program wants to perform random record access, it must set the
  5819.    record number into the FCB before issuing each function call; when
  5820.    sequential record access is being used, MS-DOS maintains the FCB and no
  5821.    special intervention is needed from the application.
  5822.  
  5823.    Byte offset
  5824.    00H ┌───────────────────────────────────────────────────────┐
  5825.        │                 Drive identification                  │ Note 1
  5826.    01H ├───────────────────────────────────────────────────────┤
  5827.        │                Filename (8 characters)                │ Note 2
  5828.    09H ├───────────────────────────────────────────────────────┤
  5829.        │               Extension (3 characters)                │ Note 2
  5830.    0CH ├───────────────────────────────────────────────────────┤
  5831.        │                 Current block number                  │ Note 9
  5832.    0EH ├───────────────────────────────────────────────────────┤
  5833.        │                      Record size                      │ Note 10
  5834.    10H ├───────────────────────────────────────────────────────┤
  5835.        │                  File size (4 bytes)                  │ Notes 3, 6
  5836.    14H ├───────────────────────────────────────────────────────┤
  5837.        │                 Date created/updated                  │ Note 7
  5838.    16H ├───────────────────────────────────────────────────────┤
  5839.        │                 Time created/updated                  │ Note 8
  5840.    18H ├───────────────────────────────────────────────────────┤
  5841.        │                       Reserved                        │
  5842.    20H ├───────────────────────────────────────────────────────┤
  5843.        │                 Current-record number                 │ Note 9
  5844.    21H ├───────────────────────────────────────────────────────┤
  5845.        │           Relative-record number (4 bytes)            │ Note 5
  5846.        └───────────────────────────────────────────────────────┘
  5847.  
  5848.    Figure 8-1.  Normal file control block. Total length is 37 bytes (25H
  5849.    bytes). See notes on pages 133─34.
  5850.  
  5851.    In general, MS-DOS functions that use FCBs accept the full address of the
  5852.    FCB in the DS:DX register and pass back a return code in the AL register
  5853.    (Figure 8-2). For file-management calls (open, close, create, and
  5854.    delete), this return code is zero if the function was successful and 0FFH
  5855.    (255) if the function failed. For the FCB-type record read and write
  5856.    functions, the success code returned in the AL register is again zero, but
  5857.    there are several failure codes. Under MS-DOS version 3.0 or later, more
  5858.    detailed error reporting can be obtained by calling Int 21H Function 59H
  5859.    (Get Extended Error Information) after a failed FCB function call.
  5860.  
  5861.    When a program is loaded under MS-DOS, the operating system sets up two
  5862.    FCBs in the program segment prefix, at offsets 005CH and 006CH. These are
  5863.    often referred to as the default FCBs, and they are included to provide
  5864.    upward compatibility from CP/M. MS-DOS parses the first two parameters in
  5865.    the command line that invokes the program (excluding any redirection
  5866.    directives) into the default FCBs, under the assumption that they may be
  5867.    file specifications. The application must determine whether they really
  5868.    are filenames or not. In addition, because the default FCBs overlap and
  5869.    are not in a particularly convenient location (especially for .EXE
  5870.    programs), they usually must be copied elsewhere in order to be used
  5871.    safely. (See Chapter 3.)
  5872.  
  5873.    ──────────────────────────────────────────────────────────────────────────
  5874.                                                 ; filename was previously
  5875.                                                 ; parsed into "my_fcb"
  5876.                    mov   dx,seg my_fcb          ; DS:DX = address of
  5877.                    mov   ds,dx                  ; file control block
  5878.                    mov   dx,offset my_fcb
  5879.                    mov   ah,0fh                 ; function 0fh = open
  5880.                    int   21h
  5881.                    or    al,al                  ; was open successful?
  5882.                    jnz   error                  ; no, jump to error routine
  5883.                    .
  5884.                    .
  5885.                    .
  5886.    my_fcb          db    37 dup (0)             ; file control block
  5887.    ──────────────────────────────────────────────────────────────────────────
  5888.  
  5889.    Figure 8-2.  A typical FCB file operation. This sequence of code attempts
  5890.    to open the file whose name was previously parsed into the FCB named
  5891.    my_fcb.
  5892.  
  5893.    Note that the structures of FCBs under CP/M and MS-DOS are not identical.
  5894.    However, the differences lie chiefly in the reserved areas of the FCBs
  5895.    (which should not be manipulated by application programs in any case), so
  5896.    well-behaved CP/M applications should be relatively easy to port into
  5897.    MS-DOS. It seems, however, that few such applications exist. Many of the
  5898.    tricks that were played by clever CP/M programmers to increase performance
  5899.    or circumvent the limitations of that operating system can cause severe
  5900.    problems under MS-DOS, particularly in networking environments. At any
  5901.    rate, much better performance can be achieved by thoroughly rewriting the
  5902.    CP/M applications to take advantage of the superior capabilities of
  5903.    MS-DOS.
  5904.  
  5905.    You can use a special FCB variant called an extended file control block to
  5906.    create or access files with special attributes (such as hidden or
  5907.    read-only files), volume labels, and subdirectories. An extended FCB has a
  5908.    7-byte header followed by the 37-byte structure of a normal FCB (Figure
  5909.    8-3). The first byte contains 0FFH, which could never be a legal drive
  5910.    code and thus indicates to MS-DOS that an extended FCB is being used. The
  5911.    next 5 bytes are reserved and are unused in current versions of MS-DOS.
  5912.    The seventh byte contains the attribute of the special file type that is
  5913.    being accessed. (Attribute bytes are discussed in more detail in Chapter
  5914.    9.) Any MS-DOS function that uses a normal FCB can also use an extended
  5915.    FCB.
  5916.  
  5917.    The FCB file- and record-management functions may be gathered into the
  5918.    following broad classifications:
  5919.  
  5920.    Byte
  5921.    offset
  5922.    00H ┌───────────────────────────────────────────────────────┐
  5923.        │                         0FFH                          │ Note 11
  5924.    01H ├───────────────────────────────────────────────────────┤
  5925.        │           Reserved (5 bytes, must be zero)            │
  5926.    06H ├───────────────────────────────────────────────────────┤
  5927.        │                    Attribute byte                     │ Note 12
  5928.    07H ├───────────────────────────────────────────────────────┤
  5929.        │                 Drive identification                  │ Note 1
  5930.    08H ├───────────────────────────────────────────────────────┤
  5931.        │                Filename (8 characters)                │ Note 2
  5932.    10H ├───────────────────────────────────────────────────────┤
  5933.        │               Extension (3 characters)                │ Note 2
  5934.    13H ├───────────────────────────────────────────────────────┤
  5935.        │                 Current-block number                  │ Note 9
  5936.    15H ├───────────────────────────────────────────────────────┤
  5937.        │                      Record size                      │ Note 10
  5938.    17H ├───────────────────────────────────────────────────────┤
  5939.        │                  File size (4 bytes)                  │ Notes 3, 6
  5940.    1BH ├───────────────────────────────────────────────────────┤
  5941.        │                 Date created/updated                  │ Note 7
  5942.    1DH ├───────────────────────────────────────────────────────┤
  5943.        │                 Time created/updated                  │ Note 8
  5944.    1FH ├───────────────────────────────────────────────────────┤
  5945.        │                       Reserved                        │
  5946.    27H ├───────────────────────────────────────────────────────┤
  5947.        │                 Current-record number                 │ Note 9
  5948.    28H ├───────────────────────────────────────────────────────┤
  5949.        │           Relative-record number (4 bytes)            │ Note 5
  5950.        └───────────────────────────────────────────────────────┘
  5951.  
  5952.    Figure 8-3.  Extended file control block. Total length is 44 bytes (2CH
  5953.    bytes). See notes on pages 133─34.
  5954.  
  5955. ╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
  5956.    Function                 Action
  5957.    ──────────────────────────────────────────────────────────────────────────
  5958.    Common FCB file operations
  5959.    0FH                     Open file.
  5960.    10H                     Close file.
  5961.    16H                     Create file.
  5962.  
  5963.    Common FCB record operations
  5964.    14H                     Perform sequential read.
  5965.    15H                     Perform sequential write.
  5966.    Function                 Action
  5967.    ──────────────────────────────────────────────────────────────────────────
  5968.   15H                     Perform sequential write.
  5969.    21H                     Perform random read.
  5970.    22H                     Perform random write.
  5971.    27H                     Perform random block read.
  5972.    28H                     Perform random block write.
  5973.  
  5974.    Other vital FCB operations
  5975.    1AH                     Set disk transfer address.
  5976.    29H                     Parse filename.
  5977.  
  5978.    Less commonly used FCB file operations
  5979.    13H                     Delete file.
  5980.    17H                     Rename file.
  5981.  
  5982.    Less commonly used FCB record operations
  5983.    23H                     Obtain file size.
  5984.    24H                     Set relative-record number.
  5985.    ──────────────────────────────────────────────────────────────────────────
  5986.  
  5987.    Function                 Action
  5988.    ──────────────────────────────────────────────────────────────────────────
  5989. 
  5990.  
  5991.    Several of these functions have special properties. For example, Int 21H
  5992.    Functions 27H (Random Block Read) and 28H (Random Block Write) allow
  5993.    reading and writing of multiple records of any size and also update the
  5994.    random-record field automatically (unlike Int 21H Functions 21H and
  5995.    22H). Int 21H Function 28H can truncate a file to any desired size, and
  5996.    Int 21H Function 17H used with an extended FCB can alter a volume label
  5997.    or rename a subdirectory.
  5998.  
  5999.    Section 2 of this book, "MS-DOS Functions Reference," gives detailed
  6000.    specifications for each of the FCB file and record functions, along with
  6001.    assembly-language examples. It is also instructive to compare the
  6002.    preceding groups with the corresponding groups of handle-type functions
  6003.    listed on pages 140─41.
  6004.  
  6005.    ──────────────────────────────────────────────────────────────────────────
  6006.    Notes for Figures 8-1 and 8-3
  6007.      1.  The drive identification is a binary number: 00=default drive,
  6008.          01=drive A:, 02=drive B:, and so on. If the application program
  6009.          supplies the drive code as zero (default drive), MS-DOS fills in the
  6010.          code for the actual current disk drive after a successful open or
  6011.          create call.
  6012.  
  6013.      2.  File and extension names must be left justified and padded with
  6014.          blanks.
  6015.  
  6016.      3.  The file size, date, time, and reserved fields should not be
  6017.          modified by applications.
  6018.  
  6019.      4.  All word fields are stored with the least significant byte at the
  6020.          lower address.
  6021.  
  6022.      5.  The relative-record field is treated as 4 bytes if the record size
  6023.          is less than 64 bytes; otherwise, only the first 3 bytes of this
  6024.          field are used.
  6025.  
  6026.      6.  The file-size field is in the same format as in the directory, with
  6027.          the less significant word at the lower address.
  6028.  
  6029.      7.  The date field is mapped as in the directory. Viewed as a 16-bit
  6030.          word (as it would appear in a register), the field is broken down as
  6031.          follows:
  6032.  
  6033.        F  E  D  C  B  A  9   8     7     6     5    4   3   2   1   0
  6034.      ┌─────────────────────┬─────────────────────┬─────────────────────┐
  6035.      │        Year         │        Month        │         Day         │
  6036.      └─────────────────────┴─────────────────────┴─────────────────────┘
  6037.  
  6038.      Bits              Contents
  6039.      ────────────────────────────────────────────────────────────────────────
  6040.      00H─04H           Day (1─31)
  6041.      05H─08H           Month (1─12)
  6042.      09H─0FH           Year, relative to 1980
  6043.      ────────────────────────────────────────────────────────────────────────
  6044.  
  6045.      8.  The time field is mapped as in the directory. Viewed as a 16-bit
  6046.          word (as it would appear in a register), the field is broken down as
  6047.          follows:
  6048.  
  6049.        F   E   D   C   B   A   9   8   7   6   5   4   3   2   1   0
  6050.      ┌───────────────────┬───────────────────────┬─────────────────────┐
  6051.      │     Hours         │        Minutes        │ 2-second increments │
  6052.      └───────────────────┴───────────────────────┴─────────────────────┘
  6053.  
  6054.      Bits              Contents
  6055.      ────────────────────────────────────────────────────────────────────────
  6056.      00H─04H           2-second increments (0─29)
  6057.      05H─0AH           Minutes (0─59)
  6058.      0BH─0FH           Hours (0─23)
  6059.      ────────────────────────────────────────────────────────────────────────
  6060.  
  6061.      9.  The current-block and current-record numbers are used together on
  6062.          sequential reads and writes. This simulates the behavior of CP/M.
  6063.  
  6064.      10. The Int 21H open (0FH) and create (16H) functions set the
  6065.          record-size field to 128 bytes, to provide compatibility with CP/M.
  6066.          If you use another record size, you must fill it in after the open
  6067.          or create operation.
  6068.  
  6069.      11. An 0FFH (255) in the first byte of the structure signifies that it
  6070.          is an extended file control block. You can use extended FCBs with
  6071.          any of the functions that accept an ordinary FCB. (See also note
  6072.          12.)
  6073.  
  6074.      12. The attribute byte in an extended FCB allows access to files with
  6075.          the special characteristics hidden, system, or read-only. You can
  6076.          also use extended FCBs to read volume labels and the contents of
  6077.          special subdirectory files.
  6078.  
  6079.    ──────────────────────────────────────────────────────────────────────────
  6080.  
  6081.  FCB File-Access Skeleton
  6082.  
  6083.    The following is a typical program sequence to access a file using the
  6084.    FCB, or traditional, functions (Figure 8-4):
  6085.  
  6086.    1.  Zero out the prospective FCB.
  6087.  
  6088.    2.  Obtain the filename from the user, from the default FCBs, or from the
  6089.        command tail in the PSP.
  6090.  
  6091.    3.  If the filename was not obtained from one of the default FCBs, parse
  6092.        the filename into the new FCB using Int 21H Function 29H.
  6093.  
  6094.    4.  Open the file (Int 21H Function 0FH) or, if writing new data only,
  6095.        create the file or truncate any existing file of the same name to zero
  6096.        length (Int 21H Function 16H).
  6097.  
  6098.    5.  Set the record-size field in the FCB, unless you are using the default
  6099.        record size. Recall that it is important to do this after a successful
  6100.        open or create operation. (See Figure 8-5.)
  6101.  
  6102.    6.  Set the relative-record field in the FCB if you are performing random
  6103.        record I/O.
  6104.  
  6105.    7.  Set the disk transfer area address using Int 21H Function 1AH, unless
  6106.        the buffer address has not been changed since the last call to this
  6107.        function. If the application never performs a set DTA, the DTA address
  6108.        defaults to offset 0080H in the PSP.
  6109.  
  6110.    8.  Request the needed read- or write-record operation (Int 21H Function
  6111.        14H─Sequential Read, 15H─Sequential Write, 21H─Random Read,
  6112.        22H─Random Write, 27H─Random Block Read, 28H─Random Block Write).
  6113.  
  6114.    9.  If the program is not finished processing the file, go to step 6;
  6115.        otherwise, close the file (Int 21H Function 10H). If the file was
  6116.        used for reading only, you can skip the close operation under early
  6117.        versions of MS-DOS. However, this shortcut can cause problems under
  6118.        MS-DOS versions 3.0 and later, especially when the files are being
  6119.        accessed across a network.
  6120.  
  6121.    ──────────────────────────────────────────────────────────────────────────
  6122.    recsize      equ   1024                   ; file record size
  6123.                 .
  6124.                 .
  6125.                 .
  6126.                 mov   ah,29h                 ; parse input filename
  6127.                 mov   al,1                   ; skip leading blanks
  6128.                 mov   si,offset fname1       ; address of filename
  6129.                 mov   di,offset fcb1         ; address of FCB
  6130.                 int   21h
  6131.                 or    al,al                  ; jump if name
  6132.                 jnz   name_err               ; was bad
  6133.                 .
  6134.                 .
  6135.                 .
  6136.                 mov   ah,29h                 ; parse output filename
  6137.                 mov   al,1                   ; skip leading blanks
  6138.                 mov   si,offset fname2       ; address of filename
  6139.                 mov   di,offset fcb2         ; address of FCB
  6140.                 int   21h
  6141.                 or    al,al                  ; jump if name
  6142.                 jnz   name_err               ; was bad
  6143.                 .
  6144.                 .
  6145.                 .
  6146.                 mov   ah,0fh                 ; open input file
  6147.                 mov   dx,offset fcb1
  6148.                 int   21h
  6149.                 or    al,al                  ; open successful?
  6150.                 jnz   no_file                ; no, jump
  6151.                 .
  6152.                 .
  6153.                 .
  6154.                 mov   ah,16h                 ; create and open
  6155.                 mov   dx,offset fcb2         ; output file
  6156.                 int   21h
  6157.                 or    al,al                  ; create successful?
  6158.                 jnz   disk_full              ; no, jump
  6159.                 .
  6160.                 .
  6161.                 .                            ; set record sizes
  6162.                 mov   word ptr fcb1+0eh,recsize
  6163.                 mov   word ptr fcb2+0eh,recsize
  6164.                 .
  6165.                 .
  6166.                 .
  6167.                 mov   ah,1ah                 ; set disk transfer
  6168.                 mov   dx,offset buffer       ; address for reads
  6169.                 int   21h                    ; and writes
  6170.                 .
  6171.    next:        .                            ; process next record
  6172.                 .
  6173.                 mov   ah,14h                 ; sequential read from
  6174.                 mov   dx,offset fcb1         ; input file
  6175.                 int   21h
  6176.                 cmp   al,01                  ; check for end of file
  6177.                 je    file_end               ; jump if end of file
  6178.                 cmp   al,03
  6179.                 je    file_end               ; jump if end of file
  6180.                 or    al,al                  ; other read fault?
  6181.                 jnz   bad_read               ; jump if bad read
  6182.                 .
  6183.                 .
  6184.                 .
  6185.                 mov   ah,15h                 ; sequential write to
  6186.                 mov   dx,offset fcb2         ; output file
  6187.                 int   21h
  6188.                 or    al,al                  ; write successful?
  6189.                 jnz   bad_write              ; jump if write failed
  6190.                 .
  6191.                 .
  6192.                 .
  6193.                 jmp   next                   ; process next record
  6194.                 .
  6195.    file_end:    .                            ; reached end of input
  6196.                 .
  6197.                 mov   ah,10h                 ; close input file
  6198.                 mov   dx,offset fcb1
  6199.                 int   21h
  6200.                 .
  6201.                 .
  6202.                 .
  6203.                 mov   ah,10h                 ; close output file
  6204.                 mov   dx,offset fcb2
  6205.                 int   21h
  6206.                 .
  6207.                 .
  6208.                 .
  6209.                 mov   ax,4c00h               ; exit with return
  6210.                 int   21h                    ; code of zero
  6211.                 .
  6212.                 .
  6213.                 .
  6214.    fname1       db    'OLDFILE.DAT',0        ; name of input file
  6215.    fname2       db    'NEWFILE.DAT',0        ; name of output file
  6216.    fcb1         db    37 dup (0)             ; FCB for input file
  6217.    fcb2         db    37 dup (0)             ; FCB for output file
  6218.    buffer       db    recsize dup (?)        ; buffer for file I/O
  6219.    ──────────────────────────────────────────────────────────────────────────
  6220.  
  6221.    Figure 8-4.  Skeleton of an assembly-language program that performs file
  6222.    and record I/O using the FCB family of functions.
  6223.  
  6224.    Byte Offset  FCB before open       FCB contents       FCB after open
  6225.             ┌────────────────────┬────────────────────┬────────────────────┐
  6226.         00H │         00         │       Drive        │         03         │
  6227.             ├────────────────────┼────────────────────┼────────────────────┤
  6228.         01H │         4D         │                    │         4D         │
  6229.         02H │         59         │                    │         59         │
  6230.         03H │         46         │                    │         46         │
  6231.         04H │         49         │      Filename      │         49         │
  6232.         05H │         4C         │                    │         4C         │
  6233.         06H │         45         │                    │         45         │
  6234.         07H │         20         │                    │         20         │
  6235.         08H │         20         │                    │         20         │
  6236.             ├────────────────────┼────────────────────┼────────────────────┤
  6237.         09H │         44         │                    │         44         │
  6238.         0AH │         41         │     Extension      │         41         │
  6239.         0BH │         54         │                    │         54         │
  6240.             ├────────────────────┼────────────────────┼────────────────────┤
  6241.         0CH │         00         │                    │         00         │
  6242.         0DH │         00         │   Current block    │         00         │
  6243.             ├────────────────────┼────────────────────┼────────────────────┤
  6244.         0EH │         00         │                    │         80         │
  6245.         0FH │         00         │    Record size     │         00         │
  6246.             ├────────────────────┼────────────────────┼────────────────────┤
  6247.         10H │         00         │                    │         80         │
  6248.         11H │         00         │                    │         3D         │
  6249.         12H │         00         │     File size      │         00         │
  6250.         13H │         00         │                    │         00         │
  6251.             ├────────────────────┼────────────────────┼────────────────────┤
  6252.         14H │         00         │                    │         43         │
  6253.         15H │         00         │     File date      │         0B         │
  6254.             ├────────────────────┼────────────────────┼────────────────────┤
  6255.         16H │         00         │                    │         A1         │
  6256.         17H │         00         │     File time      │         52         │
  6257.             ├────────────────────┼────────────────────┼────────────────────┤
  6258.         18H │         00         │                    │         03         │
  6259.         19H │         00         │                    │         02         │
  6260.         1AH │         00         │                    │         42         │
  6261.         1BH │         00         │                    │         73         │
  6262.         1CH │         00         │      Reserved      │         00         │
  6263.         1DH │         00         │                    │         01         │
  6264.         1EH │         00         │                    │         35         │
  6265.         1FH │         00         │                    │         0F         │
  6266.             ├────────────────────┼────────────────────┼────────────────────┤
  6267.         20H │         00         │   Current record   │         00         │
  6268.             ├────────────────────┼────────────────────┼────────────────────┤
  6269.         21H │         00         │                    │         00         │
  6270.         22H │         00         │  Relative-record   │         00         │
  6271.         23H │         00         │       number       │         00         │
  6272.         24H │         00         │                    │         00         │
  6273.             └────────────────────┴────────────────────┴────────────────────┘
  6274.  
  6275.    Figure 8-5.  A typical file control block before and after a successful
  6276.    open call (Int 21H Function 0FH).
  6277.  
  6278.  Points to Remember
  6279.  
  6280.    Here is a summary of the pros and cons of using the FCB-related file and
  6281.    record functions in your programs.
  6282.  
  6283.    Advantages:
  6284.  
  6285.    ■  Under MS-DOS versions 1 and 2, the number of files that can be open
  6286.       concurrently when using FCBs is unlimited. (This is not true under
  6287.       MS-DOS versions 3.0 and later, especially if networking software is
  6288.       running.)
  6289.  
  6290.    ■  File-access methods using FCBs are familiar to programmers with a CP/M
  6291.       background, and well-behaved CP/M applications require little change in
  6292.       logical flow to run under MS-DOS.
  6293.  
  6294.    ■  MS-DOS supplies the size, time, and date for a file to its FCB after
  6295.       the file is opened. The calling program can inspect this information.
  6296.  
  6297.    Disadvantages:
  6298.  
  6299.    ■  FCBs take up room in the application program's memory space.
  6300.  
  6301.    ■  FCBs offer no support for the hierarchical file structure (no access to
  6302.       files outside the current directory).
  6303.  
  6304.    ■  FCBs provide no support for file locking/sharing or record locking in
  6305.       networking environments.
  6306.  
  6307.    ■  In addition to the read or write call itself, file reads or writes
  6308.       using FCBs require manipulation of the FCB to set record size and
  6309.       record number, plus a previous call to a separate MS-DOS function to
  6310.       set the DTA address.
  6311.  
  6312.    ■  Random record I/O using FCBs for a file containing variable-length
  6313.       records is very clumsy and inconvenient.
  6314.  
  6315.    ■  You must use extended FCBs, which are incompatible with CP/M anyway, to
  6316.       access or create files with special attributes such as hidden,
  6317.       read-only, or system.
  6318.  
  6319.    ■  The FCB file functions have poor error reporting. This situation has
  6320.       been improved somewhat in MS-DOS version 3 because a program can call
  6321.       the added Int 21H Function 59H (Get Extended Error Information) after
  6322.       a failed FCB function to obtain additional information.
  6323.  
  6324.    ■  Microsoft discourages use of FCBs. FCBs will make your program more
  6325.       difficult to port to MS OS/2 later because MS OS/2 does not support
  6326.       FCBs in protected mode at all.
  6327.  
  6328.  
  6329.  Using the Handle Functions
  6330.  
  6331.    The handle file- and record-management functions access files in a fashion
  6332.    similar to that used under the UNIX/XENIX operating system. Files are
  6333.    designated by an ASCIIZ string (an ASCII character string terminated by a
  6334.    null, or zero, byte) that can contain a drive designator, path, filename,
  6335.    and extension. For example, the file specification
  6336.  
  6337.    C:\SYSTEM\COMMAND.COM
  6338.  
  6339.    would appear in memory as the following sequence of bytes:
  6340.  
  6341.    43 3A 5C 53 59 53 54 45 4D 5C 43 4F 4D 4D 41 4E 44 2E 43 4F 4D 00
  6342.  
  6343.    When a program wishes to open or create a file, it passes the address of
  6344.    the ASCIIZ string specifying the file to MS-DOS in the DS:DX registers
  6345.    (Figure 8-6). If the operation is successful, MS-DOS returns a 16-bit
  6346.    handle to the program in the AX register. The program must save this
  6347.    handle for further reference.
  6348.  
  6349.    ──────────────────────────────────────────────────────────────────────────
  6350.                 mov   ah,3dh                  ; function 3dh = open
  6351.                 mov   al,2                    ; mode 2 = read/write
  6352.                 mov   dx,seg filename         ; address of ASCIIZ
  6353.                 mov   ds,dx                   ; file specification
  6354.                 mov   dx,offset filename
  6355.                 int   21h                     ; request open from DOS
  6356.                 jc    error                   ; jump if open failed
  6357.                 mov   handle,ax               ; save file handle
  6358.                 .
  6359.                 .
  6360.                 .
  6361.    filename     db    'C:\MYDIR\MYFILE.DAT',0 ; filename
  6362.    handle       dw    0                       ; file handle
  6363.    ──────────────────────────────────────────────────────────────────────────
  6364.  
  6365.    Figure 8-6.  A typical handle file operation. This sequence of code
  6366.    attempts to open the file designated in the ASCIIZ string whose address is
  6367.    passed to MS-DOS in the DS:DX registers.
  6368.  
  6369.    When the program requests subsequent operations on the file, it usually
  6370.    places the handle in the BX register before the call to MS-DOS. All the
  6371.    handle functions return with the CPU's carry flag cleared if the operation
  6372.    was successful, or set if the operation failed; in the latter case, the AX
  6373.    register contains a code describing the failure.
  6374.  
  6375.    MS-DOS restricts the number of handles that can be active at any one
  6376.    time──that is, the number of files and devices that can be open
  6377.    concurrently when using the handle family of functions──in two different
  6378.    ways:
  6379.  
  6380.    ■  The maximum number of concurrently open files in the system, for all
  6381.       active processes combined, is specified by the entry
  6382.  
  6383.       FILES=nn
  6384.  
  6385.       in the CONFIG.SYS file. This entry determines the number of entries
  6386.       to be allocated in the system file table; under MS-DOS version 3, the
  6387.       default value is 8 and the maximum is 255. After MS-DOS is booted and
  6388.       running, you cannot expand this table to increase the total number of
  6389.       files that can be open. You must use an editor to modify the CONFIG.SYS
  6390.       file and then restart the system.
  6391.  
  6392.    ■  The maximum number of concurrently open files for a single process is
  6393.       20, assuming that sufficient entries are also available in the system
  6394.       file table. When a program is loaded, MS-DOS preassigns 5 of its
  6395.       potential 20 handles to the standard devices. Each time the process
  6396.       issues an open or create call, MS-DOS assigns a handle from the
  6397.       process's private allocation of 20, until all the handles are used up
  6398.       or the system file table is full. In MS-DOS versions 3.3 and later, you
  6399.       can expand the per-process limit of 20 handles with a call to Int 21H
  6400.       Function 67H (Set Handle Count).
  6401.  
  6402.    The handle file- and record-management calls may be gathered into the
  6403.    following broad classifications for study:
  6404.  
  6405. ╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
  6406.    Function                 Action
  6407.    Function                 Action
  6408.    ──────────────────────────────────────────────────────────────────────────
  6409.    Common handle file operations
  6410.    3CH                     Create file (requires ASCIIZ string).
  6411.    3DH                     Open file (requires ASCIIZ string).
  6412.    3EH                     Close file.
  6413.  
  6414.    Common handle record operations
  6415.    42H                     Set file pointer (also used to find file size).
  6416.    3FH                     Read file.
  6417.    40H                     Write file.
  6418.  
  6419.    Less commonly used handle operations
  6420.    41H                     Delete file.
  6421.    43H                     Get or modify file attributes.
  6422.    44H                     IOCTL (I/O Control).
  6423.    45H                     Duplicate handle.
  6424.    46H                     Redirect handle.
  6425.    56H                     Rename file.
  6426.    57H                     Get or set file date and time.
  6427.    5AH                     Create temporary file (versions 3.0 and later).
  6428.    Function                 Action
  6429.    ──────────────────────────────────────────────────────────────────────────
  6430.   5AH                     Create temporary file (versions 3.0 and later).
  6431.    5BH                     Create file (fails if file already exists;
  6432.                             versions 3.0 and later).
  6433.    5CH                     Lock or unlock file region (versions 3.0 and
  6434.                             later).
  6435.    67H                     Set handle count (versions 3.3 and later).
  6436.    68H                     Commit file (versions 3.3 and later).
  6437.    6CH                     Extended open file (version 4).
  6438.    ──────────────────────────────────────────────────────────────────────────
  6439.  
  6440.  
  6441.    Compare the groups of handle-type functions in the preceding table with
  6442.    the groups of FCB functions outlined earlier, noting the degree of
  6443.    functional overlap. Section 2 of this book, "MS-DOS Functions Reference,"
  6444.    gives detailed specifications for each of the handle functions, along with
  6445.    assembly-language examples.
  6446.  
  6447.  Handle File-Access Skeleton
  6448.  
  6449.    The following is a typical program sequence to access a file using the
  6450.    handle family of functions (Figure 8-7):
  6451.  
  6452.    1.  Get the filename from the user by means of the buffered input service
  6453.        (Int 21H Function 0AH) or from the command tail supplied by MS-DOS in
  6454.        the PSP.
  6455.  
  6456.    2.  Put a zero at the end of the file specification in order to create an
  6457.        ASCIIZ string.
  6458.  
  6459.    3.  Open the file using Int 21H Function 3DH and mode 2 (read/write
  6460.        access), or create the file using Int 21H Function 3CH. (Be sure to
  6461.        set the CX register to zero, so that you don't accidentally make a
  6462.        file with special attributes.) Save the handle that is returned.
  6463.  
  6464.    4.  Set the file pointer using Int 21H Function 42H. You may set the
  6465.        file-pointer position relative to one of three different locations:
  6466.        the start of the file, the current pointer position, or the end of the
  6467.        file. If you are performing sequential record I/O, you can usually
  6468.        skip this step because MS-DOS will maintain the file pointer for you
  6469.        automatically.
  6470.  
  6471.    5.  Read from the file (Int 21H Function 3FH) or write to the file (Int
  6472.        21H Function 40H). Both of these functions require that the BX
  6473.        register contain the file's handle, the CX register contain the length
  6474.        of the record, and the DS:DX registers point to the data being
  6475.        transferred. Both return the actual number of bytes transferred in the
  6476.        AX register.
  6477.  
  6478.        In a read operation, if the number of bytes read is less than the
  6479.        number requested, the end of the file has been reached. In a write
  6480.        operation, if the number of bytes written is less than the number
  6481.        requested, the disk containing the file is full. Neither of these
  6482.        conditions is returned as an error code; that is, the carry flag is
  6483.        not set.
  6484.  
  6485.    6.  If the program is not finished processing the file, go to step 4;
  6486.        otherwise, close the file (Int 21H Function 3EH). Any normal exit
  6487.        from the program will also close all active handles.
  6488.  
  6489.    ──────────────────────────────────────────────────────────────────────────
  6490.    recsize      equ     1024                 ; file record size
  6491.                 .
  6492.                 .
  6493.                 .
  6494.                 mov   ah,3dh                 ; open input file
  6495.                 mov   al,0                   ; mode = read only
  6496.                 mov   dx,offset fname1       ; name of input file
  6497.                 int   21h
  6498.                 jc    no_file                ; jump if no file
  6499.                 mov   handle1,ax             ; save token for file
  6500.                 .
  6501.                 .
  6502.                 .
  6503.                 mov   ah,3ch                 ; create output file
  6504.                 mov   cx,0                   ; attribute = normal
  6505.                 mov   dx,offset fname2       ; name of output file
  6506.                 int   21h
  6507.                 jc    disk_full              ; jump if create fails
  6508.                 mov   handle2,ax             ; save token for file
  6509.                 .
  6510.    next:        .                            ; process next record
  6511.                 .
  6512.                 mov   ah,3fh                 ; sequential read from
  6513.                 mov   bx,handle1             ; input file
  6514.                 mov   cx,recsize
  6515.                 mov   dx,offset buffer
  6516.                 int   21h
  6517.                 jc    bad_read               ; jump if read error
  6518.                 or    ax,ax                  ; check bytes transferred
  6519.                 jz    file_end               ; jump if end of file
  6520.                 .
  6521.                 .
  6522.                 .
  6523.                 mov   ah,40h                 ; sequential write to
  6524.                 mov   bx,handle2             ; output file
  6525.                 mov   cx,recsize
  6526.                 mov   dx,offset buffer
  6527.                 int   21h
  6528.                 jc    bad_write              ; jump if write error
  6529.                 cmp   ax,recsize             ; whole record written?
  6530.                 jne   disk_full              ; jump if disk is full
  6531.                 .
  6532.                 .
  6533.                 .
  6534.                 jmp   next                   ; process next record
  6535.                 .
  6536.    file_end:    .                            ; reached end of input
  6537.                 .
  6538.                 mov   ah,3eh                 ; close input file
  6539.                 mov   bx,handle1
  6540.                 int   21h
  6541.                 .
  6542.                 .
  6543.                 .
  6544.                 mov   ah,3eh                 ; close output file
  6545.                 mov   bx,handle2
  6546.                 int   21h
  6547.                 .
  6548.                 .
  6549.                 .
  6550.                 mov   ax,4c00h               ; exit with return
  6551.                 int   21h                    ; code of zero
  6552.                 .
  6553.                 .
  6554.                 .
  6555.    fname1       db    'OLDFILE.DAT',0        ; name of input file
  6556.    fname2       db    'NEWFILE.DAT',0        ; name of output file
  6557.    handle1      dw    0                      ; token for input file
  6558.    handle2      dw    0                      ; token for output file
  6559.    buffer       db    recsize dup (?)        ; buffer for file I/O
  6560.    ──────────────────────────────────────────────────────────────────────────
  6561.  
  6562.    Figure 8-7.  Skeleton of an assembly-language program that performs
  6563.    sequential processing on an input file and writes the results to an output
  6564.    file using the handle file and record functions. This code assumes that
  6565.    the DS and ES registers have already been set to point to the segment
  6566.    containing the buffers and filenames.
  6567.  
  6568.  Points to Remember
  6569.  
  6570.    Here is a summary of the pros and cons of using the handle file and record
  6571.    operations in your program. Compare this list with the one given earlier
  6572.    in the chapter for the FCB family of functions.
  6573.  
  6574.    Advantages:
  6575.  
  6576.    ■  The handle calls provide direct support for I/O redirection and pipes
  6577.       with the standard input and output devices in a manner functionally
  6578.       similar to that used by UNIX/XENIX.
  6579.  
  6580.    ■  The handle functions provide direct support for directories (the
  6581.       hierarchical file structure) and special file attributes.
  6582.  
  6583.    ■  The handle calls support file sharing/locking and record locking in
  6584.       networking environments.
  6585.  
  6586.    ■  Using the handle functions, the programmer can open channels to
  6587.       character devices and treat them as files.
  6588.  
  6589.    ■  The handle calls make the use of random record access extremely easy.
  6590.       The current file pointer can be moved to any byte offset relative to
  6591.       the start of the file, the end of the file, or the current pointer
  6592.       position. Records of any length, up to an entire segment (65,535
  6593.       bytes), can be read to any memory address in one operation.
  6594.  
  6595.    ■  The handle functions have relatively good error reporting in MS-DOS
  6596.       version 2, and error reporting has been enhanced even further in MS-DOS
  6597.       versions 3.0 and later.
  6598.  
  6599.    ■  Microsoft strongly encourages use of the handle family of functions in
  6600.       order to provide upward compatibility with MS OS/2.
  6601.  
  6602.    Disadvantages:
  6603.  
  6604.    ■  There is a limit per program of 20 concurrently open files and devices
  6605.       using handles in MS-DOS versions 2.0 through 3.2.
  6606.  
  6607.    ■  Minor gaps still exist in the implementation of the handle functions.
  6608.       For example, you must still use extended FCBs to change volume labels
  6609.       and to access the contents of the special files that implement
  6610.       directories.
  6611.  
  6612.  
  6613.  MS-DOS Error Codes
  6614.  
  6615.    When one of the handle file functions fails with the carry flag set, or
  6616.    when a program calls Int 21H Function 59H (Get Extended Error
  6617.    Information) following a failed FCB function or other system service, one
  6618.    of the following error codes may be returned:
  6619.  
  6620. ╓┌─┌────────────────────────┌────────────────────────────────────────────────╖
  6621.    Value                    Meaning
  6622.    ──────────────────────────────────────────────────────────────────────────
  6623.    MS-DOS version 2 error codes
  6624.    01H                      Function number invalid
  6625.    02H                      File not found
  6626.    03H                      Path not found
  6627.    04H                      Too many open files
  6628.    05H                      Access denied
  6629.    06H                      Handle invalid
  6630.    07H                      Memory control blocks destroyed
  6631.    08H                      Insufficient memory
  6632.    09H                      Memory block address invalid
  6633.    0AH (10)                 Environment invalid
  6634.    0BH (11)                 Format invalid
  6635.    0CH (12)                 Access code invalid
  6636.    0DH (13)                 Data invalid
  6637.    0EH (14)                 Unknown unit
  6638.    Value                    Meaning
  6639.    ──────────────────────────────────────────────────────────────────────────
  6640.   0EH (14)                 Unknown unit
  6641.    0FH (15)                 Disk drive invalid
  6642.    10H (16)                 Attempted to remove current directory
  6643.    11H (17)                 Not same device
  6644.    12H (18)                 No more files
  6645.  
  6646.    Mappings to critical-error codes
  6647.    13H (19)                 Write-protected disk
  6648.    14H (20)                 Unknown unit
  6649.    15H (21)                 Drive not ready
  6650.    16H (22)                 Unknown command
  6651.    17H (23)                 Data error (CRC)
  6652.    18H (24)                 Bad request-structure length
  6653.    19H (25)                 Seek error
  6654.    1AH (26)                 Unknown media type
  6655.    1BH (27)                 Sector not found
  6656.    1CH (28)                 Printer out of paper
  6657.    1DH (29)                 Write fault
  6658.    1EH (30)                 Read fault
  6659.    Value                    Meaning
  6660.    ──────────────────────────────────────────────────────────────────────────
  6661.   1EH (30)                 Read fault
  6662.    1FH (31)                 General failure
  6663.  
  6664.    MS-DOS version 3 and later extended error codes
  6665.    20H (32)                 Sharing violation
  6666.    21H (33)                 File-lock violation
  6667.    22H (34)                 Disk change invalid
  6668.    23H (35)                 FCB unavailable
  6669.    24H (36)                 Sharing buffer exceeded
  6670.    25H─31H (37─49)          Reserved
  6671.    32H (50)                 Unsupported network request
  6672.    33H (51)                 Remote machine not listening
  6673.    34H (52)                 Duplicate name on network
  6674.    35H (53)                 Network name not found
  6675.    36H (54)                 Network busy
  6676.    37H (55)                 Device no longer exists on network
  6677.    38H (56)                 NetBIOS command limit exceeded
  6678.    39H (57)                 Error in network adapter hardware
  6679.    3AH (58)                 Incorrect response from network
  6680.    Value                    Meaning
  6681.    ──────────────────────────────────────────────────────────────────────────
  6682.   3AH (58)                 Incorrect response from network
  6683.    3BH (59)                 Unexpected network error
  6684.    3CH (60)                 Remote adapter incompatible
  6685.    3DH (61)                 Print queue full
  6686.    3EH (62)                 Not enough room for print file
  6687.    3FH (63)                 Print file was deleted
  6688.    40H (64)                 Network name deleted
  6689.    41H (65)                 Network access denied
  6690.    42H (66)                 Incorrect network device type
  6691.    43H (67)                 Network name not found
  6692.    44H (68)                 Network name limit exceeded
  6693.    45H (69)                 NetBIOS session limit exceeded
  6694.    46H (70)                 Temporary pause
  6695.    47H (71)                 Network request not accepted
  6696.    48H (72)                 Print or disk redirection paused
  6697.    49H─4FH (73─79)          Reserved
  6698.    50H (80)                 File already exists
  6699.    51H (81)                 Reserved
  6700.    52H (82)                 Cannot make directory
  6701.    Value                    Meaning
  6702.    ──────────────────────────────────────────────────────────────────────────
  6703.   52H (82)                 Cannot make directory
  6704.    53H (83)                 Fail on Int 24H (critical error)
  6705.    54H (84)                 Too many redirections
  6706.    55H (85)                 Duplicate redirection
  6707.    56H (86)                 Invalid password
  6708.    57H (87)                 Invalid parameter
  6709.    58H (88)                 Net write fault
  6710.    ──────────────────────────────────────────────────────────────────────────
  6711.  
  6712.  
  6713.    Under MS-DOS versions 3.0 and later, you can also use Int 21H Function
  6714.    59H to obtain other information about the error, such as the error locus
  6715.    and the recommended recovery action.
  6716.  
  6717.  Critical-Error Handlers
  6718.  
  6719.    In Chapter 5, we discussed how an application program can take over the
  6720.    Ctrl-C handler vector (Int 23H) and replace the MS-DOS default handler, to
  6721.    avoid losing control of the computer when the user enters a Ctrl-C or
  6722.    Ctrl-Break at the keyboard. Similarly, MS-DOS provides a
  6723.    critical-error-handler vector (Int 24H) that defines the routine to be
  6724.    called when unrecoverable hardware faults occur. The default MS-DOS
  6725.    critical-error handler is the routine that displays a message describing
  6726.    the error type and the cue
  6727.  
  6728.    Abort, Retry, Ignore?
  6729.  
  6730.    This message appears after such actions as the following:
  6731.  
  6732.    ■  Attempting to open a file on a disk drive that doesn't contain a floppy
  6733.       disk or whose door isn't closed
  6734.  
  6735.    ■  Trying to read a disk sector that contains a CRC error
  6736.  
  6737.    ■  Trying to print when the printer is off line
  6738.  
  6739.    The unpleasant thing about MS-DOS's default critical-error handler is, of
  6740.    course, that if the user enters an A for Abort, the application that is
  6741.    currently executing is terminated abruptly and never has a chance to clean
  6742.    up and make a graceful exit. Intermediate files may be left on the disk,
  6743.    files that have been extended using FCBs are not properly closed so that
  6744.    the directory is updated, interrupt vectors may be left pointing into the
  6745.    transient program area, and so forth.
  6746.  
  6747.    To write a truly bombproof MS-DOS application, you must take over the
  6748.    critical-error-handler vector and point it to your own routine, so that
  6749.    your program intercepts all catastrophic hardware errors and handles them
  6750.    appropriately. You can use MS-DOS Int 21H Function 25H to alter the Int
  6751.    24H vector in a well-behaved manner. When your application exits, MS-DOS
  6752.    will automatically restore the previous contents of the Int 24H vector
  6753.    from information saved in the program segment prefix.
  6754.  
  6755.    MS-DOS calls the critical-error handler for two general classes of
  6756.    errors── disk-related and non-disk-related──and passes different
  6757.    information to the handler in the registers for each of these classes.
  6758.  
  6759.    For disk-related errors, MS-DOS sets the registers as shown on the
  6760.    following page. (Bits 3─5 of the AH register are relevant only in MS-DOS
  6761.    versions 3.1 and later.)
  6762.  
  6763. ╓┌─┌──────────────────┌─────────────────┌────────────────────────────────────╖
  6764.    Register           Bit(s)            Significance
  6765.    ──────────────────────────────────────────────────────────────────────────
  6766.    AH                 7                 0, to signify disk error
  6767.                       6                 Reserved
  6768.                       5                 0 = ignore response not allowed
  6769.                                         1 = ignore response allowed
  6770.                       4                 0 = retry response not allowed
  6771.                                         1 = retry response allowed
  6772.                       3                 0 = fail response not allowed
  6773.                                         1 = fail response allowed
  6774.                       1─2               Area where disk error occurred
  6775.                                         00 = MS-DOS area
  6776.                                         01 = file allocation table
  6777.                                         10 = root directory
  6778.                                         11 = files area
  6779.                       0                 0 = read operation
  6780.                                         1 = write operation
  6781.    AL                 0─7               Drive code (0 = A, 1 = B, and so
  6782.                                         forth)
  6783.    DI                 0─7               Driver error code
  6784.                       8─15              Not used
  6785.    Register           Bit(s)            Significance
  6786.    ──────────────────────────────────────────────────────────────────────────
  6787.                      8─15              Not used
  6788.    BP:SI                                Segment:offset of device-driver
  6789.                                         header
  6790.    ──────────────────────────────────────────────────────────────────────────
  6791.  
  6792.  
  6793.    For non-disk-related errors, the interrupt was generated either as the
  6794.    result of a character-device error or because a corrupted memory image of
  6795.    the file allocation table was detected. In this case, MS-DOS sets the
  6796.    registers as follows:
  6797.  
  6798.    Register           Bit(s)            Significance
  6799.    ──────────────────────────────────────────────────────────────────────────
  6800.    AH                 7                 1, to signify a non-disk error
  6801.    DI                 0─7               Driver error code
  6802.                       8─15              Not used
  6803.    BP:SI                                Segment:offset of device-driver
  6804.                                         header
  6805.    ──────────────────────────────────────────────────────────────────────────
  6806.  
  6807.    To determine whether the critical error was caused by a character device,
  6808.    use the address in the BP:SI registers to examine the device attribute
  6809.    word at offset 0004H in the presumed device-driver header. If bit 15 is
  6810.    set, then the error was indeed caused by a character device, and the
  6811.    program can inspect the name field of the driver's header to determine the
  6812.    device.
  6813.  
  6814.    At entry to a critical-error handler, MS-DOS has already disabled
  6815.    interrupts and set up the stack as shown in Figure 8-8. A critical-error
  6816.    handler cannot use any MS-DOS services except Int 21H Functions 01H
  6817.    through 0CH (Traditional Character I/O), Int 21H Function 30H (Get MS-DOS
  6818.    Version), and Int 21H Function 59H (Get Extended Error Information).
  6819.    These functions use a special stack so that the context of the original
  6820.    function (which generated the critical error) will not be lost.
  6821.  
  6822.    ┌───────┐─┐
  6823.    │ Flags │ │
  6824.    ├───────┤ │  Flags and CS:IP pushed
  6825.    │  CS   │ ├─ on stack by original
  6826.    ├───────┤ │  Int 21H call
  6827.    │  IP   │ │
  6828.    ├───────┤═╡─SS:SP on entry to
  6829.    │  ES   │ │  Int 21H handler
  6830.    ├───────┤ │
  6831.    │  DS   │ │
  6832.    ├───────┤ │
  6833.    │  BP   │ │
  6834.    ├───────┤ │
  6835.    │  DI   │ │
  6836.    ├───────┤ ├─ Registers at point of
  6837.    │  SI   │ │  original Int 21H call
  6838.    ├───────┤ │
  6839.    │  DX   │ │
  6840.    ├───────┤ │
  6841.    │  CX   │ │
  6842.    ├───────┤ │
  6843.    │  BX   │ │
  6844.    ├───────┤ │
  6845.    │  AX   │ │
  6846.    ├───────┤═╡
  6847.    │ Flags │ │
  6848.    ├───────┤ │
  6849.    │  CS   │ ├─ Return address for
  6850.    ├───────┤ │  Int 24H handler
  6851.    │  IP   │ │
  6852.    └──────┘─┘
  6853.          └───── SS:SP on entry to
  6854.                 Int 24H handler
  6855.  
  6856.    Figure 8-8.  The stack at entry to a critical-error handler.
  6857.  
  6858.    The critical-error handler should return to MS-DOS by executing an IRET,
  6859.    passing one of the following action codes in the AL register:
  6860.  
  6861.    Code               Meaning
  6862.    ──────────────────────────────────────────────────────────────────────────
  6863.    0                  Ignore the error (MS-DOS acts as though the original
  6864.                       function call had succeeded).
  6865.    1                  Retry the operation.
  6866.    2                  Terminate the process that encountered the error.
  6867.    3                  Fail the function (an error code is returned to the
  6868.                       requesting process). Versions 3.1 and later only.
  6869.    ──────────────────────────────────────────────────────────────────────────
  6870.  
  6871.    The critical-error handler should preserve all other registers and must
  6872.    not modify the device-driver header pointed to by BP:SI. A skeleton
  6873.    example of a critical-error handler is shown in Figure 8-9.
  6874.  
  6875.    ──────────────────────────────────────────────────────────────────────────
  6876.                                    ; prompt message used by
  6877.                                    ; critical-error handler
  6878.    prompt  db      cr,lf,'Critical Error Occurred: '
  6879.            db      'Abort, Retry, Ignore, Fail? $'
  6880.  
  6881.    keys    db      'aArRiIfF'      ; possible user response keys
  6882.    keys_len equ $-keys             ; (both cases of each allowed)
  6883.  
  6884.    codes   db      2,2,1,1,0,0,3,3 ; codes returned to MS-DOS kernel
  6885.                                    ; for corresponding response keys
  6886.  
  6887.    ;
  6888.    ; This code is executed during program's initialization
  6889.    ; to install the new critical-error handler.
  6890.    ;
  6891.            .
  6892.            .
  6893.            .
  6894.            push    ds              ; save our data segment
  6895.  
  6896.            mov     dx,seg int24    ; DS:DX = handler address
  6897.            mov     ds,dx
  6898.            mov     dx,offset int24
  6899.            mov     ax,2524h        ; function 25h = set vector
  6900.            int     21h             ; transfer to MS-DOS
  6901.  
  6902.            pop     ds              ; restore data segment
  6903.            .
  6904.            .
  6905.            .
  6906.    ;
  6907.    ; This is the replacement critical-error handler. It
  6908.    ; prompts the user for Abort, Retry, Ignore, or Fail, and
  6909.    ; returns the appropriate code to the MS-DOS kernel.
  6910.    ;
  6911.  
  6912.    int24   proc    far             ; entered from MS-DOS kernel
  6913.  
  6914.            push    bx              ; save registers
  6915.            push    cx
  6916.            push    dx
  6917.            push    si
  6918.            push    di
  6919.            push    bp
  6920.            push    ds
  6921.            push    es
  6922.    int24a: mov     ax,seg prompt   ; display prompt for user
  6923.            mov     ds,ax           ; using function 9 (print string
  6924.            mov     es,ax           ; terminated by $ character)
  6925.            mov     dx,offset prompt
  6926.            mov     ah,9
  6927.            int     21h
  6928.  
  6929.            mov     ah,1            ; get user's response
  6930.            int     21h             ; function 1 = read one character
  6931.  
  6932.            mov     di,offset keys  ; look up code for response key
  6933.            mov     cx,keys_len
  6934.            cld
  6935.            repne scasb
  6936.            jnz     int24a          ; prompt again if bad response
  6937.  
  6938.                                    ; set AL = action code for MS-DOS
  6939.                                    ; according to key that was entered:
  6940.                                    ; 0 = ignore, 1 = retry, 2 = abort,
  6941.                                    ; 3 = fail
  6942.            mov     al,[di+keys_len-1]
  6943.  
  6944.            pop     es              ; restore registers
  6945.            pop     ds
  6946.            pop     bp
  6947.            pop     di
  6948.            pop     si
  6949.            pop     dx
  6950.            pop     cx
  6951.            pop     bx
  6952.            iret                    ; exit critical-error handler
  6953.  
  6954.    int24   endp
  6955.    ──────────────────────────────────────────────────────────────────────────
  6956.  
  6957.    Figure 8-9.  A skeleton example of a replacement critical-error handler.
  6958.  
  6959.  
  6960.  Example Programs: DUMP.ASM and DUMP.C
  6961.  
  6962.    The programs DUMP.ASM (Figure 8-10) and DUMP.C (Figure 8-11) are
  6963.    parallel examples of the use of the handle file and record functions. The
  6964.    assembly-language version, in particular, illustrates features of a
  6965.    well-behaved MS-DOS utility:
  6966.  
  6967.    ■  The program checks the version of MS-DOS to ensure that all the
  6968.       functions it is going to use are really available.
  6969.  
  6970.    ■  The program parses the drive, path, and filename from the command tail
  6971.       in the program segment prefix.
  6972.  
  6973.    ■  The program uses buffered I/O for speed.
  6974.  
  6975.    ■  The program sends error messages to the standard error device.
  6976.  
  6977.    ■  The program sends normal program output to the standard output device,
  6978.       so that the dump output appears by default on the system console but
  6979.       can be redirected to other character devices (such as the line printer)
  6980.       or to a file.
  6981.  
  6982.    The same features are incorporated into the C version of the program, but
  6983.    some of them are taken care of behind the scenes by the C runtime library.
  6984.  
  6985.    ──────────────────────────────────────────────────────────────────────────
  6986.            name    dump
  6987.            page    55,132
  6988.            title   DUMP--display file contents
  6989.  
  6990.    ;
  6991.    ;  DUMP--Display contents of file in hex and ASCII
  6992.    ;
  6993.    ;  Build:   C>MASM DUMP;
  6994.    ;           C>LINK DUMP;
  6995.    ;
  6996.    ;  Usage:   C>DUMP unit:\path\filename.exe [ >device ]
  6997.    ;
  6998.    ;  Copyright (C) 1988 Ray Duncan
  6999.    ;
  7000.  
  7001.    cr      equ     0dh             ; ASCII carriage return
  7002.    lf      equ     0ah             ; ASCII line feed
  7003.    tab     equ     09h             ; ASCII tab code
  7004.    blank   equ     20h             ; ASCII space code
  7005.  
  7006.    cmd     equ     80h             ; buffer for command tail
  7007.  
  7008.    blksize equ     16              ; input file record size
  7009.  
  7010.    stdin   equ     0               ; standard input handle
  7011.    stdout  equ     1               ; standard output handle
  7012.    stderr  equ     2               ; standard error handle
  7013.    _TEXT   segment word public 'CODE'
  7014.  
  7015.            assume  cs:_TEXT,ds:_DATA,es:_DATA,ss:STACK
  7016.  
  7017.    dump    proc    far             ; entry point from MS-DOS
  7018.  
  7019.            push    ds              ; save DS:0000 for final
  7020.            xor     ax,ax           ; return to MS-DOS, in case
  7021.            push    ax              ; function 4ch can't be used
  7022.  
  7023.            mov     ax,_DATA        ; make our data segment
  7024.            mov     ds,ax           ; addressable via DS register
  7025.  
  7026.                                    ; check MS-DOS version
  7027.            mov     ax,3000h        ; function 30h = get version
  7028.            int     21h             ; transfer to MS-DOS
  7029.            cmp     al,2            ; major version 2 or later?
  7030.            jae     dump1           ; yes, proceed
  7031.  
  7032.                                    ; if MS-DOS 1.x, display
  7033.                                    ; error message and exit
  7034.            mov     dx,offset msg3  ; DS:DX = message address
  7035.            mov     ah,9            ; function 9 = print string
  7036.            int     21h             ; transfer to MS-DOS
  7037.            ret                     ; then exit the old way
  7038.  
  7039.    dump1:                          ; check if filename present
  7040.            mov     bx,offset cmd   ; ES:BX = command tail
  7041.            call    argc            ; count command arguments
  7042.            cmp     ax,2            ; are there 2 arguments?
  7043.            je      dump2           ; yes, proceed
  7044.  
  7045.                                    ; missing filename, display
  7046.                                    ; error message and exit
  7047.            mov     dx,offset msg2  ; DS:DX = message address
  7048.            mov     cx,msg2_len     ; CX = message length
  7049.            jmp     dump9           ; go display it
  7050.  
  7051.    dump2:                          ; get address of filename
  7052.            mov     ax,1            ; AX = argument number
  7053.                                    ; ES:BX still = command tail
  7054.            call    argv            ; returns ES:BX = address,
  7055.                                    ; and AX = length
  7056.  
  7057.            mov     di,offset fname ; copy filename to buffer
  7058.            mov     cx,ax           ; CX = length
  7059.    dump3:  mov     al,es:[bx]      ; copy one byte
  7060.            mov     [di],al
  7061.            inc     bx              ; bump string pointers
  7062.            inc     di
  7063.            loop    dump3           ; loop until string done
  7064.            mov     byte ptr [di],0 ; add terminal null byte
  7065.  
  7066.            mov     ax,ds           ; make our data segment
  7067.            mov     es,ax           ; addressable by ES too
  7068.                                    ; now open the file
  7069.            mov     ax,3d00h        ; function 3dh = open file
  7070.                                    ; mode 0 = read only
  7071.            mov     dx,offset fname ; DS:DX = filename
  7072.            int     21h             ; transfer to MS-DOS
  7073.            jnc     dump4           ; jump, open successful
  7074.  
  7075.                                    ; open failed, display
  7076.                                    ; error message and exit
  7077.            mov     dx,offset msg1  ; DS:DX = message address
  7078.            mov     cx,msg1_len     ; CX = message length
  7079.            jmp     dump9           ; go display it
  7080.  
  7081.    dump4:  mov     fhandle,ax      ; save file handle
  7082.  
  7083.    dump5:                          ; read block of file data
  7084.            mov     bx,fhandle      ; BX = file handle
  7085.            mov     cx,blksize      ; CX = record length
  7086.            mov     dx,offset fbuff ; DS:DX = buffer
  7087.            mov     ah,3fh          ; function 3fh = read
  7088.            int     21h             ; transfer to MS-DOS
  7089.  
  7090.            mov     flen,ax         ; save actual length
  7091.            cmp     ax,0            ; end of file reached?
  7092.            jne     dump6           ; no, proceed
  7093.  
  7094.            cmp     word ptr fptr,0 ; was this the first read?
  7095.            jne     dump8           ; no, exit normally
  7096.  
  7097.                                    ; display empty file
  7098.                                    ; message and exit
  7099.            mov     dx,offset msg4  ; DS:DX = message address
  7100.            mov     cx,msg4_len     ; CX = length
  7101.            jmp     dump9           ; go display it
  7102.    dump6:                          ; display heading at
  7103.                                    ; each 128-byte boundary
  7104.            test    fptr,07fh       ; time for a heading?
  7105.            jnz     dump7           ; no, proceed
  7106.  
  7107.                                    ; display a heading
  7108.            mov     dx,offset hdg   ; DS:DX = heading address
  7109.            mov     cx,hdg_len      ; CX = heading length
  7110.            mov     bx,stdout       ; BX = standard output
  7111.            mov     ah,40h          ; function 40h = write
  7112.            int     21h             ; transfer to MS-DOS
  7113.  
  7114.    dump7:  call    conv            ; convert binary record
  7115.                                    ; to formatted ASCII
  7116.  
  7117.                                    ; display formatted output
  7118.            mov     dx,offset fout  ; DX:DX = output address
  7119.            mov     cx,fout_len     ; CX = output length
  7120.            mov     bx,stdout       ; BX = standard output
  7121.            mov     ah,40h          ; function 40h = write
  7122.            int     21h             ; transfer to MS-DOS
  7123.            jmp     dump5           ; go get another record
  7124.  
  7125.    dump8:                          ; close input file
  7126.            mov     bx,fhandle      ; BX = file handle
  7127.            mov     ah,3eh          ; function 3eh = close
  7128.            int     21h             ; transfer to MS-DOS
  7129.  
  7130.            mov     ax,4c00h        ; function 4ch = terminate,
  7131.                                    ; return code = 0
  7132.            int     21h             ; transfer to MS-DOS
  7133.  
  7134.    dump9:                          ; display message on
  7135.                                    ; standard error device
  7136.                                    ; DS:DX = message address
  7137.                                    ; CX = message length
  7138.            mov     bx,stderr       ; standard error handle
  7139.            mov     ah,40h          ; function 40h = write
  7140.            int     21h             ; transfer to MS-DOS
  7141.  
  7142.            mov     ax,4c01h        ; function 4ch = terminate,
  7143.                                    ; return code = 1
  7144.            int     21h             ; transfer to MS-DOS
  7145.  
  7146.    dump    endp
  7147.    conv    proc    near            ; convert block of data
  7148.                                    ; from input file
  7149.  
  7150.            mov     di,offset fout  ; clear output format
  7151.            mov     cx,fout_len-2   ; area to blanks
  7152.            mov     al,blank
  7153.            rep stosb
  7154.  
  7155.            mov     di,offset fout  ; convert file offset
  7156.            mov     ax,fptr         ; to ASCII for output
  7157.            call    w2a
  7158.  
  7159.            mov     bx,0            ; init buffer pointer
  7160.  
  7161.    conv1:  mov     al,[fbuff+bx]   ; fetch byte from buffer
  7162.            mov     di,offset foutb ; point to output area
  7163.  
  7164.                                    ; format ASCII part...
  7165.                                    ; store '.' as default
  7166.            mov     byte ptr [di+bx],'.'
  7167.  
  7168.            cmp     al,blank        ; in range 20h-7eh?
  7169.            jb      conv2           ; jump, not alphanumeric
  7170.  
  7171.            cmp     al,7eh          ; in range 20h-7eh?
  7172.            ja      conv2           ; jump, not alphanumeric
  7173.  
  7174.            mov     [di+bx],al      ; store ASCII character
  7175.  
  7176.    conv2:                          ; format hex part...
  7177.            mov     di,offset fouta ; point to output area
  7178.            add     di,bx           ; base addr + (offset*3)
  7179.            add     di,bx
  7180.            add     di,bx
  7181.            call    b2a             ; convert byte to hex
  7182.  
  7183.            inc     bx              ; advance through record
  7184.            cmp     bx,flen         ; entire record converted?
  7185.            jne     conv1           ; no, get another byte
  7186.  
  7187.                                    ; update file pointer
  7188.            add     word ptr fptr,blksize
  7189.  
  7190.            ret
  7191.  
  7192.    conv    endp
  7193.    w2a     proc    near            ; convert word to hex ASCII
  7194.                                    ; call with AX = value
  7195.                                    ;           DI = addr for string
  7196.                                    ; returns AX, DI, CX destroyed
  7197.  
  7198.            push    ax              ; save copy of value
  7199.            mov     al,ah
  7200.            call    b2a             ; convert upper byte
  7201.  
  7202.            pop     ax              ; get back copy
  7203.            call    b2a             ; convert lower byte
  7204.            ret
  7205.  
  7206.    w2a     endp
  7207.  
  7208.    b2a     proc    near            ; convert byte to hex ASCII
  7209.                                    ; call with AL = binary value
  7210.                                    ;           DI = addr for string
  7211.                                    ; returns   AX, DI, CX modified
  7212.  
  7213.            sub     ah,ah           ; clear upper byte
  7214.            mov     cl,16
  7215.            div     cl              ; divide byte by 16
  7216.            call    ascii           ; quotient becomes the first
  7217.            stosb                   ; ASCII character
  7218.            mov     al,ah
  7219.            call    ascii           ; remainder becomes the
  7220.            stosb                   ; second ASCII character
  7221.            ret
  7222.  
  7223.    b2a     endp
  7224.  
  7225.    ascii   proc    near            ; convert value 0-0fh in AL
  7226.                                    ; into "hex ASCII" character
  7227.  
  7228.            add     al,'0'          ; offset to range 0-9
  7229.            cmp     al,'9'          ; is it > 9?
  7230.            jle     ascii2          ; no, jump
  7231.            add     al,'A'-'9'-1    ; offset to range A-F,
  7232.  
  7233.    ascii2: ret                     ; return AL = ASCII char
  7234.  
  7235.    ascii   endp
  7236.  
  7237.    argc    proc    near            ; count command-line arguments
  7238.                                    ; call with ES:BX = command line
  7239.                                    ; returns   AX = argument count
  7240.            push    bx              ; save original BX and CX
  7241.            push    cx              ; for later
  7242.            mov     ax,1            ; force count >= 1
  7243.  
  7244.    argc1:  mov     cx,-1           ; set flag = outside argument
  7245.  
  7246.    argc2:  inc     bx              ; point to next character
  7247.            cmp     byte ptr es:[bx],cr
  7248.            je      argc3           ; exit if carriage return
  7249.            cmp     byte ptr es:[bx],blank
  7250.            je      argc1           ; outside argument if ASCII blank
  7251.            cmp     byte ptr es:[bx],tab
  7252.            je      argc1           ; outside argument if ASCII tab
  7253.  
  7254.                                    ; otherwise not blank or tab,
  7255.            jcxz    argc2           ; jump if already inside argument
  7256.  
  7257.            inc     ax              ; else found argument, count it
  7258.            not     cx              ; set flag = inside argument
  7259.            jmp     argc2           ; and look at next character
  7260.  
  7261.    argc3:  pop     cx              ; restore original BX and CX
  7262.            pop     bx
  7263.            ret                     ; return AX = argument count
  7264.  
  7265.    argc    endp
  7266.  
  7267.    argv    proc    near            ; get address & length of
  7268.                                    ; command line argument
  7269.                                    ; call with ES:BX = command line
  7270.                                    ;           AX    = argument #
  7271.                                    ; returns   ES:BX = address
  7272.                                    ;           AX    = length
  7273.  
  7274.            push    cx              ; save original CX and DI
  7275.            push    di
  7276.  
  7277.            or      ax,ax           ; is it argument 0?
  7278.            jz      argv8           ; yes, jump to get program name
  7279.  
  7280.            xor     ah,ah           ; initialize argument counter
  7281.  
  7282.    argv1:  mov     cx,-1           ; set flag = outside argument
  7283.    argv2:  inc     bx              ; point to next character
  7284.            cmp     byte ptr es:[bx],cr
  7285.            je      argv7           ; exit if carriage return
  7286.            cmp     byte ptr es:[bx],blank
  7287.            je      argv1           ; outside argument if ASCII blank
  7288.            cmp     byte ptr es:[bx],tab
  7289.            je      argv1           ; outside argument if ASCII tab
  7290.  
  7291.                                    ; if not blank or tab...
  7292.            jcxz    argv2           ; jump if already inside argument
  7293.  
  7294.            inc     ah              ; else count arguments found
  7295.            cmp     ah,al           ; is this the one we're looking for?
  7296.            je      argv4           ; yes, go find its length
  7297.            not     cx              ; no, set flag = inside argument
  7298.            jmp     argv2           ; and look at next character
  7299.  
  7300.    argv4:                          ; found desired argument, now
  7301.                                    ; determine its length...
  7302.            mov     ax,bx           ; save param starting address
  7303.  
  7304.    argv5:  inc     bx              ; point to next character
  7305.            cmp     byte ptr es:[bx],cr
  7306.            je      argv6           ; found end if carriage return
  7307.            cmp     byte ptr es:[bx],blank
  7308.            je      argv6           ; found end if ASCII blank
  7309.            cmp     byte ptr es:[bx],tab
  7310.            jne     argv5           ; found end if ASCII tab
  7311.  
  7312.    argv6:  xchg    bx,ax           ; set ES:BX = argument address
  7313.            sub     ax,bx           ; and AX = argument length
  7314.            jmp     argvx           ; return to caller
  7315.  
  7316.    argv7:  xor     ax,ax           ; set AX = 0, argument not found
  7317.            jmp     argvx           ; return to caller
  7318.  
  7319.    argv8:                          ; special handling for argv = 0
  7320.            mov     ax,3000h        ; check if DOS 3.0 or later
  7321.            int     21h             ; (force AL = 0 in case DOS 1)
  7322.            cmp     al,3
  7323.            jb      argv7           ; DOS 1 or 2, return null param
  7324.            mov     es,es:[2ch]     ; get environment segment from PSP
  7325.            xor     di,di           ; find the program name by
  7326.            xor     al,al           ; first skipping over all the
  7327.            mov     cx,-1           ; environment variables...
  7328.            cld
  7329.    argv9:  repne scasb             ; scan for double null (can't use
  7330.            scasb                   ; SCASW since might be odd addr)
  7331.            jne     argv9           ; loop if it was a single null
  7332.            add     di,2            ; skip count word in environment
  7333.            mov     bx,di           ; save program name address
  7334.            mov     cx,-1           ; now find its length...
  7335.            repne scasb             ; scan for another null byte
  7336.            not     cx              ; convert CX to length
  7337.            dec     cx
  7338.            mov     ax,cx           ; return length in AX
  7339.  
  7340.    argvx:                          ; common exit point
  7341.            pop     di              ; restore original CX and DI
  7342.            pop     cx
  7343.            ret                     ; return to caller
  7344.  
  7345.    argv    endp
  7346.  
  7347.    _TEXT    ends
  7348.  
  7349.    _DATA   segment word public 'DATA'
  7350.  
  7351.    fname   db      64 dup (0)      ; buffer for input filespec
  7352.  
  7353.    fhandle dw      0               ; token from PCDOS for input file
  7354.  
  7355.    flen    dw      0               ; actual length read
  7356.  
  7357.    fptr    dw      0               ; relative address in file
  7358.  
  7359.    fbuff   db      blksize dup (?) ; data from input file
  7360.  
  7361.    fout    db      'nnnn'          ; formatted output area
  7362.            db      blank,blank
  7363.    fouta   db      16 dup ('nn',blank)
  7364.            db      blank
  7365.    foutb   db      16 dup (blank),cr,lf
  7366.    fout_len equ    $-fout
  7367.  
  7368.    hdg     db      cr,lf           ; heading for each 128 bytes
  7369.            db      7 dup (blank)   ; of formatted output
  7370.            db      '0  1  2  3  4  5  6  7  '
  7371.            db      '8  9  A  B  C  D  E  F',cr,lf
  7372.    hdg_len equ     $-hdg
  7373.    msg1    db      cr,lf
  7374.            db      'dump: file not found'
  7375.            db      cr,lf
  7376.    msg1_len equ    $-msg1
  7377.  
  7378.    msg2    db      cr,lf
  7379.            db      'dump: missing file name'
  7380.            db      cr,lf
  7381.    msg2_len equ    $-msg2
  7382.  
  7383.    msg3    db      cr,lf
  7384.            db      'dump: wrong MS-DOS version'
  7385.            db      cr,lf,'$'
  7386.  
  7387.    msg4    db      cr,lf
  7388.            db      'dump: empty file'
  7389.            db      cr,lf
  7390.    msg4_len equ    $-msg4
  7391.  
  7392.    _DATA   ends
  7393.  
  7394.    STACK   segment para stack 'STACK'
  7395.  
  7396.            db      64 dup (?)
  7397.  
  7398.    STACK   ends
  7399.  
  7400.            end     dump
  7401.    ──────────────────────────────────────────────────────────────────────────
  7402.  
  7403.    Figure 8-10.  The assembly-language version: DUMP.ASM.
  7404.  
  7405.    ──────────────────────────────────────────────────────────────────────────
  7406.    /*
  7407.        DUMP.C      Displays the binary contents of a file in
  7408.                    hex and ASCII on the standard output device.
  7409.  
  7410.        Compile:    C>CL DUMP.C
  7411.  
  7412.        Usage:      C>DUMP unit:path\filename.ext
  7413.  
  7414.        Copyright (C) 1988 Ray Duncan
  7415.    */
  7416.  
  7417.    #include <stdio.h>
  7418.    #include <io.h>
  7419.    #include <fcntl.h>
  7420.    #define REC_SIZE 16               /* input file record size    */
  7421.  
  7422.    main(int argc, char *argv[])
  7423.    {
  7424.        int fd;                       /* input file handle         */
  7425.          int status = 0;             /* status from file read     */
  7426.        long fileptr = 0L;            /* current file byte offset  */
  7427.        char filebuf[REC_SIZE];       /* data from file            */
  7428.  
  7429.        if(argc != 2)                 /* abort if missing filename */
  7430.        {   fprintf(stderr,"\ndump: wrong number of parameters\n");
  7431.            exit(1);
  7432.        }
  7433.  
  7434.                                      /* open file in binary mode,
  7435.                                         abort if open fails       */
  7436.        if((fd = open(argv[1],O_RDONLY | O_BINARY) ) == -1)
  7437.        {   fprintf(stderr, "\ndump: can't find file %s \n", argv[1]);
  7438.            exit(1);
  7439.        }
  7440.  
  7441.                                      /* read and dump records
  7442.                                         until end of file         */
  7443.        while((status = read(fd,filebuf,REC_SIZE) ) != 0)
  7444.        {   dump_rec(filebuf, fileptr, status);
  7445.            fileptr += REC_SIZE;
  7446.        }
  7447.  
  7448.        close(fd);                    /* close input file          */
  7449.        exit(0);                      /* return success code       */
  7450.    }
  7451.  
  7452.    /*
  7453.        Display record (16 bytes) in hex and ASCII on standard output
  7454.    */
  7455.  
  7456.    dump_rec(char *filebuf, long fileptr, int length)
  7457.    {
  7458.        int i;                        /* index to current record   */
  7459.  
  7460.        if(fileptr % 128 == 0)        /* display heading if needed */
  7461.            printf("\n\n       0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F")
  7462.  
  7463.        printf("\n%04lX ",fileptr);   /* display file offset       */
  7464.  
  7465.                                      /* display hex equivalent of
  7466.                                         each byte from file       */
  7467.        for(i = 0; i < length; i++)
  7468.            printf(" %02X", (unsigned char) filebuf[i]);
  7469.  
  7470.        if(length != 16)              /* spaces if partial record  */
  7471.            for (i=0; i<(16-length); i++) printf("   ");
  7472.  
  7473.                                      /* display ASCII equivalent of
  7474.                                         each byte from file       */
  7475.        printf("  ");
  7476.        for(i = 0; i < length; i++)
  7477.        {   if(filebuf[i] < 32 || filebuf[i] > 126) putchar('.');
  7478.            else putchar(filebuf[i]);
  7479.        }
  7480.    }
  7481.    ──────────────────────────────────────────────────────────────────────────
  7482.  
  7483.    Figure 8-11.  The C version: DUMP.C.
  7484.  
  7485.    The assembly-language version of the DUMP program contains a number of
  7486.    subroutines that you may find useful in your own programming efforts.
  7487.    These include the following:
  7488.  
  7489.    Subroutine  Action
  7490.    ──────────────────────────────────────────────────────────────────────────
  7491.    argc        Returns the number of command-line arguments.
  7492.    argv        Returns the address and length of a particular command-line
  7493.                argument.
  7494.    w2a         Converts a binary word (16 bits) into hex ASCII for output.
  7495.    b2a         Converts a binary byte (8 bits) into hex ASCII for output.
  7496.    ascii       Converts 4 bits into a single hex ASCII character.
  7497.    ──────────────────────────────────────────────────────────────────────────
  7498.  
  7499.    It is interesting to compare these two equivalent programs. The C program
  7500.    contains only 77 lines, whereas the assembly-language program has 436
  7501.    lines. Clearly, the C source code is less complex and easier to maintain.
  7502.    On the other hand, if size and efficiency are important, the DUMP.EXE file
  7503.    generated by the C compiler is 8563 bytes, whereas the assembly-language
  7504.    DUMP.EXE file is only 1294 bytes and runs twice as fast as the C program.
  7505.  
  7506.  
  7507.  
  7508.  ────────────────────────────────────────────────────────────────────────────
  7509.  Chapter 9  Volumes and Directories
  7510.  
  7511.    Each file in an MS-DOS system is uniquely identified by its name and its
  7512.    location. The location, in turn, has two components: the logical drive
  7513.    that contains the file and the directory on that drive where the filename
  7514.    can be found.
  7515.  
  7516.    Logical drives are specified by a single letter followed by a colon (for
  7517.    example, A:). The number of logical drives in a system is not necessarily
  7518.    the same as the number of physical drives; for example, it is common for
  7519.    large fixed-disk drives to be divided into two or more logical drives. The
  7520.    key aspect of a logical drive is that it contains a self-sufficient file
  7521.    system; that is, it contains one or more directories, zero or more
  7522.    complete files, and all the information needed to locate the files and
  7523.    directories and to determine which disk space is free and which is already
  7524.    in use.
  7525.  
  7526.    Directories are simply lists or catalogs. Each entry in a directory
  7527.    consists of the name, size, starting location, attributes, and last
  7528.    modification date and time of a file or another directory that the disk
  7529.    contains. The detailed information about the location of every block of
  7530.    data assigned to a file or directory is in a separate control area on the
  7531.    disk called the file allocation table (FAT). (See Chapter 10 for a
  7532.    detailed discussion of the internal format of directories and the FAT.)
  7533.  
  7534.    Every disk potentially has two distinct kinds of directories: the root
  7535.    directory and all other directories. The root directory is always present
  7536.    and has a maximum number of entries, determined when the disk is
  7537.    formatted; this number cannot be changed. The subdirectories of the root
  7538.    directory, which may or may not be present on a given disk, can be nested
  7539.    to any level and can grow to any size (Figure 9-1). This is the
  7540.    hierarchical, or tree, directory structure referred to in earlier
  7541.    chapters. Every directory has a name, except for the root directory, which
  7542.    is designated by a single backslash (\) character.
  7543.  
  7544.    MS-DOS keeps track of a "current drive" for the system and uses this drive
  7545.    when a file specification does not include an explicit drive code.
  7546.    Similarly, MS-DOS maintains a "current directory" for each logical drive.
  7547.    You can select any particular directory on a drive by naming in order──
  7548.    either from the root directory or relative to the current directory──the
  7549.    directories that lead to its location in the tree structure. Such a list
  7550.    of directories, separated by backslash delimiters, is called a path. When
  7551.    a complete path from the root directory is prefixed by a logical drive
  7552.    code and followed by a filename and extension, the resulting string is a
  7553.    fully qualified filename and unambiguously specifies a file.
  7554.  
  7555.                             ┌────────────┐
  7556.                             │   Drive    │
  7557.                             │ identifier │
  7558.                             └─────┬──────┘
  7559.                                   │
  7560.                           ┌───────┴────────┐
  7561.                           │ Root directory │
  7562.                           │ (volume label) │
  7563.                           └─┬──┬──┬───┬──┬─┘
  7564.         ┌───────────────────┘  │  │   │  └───────────────────┐
  7565.         │          ┌───────────┘  │   └───────────┐          │
  7566.    ┌────┴───┐ ┌────┴──────┐   ┌───┴────┐   ┌──────┴────┐ ┌───┴────┐
  7567.    │ File A │ │ Directory │   │ File B │   │ Directory │ │ File C │
  7568.    └────────┘ └─┬───────┬─┘   └────────┘   └─┬─────────┘ └─┬──────┘
  7569.                 │       │                    │             │
  7570.                 │       │                    │             │
  7571.           ┌─────┘       │                    │             │
  7572.           │             │                    │             │
  7573.      ┌────┴──────┐   ┌──┴─────┐        ┌─────┴──┐      ┌───┴────┐
  7574.      │ Directory │   │ File D │        │ File E │      │ File F │
  7575.      └───────────┘   └────────┘        └────────┘      └────────┘
  7576.  
  7577.    Figure 9-1.  An MS-DOS file-system structure.
  7578.  
  7579.  
  7580.  Drive and Directory Control
  7581.  
  7582.    You can examine, select, create, and delete disk directories interactively
  7583.    with the DIR, CHDIR (CD), MKDIR (MD), and RMDIR (RD) commands. You can
  7584.    select a new current drive by entering the letter of the desired drive,
  7585.    followed by a colon. MS-DOS provides the following Int 21H functions to
  7586.    give application programs similar control over drives and directories:
  7587.  
  7588.    Function                 Action
  7589.    ──────────────────────────────────────────────────────────────────────────
  7590.    0EH                     Select current drive.
  7591.    19H                     Get current drive.
  7592.    39H                     Create directory.
  7593.    3AH                     Remove directory.
  7594.    3BH                     Select current directory.
  7595.    47H                     Get current directory.
  7596.    ──────────────────────────────────────────────────────────────────────────
  7597.  
  7598.    The two functions that deal with disk drives accept or return a binary
  7599.    drive code──0 represents drive A, 1 represents drive B, and so on. This
  7600.    differs from most other MS-DOS functions, which use 0 to indicate the
  7601.    current drive, 1 for drive A, and so on.
  7602.  
  7603.    The first three directory functions in the preceding list require an
  7604.    ASCIIZ string that describes the path to the desired directory. As with
  7605.    the handle-based file open and create functions, the address of the ASCIIZ
  7606.    string is passed in the DS:DX registers. On return, the carry flag is
  7607.    clear if the function succeeds or set if the function failed, with an
  7608.    error code in the AX register. The directory functions can fail for a
  7609.    variety of reasons, but the most common cause of an error is that some
  7610.    element of the indicated path does not exist.
  7611.  
  7612.    The last function in the preceding list, Int 21H Function 47H, allows you
  7613.    to obtain an ASCIIZ path for the current directory on the specified or
  7614.    default drive. MS-DOS supplies the path string without the drive
  7615.    identifier or a leading backslash. Int 21H Function 47H is most commonly
  7616.    used with Int 21H Function 19H to build fully qualified filenames. Such
  7617.    filenames are desirable because they remain valid if the user changes the
  7618.    current drive or directory.
  7619.  
  7620.    Section 2 of this book, "MS-DOS Functions Reference," gives detailed
  7621.    information on the drive and directory control functions.
  7622.  
  7623.  Searching Directories
  7624.  
  7625.    When you request an open operation on a file, you are implicitly
  7626.    performing a search of a directory. MS-DOS examines each entry of the
  7627.    directory to find a match for the filename you have given as an argument;
  7628.    if the file is found, MS-DOS copies certain information from the directory
  7629.    into a data structure that it can use to control subsequent read or write
  7630.    operations to the file. Thus, if you wish to test for the existence of a
  7631.    specific file, you need only perform an open operation and observe whether
  7632.    it is successful. (If it is, you should, of course, perform a subsequent
  7633.    close operation to avoid needless expenditure of handles.)
  7634.  
  7635.    Sometimes you may need to perform more elaborate searches of a disk
  7636.    directory. Perhaps you wish to find all the files with a certain
  7637.    extension, a file with a particular attribute, or the names of the
  7638.    subdirectories of a certain directory. Although the locations of a disk's
  7639.    directories and the specifics of the entries that are found in them are of
  7640.    necessity hardware dependent (for example, interpretation of the field
  7641.    describing the starting location of a file depends upon the physical disk
  7642.    format), MS-DOS does provide functions that will allow examination of a
  7643.    disk directory in a hardware-independent fashion.
  7644.  
  7645.    In order to search a disk directory successfully, you must understand two
  7646.    types of MS-DOS search services. The first type is the "search for first"
  7647.    function, which accepts a file specification──possibly including wildcard
  7648.    characters──and looks for the first matching file in the directory of
  7649.    interest. If it finds a match, the function fills a buffer owned by the
  7650.    requesting program with information about the file; if it does not find a
  7651.    match, it returns an error flag.
  7652.  
  7653.    A program can call the second type of search service, called "search for
  7654.    next," only after a successful "search for first." If the file
  7655.    specification that was originally passed to "search for first" included
  7656.    wildcard characters and at least one matching file was present, the
  7657.    program can call "search for next" as many times as necessary to find all
  7658.    additional matching files. Like "search for first," "search for next"
  7659.    returns information about the matched files in a buffer designated by the
  7660.    requesting program. When it can find no more matching files, "search for
  7661.    next" returns an error flag.
  7662.  
  7663.    As with nearly every other operation, MS-DOS provides two parallel sets of
  7664.    directory-searching services:
  7665.  
  7666.    Action             FCB function      Handle function
  7667.    ──────────────────────────────────────────────────────────────────────────
  7668.    Search for first   11H               4EH
  7669.    Search for next    12H               4FH
  7670.    ──────────────────────────────────────────────────────────────────────────
  7671.  
  7672.    The FCB directory functions allow searches to match a filename and
  7673.    extension, both possibly containing wildcard characters, within the
  7674.    current directory for the specified or current drive. The handle directory
  7675.    functions, on the other hand, allow a program to perform searches within
  7676.    any directory on any drive, regardless of the current directory.
  7677.  
  7678.    Searches that use normal FCBs find only normal files. Searches that use
  7679.    extended FCBs, or the handle-type functions, can be qualified with file
  7680.    attributes. The attribute bits relevant to searches are as follows:
  7681.  
  7682.    Bit                      Significance
  7683.    ──────────────────────────────────────────────────────────────────────────
  7684.    0                        Read-only file
  7685.    1                        Hidden file
  7686.    2                        System file
  7687.    3                        Volume label
  7688.    4                        Directory
  7689.    5                        Archive needed (set when file modified)
  7690.    ──────────────────────────────────────────────────────────────────────────
  7691.  
  7692.    The remaining bits of a search function's attribute parameter should be
  7693.    zero. When any of the preceding attribute bits are set, the search
  7694.    function returns all normal files plus any files with the specified
  7695.    attributes, except in the case of the volume-label attribute bit, which
  7696.    receives special treatment as described later in this chapter. Note that
  7697.    by setting bit 4 you can include directories in a search, exactly as
  7698.    though they were files.
  7699.  
  7700.    Both the FCB and handle directory-searching functions require that the
  7701.    disk transfer area address be set (with Int 21H Function 1AH), before the
  7702.    call to "search for first," to point to a working buffer for use by
  7703.    MS-DOS. The DTA address should not be changed between calls to "search for
  7704.    first" and "search for next." When it finds a matching file, MS-DOS places
  7705.    the information about the file in the buffer and then inspects the buffer
  7706.    on the next "search for next" call, to determine where to resume the
  7707.    search. The format of the data returned in the buffer is different for the
  7708.    FCB and handle functions, so read the detailed descriptions in Section 2
  7709.    of this book, "MS-DOS Functions Reference," before attempting to interpret
  7710.    the buffer contents.
  7711.  
  7712.    Figures 9-2 and 9-3 provide equivalent examples of searches for all
  7713.    files in a given directory that have the .ASM extension, one example using
  7714.    the FCB directory functions (Int 21H Functions 11H and 12H) and the
  7715.    other using the handle functions (Int 21H Functions 4EH and 4FH). (Both
  7716.    programs use the handle write function with the standard output handle to
  7717.    display the matched filenames, to avoid introducing tangential differences
  7718.    in the listings.)
  7719.  
  7720.    ──────────────────────────────────────────────────────────────────────────
  7721.    start:                          ; set DTA address for buffer
  7722.                                    ; used by search functions
  7723.            mov     dx,seg buff     ; DS:DX = buffer address
  7724.            mov     ds,dx
  7725.            mov     dx,offset buff
  7726.            mov     ah,1ah          ; function 1ah = search for first
  7727.            int     21h             ; transfer to MS-DOS
  7728.                                    ; search for first match...
  7729.            mov     dx,offset fcb   ; DS:DX = FCB address
  7730.            mov     ah,11h          ; function 11h = search for first
  7731.            int     21h             ; transfer to MS-DOS
  7732.            or      al,al           ; any matches at all?
  7733.            jnz     exit            ; no, quit
  7734.  
  7735.    disp:                           ; go to a new line...
  7736.            mov     dx,offset crlf  ; DS:DX = CR-LF string
  7737.            mov     cx,2            ; CX = string length
  7738.            mov     bx,1            ; BX = standard output handle
  7739.            mov     ah,40h          ; function 40h = write
  7740.            int     21h             ; transfer to MS-DOS
  7741.  
  7742.                                    ; display matching file
  7743.            mov     dx,offset buff+1 ; DS:DX = filename
  7744.            mov     cx,11           ; CX = length
  7745.            mov     bx,1            ; BX = standard output handle
  7746.            mov     ah,40h          ; function 40h = write
  7747.            int     21h             ; transfer to MS-DOS
  7748.  
  7749.                                    ; search for next match...
  7750.            mov     dx,offset fcb   ; DS:DX = FCB address
  7751.            mov     ah,12h          ; function 12h = search for next
  7752.            int     21h             ; transfer to MS-DOS
  7753.            or      al,al           ; any more matches?
  7754.            jz      disp            ; yes, go show filename
  7755.  
  7756.    exit:                           ; final exit point
  7757.            mov     ax,4c00h        ; function 4ch = terminate,
  7758.                                    ; return code = 0
  7759.            int     21h             ; transfer to MS-DOS
  7760.  
  7761.            .
  7762.            .
  7763.            .
  7764.  
  7765.    crlf    db      0dh,0ah         ; ASCII carriage return-
  7766.                                    ; linefeed string
  7767.  
  7768.    fcb     db      0               ; drive = current
  7769.            db      8 dup ('?')     ; filename = wildcard
  7770.            db      'ASM'           ; extension = ASM
  7771.            db      25 dup (0)      ; remainder of FCB = zero
  7772.  
  7773.    buff    db      64 dup (0)      ; receives search results
  7774.    ──────────────────────────────────────────────────────────────────────────
  7775.  
  7776.    Figure 9-2.  Example of an FCB-type directory search using Int 21H
  7777.    Functions 11H and 12H. This routine displays the names of all files in
  7778.    the current directory that have the .ASM extension.
  7779.  
  7780.    ──────────────────────────────────────────────────────────────────────────
  7781.    start:                          ; set DTA address for buffer
  7782.                                    ; used by search functions
  7783.            mov     dx,seg buff     ; DS:DX = buffer address
  7784.            mov     ds,dx
  7785.            mov     dx,offset buff
  7786.            mov     ah,1ah          ; function 1ah = search for first
  7787.            int     21h             ; transfer to MS-DOS
  7788.  
  7789.                                    ; search for first match...
  7790.            mov     dx,offset fname ; DS:DX = wildcard filename
  7791.            mov     cx,0            ; CX = normal file attribute
  7792.            mov     ah,4eh          ; function 4eh = search for first
  7793.            int     21h             ; transfer to MS-DOS
  7794.            jc      exit            ; quit if no matches at all
  7795.  
  7796.    disp:                           ; go to a new line...
  7797.            mov     dx,offset crlf  ; DS:DX = CR-LF string
  7798.            mov     cx,2            ; CX = string length
  7799.            mov     bx,1            ; BX = standard output handle
  7800.            mov     ah,40h          ; function 40h = write
  7801.            int     21h             ; transfer to MS-DOS
  7802.                                    ; find length of filename...
  7803.            mov     cx,0            ; CX will be char count
  7804.                                    ; DS:SI = start of name
  7805.            mov     si,offset buff+30
  7806.  
  7807.    disp1:  lodsb                   ; get next character
  7808.            or      al,al           ; is it null character?
  7809.            jz      disp2           ; yes, found end of string
  7810.            inc     cx              ; else count characters
  7811.            jmp     disp1           ; and get another
  7812.  
  7813.    disp2:                          ; display matching file...
  7814.                                    ; CX already contains length
  7815.                                    ; DS:DX = filename
  7816.            mov     dx,offset buff+30
  7817.            mov     bx,1            ; BX = standard output handle
  7818.            mov     ah,40h          ; function 40h = write
  7819.            int     21h             ; transfer to MS-DOS
  7820.                                    ; find next matching file...
  7821.            mov     ah,4fh          ; function 4fh = search for next
  7822.            int     21h             ; transfer to MS-DOS
  7823.            jnc     disp            ; jump if another match found
  7824.  
  7825.    exit:                           ; final exit point
  7826.            mov     ax,4c00h        ; function 4ch = terminate,
  7827.                                    ; return code = 0
  7828.            int     21h             ; transfer to MS-DOS
  7829.  
  7830.            .
  7831.            .
  7832.            .
  7833.  
  7834.    crlf    db      0dh,0ah         ; ASCII carriage return-
  7835.                                    ; linefeed string
  7836.  
  7837.    fname   db      '*.ASM',0       ; ASCIIZ filename to
  7838.                                    ; be matched
  7839.  
  7840.    buff    db      64 dup (0)      ; receives search results
  7841.    ──────────────────────────────────────────────────────────────────────────
  7842.  
  7843.    Figure 9-3.  Example of a handle-type directory search using Int 21H
  7844.    Functions 4EH and 4FH. This routine also displays the names of all files
  7845.    in the current directory that have a .ASM extension.
  7846.  
  7847.  Moving Files
  7848.  
  7849.    The rename file function that was added in MS-DOS version 2.0, Int 21H
  7850.    Function 56H, has the little-advertised capability to move a file from
  7851.    one directory to another. The function has two ASCIIZ parameters: the
  7852.    "old" and "new" names for the file. If the old and new paths differ,
  7853.    MS-DOS moves the file; if the filename or extension components differ,
  7854.    MS-DOS renames the file. MS-DOS can carry out both of these actions in the
  7855.    same function call.
  7856.  
  7857.    Of course, the old and new directories must be on the same drive, because
  7858.    the file's actual data is not moved at all; only the information that
  7859.    describes the file is removed from one directory and placed in another
  7860.    directory. Function 56H fails if the two ASCIIZ strings include different
  7861.    logical-drive codes, if the file is read-only, or if a file with the same
  7862.    name and location as the "new" filename already exists.
  7863.  
  7864.    The FCB-based rename file service, Int 21H Function 17H, works only on
  7865.    the current directory and cannot be used to move files.
  7866.  
  7867.  
  7868.  Volume Labels
  7869.  
  7870.    Support for volume labels was first added to MS-DOS in version 2.0. A
  7871.    volume label is an optional name of from 1 to 11 characters that the user
  7872.    assigns to a disk during a FORMAT operation. You can display a volume
  7873.    label with the DIR, TREE, CHKDSK, or VOL command. Beginning with MS-DOS
  7874.    version 3.0, you can use the LABEL command to add, display, or alter the
  7875.    label after formatting. In MS-DOS version 4, the FORMAT program also
  7876.    assigns a semi-random 32-bit binary ID to each disk it formats; you can
  7877.    display this value, but you cannot change it.
  7878.  
  7879.    The distinction between volumes and drives is important. A volume label is
  7880.    associated with a specific storage medium. A drive identifier (such as A)
  7881.    is associated with a physical device that a storage medium can be mounted
  7882.    on. In the case of fixed-disk drives, the medium associated with a drive
  7883.    identifier does not change (hence the name). In the case of floppy disks
  7884.    or other removable media, the disk accessed with a given drive identifier
  7885.    might have any volume label or none at all.
  7886.  
  7887.    Hence, volume labels do not take the place of the logical-drive identifier
  7888.    and cannot be used as part of a pathname to identify a file. In fact, in
  7889.    MS-DOS version 2, the system does not use volume labels internally at all.
  7890.    In MS-DOS versions 3.0 and later, a disk driver can use volume labels to
  7891.    detect whether the user has replaced a disk while a file is open; this use
  7892.    is optional, however, and is not implemented in all systems.
  7893.  
  7894.    MS-DOS volume labels are implemented as a special type of entry in a
  7895.    disk's root directory. The entry contains a time-and-date stamp and has an
  7896.    attribute value of 8 (i.e., bit 3 set). Except for the attribute, a volume
  7897.    label is identical to the directory entry for a file that was created but
  7898.    never had any data written into it, and you can manipulate volume labels
  7899.    with Int 21H functions much as you manipulate files. However, a volume
  7900.    label receives special handling at several levels:
  7901.  
  7902.    ■  When you create a volume label after a disk is formatted, MS-DOS always
  7903.       places it in the root directory, regardless of the current directory.
  7904.  
  7905.    ■  A disk can contain only one volume label; attempts to create additional
  7906.       volume labels (even with different names) will fail.
  7907.  
  7908.    ■  MS-DOS always carries out searches for volume labels in the root
  7909.       directory, regardless of the current directory, and does not also
  7910.       return all normal files.
  7911.  
  7912.    In MS-DOS version 2, support for volume labels is not completely
  7913.    integrated into the handle file functions, and you must use extended FCBs
  7914.    instead to manipulate volume labels. For example, the code in Figure 9-4
  7915.    searches for the volume label in the root directory of the current drive.
  7916.    You can also change volume labels with extended FCBs and the rename file
  7917.    function (Int 21H Function 17H), but you should not attempt to remove an
  7918.    existing volume label with Int 21H Function 13H under MS-DOS version 2,
  7919.    because this operation can damage the disk's FAT in an unpredictable
  7920.    manner.
  7921.  
  7922.    In MS-DOS versions 3.0 and later, you can create a volume label in the
  7923.    expected manner, using Int 21H Function 3CH and an attribute of 8, and
  7924.    you can use the handle-type "search for first" function (4EH) to obtain
  7925.    an existing volume label for a logical drive (Figure 9-5). However, you
  7926.    still must use extended FCBs to change a volume label.
  7927.  
  7928.    ──────────────────────────────────────────────────────────────────────────
  7929.    buff    db      64 dup (?)   ; receives search results
  7930.  
  7931.    xfcb    db      0ffh         ; flag signifying extended FCB
  7932.            db      5 dup (0)    ; reserved
  7933.            db      8            ; volume attribute byte
  7934.            db      0            ; drive code (0 = current)
  7935.            db      11 dup ('?') ; wildcard filename and extension
  7936.            db      25 dup (0)   ; remainder of FCB (not used)
  7937.            .
  7938.            .
  7939.            .
  7940.                                 ; set DTA address for buffer
  7941.                                 ; used by search functions
  7942.            mov     dx,seg buff  ; DS:DX = buffer address
  7943.            mov     ds,dx
  7944.            mov     dx,offset buff
  7945.            mov     ah,1ah       ; function 1ah = set DTA
  7946.            int     21h          ; transfer to MS-DOS
  7947.  
  7948.                                 ; now search for label...
  7949.                                 ; DS:DX = extended FCB
  7950.            mov     dx,offset xfcb
  7951.            mov     ah,11h       ; function 11h = search for first
  7952.            int     21h          ; transfer to MS-DOS
  7953.            cmp     al,0ffh      ; search successful?
  7954.            je      no_label     ; jump if no volume label
  7955.            .
  7956.            .
  7957.            .
  7958.    ──────────────────────────────────────────────────────────────────────────
  7959.  
  7960.    Figure 9-4.  A volume-label search under MS-DOS version 2, using an
  7961.    extended file control block. If the search is successful, the volume label
  7962.    is returned in buff, formatted in the filename and extension fields of an
  7963.    extended FCB.
  7964.  
  7965.    ──────────────────────────────────────────────────────────────────────────
  7966.    buff    db      64 dup (?)   ; receives search results
  7967.  
  7968.    wildcd  db      '*.*',0      ; wildcard ASCIIZ filename
  7969.            .
  7970.            .
  7971.            .
  7972.                                 ; set DTA address for buffer
  7973.                                 ; used by search functions
  7974.            mov     dx,seg buff  ; DS:DX = buffer address
  7975.            mov     ds,dx
  7976.            mov     dx,offset buff
  7977.            mov     ah,1ah       ; function 1ah = set DTA
  7978.            int     21h          ; transfer to MS-DOS
  7979.  
  7980.                                 ; now search for label...
  7981.                                 ; DS:DX = ASCIIZ string
  7982.            mov     dx,offset wildcd
  7983.            mov     cx,8         ; CX = volume attribute
  7984.            mov     ah,4eh       ; function 4eh = search for first
  7985.            int     21h          ; transfer to MS-DOS
  7986.            jc      no_label     ; jump if no volume label
  7987.            .
  7988.            .
  7989.            .
  7990.    ──────────────────────────────────────────────────────────────────────────
  7991.  
  7992.    Figure 9-5.  A volume-label search under MS-DOS version 3, using the
  7993.    handle-type file functions. If the search is successful (carry flag
  7994.    returned clear), the volume name is placed at location buff+1EH in the
  7995.    form of an ASCIIZ string.
  7996.  
  7997.  
  7998.  
  7999.  ────────────────────────────────────────────────────────────────────────────
  8000.  Chapter 10  Disk Internals
  8001.  
  8002.    MS-DOS disks are organized according to a rather rigid scheme that is
  8003.    easily understood and therefore easily manipulated. Although you will
  8004.    probably never need to access the special control areas of a disk
  8005.    directly, an understanding of their internal structure leads to a better
  8006.    understanding of the behavior and performance of MS-DOS as a whole.
  8007.  
  8008.    From the application programmer's viewpoint, MS-DOS presents disk devices
  8009.    as logical volumes that are associated with a drive code (A, B, C, and so
  8010.    on) and that have a volume name (optional), a root directory, and from
  8011.    zero to many additional directories and files. MS-DOS shields the
  8012.    programmer from the physical characteristics of the medium by providing a
  8013.    battery of disk services through Int 21H. Using these services, the
  8014.    programmer can create, open, read, write, close, and delete files in a
  8015.    uniform way, regardless of the disk drive's size, speed, number of
  8016.    read/write heads, number of tracks, and so forth.
  8017.  
  8018.    Requests from an application program for file operations actually go
  8019.    through two levels of translation before resulting in the physical
  8020.    transfer of data between the disk device and random-access memory:
  8021.  
  8022.    1.  Beneath the surface, MS-DOS views each logical volume, whether it is
  8023.        an entire physical unit such as a floppy disk or only a part of a
  8024.        fixed disk, as a continuous sequence of logical sectors, starting at
  8025.        sector 0. (A logical disk volume can also be implemented on other
  8026.        types of storage. For example, RAM disks map a disk structure onto an
  8027.        area of random-access memory.) MS-DOS translates an application
  8028.        program's Int 21H file-management requests into requests for transfers
  8029.        of logical sectors, using the information found in the volume's
  8030.        directories and allocation tables. (For those rare situations where it
  8031.        is appropriate, programs can also access logical sectors directly with
  8032.        Int 25H and Int 26H.)
  8033.  
  8034.    2.  MS-DOS then passes the requests for logical sectors to the disk
  8035.        device's driver, which maps them onto actual physical addresses (head,
  8036.        track, and sector). Disk drivers are extremely hardware dependent and
  8037.        are always written in assembly language for maximum speed. In most
  8038.        versions of MS-DOS, a driver for IBM-compatible floppy- and fixed-disk
  8039.        drives is built into the MS-DOS BIOS module (IO.SYS) and is always
  8040.        loaded during system initialization; you can install additional
  8041.        drivers for non-IBM-compatible disk devices by including the
  8042.        appropriate DEVICE directives in the CONFIG.SYS file.
  8043.  
  8044.    Each MS-DOS logical volume is divided into several fixed-size control
  8045.    areas and a files area (Figure 10-1). The size of each control area
  8046.    depends on several factors──the size of the volume and the version of
  8047.    FORMAT used to initialize the volume, for example──but all of the
  8048.    information needed to interpret the structure of a particular logical
  8049.    volume can be found on the volume itself in the boot sector.
  8050.  
  8051.    ┌───────────────────────────────────────────────────────┐
  8052.    │                      Boot sector                      │
  8053.    │                     Reserved area                     │
  8054.    ├───────────────────────────────────────────────────────┤
  8055.    │               File allocation table #1                │
  8056.    ├───────────────────────────────────────────────────────┤
  8057.    │           Possible additional copies of FAT           │
  8058.    ├───────────────────────────────────────────────────────┤
  8059.    │                    Root directory                     │
  8060.    ├───────────────────────────────────────────────────────┤
  8061.    │                                                       │
  8062.    │                      Files area                       │
  8063.    │                                                       │
  8064.    └───────────────────────────────────────────────────────┘
  8065.  
  8066.    Figure 10-1.  Map of a typical MS-DOS logical volume. The boot sector
  8067.    (logical sector 0) contains the OEM identification, BIOS parameter block
  8068.    (BPB), and disk bootstrap. The remaining sectors are divided among an
  8069.    optional reserved area, one or more copies of the file allocation table,
  8070.    the root directory, and the files area.
  8071.  
  8072.  
  8073.  The Boot Sector
  8074.  
  8075.    Logical sector 0, known as the boot sector, contains all of the critical
  8076.    information regarding the disk medium's characteristics (Figure 10-2).
  8077.    The first byte in the sector is always an 80x86 jump instruction──either a
  8078.    normal intrasegment JMP (opcode 0E9H) followed by a 16-bit displacement or
  8079.    a "short" JMP (opcode 0EBH) followed by an 8-bit displacement and then by
  8080.    an NOP (opcode 90H). If neither of these two JMP opcodes is present, the
  8081.    disk has not been formatted or was not formatted for use with MS-DOS. (Of
  8082.    course, the presence of the JMP opcode does not in itself ensure that the
  8083.    disk has an MS-DOS format.)
  8084.  
  8085.    Following the initial JMP instruction is an 8-byte field that is reserved
  8086.    by Microsoft for OEM identification. The disk-formatting program, which is
  8087.    specialized for each brand of computer, disk controller, and medium, fills
  8088.    in this area with the name of the computer manufacturer and the
  8089.    manufacturer's internal MS-DOS version number.
  8090.  
  8091.    00H ┌───────────────────────────────────────────────┐
  8092.        │             E9 XX XX or EB XX 90              │
  8093.    03H ├───────────────────────────────────────────────┤
  8094.        │             OEM name and version              │
  8095.        │                   (8 bytes)                   │
  8096.    OBH ├───────────────────────────────────────────────┤─┐
  8097.        │          Bytes per sector (2 bytes)           │ │
  8098.    ODH ├───────────────────────────────────────────────┤ │
  8099.        │     Sectors per allocation unit (1 byte)      │ │
  8100.    0EH ├───────────────────────────────────────────────┤ │
  8101.        │   Reserved sectors, starting at 0 (2 bytes)   │ │
  8102.    10H ├───────────────────────────────────────────────┤ │
  8103.        │            Number of FATs (1 byte)            │ B
  8104.    11H ├───────────────────────────────────────────────┤ P
  8105.        │  Number of root-directory entries (2 bytes)   │ B
  8106.    13H ├───────────────────────────────────────────────┤ │
  8107.        │   Total sectors in logical volume (2 bytes)   │ │
  8108.    15H ├───────────────────────────────────────────────┤ │ MS-DOS
  8109.        │             Media descriptor byte             │ │ version 2.0
  8110.    16H ├───────────────────────────────────────────────┤ │
  8111.        │      Number of sectors per FAT (2 bytes)      │ │
  8112.    18H ├───────────────────────────────────────────────┤═╡
  8113.        │          Sectors per track (2 bytes)          │ │
  8114.    1AH ├───────────────────────────────────────────────┤ │
  8115.        │           Number of heads (2 bytes)           │ │ MS-DOS
  8116.    1CH ├───────────────────────────────────────────────┤ │ version 3.0
  8117.        │      Number of hidden sectors (4 bytes)       │═╡
  8118.    20H ├───────────────────────────────────────────────┤ │ MS-DOS
  8119.        │        Total sectors in logical volume        │ │ version 4.0
  8120.        │      (MS-DOS 4.0 and volume size >32 MB)      │ │
  8121.    24H ├───────────────────────────────────────────────┤═╡
  8122.        │             Physical drive number             │ │
  8123.    25H ├───────────────────────────────────────────────┤ │
  8124.        │                   Reserved                    │ │
  8125.    26H ├───────────────────────────────────────────────┤ │
  8126.        │     Extended boot signature record (29H)      │ │ Additional
  8127.    27H ├───────────────────────────────────────────────┤ │ MS-DOS 4.0
  8128.        │            32-bit binary volume ID            │ │ information
  8129.    2BH ├───────────────────────────────────────────────┤ │
  8130.        │            Volume label (11 bytes)            │ │
  8131.    36H ├───────────────────────────────────────────────┤ │
  8132.        │              Reserved (8 bytes)               │ │
  8133.    3EH ├───────────────────────────────────────────────┤─┘
  8134.        │                   Bootstrap                   │
  8135.        └───────────────────────────────────────────────┘
  8136.  
  8137.    Figure 10-2.  Map of the boot sector of an MS-DOS disk. Note the JMP at
  8138.    offset 0, the OEM identification field, the MS-DOS version 2 compatible
  8139.    BIOS parameter block (bytes 0BH─17H), the three additional WORD fields for
  8140.    MS-DOS version 3, the double-word number-of-sectors field and 32-bit
  8141.    binary volume ID for MS-DOS version 4.0, and the bootstrap code.
  8142.  
  8143.    The third major component of the boot sector is the BIOS parameter block
  8144.    (BPB) in bytes 0BH through 17H. (Additional fields are present in MS-DOS
  8145.    versions 3.0 and later.) This data structure describes the physical disk
  8146.    characteristics and allows the device driver to calculate the proper
  8147.    physical disk address for a given logical-sector number; it also contains
  8148.    information that is used by MS-DOS and various system utilities to
  8149.    calculate the address and size of each of the disk control areas (file
  8150.    allocation tables and root directory).
  8151.  
  8152.    The final element of the boot sector is the disk bootstrap routine. The
  8153.    disk bootstrap is usually read into memory by the ROM bootstrap, which is
  8154.    executed automatically when the computer is turned on. The ROM bootstrap
  8155.    is usually just smart enough to home the head of the disk drive (move it
  8156.    to track 0), read the first physical sector into RAM at a predetermined
  8157.    location, and jump to it. The disk bootstrap is more sophisticated. It
  8158.    calculates the physical disk address of the beginning of the files area,
  8159.    reads the files containing the operating system into memory, and transfers
  8160.    control to the BIOS module at location 0070:0000H. (See Chapter 2.)
  8161.  
  8162.    Figures 10-3 and 10-4 show a partial hex dump and disassembly of a
  8163.    PC-DOS 3.3 floppy-disk boot sector.
  8164.  
  8165.    ──────────────────────────────────────────────────────────────────────────
  8166.           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
  8167.    0000  EB 34 90 49 42 4D 20 20 33 2E 33 00 02 02 01 00  .4.IBM  3.3.....
  8168.    0010  02 70 00 D0 02 FD 02 00 09 00 02 00 00 00 00 00  .p..............
  8169.    0020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12  ................
  8170.    0030  00 00 00 00 01 00 FA 33 C0 8E D0 BC 00 7C 16 07  .......3.....|..
  8171.          .
  8172.          .
  8173.          .
  8174.    01C0  0D 0A 44 69 73 6B 20 42 6F 6F 74 20 66 61 69 6C  ..Disk Boot fail
  8175.    01D0  75 72 65 0D 0A 00 49 42 4D 42 49 4F 20 20 43 4F  ure...IBMBIO  CO
  8176.    01E0  4D 49 42 4D 44 4F 53 20 20 43 4F 4D 00 00 00 00  MIBMDOS  COM....
  8177.    01F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA  ..............U.
  8178.    ──────────────────────────────────────────────────────────────────────────
  8179.  
  8180.    Figure 10-3.  Partial hex dump of the boot sector (track 0, head 0, sector
  8181.    1) of a PC-DOS version 3.3 floppy disk. This sector contains the OEM
  8182.    identification, a copy of the BIOS parameter block describing the medium,
  8183.    and the bootstrap routine that reads the BIOS into memory and transfers
  8184.    control to it. See also Figures 10-2 and 10-4.
  8185.  
  8186.    ──────────────────────────────────────────────────────────────────────────
  8187.            jmp     $+54            ; jump to bootstrap
  8188.            nop
  8189.  
  8190.            db      'IBM  3.3'      ; OEM identification
  8191.  
  8192.                                    ; BIOS parameter block
  8193.            dw      512             ; bytes per sector
  8194.            db      2               ; sectors per cluster
  8195.            dw      1               ; reserved sectors
  8196.            db      2               ; number of FATs
  8197.            dw      112             ; root directory entries
  8198.            dw      720             ; total sectors
  8199.            db      0fdh            ; media descriptor byte
  8200.            dw      2               ; sectors per FAT
  8201.  
  8202.            dw      9               ; sectors per track
  8203.            dw      2               ; number of heads
  8204.            dd      0               ; hidden sectors
  8205.            .
  8206.            .
  8207.            .
  8208.    ──────────────────────────────────────────────────────────────────────────
  8209.  
  8210.    Figure 10-4.  Partial disassembly of the boot sector shown in Figure
  8211.    10-3.
  8212.  
  8213.  
  8214.  The Reserved Area
  8215.  
  8216.    The boot sector is actually part of a reserved area that can span from one
  8217.    to several sectors. The reserved-sectors word in the BPB, at offset 0EH in
  8218.    the boot sector, describes the size of this area. Remember that the number
  8219.    in the BPB field includes the boot sector itself, so if the value is 1 (as
  8220.    it is on IBM PC floppy disks), the length of the reserved area is actually
  8221.    0 sectors.
  8222.  
  8223.  
  8224.  The File Allocation Table
  8225.  
  8226.    When a file is created or extended, MS-DOS assigns it groups of disk
  8227.    sectors from the files area in powers of 2. These are known as allocation
  8228.    units or clusters. The number of sectors per cluster for a given medium is
  8229.    defined in the BPB and can be found at offset 0DH in the disk's boot
  8230.    sector. Below are some example cluster sizes:
  8231.  
  8232.    Disk type                     Power of 2    Sectors/cluster
  8233.    ──────────────────────────────────────────────────────────────────────────
  8234.    5.25" 180 KB floppy disk      0             1
  8235.    5.25" 360 KB floppy disk      1             2
  8236.    PC/AT fixed disk              2             4
  8237.    PC/XT fixed disk              3             8
  8238.    ──────────────────────────────────────────────────────────────────────────
  8239.  
  8240.    The file allocation table (FAT) is divided into fields that correspond
  8241.    directly to the assignable clusters on the disk. These fields are 12 bits
  8242.    in MS-DOS versions 1 and 2 and may be either 12 bits or 16 bits in
  8243.    versions 3.0 and later, depending on the size of the medium (12 bits if
  8244.    the disk contains fewer than 4087 clusters, 16 bits otherwise).
  8245.  
  8246.    The first two fields in the FAT are always reserved. On IBM-compatible
  8247.    media, the first 8 bits of the first reserved FAT entry contain a copy of
  8248.    the media descriptor byte, which is also found in the BPB in the boot
  8249.    sector. The second, third, and (if applicable) fourth bytes, which
  8250.    constitute the remainder of the first two reserved FAT fields, always
  8251.    contain 0FFH. The currently defined IBM-format media descriptor bytes are
  8252.    as follows:
  8253.  
  8254.                                                               MS-DOS version
  8255.                                                               where first
  8256.    Descriptor     Medium                                      supported
  8257.    ──────────────────────────────────────────────────────────────────────────
  8258.    0F0H           3.5" floppy disk, 2-sided, 18-sector        3.3
  8259.    0F8H           Fixed disk                                  2.0
  8260.    0F9H           5.25" floppy disk, 2-sided, 15-sector       3.0
  8261.                   3.5" floppy disk, 2-sided, 9-sector         3.2
  8262.    0FCH           5.25" floppy disk, 1-sided, 9-sector        2.0
  8263.    0FDH           5.25" floppy disk, 2-sided, 9-sector        2.0
  8264.                   8" floppy disk, 1-sided, single-density
  8265.    0FEH           5.25" floppy disk, 1-sided, 8-sector        1.0
  8266.                   8" floppy disk, 1-sided, single-density
  8267.                   8" floppy disk, 2-sided, double-density
  8268.    0FFH           5.25" floppy disk, 2-sided, 8-sector        1.1
  8269.    ──────────────────────────────────────────────────────────────────────────
  8270.  
  8271.    The remainder of the FAT entries describe the use of their corresponding
  8272.    disk clusters. The contents of the FAT fields are interpreted as follows:
  8273.  
  8274.    Value              Meaning
  8275.    ──────────────────────────────────────────────────────────────────────────
  8276.    (0)000H            Cluster available
  8277.    (F)FF0─(F)FF6H     Reserved cluster
  8278.    (F)FF7H            Bad cluster, if not part of chain
  8279.    (F)FF8─(F)FFFH     Last cluster of file
  8280.    (X)XXX             Next cluster in file
  8281.    ──────────────────────────────────────────────────────────────────────────
  8282.  
  8283.    Each file's entry in a directory contains the number of the first cluster
  8284.    assigned to that file, which is used as an entry point into the FAT. From
  8285.    the entry point on, each FAT slot contains the cluster number of the next
  8286.    cluster in the file, until a last-cluster mark is encountered.
  8287.  
  8288.    At the computer manufacturer's option, MS-DOS can maintain two or more
  8289.    identical copies of the FAT on each volume. MS-DOS updates all copies
  8290.    simultaneously whenever files are extended or the directory is modified.
  8291.    If access to a sector in a FAT fails due to a read error, MS-DOS tries the
  8292.    other copies until a successful disk read is obtained or all copies are
  8293.    exhausted. Thus, if one copy of the FAT becomes unreadable due to wear or
  8294.    a software accident, the other copies may still make it possible to
  8295.    salvage the files on the disk. As part of its procedure for checking the
  8296.    integrity of a disk, the CHKDSK program compares the multiple copies
  8297.    (usually two) of the FAT to make sure they are all readable and
  8298.    consistent.
  8299.  
  8300.  
  8301.  The Root Directory
  8302.  
  8303.    Following the file allocation tables is an area known in MS-DOS versions
  8304.    2.0 and later as the root directory. (Under MS-DOS version 1, it was the
  8305.    only directory on the disk.) The root directory contains 32-byte entries
  8306.    that describe files, other directories, and the optional volume label
  8307.    (Figure 10-5). An entry beginning with the byte value E5H is available
  8308.    for reuse; it represents a file or directory that has been erased. An
  8309.    entry beginning with a null (zero) byte is the logical end-of-directory;
  8310.    that entry and all subsequent entries have never been used.
  8311.  
  8312.    00H ┌──────────────────────────────┐
  8313.        │           Filename           │ Note 1
  8314.    08H ├──────────────────────────────┤
  8315.        │          Extension           │
  8316.    0BH ├──────────────────────────────┤
  8317.        │        File attribute        │ Note 2
  8318.    0CH ├──────────────────────────────┤
  8319.        │           Reserved           │
  8320.    16H ├──────────────────────────────┤
  8321.        │ Time created or last updated │ Note 3
  8322.    18H ├──────────────────────────────┤
  8323.        │ Date created or last updated │ Note 4
  8324.    1AH ├──────────────────────────────┤
  8325.        │       Starting cluster       │
  8326.    1CH ├──────────────────────────────┤
  8327.        │      File size, 4 bytes      │ Note 5
  8328.    20H └──────────────────────────────┘
  8329.  
  8330.    Figure 10-5.  Format of a single entry in a disk directory. Total length
  8331.    is 32 bytes (20H bytes).
  8332.  
  8333.    ──────────────────────────────────────────────────────────────────────────
  8334.    Notes for Figure 10-5
  8335.      1.  The first byte of the filename field of a directory entry may
  8336.          contain the following special information:
  8337.  
  8338.      Value             Meaning
  8339.      ────────────────────────────────────────────────────────────────────────
  8340.      00H               Directory entry has never been used; end of occupied
  8341.                        portion of directory.
  8342.      05H               First character of filename is actually E5H.
  8343.      2EH               Entry is an alias for the current or parent directory.
  8344.                        If the next byte is also 2EH, the cluster field
  8345.                        contains the cluster number of the parent directory
  8346.                        (zero if the parent directory is the root directory).
  8347.      E5H               File has been erased.
  8348.      ────────────────────────────────────────────────────────────────────────
  8349.  
  8350.      2.  The attribute byte of the directory entry is mapped as follows:
  8351.  
  8352.      Bit               Meaning
  8353.      ────────────────────────────────────────────────────────────────────────
  8354.      0                 Read-only; attempts to open file for write or to
  8355.                        delete file will fail.
  8356.      1                 Hidden file; excluded from normal searches.
  8357.      2                 System file; excluded from normal searches.
  8358.      3                 Volume label; can exist only in root directory.
  8359.      4                 Directory; excluded from normal searches.
  8360.      5                 Archive bit; set whenever file is modified.
  8361.      6                 Reserved.
  8362.      7                 Reserved.
  8363.      ────────────────────────────────────────────────────────────────────────
  8364.  
  8365.      3.  The time field is encoded as follows:
  8366.  
  8367.      Bits              Contents
  8368.      ────────────────────────────────────────────────────────────────────────
  8369.      00H─04H           Binary number of 2-second increments (0─29,
  8370.                        corresponding to 0─58 seconds)
  8371.      05H─0AH           Binary number of minutes (0─59)
  8372.      0BH─0FH           Binary number of hours (0─23)
  8373.      ────────────────────────────────────────────────────────────────────────
  8374.  
  8375.      4.  The date field is encoded as follows:
  8376.  
  8377.      Bits              Contents
  8378.      ────────────────────────────────────────────────────────────────────────
  8379.      00H─04H           Day of month (1─31)
  8380.      05H─08H           Month (1─12)
  8381.      09H─0FH           Year (relative to 1980)
  8382.      ────────────────────────────────────────────────────────────────────────
  8383.  
  8384.      5.  The file-size field is interpreted as a 4-byte integer, with the
  8385.          low-order 2 bytes of the number stored first.
  8386.  
  8387.    ──────────────────────────────────────────────────────────────────────────
  8388.  
  8389.    The root directory has a number of special properties. Its size and
  8390.    position are fixed and are determined by the FORMAT program when a disk is
  8391.    initialized. This information can be obtained from the boot sector's BPB.
  8392.    If the disk is bootable, the first two entries in the root directory
  8393.    always describe the files containing the MS-DOS BIOS and the MS-DOS
  8394.    kernel. The disk bootstrap routine uses these entries to bring the
  8395.    operating system into memory and start it up.
  8396.  
  8397.    Figure 10-6 shows a partial hex dump of the first sector of the root
  8398.    directory on a bootable PC-DOS 3.3 floppy disk.
  8399.  
  8400.    ──────────────────────────────────────────────────────────────────────────
  8401.           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
  8402.    0000  49 42 4D 42 49 4F 20 20 43 4F 4D 27 00 00 00 00  IBMBIO  COM'....
  8403.    0010  00 00 00 00 00 00 00 60 72 0E 02 00 54 56 00 00  .......'r...TV..
  8404.    0020  49 42 4D 44 4F 53 20 20 43 4F 4D 27 00 00 00 00  IBMDOS  COM'....
  8405.    0030  00 00 00 00 00 00 00 60 71 0E 18 00 CF 75 00 00  .......'q....u..
  8406.    0040  43 4F 4D 4D 41 4E 44 20 43 4F 4D 20 00 00 00 00  COMMAND COM ....
  8407.    0050  00 00 00 00 00 00 00 60 71 0E 36 00 DB 62 00 00  .......'q.6..b..
  8408.    0060  42 4F 4F 54 44 49 53 4B 20 20 20 28 00 00 00 00  BOOTDISK   (....
  8409.    0070  00 00 00 00 00 00 A1 00 21 00 00 00 00 00 00 00  ........!.......
  8410.    0080  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  8411.    0090  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  8412.          .
  8413.          .
  8414.          .
  8415.    ──────────────────────────────────────────────────────────────────────────
  8416.  
  8417.    Figure 10-6.  Partial hex dump of the first sector of the root directory
  8418.    for a PC-DOS 3.3 disk containing the three system files and a volume
  8419.    label.
  8420.  
  8421.  
  8422.  The Files Area
  8423.  
  8424.    The remainder of the volume after the root directory is known as the files
  8425.    area. MS-DOS views the sectors in this area as a pool of clusters, each
  8426.    containing one or more logical sectors, depending on the disk format. Each
  8427.    cluster has a corresponding entry in the FAT that describes its current
  8428.    use: available, reserved, assigned to a file, or unusable (because of
  8429.    defects in the medium). Because the first two fields of the FAT are
  8430.    reserved, the first cluster in the files area is assigned the number 2.
  8431.  
  8432.    When a file is extended under versions 1 and 2, MS-DOS searches the FAT
  8433.    from the beginning until it finds a free cluster (designated by a zero FAT
  8434.    field); it then changes that FAT field to a last-cluster mark and updates
  8435.    the previous last cluster of the file's chain to point to the new last
  8436.    cluster. Under versions 3.0 and later, however, MS-DOS searches the FAT
  8437.    from the most recently allocated cluster; this reduces file fragmentation
  8438.    and improves overall access times.
  8439.  
  8440.    Directories other than the root directory are simply a special type of
  8441.    file. Their storage is allocated from the files area, and their contents
  8442.    are 32-byte entries──in the same format as those used in the root
  8443.    directory──that describe files or other directories. Directory entries
  8444.    that describe other directories contain an attribute byte with bit 4 set,
  8445.    zero in the file-length field, and the date and time that the directory
  8446.    was created (Figure 10-7). The first cluster field points, of course, to
  8447.    the first cluster in the files area that belongs to the directory. (The
  8448.    directory's other clusters can be found only by tracing through the FAT.)
  8449.  
  8450.    All directories except the root directory contain two special directory
  8451.    entries with the names . and ... MS-DOS puts these entries in place when
  8452.    it creates a directory, and they cannot be deleted. The . entry is an
  8453.    alias for the current directory; its cluster field points to the cluster
  8454.    in which it is found. The .. entry is an alias for the directory's parent
  8455.    (the directory immediately above it in the tree structure); its cluster
  8456.    field points to the first cluster of the parent directory. If the parent
  8457.    is the root directory, the cluster field of the .. entry contains zero
  8458.    (Figure 10-8).
  8459.  
  8460.    ──────────────────────────────────────────────────────────────────────────
  8461.          .
  8462.          .
  8463.          .
  8464.    0080  4D 59 44 49 52 20 20 20 20 20 20 10 00 00 00 00  MYDIR      .....
  8465.    0090  00 00 00 00 00 00 87 9A 9B 0A 2A 00 00 00 00 00  ..........*.....
  8466.          .
  8467.          .
  8468.          .
  8469.    ──────────────────────────────────────────────────────────────────────────
  8470.  
  8471.    Figure 10-7.  Extract from the root directory of an MS-DOS disk, showing
  8472.    the entry for a subdirectory named MYDIR. Bit 4 in the attribute byte is
  8473.    set, the cluster field points to the first cluster of the subdirectory
  8474.    file, the date and time stamps are valid, but the file length is zero.
  8475.  
  8476.    ──────────────────────────────────────────────────────────────────────────
  8477.           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
  8478.    0000  2E 20 20 20 20 20 20 20 20 20 20 10 00 00 00 00  .         .....
  8479.    0010  00 00 00 00 00 00 87 9A 9B 0A 2A 00 00 00 00 00  ..........*.....
  8480.    0020  2E 2E 20 20 20 20 20 20 20 20 20 10 00 00 00 00  ..        .....
  8481.    0030  00 00 00 00 00 00 87 9A 9B 0A 00 00 00 00 00 00  ................
  8482.    0040  4D 59 46 49 4C 45 20 20 44 41 54 20 00 00 00 00  MYFILE  DAT ....
  8483.    0050  00 00 00 00 00 00 98 9A 9B 0A 2B 00 15 00 00 00  ..........+.....
  8484.    0060  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  8485.    0070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  8486.          .
  8487.          .
  8488.          .
  8489.    ──────────────────────────────────────────────────────────────────────────
  8490.  
  8491.    Figure 10-8.  Hex dump of the first block of the directory MYDIR. Note the
  8492.    . and .. entries. This directory contains exactly one file, MYFILE.DAT.
  8493.  
  8494.  
  8495.  Interpreting the File Allocation Table
  8496.  
  8497.    Now that we understand how the disk is structured, let's see how we can
  8498.    use this knowledge to find a FAT position from a cluster number.
  8499.  
  8500.    If the FAT has 12-bit entries, use the following procedure:
  8501.  
  8502.    1.  Use the directory entry to find the starting cluster of the file in
  8503.        question.
  8504.  
  8505.    2.  Multiply the cluster number by 1.5.
  8506.  
  8507.    3.  Use the integral part of the product as the offset into the FAT and
  8508.        move the word at that offset into a register. Remember that a FAT
  8509.        position can span a physical disk-sector boundary.
  8510.  
  8511.    4.  If the product is a whole number, AND the register with 0FFFH.
  8512.  
  8513.    5.  Otherwise, "logical shift" the register right 4 bits.
  8514.  
  8515.    6.  If the result is a value from 0FF8H through 0FFFH, the file has no
  8516.        more clusters. Otherwise, the result is the number of the next cluster
  8517.        in the file.
  8518.  
  8519.    On disks with at least 4087 clusters formatted under MS-DOS version 3.0 or
  8520.    later, the FAT entries use 16 bits, and the extraction of a cluster number
  8521.    from the table is much simpler:
  8522.  
  8523.    1.  Use the directory entry to find the starting cluster of the file in
  8524.        question.
  8525.  
  8526.    2.  Multiply the cluster number by 2.
  8527.  
  8528.    3.  Use the product as the offset into the FAT and move the word at that
  8529.        offset into a register.
  8530.  
  8531.    4.  If the result is a value from 0FFF8H through 0FFFFH, the file has no
  8532.        more clusters. Otherwise, the result is the number of the next cluster
  8533.        in the file.
  8534.  
  8535.    To convert cluster numbers to logical sectors, subtract 2, multiply the
  8536.    result by the number of sectors per cluster, and add the logical-sector
  8537.    number of the beginning of the data area (this can be calculated from the
  8538.    information in the BPB).
  8539.  
  8540.    As an example, let's work out the disk location of the file IBMBIO.COM,
  8541.    which is the first entry in the directory shown in Figure 10-6. First, we
  8542.    need some information from the BPB, which is in the boot sector of the
  8543.    medium. (See Figures 10-3 and 10-4.) The BPB tells us that there are
  8544.  
  8545.    ■  512 bytes per sector
  8546.  
  8547.    ■  2 sectors per cluster
  8548.  
  8549.    ■  2 sectors per FAT
  8550.  
  8551.    ■  2 FATs
  8552.  
  8553.    ■  112 entries in the root directory
  8554.  
  8555.    From the BPB information, we can calculate the starting logical-sector
  8556.    number of each of the disk's control areas and the files area by
  8557.    constructing a table, as follows:
  8558.  
  8559.                                                     Length       Sector
  8560.    Area                                             (sectors)    numbers
  8561.    ──────────────────────────────────────────────────────────────────────────
  8562.    Boot sector                                      1            00H
  8563.    2 FATs * 2 sectors/FAT                           4            01H─04H
  8564.    112 directory entries                            7            05H─0BH
  8565.      *32 bytes/entry
  8566.      /512 bytes/sector
  8567.    Total sectors occupied by bootstrap, FATs, and   12
  8568.    root directory
  8569.    ──────────────────────────────────────────────────────────────────────────
  8570.  
  8571.    Therefore, the first sector of the files area is 12 (0CH).
  8572.  
  8573.    The word at offset 01AH in the directory entry for IBMBIO.COM gives us the
  8574.    starting cluster number for that file: cluster 2. To find the
  8575.    logical-sector number of the first block in the file, we can follow the
  8576.    procedure given earlier:
  8577.  
  8578.    1.  Cluster number - 2 = 2 - 2 = 0.
  8579.  
  8580.    2.  Multiply by sectors per cluster = 0 * 2 = 0.
  8581.  
  8582.    3.  Add logical-sector number of start of the files area = 0 + 0CH = 0CH.
  8583.  
  8584.    So the calculated sector number of the beginning of the file IBMBIO.COM is
  8585.    0CH, which is exactly what we expect knowing that the FORMAT program
  8586.    always places the system files in contiguous sectors at the beginning of
  8587.    the data area.
  8588.  
  8589.    Now let's trace IBMBIO.COM's chain through the file allocation table
  8590.    (Figures 10-9 and 10-10). This will be a little tedious, but a detailed
  8591.    understanding of the process is crucial. In an actual program, we would
  8592.    first read the boot sector using Int 25H, then calculate the address of
  8593.    the FAT from the contents of the BPB, and finally read the FAT into
  8594.    memory, again using Int 25H.
  8595.  
  8596.    From IBMBIO.COM's directory entry, we already know that the first cluster
  8597.    in the file is cluster 2. To examine that cluster's entry in the FAT, we
  8598.    multiply the cluster number by 1.5, which gives 0003H as the FAT offset,
  8599.    and fetch the word at that offset (which contains 4003H). Because the
  8600.    product of the cluster and 1.5 is a whole number, we AND the word from the
  8601.    FAT with 0FFFH, yielding the number 3, which is the number of the second
  8602.    cluster assigned to the file.
  8603.  
  8604.    ──────────────────────────────────────────────────────────────────────────
  8605.           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
  8606.    0000  FD FF FF 03 40 00 05 60 00 07 80 00 09 A0 00 0B  ....@..'........
  8607.    0010  C0 00 0D E0 00 0F 00 01 11 20 01 13 40 01 15 60  ......... ..@..'
  8608.    0020  01 17 F0 FF 19 A0 01 1B C0 01 1D E0 01 1F 00 02  ................
  8609.    0030  21 20 02 23 40 02 25 60 02 27 80 02 29 A0 02 2B  ! .#@.%'.'..)..+
  8610.          .
  8611.          .
  8612.          .
  8613.    ──────────────────────────────────────────────────────────────────────────
  8614.  
  8615.    Figure 10-9.  Hex dump of the first block of the file allocation table
  8616.    (track 0, head 0, sector 2) for the PC-DOS 3.3 disk whose root directory
  8617.    is shown in Figure 10-6. Notice that the first byte of the FAT contains
  8618.    the media descriptor byte for a 5.25-inch, 2-sided, 9-sector floppy disk.
  8619.  
  8620.    ──────────────────────────────────────────────────────────────────────────
  8621.    getfat    proc      near      ; extracts the FAT field
  8622.                                  ; for a given cluster
  8623.                                  ; call    AX = cluster #
  8624.                                  ;      DS:BX = addr of FAT
  8625.                                  ; returns AX = FAT field
  8626.                                  ; other registers unchanged
  8627.  
  8628.              push      bx        ; save affected registers
  8629.              push      cx
  8630.              mov       cx,ax
  8631.              shl       ax,1      ; cluster * 2
  8632.              add       ax,cx     ; cluster * 3
  8633.              test      ax,1
  8634.              pushf               ; save remainder in Z flag
  8635.              shr       ax,1      ; cluster * 1.5
  8636.              add       bx,ax
  8637.              mov       ax,[bx]
  8638.              popf                ; was cluster * 1.5 whole number?
  8639.              jnz       getfat1   ; no, jump
  8640.              and       ax,0fffh  ; yes, isolate bottom 12 bits
  8641.              jmp       getfat2
  8642.    getfat1:  mov       cx,4      ; shift word right 4 bits
  8643.              shr       ax,cx
  8644.    getfat2:  pop       cx        ; restore registers and exit
  8645.              pop       bx
  8646.              ret
  8647.    getfat    endp
  8648.    ──────────────────────────────────────────────────────────────────────────
  8649.  
  8650.    Figure 10-10.  Assembly-language procedure to access the file allocation
  8651.    table (assumes 12-bit FAT fields). Given a cluster number, the procedure
  8652.    returns the contents of that cluster's FAT entry in the AX register. This
  8653.    simple example ignores the fact that FAT entries can span sector
  8654.    boundaries.
  8655.  
  8656.    To examine cluster 3's entry in the FAT, we multiply 3 by 1.5, which gives
  8657.    4.5, and fetch the word at offset 0004H (which contains 0040H). Because
  8658.    the product of 3 and 1.5 is not a whole number, we shift the word right
  8659.    4 bits, yielding the number 4, which is the number of the third cluster
  8660.    assigned to IBMBIO.COM.
  8661.  
  8662.    In this manner, we can follow the chain through the FAT until we come to a
  8663.    cluster (number 23, in this case) whose FAT entry contains the value
  8664.    0FFFH, which is an end-of-file marker in FATs with 12-bit entries.
  8665.  
  8666.    We have now established that the file IBMBIO.COM contains clusters 2
  8667.    through 23 (02H─17H), from which we can calculate that logical sectors 0CH
  8668.    through 38H are assigned to the file. Of course, the last cluster may be
  8669.    only partially filled with actual data; the portion of the last cluster
  8670.    used is the remainder of the file's size in bytes (found in the directory
  8671.    entry) divided by the bytes per cluster.
  8672.  
  8673.  
  8674.  Fixed-Disk Partitions
  8675.  
  8676.    Fixed disks have another layer of organization beyond the logical volume
  8677.    structure already discussed: partitions. The FDISK utility divides a fixed
  8678.    disk into one or more partitions consisting of an integral number of
  8679.    cylinders. Each partition can contain an independent file system and, for
  8680.    that matter, its own copy of an operating system.
  8681.  
  8682.    The first physical sector on a fixed disk (track 0, head 0, sector 1)
  8683.    contains the master boot record, which is laid out as follows:
  8684.  
  8685.    Bytes              Contents
  8686.    ──────────────────────────────────────────────────────────────────────────
  8687.    000─1BDH           Reserved
  8688.    1BE─1CDH           Partition #1 descriptor
  8689.    1CE─1DDH           Partition #2 descriptor
  8690.    1DE─1EDH           Partition #3 descriptor
  8691.    1EE─1FDH           Partition #4 descriptor
  8692.    1FE─1FFH           Signature word (AA55H)
  8693.    ──────────────────────────────────────────────────────────────────────────
  8694.  
  8695.    The partition descriptors in the master boot record define the size,
  8696.    location, and type of each partition, as follows:
  8697.  
  8698.    Byte(s)            Contents
  8699.    ──────────────────────────────────────────────────────────────────────────
  8700.    00H                Active flag (0 = not bootable, 80H = bootable)
  8701.    01H                Starting head
  8702.    02H─03H            Starting cylinder/sector
  8703.    04H                Partition type
  8704.    00H                not used
  8705.    01H                FAT file system, 12-bit FAT entries
  8706.    04H                FAT file system, 16-bit FAT entries
  8707.    05H                extended partition
  8708.    06H                "huge partition" (MS-DOS versions 4.0 and later)
  8709.    05H                Ending head
  8710.    06H─07H            Ending cylinder/sector
  8711.    08H─0BH            Starting sector for partition, relative to beginning of
  8712.                       disk
  8713.    0CH─0FH            Partition length in sectorsThe active flag, which
  8714.                       indicates that the partition is bootable, can be set on
  8715.                       only one partition at a time.
  8716.    ──────────────────────────────────────────────────────────────────────────
  8717.  
  8718.    MS-DOS treats partition types 1, 4, and 6 as normal logical volumes and
  8719.    assigns them their own drive identifiers during the system boot process.
  8720.    Partition type 5 can contain multiple logical volumes and has a special
  8721.    extended boot record that describes each volume. The FORMAT utility
  8722.    initializes MS-DOS fixed-disk partitions, creating the file system within
  8723.    the partition (boot record, file allocation table, root directory, and
  8724.    files area) and optionally placing a bootable copy of the operating system
  8725.    in the file system.
  8726.  
  8727.    Figure 10-11 contains a partial hex dump of a master block from a fixed
  8728.    disk formatted under PC-DOS version 3.3. This dump illustrates the
  8729.    partition descriptors for a normal partition with a 16-bit FAT and an
  8730.    extended partition.
  8731.  
  8732.    ──────────────────────────────────────────────────────────────────────────
  8733.    0000   .
  8734.           .
  8735.           .
  8736.    0180  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  8737.    0190  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  8738.    01A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  8739.    01B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 01
  8740.    01C0  01 00 04 04 D1 02 11 00 00 00 EE FF 00 00 00 00
  8741.    01D0  C1 04 05 04 D1 FD 54 00 01 00 02 53 00 00 00 00
  8742.    01E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  8743.    01F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA
  8744.    ──────────────────────────────────────────────────────────────────────────
  8745.  
  8746.    Figure 10-11.  A partial hex dump of a master block from a fixed disk
  8747.    formatted under PC-DOS version 3.3. This disk contains two partitions. The
  8748.    first partition has a 16-bit FAT and is marked "active" to indicate that
  8749.    it contains a bootable copy of PC-DOS. The second partition is an
  8750.    "extended" partition. The third and fourth partition entries are not used
  8751.    in this example.
  8752.  
  8753.  
  8754.  
  8755.  ────────────────────────────────────────────────────────────────────────────
  8756.  Chapter 11  Memory Management
  8757.  
  8758.    Current versions of MS-DOS can manage as much as 1 megabyte of contiguous
  8759.    random-access memory. On IBM PCs and compatibles, the memory occupied by
  8760.    MS-DOS and other programs starts at address 0000H and may reach as high as
  8761.    address 09FFFFH; this 640 KB area of RAM is sometimes referred to as
  8762.    conventional memory. Memory above this address is reserved for ROM
  8763.    hardware drivers, video refresh buffers, and the like. Computers that are
  8764.    not IBM compatible may use other memory layouts.
  8765.  
  8766.    The RAM area under the control of MS-DOS is divided into two major
  8767.    sections:
  8768.  
  8769.    ■  The operating-system area
  8770.  
  8771.    ■  The transient-program area
  8772.  
  8773.    The operating-system area starts at address 0000H──that is, it occupies
  8774.    the lowest portion of RAM. It holds the interrupt vector table, the
  8775.    operating system proper and its tables and buffers, any additional
  8776.    installable drivers specified in the CONFIG.SYS file, and the resident
  8777.    part of the COMMAND.COM command interpreter. The amount of memory occupied
  8778.    by the operating-system area varies with the version of MS-DOS used, the
  8779.    number of disk buffers, the size of installed device drivers, and so
  8780.    forth.
  8781.  
  8782.    The transient-program area (TPA), sometimes called the memory arena, is
  8783.    the remainder of memory above the operating-system area. The memory arena
  8784.    is dynamically allocated in blocks called arena entries. Each arena entry
  8785.    has a special control structure called an arena header, and all of the
  8786.    arena headers are chained together. Three MS-DOS Int 21H functions allow
  8787.    programs to allocate, resize, and release blocks of memory from the TPA:
  8788.  
  8789.    Function                 Action
  8790.    ──────────────────────────────────────────────────────────────────────────
  8791.    48H                     Allocate memory block.
  8792.    49H                     Release memory block.
  8793.    4AH                     Resize memory block.
  8794.    ──────────────────────────────────────────────────────────────────────────
  8795.  
  8796.    MS-DOS itself uses these functions when loading a program from disk at the
  8797.    request of COMMAND.COM or another program. The EXEC function, which is the
  8798.    MS-DOS program loader, calls Int 21H Function 48H to allocate a memory
  8799.    block for the loaded program's environment and another for the program
  8800.    itself and its program segment prefix. It then reads the program from the
  8801.    disk into the assigned memory area. When the program terminates, MS-DOS
  8802.    calls Int 21H Function 49H to release all memory owned by the program.
  8803.  
  8804.    Transient programs can also employ the MS-DOS memory-management functions
  8805.    to dynamically manage the memory available in the TPA. Proper use of these
  8806.    functions is one of the most important criteria of whether a program is
  8807.    well behaved under MS-DOS. Well-behaved programs are most likely to be
  8808.    portable to future versions of the operating system and least likely to
  8809.    cause interference with other processes under multitasking user interfaces
  8810.    such as Microsoft Windows.
  8811.  
  8812.  
  8813.  Using the Memory-Allocation Functions
  8814.  
  8815.    The memory-allocation functions have two common uses:
  8816.  
  8817.    ■  To shrink a program's initial memory allocation so that there is enough
  8818.       room to load and execute another program under its control.
  8819.  
  8820.    ■  To dynamically allocate additional memory required by the program and
  8821.       to release the same memory when it is no longer needed.
  8822.  
  8823.  Shrinking the Initial Memory Allocation
  8824.  
  8825.    Although many MS-DOS application programs simply assume they own all
  8826.    memory, this assumption is a relic of MS-DOS version 1 (and CP/M), which
  8827.    could support only one active process at a time. Well-behaved MS-DOS
  8828.    programs take pains to modify only memory that they actually own and to
  8829.    release any memory that they don't need.
  8830.  
  8831.    Unfortunately, under current versions of MS-DOS, the amount of memory that
  8832.    a program will own is not easily predicted in advance. It turns out that
  8833.    the amount of memory allocated to a program when it is first loaded
  8834.    depends upon two factors:
  8835.  
  8836.    ■  The type of file the program is loaded from
  8837.  
  8838.    ■  The amount of memory available in the TPA
  8839.  
  8840.    MS-DOS always allocates all of the largest available memory block in the
  8841.    TPA to programs loaded from .COM (memory-image) files. Because .COM
  8842.    programs contain no file header that can pass segment and memory-use
  8843.    information to MS-DOS, MS-DOS simply assumes the worst case and gives such
  8844.    a program everything. MS-DOS will load the program as long as there is an
  8845.    available memory block as large as the size of the file plus 256 bytes for
  8846.    the PSP and 2 bytes for the stack. The .COM program, when it receives
  8847.    control, must determine whether enough memory is available to carry out
  8848.    its functions.
  8849.  
  8850.    MS-DOS uses more complicated rules to allocate memory to programs loaded
  8851.    from .EXE files. First, of course, a memory block large enough to hold the
  8852.    declared code, data, and stack segments must be available in the TPA. In
  8853.    addition, the linker sets two fields in a .EXE file's header to inform
  8854.    MS-DOS about the program's memory requirements. The first field,
  8855.    MIN_ALLOC, defines the minimum number of paragraphs required by the
  8856.    program, in addition to those for the code, data, and stack segments. The
  8857.    second, MAX_ALLOC, defines the maximum number of paragraphs of additional
  8858.    memory the program would use if they were available.
  8859.  
  8860.    When loading a .EXE file, MS-DOS first attempts to allocate the number of
  8861.    paragraphs in MAX_ALLOC plus the number of paragraphs required by the
  8862.    program itself. If that much memory is not available, MS-DOS assigns all
  8863.    of the largest available block to the program, provided that this is at
  8864.    least the amount specified by MIN_ALLOC plus the size of the program
  8865.    image. If that condition is not satisfied, the program cannot be executed.
  8866.  
  8867.    After a .COM or .EXE program is loaded and running, it can use Int 21H
  8868.    Function 4AH (Resize Memory Block) to release all the memory it does not
  8869.    immediately need. This is conveniently done right after the program
  8870.    receives control from MS-DOS, by calling the resize function with the
  8871.    segment of the program's PSP in the ES register and the number of
  8872.    paragraphs that the program requires to run in the BX register (Figure
  8873.    11-1).
  8874.  
  8875.    ──────────────────────────────────────────────────────────────────────────
  8876.            .
  8877.            .
  8878.            .
  8879.            org     100h
  8880.  
  8881.    main    proc    near            ; entry point from MS-DOS
  8882.                                    ; DS, ES = PSP address
  8883.  
  8884.            mov     sp,offset stk   ; COM program must move
  8885.                                    ; stack to safe area
  8886.  
  8887.                                    ; release extra memory...
  8888.            mov     ah,4ah          ; function 4Ah =
  8889.                                    ; resize memory block
  8890.                                    ; BX = paragraphs to keep
  8891.            mov     bx,(offset stk - offset main + 10FH) / 16
  8892.            int     21h             ; transfer to MS-DOS
  8893.            jc      error           ; jump if resize failed
  8894.            .
  8895.            .
  8896.            .
  8897.    main    endp
  8898.  
  8899.            .
  8900.            .
  8901.            .
  8902.  
  8903.            dw      64 dup (?)      ; new stack area
  8904.    stk     equ     $               ; new base of stack
  8905.  
  8906.            end     main            ; defines entry point
  8907.    ──────────────────────────────────────────────────────────────────────────
  8908.  
  8909.    Figure 11-1.  An example of a .COM program releasing excess memory after
  8910.    it receives control from MS-DOS. Int 21H Function 4AH is called with ES
  8911.    pointing to the program's PSP and BX containing the number of paragraphs
  8912.    that the program needs to execute. In this case, the new size for the
  8913.    program's memory block is calculated as the program image size plus the
  8914.    size of the PSP (256 bytes), rounded up to the next paragraph. .EXE
  8915.    programs use similar code.
  8916.  
  8917.  Dynamic Allocation of Additional Memory
  8918.  
  8919.    When a well-behaved program needs additional memory space──for an I/O
  8920.    buffer or an array of intermediate results, for example──it can call Int
  8921.    21H Function 48H (Allocate Memory Block) with the desired number of
  8922.    paragraphs. If a sufficiently large block of unallocated memory is
  8923.    available, MS-DOS returns the segment address of the base of the assigned
  8924.    area and clears the carry flag (0), indicating that the function was
  8925.    successful.
  8926.  
  8927.    If no unallocated block of sufficient size is available, MS-DOS sets the
  8928.    carry flag (1), returns an error code in the AX register, and returns the
  8929.    size (in paragraphs) of the largest block available in the BX register
  8930.    (Figure 11-2). In this case, no memory has yet been allocated. The
  8931.    program can use the value returned in the BX register to determine whether
  8932.    it can continue in a "degraded" fashion, with less memory. If it can, it
  8933.    must call Int 21H Function 48H again to allocate the smaller memory
  8934.    block.
  8935.  
  8936.    When the MS-DOS memory manager is searching the chain of arena headers to
  8937.    satisfy a memory-allocation request, it can use one of the following
  8938.    strategies:
  8939.  
  8940.    ■  First fit: Use the arena entry at the lowest address that is large
  8941.       enough to satisfy the request.
  8942.  
  8943.    ■  Best fit: Use the smallest arena entry that will satisfy the request,
  8944.       regardless of its location.
  8945.  
  8946.    ■  Last fit: Use the arena entry at the highest address that is large
  8947.       enough to satisfy the request.
  8948.  
  8949.    ──────────────────────────────────────────────────────────────────────────
  8950.                  .
  8951.                  .
  8952.                  .
  8953.                  mov   ah,48h                 ; function 48h = allocate mem bl
  8954.                  mov   bx,0800h               ; 800h paragraphs = 32 KB
  8955.                  int   21h                    ; transfer to MS-DOS
  8956.                  jc    error                  ; jump if allocation failed
  8957.                  mov   buff_seg,ax            ; save segment of allocated bloc
  8958.                  .
  8959.                  .
  8960.                  .
  8961.                  mov   es,buff_seg            ; ES:DI = address of block
  8962.                  xor   di,di
  8963.                  mov   cx,08000h              ; store 32,768 bytes
  8964.                  mov   al,0ffh                ; fill buffer with -1s
  8965.                  cld
  8966.                  rep   stosb                  ; now perform fast fill
  8967.                  .
  8968.                  .
  8969.                  .
  8970.                  mov   cx,08000h              ; length to write, bytes
  8971.                  mov   bx,handle              ; handle for prev opened file
  8972.                  push  ds                     ; save our data segment
  8973.                  mov   ds,buff_seg            ; let DS:DX = buffer address
  8974.                  mov   dx,0
  8975.                  mov   ah,40h                 ; function 40h = write
  8976.                  int   21h                    ; transfer to MS-DOS
  8977.                  pop   ds                     ; restore our data segment
  8978.                  jc    error                  ; jump if write failed
  8979.                  .
  8980.                  .
  8981.                  .
  8982.                  mov   es,buff_seg            ; ES = seg of prev allocated blo
  8983.                  mov   ah,49h                 ; function 49h = release mem blo
  8984.                  int   21h                    ; transfer to MS-DOS
  8985.                  jc    error                  ; jump if release failed
  8986.                  .
  8987.    error:        .
  8988.                  .
  8989.    handle        dw    0                      ; file handle
  8990.    buff_seg      dw    0                      ; segment of allocated block
  8991.                  .
  8992.                  .
  8993.                  .
  8994.    ──────────────────────────────────────────────────────────────────────────
  8995.  
  8996.    Figure 11-2.  Example of dynamic memory allocation. The program requests a
  8997.    32 KB memory block from MS-DOS, fills it with -1s, writes it to disk, and
  8998.    then releases it.
  8999.  
  9000.    If the arena entry selected is larger than the size requested, MS-DOS
  9001.    divides it into two parts: one block of the size requested, which is
  9002.    assigned to the program that called Int 21H Function 48H, and an unowned
  9003.    block containing the remaining memory.
  9004.  
  9005.    The default MS-DOS allocation strategy is first fit. However, under MS-DOS
  9006.    versions 3.0 and later, an application program can change the strategy
  9007.    with Int 21H Function 58H.
  9008.  
  9009.    When a program is through with an allocated memory block, it should use
  9010.    Int 21H Function 49H to release the block. If it does not, MS-DOS will
  9011.    automatically release all memory allocations for the program when it
  9012.    terminates.
  9013.  
  9014.  
  9015.  Arena Headers
  9016.  
  9017.    Microsoft has not officially documented the internal structure of arena
  9018.    headers for the outside world at present. This is probably to deter
  9019.    programmers from trying to manipulate their memory allocations directly
  9020.    instead of through the MS-DOS functions provided for that purpose.
  9021.  
  9022.    Arena headers have identical structures in MS-DOS versions 2 and 3. They
  9023.    are 16 bytes (one paragraph) and are located immediately before the memory
  9024.    area that they control (Figure 11-3). An arena header contains the
  9025.    following information:
  9026.  
  9027.    ■  A byte signifying whether the header is a member or the last entry in
  9028.       the entire chain of such headers
  9029.  
  9030.    ■  A word indicating whether the area it controls is available or whether
  9031.       it already belongs to a program (if the latter, the word points to the
  9032.       program's PSP)
  9033.  
  9034.    ■  A word indicating the size (in paragraphs) of the controlled memory
  9035.       area (arena entry)
  9036.  
  9037.    MS-DOS inspects the chain of arena headers whenever the program requests a
  9038.    memory-block allocation, modification, or release function, or when a
  9039.    program is EXEC'd or terminated. If any of the blocks appear to be
  9040.    corrupted or if the chain is broken, MS-DOS displays the dreaded message
  9041.  
  9042.    Memory allocation error
  9043.  
  9044.    and halts the system.
  9045.  
  9046.    In the example illustrated in Figure 11-3, COMMAND.COM originally loaded
  9047.    PROGRAM1.COM into the TPA and, because it was a .COM file, COMMAND.COM
  9048.    allocated it all of the TPA, controlled by arena header #1. PROGRAM1.COM
  9049.    then used Int 21H Function 4AH (Resize Memory Block) to shrink its memory
  9050.    allocation to the amount it actually needed to run and loaded and executed
  9051.    PROGRAM2.EXE with the EXEC function (Int 21H Function 4BH). The EXEC
  9052.    function obtained a suitable amount of memory, controlled by arena header
  9053.    #2, and loaded PROGRAM2.EXE into it. PROGRAM2.EXE, in turn, needed some
  9054.    additional memory to store some intermediate results, so it called Int 21H
  9055.    Function 48H (Allocate Memory Block) to obtain the area controlled by
  9056.    arena header #3. The highest arena header (#4) controls all of the
  9057.    remaining TPA that has not been allocated to any program.
  9058.  
  9059.    ┌─────────────────────────────────────────────────┐ Top of RAM
  9060.    │       Unowned RAM controlled by header #4       │  controlled by MS-DOS
  9061.    ├─────────────────────────────────────────────────┤
  9062.    │                 Arena header #4                 │
  9063.    ├─────────────────────────────────────────────────┤
  9064.    │ Memory area controlled by header #3; additional │
  9065.    │  storage dynamically allocated by PROGRAM2.EXE  │
  9066.    ├─────────────────────────────────────────────────┤
  9067.    │                 Arena header #3                 │
  9068.    ├─────────────────────────────────────────────────┤
  9069.    │      Memory area controlled by header #2,       │
  9070.    │             containing PROGRAM2.EXE             │
  9071.    ├─────────────────────────────────────────────────┤
  9072.    │                 Arena header #2                 │
  9073.    ├─────────────────────────────────────────────────┤
  9074.    │      Memory area controlled by header #1,       │
  9075.    │             containing PROGRAM1.COM             │
  9076.    ├─────────────────────────────────────────────────┤
  9077.    │                 Arena header #1                 │
  9078.    └─────────────────────────────────────────────────┘ Bottom of transient-
  9079.                                                         program area
  9080.  
  9081.    Figure 11-3.  An example diagram of MS-DOS arena headers and the
  9082.    transient-program area. The environment blocks and their associated
  9083.    headers have been omitted from this figure to increase its clarity.
  9084.  
  9085.  
  9086.  Lotus/Intel/Microsoft Expanded Memory
  9087.  
  9088.    When the IBM Personal Computer and MS-DOS were first released, the 640 KB
  9089.    limit that IBM placed on the amount of RAM that could be directly managed
  9090.    by MS-DOS seemed almost unimaginably huge. But as MS-DOS has grown in both
  9091.    size and capabilities and the popular applications have become more
  9092.    powerful, that 640 KB has begun to seem a bit crowded. Although personal
  9093.    computers based on the 80286 and 80386 have the potential to manage up to
  9094.    16 megabytes of RAM under operating systems such as MS OS/2 and XENIX,
  9095.    this is little comfort to the millions of users of 8086/8088-based
  9096.    computers and MS-DOS.
  9097.  
  9098.    At the spring COMDEX in 1985, Lotus Development Corporation and Intel
  9099.    Corporation jointly announced the Expanded Memory Specification 3.0 (EMS),
  9100.    which was designed to head off rapid obsolescence of the older PCs because
  9101.    of limited memory. Shortly afterward, Microsoft announced that it would
  9102.    support the EMS and would enhance Microsoft Windows to use the memory made
  9103.    available by EMS hardware and software. EMS versions 3.2 and 4.0, released
  9104.    in fall 1985 and summer 1987, expanded support for multitasking operating
  9105.    systems.
  9106.  
  9107.    The LIM EMS (as it is usually known) has been an enormous success. EMS
  9108.    memory boards are available from scores of manufacturers, and "EMS-aware"
  9109.    software──especially spreadsheets, disk caches, and terminate-and-stay-
  9110.    resident utilities──has become the rule rather than the exception.
  9111.  
  9112.  What Is Expanded Memory?
  9113.  
  9114.    The Lotus/Intel/Microsoft Expanded Memory Specification is a functional
  9115.    definition of a bank-switched memory-expansion subsystem. It consists of
  9116.    hardware expansion modules and a resident driver program specific to those
  9117.    modules. In EMS versions 3.0 and 3.2, the expanded memory is made
  9118.    available to application software as 16 KB pages mapped into a contiguous
  9119.    64 KB area called the page frame, somewhere above the main memory area
  9120.    used by MS-DOS/PC-DOS (0─640 KB). The exact location of the page frame is
  9121.    user configurable, so it need not conflict with other hardware options. In
  9122.    EMS version 4.0, the pages may be mapped anywhere in memory and can have
  9123.    sizes other than 16 KB.
  9124.  
  9125.    The EMS provides a uniform means for applications to access as much as 8
  9126.    megabytes of memory (32 megabytes in EMS 4.0). The supporting software,
  9127.    which is called the Expanded Memory Manager (EMM), provides a
  9128.    hardware-independent interface between application software and the
  9129.    expanded memory board(s). The EMM is supplied in the form of an
  9130.    installable device driver that you link into the MS-DOS/PC-DOS system by
  9131.    adding a line to the CONFIG.SYS file on the system boot disk.
  9132.  
  9133.    Internally, the Expanded Memory Manager consists of two major portions,
  9134.    which may be referred to as the driver and the manager. The driver portion
  9135.    mimics some of the actions of a genuine installable device driver, in that
  9136.    it includes initialization and output status functions and a valid device
  9137.    header. The second, and major, portion of the EMM is the true interface
  9138.    between application software and the expanded-memory hardware. Several
  9139.    classes of services are provided:
  9140.  
  9141.    ■  Verification of functionality of hardware and software modules
  9142.  
  9143.    ■  Allocation of expanded-memory pages
  9144.  
  9145.    ■  Mapping of logical pages into the physical page frame
  9146.  
  9147.    ■  Deallocation of expanded-memory pages
  9148.  
  9149.    ■  Support for multitasking operating systems
  9150.  
  9151.    Application programs communicate with the EMM directly, by means of
  9152.    software Int 67H. MS-DOS versions 3.3 and earlier take no part in (and in
  9153.    fact are completely oblivious to) any expanded-memory manipulations that
  9154.    may occur. MS-DOS version 4.0 and Microsoft Windows, on the other hand,
  9155.    are "EMS-aware" and can use the EMS memory when it is available.
  9156.  
  9157.    Expanded memory should not be confused with extended memory. Extended
  9158.    memory is the term used by IBM to refer to the memory at physical
  9159.    addresses above 1 megabyte that can be accessed by an 80286 or 80386 CPU
  9160.    in protected mode. Current versions of MS-DOS run the 80286 and 80386 in
  9161.    real mode (8086-emulation mode), and extended memory is therefore not
  9162.    directly accessible.
  9163.  
  9164.  Checking for Expanded Memory
  9165.  
  9166.    An application program can use either of two methods to test for the
  9167.    existence of the Expanded Memory Manager:
  9168.  
  9169.    ■  Issue an open request (Int 21H Function 3DH) using the guaranteed
  9170.       device name of the EMM driver: EMMXXXX0. If the open function succeeds,
  9171.       either the driver is present or a file with the same name
  9172.       coincidentally exists on the default disk drive. To rule out the
  9173.       latter, the application can use IOCTL (Int 21H Function 44H)
  9174.       subfunctions 00H and 07H to ensure that EMM is present. In either case,
  9175.       the application should then use Int 21H Function 3EH to close the
  9176.       handle that was obtained from the open function, so that the handle can
  9177.       be reused for another file or device.
  9178.  
  9179.    ■  Use the address that is found in the Int 67H vector to inspect the
  9180.       device header of the presumed EMM. Interrupt handlers and device
  9181.       drivers must use this method. If the EMM is present, the name field at
  9182.       offset 0AH of the device header contains the string EMMXXXX0. This
  9183.       approach is nearly foolproof and avoids the relatively high overhead of
  9184.       an MS-DOS open function. However, it is somewhat less well behaved
  9185.       because it involves inspection of memory that does not belong to the
  9186.       application.
  9187.  
  9188.    These two methods of testing for the existence of the Expanded Memory
  9189.    Manager are illustrated in Figures 11-4 and 11-5.
  9190.  
  9191.    ──────────────────────────────────────────────────────────────────────────
  9192.              .
  9193.              .
  9194.              .
  9195.                                   ; attempt to "open" EMM...
  9196.              mov  dx,seg emm_name ; DS:DX = address of name
  9197.              mov  ds,dx           ; of Expanded Memory Manager
  9198.              mov  dx,offset emm_name
  9199.              mov  ax,3d00h        ; function 3dh, mode = 00h
  9200.                                   ; = open, read only
  9201.              int  21h             ; transfer to MS-DOS
  9202.              jc   error           ; jump if open failed
  9203.  
  9204.                                   ; open succeeded, be sure
  9205.                                   ; it was not a file...
  9206.              mov  bx,ax           ; BX = handle from open
  9207.              mov  ax,4400h        ; function 44h subfunction 00h
  9208.                                   ; = IOCTL get device information
  9209.              int  21h             ; transfer to MS-DOS
  9210.              jc   error           ; jump if IOCTL call failed
  9211.              and  dx,80h          ; bit 7 = 1 if character device
  9212.              jz   error           ; jump if it was a file
  9213.  
  9214.                                   ; EMM is present, be sure
  9215.                                   ; it is available...
  9216.                                   ; (BX still contains handle)
  9217.              mov  ax,4407h        ; function 44h subfunction 07h
  9218.                                   ; = IOCTL get output status
  9219.              int  21h             ; transfer to MS-DOS
  9220.              jc   error           ; jump if IOCTL call failed
  9221.              or   al,al           ; test device status
  9222.              jz   error           ; if AL = 0 EMM is not available
  9223.                                   ; now close handle ...
  9224.                                   ; (BX still contains handle)
  9225.              mov  ah,3eh          ; function 3eh = close
  9226.              int  21h             ; transfer to MS-DOS
  9227.              jc   error           ; jump if close failed
  9228.              .
  9229.              .
  9230.              .
  9231.    emm_name  db   'EMMXXXX0',0    ; guaranteed device name for
  9232.                                   ; Expanded Memory Manager
  9233.    ──────────────────────────────────────────────────────────────────────────
  9234.  
  9235.    Figure 11-4.  Testing for the Expanded Memory Manager by means of the
  9236.    MS-DOS open and IOCTL functions.
  9237.  
  9238.    ──────────────────────────────────────────────────────────────────────────
  9239.    emm_int   equ  67h            ; Expanded Memory Manager
  9240.                                  ; software interrupt
  9241.              .
  9242.              .
  9243.              .
  9244.                                  ; first fetch contents of
  9245.                                  ; EMM interrupt vector...
  9246.              mov  al,emm_int     ; AL = EMM int number
  9247.              mov  ah,35h         ; function 35h = get vector
  9248.              int  21h            ; transfer to MS-DOS
  9249.                                  ; now ES:BX = handler address
  9250.  
  9251.                                  ; assume ES:0000 points
  9252.                                  ; to base of the EMM...
  9253.              mov  di,10          ; ES:DI = address of name
  9254.                                  ; field in device header
  9255.                                  ; DS:SI = EMM driver name
  9256.              mov  si,seg emm_name
  9257.              mov  ds,si
  9258.              mov  si,offset emm_name
  9259.              mov  cx,8           ; length of name field
  9260.              cld
  9261.              repz cmpsb          ; compare names...
  9262.              jnz  error          ; jump if driver absent
  9263.              .
  9264.              .
  9265.              .
  9266.  
  9267.  
  9268.    emm_name  db   'EMMXXXX0'     ; guaranteed device name for
  9269.                                  ; Expanded Memory Manager
  9270.    ──────────────────────────────────────────────────────────────────────────
  9271.  
  9272.    Figure 11-5.  Testing for the Expanded Memory Manager by inspection of the
  9273.    name field in the driver's device header.
  9274.  
  9275.  
  9276.  Using Expanded Memory
  9277.  
  9278.    After establishing that the memory-manager software is present, the
  9279.    application program communicates with it directly by means of the "user
  9280.    interrupt" 67H, bypassing MS-DOS/PC-DOS. The calling sequence for the EMM
  9281.    is as follows:
  9282.  
  9283.    ──────────────────────────────────────────────────────────────────────────
  9284.                      mov  ah,function            ; AH determines service type
  9285.                      .                           ; load other registers with
  9286.                      .                           ; values specific to the
  9287.                      .                           ; requested service
  9288.                      int  67h
  9289.    ──────────────────────────────────────────────────────────────────────────
  9290.  
  9291.    In general, AH contains the EMM function number, AL holds the subfunction
  9292.    number (if any), BX holds a number of pages (if applicable), and DX
  9293.    contains an EMM handle. Registers DS:SI and ES:DI are used to pass the
  9294.    addresses of arrays or buffers. Section 4 of this book,
  9295.    "Lotus/Intel/Microsoft EMS Functions Reference," details each of the
  9296.    expanded memory functions.
  9297.  
  9298.    Upon return from an EMM function, the AH register contains zero if the
  9299.    function was successful; otherwise, it contains an error code with the
  9300.    most significant bit set (Figures 11-6 and 11-7). Other values are
  9301.    typically returned in the AL and BX registers or in a user-specified
  9302.    buffer.
  9303.  
  9304. ╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
  9305.    Error code         Meaning
  9306.    ──────────────────────────────────────────────────────────────────────────
  9307.    00H                Function successful.
  9308.  
  9309.    80H                Internal error in Expanded Memory Manager software
  9310.                       (could be caused by corrupted memory image of driver).
  9311.  
  9312.    81H                Malfunction in expanded-memory hardware.
  9313.  
  9314.    82H                Memory manager busy.
  9315.  
  9316.    83H                Invalid handle.
  9317.  
  9318.    84H                Function requested by application not defined.
  9319.  
  9320.    85H                No more handles available.
  9321.  
  9322.    86H                Error in save or restore of mapping context.
  9323.  
  9324.    87H                Allocation request specified more logical pages than
  9325.                       physically available in system; no pages allocated.
  9326.    Error code         Meaning
  9327.    ──────────────────────────────────────────────────────────────────────────
  9328.                      physically available in system; no pages allocated.
  9329.  
  9330.    88H                Allocation request specified more logical pages than
  9331.                       currently available in system (request does not exceed
  9332.                       physical pages that exist, but some are already
  9333.                       allocated to other handles); no pages allocated.
  9334.  
  9335.                       Zero pages; cannot be allocated.
  9336.  
  9337.    8AH                Logical page requested to be mapped located outside
  9338.                       range of logical pages assigned to handle.
  9339.  
  9340.    8BH                Illegal physical page number in mapping request (not in
  9341.                       range
  9342.  
  9343.                       0─3).
  9344.  
  9345.    8CH                Page-mapping hardware-state save area full.
  9346.  
  9347.    Error code         Meaning
  9348.    ──────────────────────────────────────────────────────────────────────────
  9349. 
  9350.    8DH                Save of mapping context failed; save area already
  9351.                       contains context associated with requested handle.
  9352.  
  9353.    8EH                Restore of mapping context failed; save area does not
  9354.                       contain context for requested handle.
  9355.  
  9356.    8FH                Subfunction parameter not defined.
  9357.    ──────────────────────────────────────────────────────────────────────────
  9358.  
  9359.  
  9360.    Figure 11-6.  Expanded Memory Manager error codes common to EMS versions
  9361.    3.0, 3.2, and 4.0. After a call to EMM, the AH register contains zero if
  9362.    the function was successful or an error code in the range 80H through 8FH
  9363.    if the function failed.
  9364.  
  9365. ╓┌─┌──────────────────┌──────────────────────────────────────────────────────╖
  9366.    Error code         Meaning
  9367.    ──────────────────────────────────────────────────────────────────────────
  9368.    Error code         Meaning
  9369.    ──────────────────────────────────────────────────────────────────────────
  9370.    90H                Attribute type not defined.
  9371.  
  9372.    91H                Feature not supported.
  9373.  
  9374.    92H                Source and destination memory regions have same handle
  9375.                       and overlap; requested move was performed, but part of
  9376.                       source region was overwritten.
  9377.  
  9378.    93H                Specified length for source or destination memory
  9379.                       region is longer than actual allocated length.
  9380.  
  9381.    94H                Conventional-memory region and expanded-memory region
  9382.                       overlap.
  9383.  
  9384.    95H                Specified offset is outside logical page.
  9385.  
  9386.    96H                Region length exceeds 1 MB.
  9387.  
  9388.    97H                Source and destination memory regions have same handle
  9389.    Error code         Meaning
  9390.    ──────────────────────────────────────────────────────────────────────────
  9391.   97H                Source and destination memory regions have same handle
  9392.                       and overlap; exchange cannot be performed.
  9393.  
  9394.    98H                Memory source and destination types undefined.
  9395.  
  9396.    99H                This error code currently unused.
  9397.  
  9398.    9AH                Alternate map or DMA register sets supported, but the
  9399.                       alternate register set specified is not supported.
  9400.  
  9401.    9BH                Alternate map or DMA register sets supported, but all
  9402.                       alternate register sets currently allocated.
  9403.  
  9404.    9CH                Alternate map or DMA register sets not supported, and
  9405.                       specified alternate register set not zero.
  9406.  
  9407.    9DH                Alternate map or DMA register sets supported, but
  9408.                       alternate register set specified is either not defined
  9409.                       or not allocated.
  9410.    Error code         Meaning
  9411.    ──────────────────────────────────────────────────────────────────────────
  9412.                      or not allocated.
  9413.  
  9414.                       Dedicated DMA channels not supported.
  9415.  
  9416.    9FH                Dedicated DMA channels supported, but specified DMA
  9417.                       channel not supported.
  9418.  
  9419.    A0H                No handle found for specified name.
  9420.  
  9421.    A1H                Handle with this name already exists.
  9422.  
  9423.    A2H                Memory address wrap; sum of the source or destination
  9424.                       region base address and length exceeds 1 MB.
  9425.  
  9426.    A3H                Invalid pointer passed to function, or contents of
  9427.                       source array corrupted.
  9428.  
  9429.    A4H                Access to function denied by operating system.
  9430.    ──────────────────────────────────────────────────────────────────────────
  9431.    Error code         Meaning
  9432.    ──────────────────────────────────────────────────────────────────────────
  9433.   ──────────────────────────────────────────────────────────────────────────
  9434.  
  9435.  
  9436.    Figure 11-7.  Expanded Memory Manager error codes unique to EMS version
  9437.    4.0. Most of these errors are related to the EMS functions for use by
  9438.    operating systems and would not normally be encountered by application
  9439.    programs.
  9440.  
  9441.    An application program that uses expanded memory should regard that memory
  9442.    as a system resource, like a file or a device, and employ only the
  9443.    documented EMM services to allocate, access, and release expanded-memory
  9444.    pages. Such a program can use the following general strategy:
  9445.  
  9446.    1.  Establish the presence of the Expanded Memory Manager by one of the
  9447.        two methods demonstrated in Figures 11-4 and 11-5.
  9448.  
  9449.    2.  After the driver is known to be present, check its operational status
  9450.        with EMS Function 40H.
  9451.  
  9452.    3.  Check the version number of EMM with EMS Function 46H, to ensure that
  9453.        all services the application will request are available.
  9454.  
  9455.    4.  Obtain the segment of the page frame used by EMM with EMS Function
  9456.        41H.
  9457.  
  9458.    5.  Allocate the desired number of expanded-memory pages with EMS Function
  9459.        43H. If the allocation is successful, EMM returns a handle that the
  9460.        application can use to refer to the expanded-memory pages that it
  9461.        owns. This step is exactly analogous to opening a file and using the
  9462.        handle obtained from the open function for read/write operations on
  9463.        the file.
  9464.  
  9465.    6.  If the requested number of pages are not available, the application
  9466.        can query EMM for the actual number of pages available (EMS Function
  9467.        42H) and determine whether it can continue.
  9468.  
  9469.    7.  After the application has successfully allocated the needed number of
  9470.        expanded-memory pages, it uses EMS Function 44H to map logical pages
  9471.        in and out of the physical page frame in order to store and retrieve
  9472.        data in expanded memory.
  9473.  
  9474.    8.  When the program finishes using its expanded-memory pages, it must
  9475.        release them by calling EMS Function 45H. Otherwise, the pages will
  9476.        be lost to use by other programs until the system is restarted.
  9477.  
  9478.    Figure 11-8 shows a skeleton program that illustrates this general
  9479.    approach.
  9480.  
  9481.    An interrupt handler or device driver that uses EMS follows the same
  9482.    general procedure outlined in steps 1 through 8, with a few minor
  9483.    variations. It may need to acquire an EMS handle and allocate pages before
  9484.    the operating system is fully functional; in particular, you cannot assume
  9485.    that the MS-DOS Open File or Device, IOCTL, and Get Interrupt Vector
  9486.    functions are available. Thus, such a handler or driver must use a
  9487.    modified version of the "get interrupt vector" technique (Figure 11-5) to
  9488.    test for the existence of EMM, fetching the contents of the Int 67H vector
  9489.    directly.
  9490.  
  9491.    A device driver or interrupt handler typically owns its expanded-memory
  9492.    pages permanently (until the system is restarted) and never deallocates
  9493.    them. Such a program must also take care to save and restore EMM's
  9494.    page-mapping context (EMS Functions 47H and 48H) whenever it accesses
  9495.    expanded memory, so that use of EMS by a foreground program will not
  9496.    be disturbed.
  9497.  
  9498.    The EMM relies on the good behavior of application software to avoid the
  9499.    corruption of expanded memory. If several applications that use expanded
  9500.    memory are running under a multitasking manager such as Microsoft Windows
  9501.    and one or more of them does not abide strictly by EMM conventions, the
  9502.    data of some or all of the applications may be destroyed.
  9503.  
  9504.    ──────────────────────────────────────────────────────────────────────────
  9505.              .
  9506.              .
  9507.              .
  9508.              mov  ah,40h         ; test EMM status
  9509.              int  67h
  9510.              or   ah,ah
  9511.              jnz  error          ; jump if bad status from EMM
  9512.  
  9513.              mov  ah,46h         ; check EMM version
  9514.              int  67h
  9515.              or   ah,ah
  9516.              jnz  error          ; jump if couldn't get version
  9517.  
  9518.              cmp  al,030h        ; make sure at least ver 3.0
  9519.              jb   error          ; jump if wrong EMM version
  9520.              mov  ah,41h         ; get page frame segment
  9521.              int  67h
  9522.              or   ah,ah
  9523.              jnz  error          ; jump if failed to get frame
  9524.              mov  page_frame,bx  ; save segment of page frame
  9525.  
  9526.              mov  ah,42h         ; get number of available pages
  9527.              int  67h
  9528.              or   ah,ah
  9529.              jnz  error          ; jump if get pages error
  9530.              mov  total_pages,dx ; save total EMM pages
  9531.              mov  avail_pages,bx ; save available EMM pages
  9532.              or   bx,bx
  9533.              jz   error          ; abort if no pages available
  9534.  
  9535.              mov  ah,43h         ; try to allocate EMM pages
  9536.              mov  bx,needed_pages
  9537.              int  67h            ; if allocation is successful
  9538.              or   ah,ah
  9539.              jnz  error          ; jump if allocation failed
  9540.  
  9541.              mov  emm_handle,dx  ; save handle for allocated pages
  9542.  
  9543.              .
  9544.              .                   ; now we are ready for other
  9545.              .                   ; processing using EMM pages
  9546.              .
  9547.                                  ; map in EMS memory page...
  9548.              mov  bx,log_page    ; BX <- EMS logical page number
  9549.              mov  al,phys_page   ; AL <- EMS physical page (0-3)
  9550.              mov  dx,emm_handle  ; EMM handle for our pages
  9551.              mov  ah,44h         ; function 44h = map EMS page
  9552.              int  67h
  9553.              or   ah,ah
  9554.              jnz  error          ; jump if mapping error
  9555.  
  9556.              .
  9557.              .
  9558.              .                   ; program ready to terminate,
  9559.                                  ; give up allocated EMM pages...
  9560.              mov  dx,emm_handle  ; handle for our pages
  9561.              mov  ah,45h         ; EMS function 45h = release pages
  9562.              int  67h
  9563.              or   ah,ah
  9564.              jnz  error          ; jump if release failed
  9565.              .
  9566.              .
  9567.              .
  9568.    ──────────────────────────────────────────────────────────────────────────
  9569.  
  9570.    Figure 11-8.  A program illustrating the general strategy for using
  9571.    expanded memory.
  9572.  
  9573.  
  9574.  Extended Memory
  9575.  
  9576.    Extended memory is RAM storage at addresses above 1 megabyte (100000H)
  9577.    that can be accessed by an 80286 or 80386 processor running in protected
  9578.    mode. IBM PC/AT─ and PS/2─compatible machines can (theoretically) have as
  9579.    much as 15 MB of extended memory installed, in addition to the usual 1 MB
  9580.    of conventional memory.
  9581.  
  9582.    Protected-mode operating systems such as Microsoft XENIX or MS OS/2 can
  9583.    use extended memory for execution of programs. MS-DOS, on the other hand,
  9584.    runs in real mode on an 80286 or 80386, and programs running under its
  9585.    control cannot ordinarily execute from extended memory or even address
  9586.    that memory for storage of data. However, the ROM BIOS contains two
  9587.    routines that allow real-mode programs restricted access to extended
  9588.    memory:
  9589.  
  9590.    ROM BIOS function                    Action
  9591.    ──────────────────────────────────────────────────────────────────────────
  9592.    Int 15H Function 87H                Move extended-memory block.
  9593.    Int 15H Function 88H                Get extended-memory size.
  9594.    ──────────────────────────────────────────────────────────────────────────
  9595.  
  9596.    These routines can be used by electronic disks (RAMdisks) and by other
  9597.    programs that want to use extended memory for fast storage and retrieval
  9598.    of information that would otherwise have to be written to a slower
  9599.    physical disk drive. Section 3 of this book, "IBM ROM BIOS and Mouse
  9600.    Functions Reference," documents both of these functions.
  9601.  
  9602.    You should use these ROM BIOS routines with caution. Data stored in
  9603.    extended memory is, of course, volatile; it is lost if the machine is
  9604.    turned off. The transfer of data to or from extended memory involves a
  9605.    switch from real mode to protected mode and back, which is a relatively
  9606.    slow process on 80286-based machines; in some cases it is only marginally
  9607.    faster than actually reading the data from a fixed disk. In addition,
  9608.    programs that use the ROM BIOS extended-memory functions are not
  9609.    compatible with the MS-DOS compatibility mode of MS OS/2.
  9610.  
  9611.    Finally, a major deficit in these ROM BIOS functions is that they do not
  9612.    make any attempt to arbitrate between two or more programs or drivers that
  9613.    are using extended memory for temporary storage. For example, if an
  9614.    application program and an installed RAMdisk driver attempt to put data in
  9615.    the same area of extended memory, no error will be returned to either
  9616.    program, but the data of one or both may be destroyed.
  9617.  
  9618.    Figure 11-9 shows an example of the code necessary to transfer data to
  9619.    and from extended memory.
  9620.  
  9621.    ──────────────────────────────────────────────────────────────────────────
  9622.    bmdt    db      30h dup (0)     ; block move descriptor table
  9623.  
  9624.    buff1   db      80h dup ('?')   ; source buffer
  9625.    buff2   db      80h dup (0)     ; destination buffer
  9626.  
  9627.            .
  9628.            .
  9629.            .
  9630.  
  9631.                                    ; copy 'buff1' to extended-
  9632.                                    ; memory address 100000h
  9633.            mov     dx,10h          ; DX:AX = destination
  9634.            mov     ax,0            ; extended-memory address
  9635.            mov     bx,seg buff1    ; DS:BX = source conventional-
  9636.            mov     ds,bx           ; memory address
  9637.            mov     bx,offset buff1
  9638.            mov     cx,80h          ; CX = bytes to move
  9639.            mov     si,seg bmdt     ; ES:SI = block move
  9640.            mov     es,si           ; descriptor table
  9641.            mov     si,offset bmdt
  9642.            call    putblk          ; request transfer
  9643.  
  9644.  
  9645.                                    ; fill buff2 from extended-
  9646.                                    ; memory address 100000h
  9647.            mov     dx,10h          ; DX:AX = source extended-
  9648.            mov     ax,0            ; memory address
  9649.            mov     bx,seg buff2    ; DS:BX = destination
  9650.            mov     ds,bx           ; conventional-memory address
  9651.            mov     bx,offset buff2
  9652.            mov     cx,80h          ; CX = bytes to move
  9653.            mov     si,seg bmdt     ; ES:SI = block move
  9654.            mov     es,si           ; descriptor table
  9655.            mov     si,offset bmdt
  9656.            call    getblk          ; request transfer
  9657.  
  9658.            .
  9659.            .
  9660.            .
  9661.    getblk  proc    near            ; transfer block from extended
  9662.                                    ; memory to real memory
  9663.                                    ; call with
  9664.                                    ; DX:AX = source linear 32-bit
  9665.                                    ;         extended-memory address
  9666.                                    ; DS:BX = segment and offset
  9667.                                    ;         destination address
  9668.                                    ; CX    = length in bytes
  9669.                                    ; ES:SI = block move descriptor
  9670.                                    ;         table
  9671.                                    ; returns
  9672.                                    ; AH    = 0 if transfer OK
  9673.  
  9674.            mov     es:[si+10h],cx  ; store length into descriptors
  9675.            mov     es:[si+18h],cx
  9676.  
  9677.                                    ; store access rights bytes
  9678.            mov     byte ptr es:[si+15h],93h
  9679.            mov     byte ptr es:[si+1dh],93h
  9680.  
  9681.            mov     es:[si+12h],ax  ; source extended-memory address
  9682.            mov     es:[si+14h],dl
  9683.  
  9684.                                    ; convert destination segment
  9685.                                    ; and offset to linear address
  9686.            mov     ax,ds           ; segment * 16
  9687.            mov     dx,16
  9688.            mul     dx
  9689.            add     ax,bx           ; + offset -> linear address
  9690.            adc     dx,0
  9691.  
  9692.            mov     es:[si+1ah],ax  ; store destination address
  9693.            mov     es:[si+1ch],dl
  9694.  
  9695.            shr     cx,1            ; convert length to words
  9696.            mov     ah,87h          ; int 15h function 87h = block move
  9697.            int     15h             ; transfer to ROM BIOS
  9698.  
  9699.            ret                     ; back to caller
  9700.  
  9701.    getblk  endp
  9702.    putblk  proc    near            ; transfer block from real
  9703.                                    ; memory to extended memory
  9704.                                    ; call with
  9705.                                    ; DX:AX = dest linear 32-bit
  9706.                                    ;         extended-memory address
  9707.                                    ; DS:BX = segment and offset
  9708.                                    ;         source address
  9709.                                    ; CX    = length in bytes
  9710.                                    ; ES:SI = block move descriptor
  9711.                                    ;         table
  9712.                                    ; returns
  9713.                                    ; AH    = 0 if transfer OK
  9714.  
  9715.            mov     es:[si+10h],cx  ; store length into descriptors
  9716.            mov     es:[si+18h],cx
  9717.  
  9718.                                    ; store access rights bytes
  9719.            mov     byte ptr es:[si+15h],93h
  9720.            mov     byte ptr es:[si+1dh],93h
  9721.  
  9722.            mov     es:[si+1ah],ax  ; store destination extended-
  9723.            mov     es:[si+1ch],dl  ; memory address
  9724.  
  9725.                                    ; convert source segment and
  9726.                                    ; offset to linear address
  9727.            mov     ax,ds           ; segment * 16
  9728.            mov     dx,16
  9729.            mul     dx
  9730.            add     ax,bx           ; + offset -> linear address
  9731.            adc     dx,0
  9732.            mov     es:[si+12h],ax  ; store source address
  9733.            mov     es:[si+14h],dl
  9734.  
  9735.            shr     cx,1            ; convert length to words
  9736.            mov     ah,87h          ; int 15h function 87h = block move
  9737.            int     15h             ; transfer to ROM BIOS
  9738.  
  9739.            ret                     ; back to caller
  9740.  
  9741.    putblk  endp
  9742.    ──────────────────────────────────────────────────────────────────────────
  9743.  
  9744.    Figure 11-9.  Moving blocks of data between conventional memory and
  9745.    extended memory, using the ROM BIOS extended-memory functions. For
  9746.    additional information on the format of the block move descriptor table,
  9747.    see the entry for Int 15H Function 87H in Section 3 of this book, "IBM
  9748.    ROM BIOS and Mouse Functions Reference." Note that you must specify the
  9749.    extended-memory address as a 32-bit linear address rather than as a
  9750.    segment and offset.
  9751.  
  9752.  
  9753.  
  9754.  ────────────────────────────────────────────────────────────────────────────
  9755.  Chapter 12  The EXEC Function
  9756.  
  9757.    The MS-DOS EXEC function (Int 21H Function 4BH) allows a program (called
  9758.    the parent) to load any other program (called the child) from a storage
  9759.    device, execute it, and then regain control when the child program is
  9760.    finished.
  9761.  
  9762.    A parent program can pass information to the child in a command line, in
  9763.    default file control blocks, and by means of a set of strings called the
  9764.    environment block (discussed later in this chapter). All files or devices
  9765.    that the parent opened using the handle file-management functions are
  9766.    duplicated in the newly created child task; that is, the child inherits
  9767.    all the active handles of the parent task. Any file operations on those
  9768.    handles by the child, such as seeks or file I/O, also affect the file
  9769.    pointers associated with the parent's handles.
  9770.  
  9771.    MS-DOS suspends execution of the parent program until the child program
  9772.    terminates. When the child program finishes its work, it can pass an exit
  9773.    code back to the parent, indicating whether it encountered any errors. It
  9774.    can also, in turn, load other programs, and so on through many levels of
  9775.    control, until the system runs out of memory.
  9776.  
  9777.    The MS-DOS command interpreter, COMMAND.COM, uses the EXEC function to run
  9778.    its external commands and other application programs. Many popular
  9779.    commercial programs, such as database managers and word processors, use
  9780.    EXEC to run other programs (spelling checkers, for example) or to load a
  9781.    second copy of COMMAND.COM, thereby allowing the user to list directories
  9782.    or copy and rename files without closing all the application files and
  9783.    stopping the main work in progress. EXEC can also be used to load program
  9784.    overlay segments, although this use is uncommon.
  9785.  
  9786.  
  9787.  Making Memory Available
  9788.  
  9789.    In order for a parent program to use the EXEC function to load a child
  9790.    program, sufficient unallocated memory must be available in the transient
  9791.    program area.
  9792.  
  9793.    When the parent itself was loaded, MS-DOS allocated it a variable amount
  9794.    of memory, depending upon its original file type──.COM or .EXE──and any
  9795.    other information that was available to the loader. (See Chapter 11 for
  9796.    further details.) Because the operating system has no foolproof way of
  9797.    predicting how much memory any given program will require, it generally
  9798.    allocates far more memory to a program than is really necessary.
  9799.  
  9800.    Therefore, a prospective parent program's first action should be to use
  9801.    Int 21H Function 4AH (Resize Memory Block) to release any excess memory
  9802.    allocation of its own to MS-DOS. In this case, the program should call Int
  9803.    21H Function 4AH with the ES register pointing to the program segment
  9804.    prefix of the program releasing memory and the BX register containing the
  9805.    number of paragraphs of memory to retain for that program. (See Figure
  9806.    11-1 for an example.)
  9807.  
  9808.    ──────────────────────────────────────────────────────────────────────────
  9809.    WARNING
  9810.      A .COM program must move its stack to a safe area if it is reducing its
  9811.      memory allocation to less than 64 KB.
  9812.    ──────────────────────────────────────────────────────────────────────────
  9813.  
  9814.  
  9815.  Requesting the EXEC Function
  9816.  
  9817.    To load and execute a child program, the parent must execute an Int 21H
  9818.    with the registers set up as follows:
  9819.  
  9820.      AH = 4BH
  9821.      AL = 00H (subfunction to load child program)
  9822.      DS:DX = segment:offset of pathname for child program
  9823.      ES:BX = segment:offset of parameter block
  9824.  
  9825.    The parameter block, in turn, contains addresses of other information
  9826.    needed by the EXEC function.
  9827.  
  9828.  The Program Name
  9829.  
  9830.    The name of the program to be run, which the calling program provides to
  9831.    the EXEC function, must be an unambiguous file specification (no wildcard
  9832.    characters) and must include an explicit .COM or .EXE extension. If the
  9833.    path and disk drive are not supplied in the program name, MS-DOS uses the
  9834.    current directory and default disk drive. (The sequential search for .COM,
  9835.    .EXE, and .BAT files in all the locations listed in the PATH variable is
  9836.    not a function of EXEC, but rather of the internal logic of COMMAND.COM.)
  9837.  
  9838.    You cannot EXEC a batch file directly; instead, you must EXEC a copy of
  9839.    COMMAND.COM and pass the name of the batch file in the command tail, along
  9840.    with the /C switch.
  9841.  
  9842.  The Parameter Block
  9843.  
  9844.    The parameter block contains the addresses of four data objects:
  9845.  
  9846.    ■  The environment block
  9847.  
  9848.    ■  The command tail
  9849.  
  9850.    ■  Two default file control blocks
  9851.  
  9852.    The space reserved in the parameter block for the address of the
  9853.    environment block is only 2 bytes and holds a segment address. The
  9854.    remaining three addresses are all double-word addresses; that is, they are
  9855.    4 bytes, with the offset in the first 2 bytes and the segment address in
  9856.    the last 2 bytes.
  9857.  
  9858.    The Environment Block
  9859.  
  9860.    Each program that the EXEC function loads inherits a data structure called
  9861.    an environment block from its parent. The pointer to the segment of the
  9862.    block is at offset 002CH in the PSP. The environment block holds certain
  9863.    information used by the system's command interpreter (usually COMMAND.COM)
  9864.    and may also hold information to be used by transient programs. It has no
  9865.    effect on the operation of the operating system proper.
  9866.  
  9867.    If the environment-block pointer in the EXEC parameter block contains
  9868.    zero, the child program acquires a copy of the parent program's
  9869.    environment block. Alternatively, the parent program can provide a segment
  9870.    pointer to a different or expanded environment. The maximum size of the
  9871.    environment block is 32 KB, so very large chunks of information can be
  9872.    passed between programs by this mechanism.
  9873.  
  9874.    The environment block for any given program is static, implying that if
  9875.    more than one generation of child programs is resident in RAM, each one
  9876.    will have a distinct and separate copy of the environment block.
  9877.    Furthermore, the environment block for a program that terminates and stays
  9878.    resident is not updated by subsequent PATH and SET commands.
  9879.  
  9880.    You will find more details about the environment block later in this
  9881.    chapter.
  9882.  
  9883.    The Command Tail
  9884.  
  9885.    MS-DOS copies the command tail into the child program's PSP at offset
  9886.    0080H, as described in Chapter 3. The information takes the form of a
  9887.    count byte, followed by a string of ASCII characters, terminated by a
  9888.    carriage return; the carriage return is not included in the count.
  9889.  
  9890.    The command tail can include filenames, switches, or other parameters.
  9891.    From the child program's point of view, the command tail should provide
  9892.    the same information that would be present if the program had been run by
  9893.    a direct user command at the MS-DOS prompt. EXEC ignores any
  9894.    I/O-redirection parameters placed in the command tail; the parent program
  9895.    must provide for redirection of the standard devices before the EXEC
  9896.    call is made.
  9897.  
  9898.    The Default File Control Blocks
  9899.  
  9900.    MS-DOS copies the two default file control blocks pointed to by the EXEC
  9901.    parameter block into the child program's PSP at offsets 005CH and 006CH.
  9902.    To emulate the function of COMMAND.COM from the child program's point of
  9903.    view, the parent program should use Int 21H Function 29H (the system
  9904.    parse-filename service) to parse the first two parameters of the command
  9905.    tail into the default file control blocks before invoking the EXEC
  9906.    function.
  9907.  
  9908.    File control blocks are not much use under MS-DOS versions 2 and 3,
  9909.    because they do not support the hierarchical file structure, but some
  9910.    application programs do inspect them as a quick way to get at the first
  9911.    two switches or other parameters in the command tail. Chapter 8 discusses
  9912.    file control blocks in more detail.
  9913.  
  9914.  
  9915.  Returning from the EXEC Function
  9916.  
  9917.    In MS-DOS version 2, the EXEC function destroys the contents of all
  9918.    registers except the code segment (CS) and instruction pointer (IP).
  9919.    Therefore, before making the EXEC call, the parent program must push the
  9920.    contents of any other registers that are important onto the stack and then
  9921.    save the stack segment (SS) and stack pointer (SP) registers in variables.
  9922.    Upon return from a successful EXEC call (that is, the child program has
  9923.    finished executing), the parent program should reload SS and SP from the
  9924.    variables where they were saved and then pop the other saved registers off
  9925.    the stack. In MS-DOS versions 3.0 and later, the stack and other registers
  9926.    are preserved across the EXEC call in the usual fashion.
  9927.  
  9928.    Finally, the parent can use Int 21H Function 4DH to obtain the
  9929.    termination type and return code of the child program.
  9930.  
  9931.    The EXEC function will fail under the following conditions:
  9932.  
  9933.    ■  Not enough unallocated memory is available to load and execute the
  9934.       requested program file.
  9935.  
  9936.    ■  The requested program can't be found on the disk.
  9937.  
  9938.    ■  The transient portion of COMMAND.COM in highest RAM (which contains the
  9939.       actual loader) has been destroyed and not enough free memory is
  9940.       available to reload it (PC-DOS version 2 only).
  9941.  
  9942.    Figure 12-1 summarizes the calling convention for function 4BH. Figure
  9943.    12-2 shows a skeleton of a typical EXEC call. This particular example
  9944.    uses the EXEC function to load and run the MS-DOS utility CHKDSK.COM. The
  9945.    SHELL.ASM program listing later in this chapter (Figure 12-5) presents a
  9946.    more complete example that includes the use of Int 21H Function 4AH to
  9947.    free unneeded memory.
  9948.  
  9949.    ──────────────────────────────────────────────────────────────────────────
  9950.  
  9951.    Called with:
  9952.  
  9953.      AH           = 4BH
  9954.      AL           = function type
  9955.                     00 = load and execute program
  9956.                     03 = load overlay
  9957.      ES:BX        = segment:offset of parameter block
  9958.      DS:DX        = segment:offset of program specification
  9959.  
  9960.    Returns:
  9961.  
  9962.    If call succeeded
  9963.  
  9964.    Carry flag clear. In MS-DOS version 2, all registers except for CS:IP may
  9965.    be destroyed. In MS-DOS versions 3.0 and later, registers are preserved in
  9966.    the usual fashion.
  9967.  
  9968.    If call failed
  9969.  
  9970.    Carry flag set and AX = error code.
  9971.  
  9972.    Parameter block format:
  9973.  
  9974.    If AL = 0 (load and execute program)
  9975.  
  9976.      Bytes 0─1          = segment pointer, environment block
  9977.      Bytes 2─3          = offset of command-line tail
  9978.      Bytes 4─5          = segment of command-line tail
  9979.      Bytes 6─7          = offset of first file control block to be copied
  9980.                           into new PSP + 5CH
  9981.      Bytes 8─9          = segment of first file control block
  9982.      Bytes 10─11        = offset of second file control block to be copied
  9983.                           into new PSP + 6CH
  9984.      Bytes 12─13        = segment of second file control block
  9985.  
  9986.    If AL = 3 (load overlay)
  9987.  
  9988.      Bytes 0─1    = segment address where file will be loaded
  9989.      Bytes 2─3    = relocation factor to apply to loaded image
  9990.  
  9991.    ──────────────────────────────────────────────────────────────────────────
  9992.  
  9993.    Figure 12-1.  Calling convention for the EXEC function (Int 21H Function
  9994.    4BH).
  9995.  
  9996.    ──────────────────────────────────────────────────────────────────────────
  9997.    cr      egu     0dh             ; ASCII carriage return
  9998.            .
  9999.            .
  10000.            .
  10001.            mov     stkseg,ss       ; save stack pointer
  10002.            mov     stkptr,sp
  10003.  
  10004.            mov     dx,offset pname ; DS:DX = program name
  10005.            mov     bx,offset pars  ; ES:BX = param block
  10006.            mov     ax,4b00h        ; function 4bh, subfunction 00h
  10007.            int     21h             ; transfer to MS-DOS
  10008.  
  10009.            mov     ax,_DATA        ; make our data segment
  10010.            mov     ds,ax           ; addressable again
  10011.            mov     es,ax
  10012.  
  10013.            cli                     ; (for bug in some 8088s)
  10014.            mov     ss,stkseg       ; restore stack pointer
  10015.            mov     sp,stkptr
  10016.            sti                     ; (for bug in some 8088s)
  10017.  
  10018.            jc      error           ; jump if EXEC failed
  10019.            .
  10020.            .
  10021.            .
  10022.  
  10023.    stkseg  dw      0               ; original SS contents
  10024.    stkptr  dw      0               ; original SP contents
  10025.  
  10026.    pname   db      '\CHKDSK.COM',0 ; pathname of child program
  10027.  
  10028.    pars    dw      envir           ; environment segment
  10029.            dd      cmdline         ; command line for child
  10030.            dd      fcb1            ; file control block #1
  10031.            dd      fcb2            ; file control block #2
  10032.  
  10033.    cmdline db      4,' *.*',cr     ; command line for child
  10034.  
  10035.    fcb1    db      0               ; file control block #1
  10036.            db      11 dup ('?')
  10037.            db      25 dup (0)
  10038.    fcb2    db      0               ; file control block #2
  10039.            db      11 dup (' ')
  10040.            db      25 dup (0)
  10041.  
  10042.  
  10043.    envir   segment para 'ENVIR'    ; environment segment
  10044.  
  10045.            db      'PATH=',0       ; empty search path
  10046.                                    ; location of COMMAND.COM
  10047.            db      'COMSPEC=A:\COMMAND.COM',0
  10048.            db      0               ; end of environment
  10049.  
  10050.    envir   ends
  10051.    ──────────────────────────────────────────────────────────────────────────
  10052.  
  10053.    Figure 12-2.  A brief example of the use of the MS-DOS EXEC call, with all
  10054.    necessary variables and command blocks. Note the protection of the
  10055.    registers for MS-DOS version 2 and the masking of interrupts during
  10056.    loading of SS:SP to circumvent a bug in some early 8088 CPUs.
  10057.  
  10058.  
  10059.  More About the Environment Block
  10060.  
  10061.    The environment block is always paragraph aligned (starts at an address
  10062.    that is a multiple of 16 bytes) and contains a series of ASCIIZ strings.
  10063.    Each of the strings takes the following form:
  10064.  
  10065.      NAME=PARAMETER
  10066.  
  10067.    An additional zero byte (Figure 12-3) indicates the end of the entire set
  10068.    of strings. Under MS-DOS version 3, the block of environment strings and
  10069.    the extra zero byte are followed by a word count and the complete drive,
  10070.    path, filename, and extension used by EXEC to load the program.
  10071.  
  10072.    ──────────────────────────────────────────────────────────────────────────
  10073.          0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 0123456789ABCDEF
  10074.    0000 43 4F 4D 53 50 45 43 3D 43 3A 5C 43 4F 4D 4D 41 COMSPEC=C:\COMMA
  10075.    0010 4E 44 2E 43 4F 4D 00 50 52 4F 4D 50 54 3D 24 70 NDcom.PROMPT=$p
  10076.    0020 24 5F 24 64 20 20 20 24 74 24 68 24 68 24 68 24 $_$d   $t$h$h$h$
  10077.    0030 68 24 68 24 68 20 24 71 24 71 24 67 00 50 41 54 h$h$h $q$q$g.PAT
  10078.    0040 48 3D 43 3A 5C 53 59 53 54 45 4D 3B 43 3A 5C 41 H=C:\SYSTEM;C:\A
  10079.    0050 53 4D 3B 43 3A 5C 57 53 3B 43 3A 5C 45 54 48 45 SM;C:\WS;C:\ETHE
  10080.    0060 52 4E 45 54 3B 43 3A 5C 46 4F 52 54 48 5C 50 43 RNET;C:\FORTH\PC
  10081.    0070 33 31 3B 00 00 01 00 43 3A 5C 46 4F 52 54 48 5C 31;....C:\FORTH\
  10082.    0080 50 43 33 31 5C 46 4F 52 54 48 2E 43 4F 4D 00 20 PC31\FORTH.COM.
  10083.    ──────────────────────────────────────────────────────────────────────────
  10084.  
  10085.    Figure 12-3.  Dump of a typical environment block under MS-DOS version 3.
  10086.    This particular example contains the default COMSPEC parameter and two
  10087.    relatively complex PATH and PROMPT control strings that were set up by
  10088.    entries in the user's AUTOEXEC file. Note the path and file specification
  10089.    of the executing program following the double zeros at offset 0073H that
  10090.    denote the end of the environment block.
  10091.  
  10092.    Under normal conditions, the environment block inherited by a program will
  10093.    contain at least three strings:
  10094.  
  10095.      COMSPEC=variable
  10096.      PATH=variable
  10097.      PROMPT=variable
  10098.  
  10099.    MS-DOS places these three strings into the environment block at system
  10100.    initialization, during the interpretation of SHELL, PATH, and PROMPT
  10101.    directives in the CONFIG.SYS and AUTOEXEC.BAT files. The strings tell the
  10102.    MS-DOS command interpreter, COMMAND.COM, the location of its executable
  10103.    file (to enable it to reload the transient portion), where to search for
  10104.    executable external commands or program files, and the format of the user
  10105.    prompt.
  10106.  
  10107.    You can add other strings to the environment block, either interactively
  10108.    or in batch files, with the SET command. Transient programs can use these
  10109.    strings for informational purposes. For example, the Microsoft C Compiler
  10110.    looks in the environment block for INCLUDE, LIB, and TMP strings to tell
  10111.    it where to find its #include files and library files and where to build
  10112.    its temporary working files.
  10113.  
  10114.  
  10115.  Example Programs: SHELL.C and SHELL.ASM
  10116.  
  10117.    As a practical example of use of the MS-DOS EXEC function, I have included
  10118.    a small command interpreter called SHELL, with equivalent Microsoft C
  10119.    (Figure 12-4) and Microsoft Macro Assembler (Figure 12-5) source code.
  10120.    The source code for the assembly-language version is considerably more
  10121.    complex than the code for the C version, but the names and functionality
  10122.    of the various procedures are quite parallel.
  10123.  
  10124.    ──────────────────────────────────────────────────────────────────────────
  10125.    /*
  10126.        SHELL.C     Simple extendable command interpreter
  10127.                    for MS-DOS versions 2.0 and later
  10128.  
  10129.        Copyright 1988 Ray Duncan
  10130.  
  10131.        Compile:    C>CL SHELL.C
  10132.  
  10133.        Usage:      C>SHELL
  10134.    */
  10135.    #include <stdio.h>
  10136.    #include <process.h>
  10137.    #include <stdlib.h>
  10138.    #include <signal.h>
  10139.  
  10140.                                        /* macro to return number of
  10141.                                           elements in a structure  */
  10142.    #define dim(x) (sizeof(x) / sizeof(x[0]))
  10143.  
  10144.    unsigned intrinsic(char *);         /* function prototypes      */
  10145.    void extrinsic(char *);
  10146.    void get_cmd(char *);
  10147.    void get_comspec(char *);
  10148.    void break_handler(void);
  10149.    void cls_cmd(void);
  10150.    void dos_cmd(void);
  10151.    void exit_cmd(void);
  10152.  
  10153.    struct cmd_table {                  /* intrinsic commands table */
  10154.                       char *cmd_name;
  10155.                       int  (*cmd_fxn)();
  10156.                     }   commands[] =
  10157.  
  10158.                     { "CLS",   cls_cmd,
  10159.                       "DOS",   dos_cmd,
  10160.                       "EXIT",  exit_cmd, };
  10161.  
  10162.    static char com_spec[64];           /* COMMAND.COM filespec     */
  10163.  
  10164.    main(int argc, char *argv[])
  10165.    {
  10166.        char inp_buf[80];               /* keyboard input buffer    */
  10167.  
  10168.        get_comspec(com_spec);          /* get COMMAND.COM filespec */
  10169.  
  10170.                                        /* register new handler
  10171.                                           for Ctrl-C interrupts    */
  10172.        if(signal(SIGINT, break_handler) == (int(*)()) -1)
  10173.        {
  10174.            fputs("Can't capture Control-C Interrupt", stderr);
  10175.            exit(1);
  10176.        }
  10177.  
  10178.        while(1)                        /* main interpreter loop    */
  10179.        {
  10180.            get_cmd(inp_buf);           /* get a command            */
  10181.            if (! intrinsic(inp_buf) )  /* if it's intrinsic,
  10182.                                           run its subroutine       */
  10183.               extrinsic(inp_buf);      /* else pass to COMMAND.COM */
  10184.            }
  10185.    }
  10186.  
  10187.  
  10188.    /*
  10189.        Try to match user's command with intrinsic command
  10190.        table. If a match is found, run the associated routine
  10191.        and return true; else return false.
  10192.    */
  10193.  
  10194.    unsigned intrinsic(char *input_string)
  10195.    {
  10196.        int i, j;                       /* some scratch variables   */
  10197.  
  10198.                                        /* scan off leading blanks  */
  10199.        while(*input_string == '\x20') input_string++ ;
  10200.  
  10201.                                        /* search command table     */
  10202.        for(i=0; i < dim(commands); i++)
  10203.        {
  10204.            j = strcmp(commands[i].cmd_name, input_string);
  10205.  
  10206.            if(j == 0)                  /* if match, run routine    */
  10207.            {
  10208.                (*commands[i].cmd_fxn)();
  10209.                return(1);              /* and return true          */
  10210.            }
  10211.        }
  10212.        return(0);                      /* no match, return false   */
  10213.    }
  10214.  
  10215.  
  10216.    /*
  10217.        Process an extrinsic command by passing it
  10218.        to an EXEC'd copy of COMMAND.COM.
  10219.    */
  10220.  
  10221.    void extrinsic(char *input_string)
  10222.    {
  10223.        int status;
  10224.        status = system(input_string);      /* call EXEC function   */
  10225.  
  10226.        if(status)                          /* if failed, display
  10227.                                               error message        */
  10228.            fputs("\nEXEC of COMMAND.COM failed\n", stderr);
  10229.    }
  10230.  
  10231.  
  10232.    /*
  10233.        Issue prompt, get user's command from standard input,
  10234.        fold it to uppercase.
  10235.    */
  10236.  
  10237.    void get_cmd(char *buffer)
  10238.    {
  10239.        printf("\nsh: ");                   /* display prompt       */
  10240.        gets(buffer);                       /* get keyboard entry   */
  10241.        strupr(buffer);                     /* fold to uppercase    */
  10242.    }
  10243.  
  10244.  
  10245.    /*
  10246.        Get the full path and file specification for COMMAND.COM
  10247.        from the COMSPEC variable in the environment.
  10248.    */
  10249.  
  10250.    void get_comspec(char *buffer)
  10251.    {
  10252.        strcpy(buffer, getenv("COMSPEC"));
  10253.  
  10254.        if(buffer[0] == NULL)
  10255.        {
  10256.            fputs("\nNo COMSPEC in environment\n", stderr);
  10257.            exit(1);
  10258.        }
  10259.    }
  10260.  
  10261.  
  10262.    /*
  10263.        This Ctrl-C handler keeps SHELL from losing control.
  10264.        It just reissues the prompt and returns.
  10265.    */
  10266.    void break_handler(void)
  10267.    {
  10268.        signal(SIGINT, break_handler);      /* reset handler        */
  10269.        printf("\nsh: ");                   /* display prompt       */
  10270.    }
  10271.  
  10272.  
  10273.    /*
  10274.        These are the subroutines for the intrinsic commands.
  10275.    */
  10276.  
  10277.    void cls_cmd(void)                      /* CLS command          */
  10278.    {
  10279.        printf("\033[2J");                  /* ANSI escape sequence */
  10280.    }                                       /* to clear screen      */
  10281.  
  10282.    void dos_cmd(void)                      /* DOS command          */
  10283.    {
  10284.        int status;
  10285.                                            /* run COMMAND.COM      */
  10286.        status = spawnlp(P_WAIT, com_spec, com_spec, NULL);
  10287.  
  10288.        if (status)
  10289.            fputs("\nEXEC of COMMAND.COM failed\n",stderr);
  10290.    }
  10291.  
  10292.    void exit_cmd(void)                     /* EXIT command         */
  10293.    {
  10294.        exit(0);                            /* terminate SHELL      */
  10295.    }
  10296.    ──────────────────────────────────────────────────────────────────────────
  10297.  
  10298.    Figure 12-4.  SHELL.C: A table-driven command interpreter written in
  10299.    Microsoft C.
  10300.  
  10301.    ──────────────────────────────────────────────────────────────────────────
  10302.            name    shell
  10303.            page    55,132
  10304.            title   SHELL.ASM--simple MS-DOS shell
  10305.    ;
  10306.    ; SHELL.ASM     Simple extendable command interpreter
  10307.    ;               for MS-DOS versions 2.0 and later
  10308.    ;
  10309.    ; Copyright 1988 by Ray Duncan
  10310.    ;
  10311.    ; Build:        C>MASM SHELL;
  10312.    ;               C>LINK SHELL;
  10313.    ;
  10314.    ; Usage:        C>SHELL;
  10315.    ;
  10316.  
  10317.    stdin   equ     0                       ; standard input handle
  10318.    stdout  equ     1                       ; standard output handle
  10319.    stderr  equ     2                       ; standard error handle
  10320.  
  10321.    cr      equ     0dh                     ; ASCII carriage return
  10322.    lf      equ     0ah                     ; ASCII linefeed
  10323.    blank   equ     20h                     ; ASCII blank code
  10324.    escape  equ     01bh                    ; ASCII escape code
  10325.  
  10326.    _TEXT   segment word public 'CODE'
  10327.  
  10328.            assume  cs:_TEXT,ds:_DATA,ss:STACK
  10329.  
  10330.    shell   proc    far                     ; at entry DS = ES = PSP
  10331.  
  10332.            mov     ax,_DATA                ; make our data segment
  10333.            mov     ds,ax                   ; addressable
  10334.  
  10335.            mov     ax,es:[002ch]           ; get environment segment
  10336.            mov     env_seg,ax              ; from PSP and save it
  10337.  
  10338.                                            ; release unneeded memory...
  10339.                                            ; ES already = PSP segment
  10340.            mov     bx,100h                 ; BX = paragraphs needed
  10341.            mov     ah,4ah                  ; function 4ah = resize block
  10342.            int     21h                     ; transfer to MS-DOS
  10343.            jnc     shell1                  ; jump if resize OK
  10344.  
  10345.            mov     dx,offset msg1          ; resize failed, display
  10346.            mov     cx,msg1_length          ; error message and exit
  10347.            jmp     shell4
  10348.  
  10349.    shell1: call    get_comspec             ; get COMMAND.COM filespec
  10350.            jnc     shell2                  ; jump if it was found
  10351.  
  10352.            mov     dx,offset msg3          ; COMSPEC not found in
  10353.            mov     cx,msg3_length          ; environment, display error
  10354.            jmp     shell4                  ; message and exit
  10355.    shell2: mov     dx,offset shell3        ; set Ctrl-C vector (int 23h)
  10356.            mov     ax,cs                   ; for this program's handler
  10357.            mov     ds,ax                   ; DS:DX = handler address
  10358.            mov     ax,2523h                ; function 25h = set vector
  10359.            int     21h                     ; transfer to MS-DOS
  10360.  
  10361.            mov     ax,_DATA                ; make our data segment
  10362.            mov     ds,ax                   ; addressable again
  10363.            mov     es,ax
  10364.  
  10365.    shell3:                                 ; main interpreter loop
  10366.  
  10367.            call    get_cmd                 ; get a command from user
  10368.  
  10369.            call    intrinsic               ; check if intrinsic function
  10370.            jnc     shell3                  ; yes, it was processed
  10371.  
  10372.            call    extrinsic               ; no, pass it to COMMAND.COM
  10373.            jmp     shell3                  ; then get another command
  10374.  
  10375.    shell4:                                 ; come here if error detected
  10376.                                            ; DS:DX = message address
  10377.                                            ; CX = message length
  10378.            mov     bx,stderr               ; BX = standard error handle
  10379.            mov     ah,40h                  ; function 40h = write
  10380.            int     21h                     ; transfer to MS-DOS
  10381.  
  10382.            mov     ax,4c01h                ; function 4ch = terminate with
  10383.                                            ; return code = 1
  10384.            int     21h                     ; transfer to MS-DOS
  10385.  
  10386.    shell   endp
  10387.  
  10388.  
  10389.  
  10390.    intrinsic proc  near                    ; decode user entry against
  10391.                                            ; the table "COMMANDS"
  10392.                                            ; if match, run the routine,
  10393.                                            ; and return carry = false
  10394.                                            ; if no match, carry = true
  10395.                                            ; return carry = true
  10396.  
  10397.            mov     si,offset commands      ; DS:SI = command table
  10398.  
  10399.    intr1:  cmp     byte ptr [si],0         ; end of table?
  10400.            je      intr7                   ; jump, end of table found
  10401.            mov     di,offset inp_buf       ; no, let DI = addr of user input
  10402.  
  10403.    intr2:  cmp     byte ptr [di],blank     ; scan off any leading blanks
  10404.            jne     intr3
  10405.  
  10406.            inc     di                      ; found blank, go past it
  10407.            jmp     intr2
  10408.  
  10409.    intr3:  mov     al,[si]                 ; next character from table
  10410.  
  10411.            or      al,al                   ; end of string?
  10412.            jz      intr4                   ; jump, entire string matched
  10413.  
  10414.            cmp     al,[di]                 ; compare to input character
  10415.            jnz     intr6                   ; jump, found mismatch
  10416.  
  10417.            inc     si                      ; advance string pointers
  10418.            inc     di
  10419.            jmp     intr3
  10420.  
  10421.    intr4:  cmp     byte ptr [di],cr        ; be sure user's entry
  10422.            je      intr5                   ; is the same length...
  10423.            cmp     byte ptr [di],blank     ; next character in entry
  10424.            jne     intr6                   ; must be blank or return
  10425.  
  10426.    intr5:  call    word ptr [si+1]         ; run the command routine
  10427.  
  10428.            clc                             ; return carry flag = false
  10429.            ret                             ; as success flag
  10430.  
  10431.    intr6:  lodsb                           ; look for end of this
  10432.            or      al,al                   ; command string (null byte)
  10433.            jnz     intr6                   ; not end yet, loop
  10434.  
  10435.            add     si,2                    ; skip over routine address
  10436.            jmp     intr1                   ; try to match next command
  10437.  
  10438.    intr7:  stc                             ; command not matched, exit
  10439.            ret                             ; with carry = true
  10440.  
  10441.    intrinsic endp
  10442.    extrinsic proc  near                    ; process extrinsic command
  10443.                                            ; by passing it to
  10444.                                            ; COMMAND.COM with a
  10445.                                            ; " /C " command tail
  10446.  
  10447.            mov     al,cr                   ; find length of command
  10448.            mov     cx,cmd_tail_length      ; by scanning for carriage
  10449.            mov     di,offset cmd_tail+1    ; return
  10450.            cld
  10451.            repnz scasb
  10452.  
  10453.            mov     ax,di                   ; calculate command-tail
  10454.            sub     ax,offset cmd_tail+2    ; length without carriage
  10455.            mov     cmd_tail,al             ; return, and store it
  10456.  
  10457.                                            ; set command-tail address
  10458.            mov     word ptr par_cmd,offset cmd_tail
  10459.            call    exec                    ; and run COMMAND.COM
  10460.            ret
  10461.  
  10462.    extrinsic endp
  10463.  
  10464.  
  10465.    get_cmd proc    near                    ; prompt user, get command
  10466.  
  10467.                                            ; display the shell prompt
  10468.            mov     dx,offset prompt        ; DS:DX = message address
  10469.            mov     cx,prompt_length        ; CX = message length
  10470.            mov     bx,stdout               ; BX = standard output handle
  10471.            mov     ah,40h                  ; function 40h = write
  10472.            int     21h                     ; transfer to MS-DOS
  10473.  
  10474.                                            ; get entry from user
  10475.            mov     dx,offset inp_buf       ; DS:DX = input buffer
  10476.            mov     cx,inp_buf_length       ; CX = max length to read
  10477.            mov     bx,stdin                ; BX = standard input handle
  10478.            mov     ah,3fh                  ; function 3fh = read
  10479.            int     21h                     ; transfer to MS-DOS
  10480.  
  10481.            mov     si,offset inp_buf       ; fold lowercase characters
  10482.            mov     cx,inp_buf_length       ; in entry to uppercase
  10483.    gcmd1:  cmp     byte ptr [si],'a'       ; check if 'a-z'
  10484.            jb      gcmd2                   ; jump, not in range
  10485.            cmp     byte ptr [si],'z'       ; check if 'a-z'
  10486.            ja      gcmd2                   ; jump, not in range
  10487.            sub     byte ptr [si],'a'-'A'   ; convert to uppercase
  10488.  
  10489.    gcmd2:  inc     si                      ; advance through entry
  10490.            loop    gcmd1
  10491.            ret                             ; back to caller
  10492.  
  10493.    get_cmd endp
  10494.  
  10495.  
  10496.  
  10497.    get_comspec proc near                   ; get location of COMMAND.COM
  10498.                                            ; from environment "COMSPEC="
  10499.                                            ; returns carry = false
  10500.                                            ; if COMSPEC found
  10501.                                            ; returns carry = true
  10502.                                            ; if no COMSPEC
  10503.  
  10504.            mov     si,offset com_var       ; DS:SI = string to match...
  10505.            call    get_env                 ; search environment block
  10506.            jc      gcsp2                   ; jump if COMSPEC not found
  10507.  
  10508.                                            ; ES:DI points past "="
  10509.            mov     si,offset com_spec      ; DS:SI = local buffer
  10510.  
  10511.    gcsp1:  mov     al,es:[di]              ; copy COMSPEC variable
  10512.            mov     [si],al                 ; to local buffer
  10513.            inc     si
  10514.            inc     di
  10515.            or      al,al                   ; null char? (turns off carry)
  10516.            jnz     gcsp1                   ; no, get next character
  10517.  
  10518.    gcsp2:  ret                             ; back to caller
  10519.  
  10520.    get_comspec endp
  10521.  
  10522.  
  10523.    get_env proc    near                    ; search environment
  10524.                                            ; call DS:SI = "NAME="
  10525.                                            ; uses contents of "ENV_SEG"
  10526.                                            ; returns carry = false and ES:DI
  10527.                                            ; pointing to parameter if found,
  10528.                                            ; returns carry = true if no match
  10529.            mov     es,env_seg              ; get environment segment
  10530.            xor     di,di                   ; initialize env offset
  10531.  
  10532.    genv1:  mov     bx,si                   ; initialize pointer to name
  10533.            cmp     byte ptr es:[di],0      ; end of environment?
  10534.            jne     genv2                   ; jump, end not found
  10535.  
  10536.            stc                             ; no match, return carry set
  10537.            ret
  10538.  
  10539.    genv2:  mov     al,[bx]                 ; get character from name
  10540.            or      al,al                   ; end of name? (turns off carry)
  10541.            jz      genv3                   ; yes, name matched
  10542.  
  10543.            cmp     al,es:[di]              ; compare to environment
  10544.            jne     genv4                   ; jump if match failed
  10545.  
  10546.            inc     bx                      ; advance environment
  10547.            inc     di                      ; and name pointers
  10548.            jmp     genv2
  10549.  
  10550.    genv3:                                  ; match found, carry = clear,
  10551.            ret                             ; ES:DI = variable
  10552.  
  10553.    genv4:  xor     al,al                   ; scan forward in environment
  10554.            mov     cx,-1                   ; for zero byte
  10555.            cld
  10556.            repnz   scasb
  10557.            jmp     genv1                   ; go compare next string
  10558.  
  10559.    get_env endp
  10560.  
  10561.  
  10562.    exec    proc    near                    ; call MS-DOS EXEC function
  10563.                                            ; to run COMMAND.COM
  10564.  
  10565.            mov     stkseg,ss               ; save stack pointer
  10566.            mov     stkptr,sp
  10567.  
  10568.                                            ; now run COMMAND.COM
  10569.            mov     dx,offset com_spec      ; DS:DX = filename
  10570.            mov     bx,offset par_blk       ; ES:BX = parameter block
  10571.            mov     ax,4b00h                ; function 4bh = EXEC
  10572.                                            ; subfunction 0 =
  10573.                                            ; load and execute
  10574.            int     21h                     ; transfer to MS-DOS
  10575.  
  10576.            mov     ax,_DATA                ; make data segment
  10577.            mov     ds,ax                   ; addressable again
  10578.            mov     es,ax
  10579.  
  10580.            cli                             ; (for bug in some 8088s)
  10581.            mov     ss,stkseg               ; restore stack pointer
  10582.            mov     sp,stkptr
  10583.            sti                             ; (for bug in some 8088s)
  10584.  
  10585.            jnc     exec1                   ; jump if no errors
  10586.  
  10587.                                            ; display error message
  10588.            mov     dx,offset msg2          ; DS:DX = message address
  10589.            mov     cx,msg2_length          ; CX = message length
  10590.            mov     bx,stderr               ; BX = standard error handle
  10591.            mov     ah,40h                  ; function 40h = write
  10592.            int     21h                     ; transfer to MS-DOS
  10593.  
  10594.    exec1:  ret                             ; back to caller
  10595.  
  10596.    exec    endp
  10597.  
  10598.  
  10599.  
  10600.    cls_cmd proc    near                    ; intrinsic CLS command
  10601.  
  10602.            mov     dx,offset cls_str       ; send the ANSI escape
  10603.            mov     cx,cls_str_length       ; sequence to clear
  10604.            mov     bx,stdout               ; the screen
  10605.            mov     ah,40h
  10606.            int     21h
  10607.            ret
  10608.  
  10609.    cls_cmd endp
  10610.  
  10611.  
  10612.    dos_cmd proc    near                    ; intrinsic DOS command
  10613.  
  10614.                                            ; set null command tail
  10615.            mov     word ptr par_cmd,offset nultail
  10616.            call    exec                    ; and run COMMAND.COM
  10617.            ret
  10618.  
  10619.    dos_cmd endp
  10620.    exit_cmd proc   near                    ; intrinsic EXIT command
  10621.  
  10622.            mov     ax,4c00h                ; call MS-DOS terminate
  10623.            int     21h                     ; function with
  10624.                                            ; return code of zero
  10625.    exit_cmd endp
  10626.  
  10627.    _TEXT   ends
  10628.  
  10629.  
  10630.    STACK   segment para stack 'STACK'      ; declare stack segment
  10631.  
  10632.            dw      64 dup (?)
  10633.  
  10634.    STACK   ends
  10635.  
  10636.    _DATA   segment word public 'DATA'
  10637.  
  10638.    commands equ $                          ; "intrinsic" commands table
  10639.                                            ; each entry is ASCIIZ string
  10640.                                            ; followed by the offset
  10641.                                            ; of the procedure to be
  10642.                                            ; executed for that command
  10643.            db      'CLS',0
  10644.            dw      cls_cmd
  10645.  
  10646.            db      'DOS',0
  10647.            dw      dos_cmd
  10648.  
  10649.            db      'EXIT',0
  10650.            dw      exit_cmd
  10651.  
  10652.            db      0                       ; end of table
  10653.  
  10654.    com_var db      'COMSPEC=',0            ; environment variable
  10655.  
  10656.                                            ; COMMAND.COM filespec
  10657.    com_spec db     80 dup (0)              ; from environment COMSPEC=
  10658.  
  10659.    nultail db      0,cr                    ; null command tail for
  10660.                                            ; invoking COMMAND.COM
  10661.                                            ; as another shell
  10662.  
  10663.    cmd_tail db     0,' /C '                ; command tail for invoking
  10664.                                            ; COMMAND.COM as a transient
  10665.    inp_buf db      80 dup (0)              ; command line from standard input
  10666.  
  10667.    inp_buf_length equ $-inp_buf
  10668.    cmd_tail_length equ $-cmd_tail-1
  10669.  
  10670.    prompt  db      cr,lf,'sh: '            ; SHELL's user prompt
  10671.    prompt_length equ $-prompt
  10672.  
  10673.    env_seg dw      0                       ; segment of environment block
  10674.  
  10675.    msg1    db      cr,lf
  10676.            db      'Unable to release memory.'
  10677.            db      cr,lf
  10678.    msg1_length equ $-msg1
  10679.  
  10680.    msg2    db      cr,lf
  10681.            db      'EXEC of COMMAND.COM failed.'
  10682.            db      cr,lf
  10683.    msg2_length equ $-msg2
  10684.  
  10685.    msg3    db      cr,lf
  10686.            db      'No COMSPEC variable in environment.'
  10687.            db      cr,lf
  10688.    msg3_length equ $-msg3
  10689.  
  10690.    cls_str db      escape,'[2J'            ; ANSI escape sequence
  10691.    cls_str_length equ $-cls_str            ; to clear the screen
  10692.  
  10693.                                            ; EXEC parameter block
  10694.    par_blk dw      0                       ; environment segment
  10695.    par_cmd dd      cmd_tail                ; command line
  10696.            dd      fcb1                    ; file control block #1
  10697.            dd      fcb2                    ; file control block #2
  10698.  
  10699.    fcb1    db      0                       ; file control block #1
  10700.            db      11 dup (' ')
  10701.            db      25 dup (0)
  10702.  
  10703.    fcb2    db      0                       ; file control block #2
  10704.            db      11 dup (' ')
  10705.            db      25 dup (0)
  10706.  
  10707.    stkseg  dw      0                       ; original SS contents
  10708.    stkptr  dw      0                       ; original SP contents
  10709.  
  10710.    _DATA   ends
  10711.  
  10712.            end     shell
  10713.    ──────────────────────────────────────────────────────────────────────────
  10714.  
  10715.    Figure 12-5.  SHELL.ASM: A simple table-driven command interpreter written
  10716.    in Microsoft Macro Assembler.
  10717.  
  10718.    The SHELL program is table driven and can easily be extended to provide a
  10719.    powerful customized user interface for almost any application. When SHELL
  10720.    takes control of the system, it displays the prompt
  10721.  
  10722.    sh:
  10723.  
  10724.    and waits for input from the user. After the user types a line terminated
  10725.    by a carriage return, SHELL tries to match the first token in the line
  10726.    against its table of internal (intrinsic) commands. If it finds a match,
  10727.    it calls the appropriate subroutine. If it does not find a match, it calls
  10728.    the MS-DOS EXEC function and passes the user's input to COMMAND.COM with
  10729.    the /C switch, essentially using COMMAND.COM as a transient command
  10730.    processor under its own control.
  10731.  
  10732.    As supplied in these listings, SHELL "knows" exactly three internal
  10733.    commands:
  10734.  
  10735.    Command            Action
  10736.    ──────────────────────────────────────────────────────────────────────────
  10737.    CLS                Uses the ANSI standard control sequence to clear the
  10738.                       display screen and home the cursor.
  10739.    DOS                Runs a copy of COMMAND.COM.
  10740.    EXIT               Exits SHELL, returning control of the system to the
  10741.                       next lower command interpreter.
  10742.    ──────────────────────────────────────────────────────────────────────────
  10743.  
  10744.    You can quickly add new intrinsic commands to either the C version or the
  10745.    assembly-language version of SHELL. Simply code a procedure with the
  10746.    appropriate action and insert the name of that procedure, along with the
  10747.    text string that defines the command, into the table COMMANDS. In
  10748.    addition, you can easily prevent SHELL from passing certain "dangerous"
  10749.    commands (such as MKDIR or ERASE) to COMMAND.COM simply by putting the
  10750.    names of the commands to be screened out into the intrinsic command table
  10751.    with the address of a subroutine that prints an error message.
  10752.  
  10753.    To summarize, the basic flow of both versions of the SHELL program is
  10754.    as follows:
  10755.  
  10756.    1.  The program calls MS-DOS Int 21H Function 4AH (Resize Memory Block)
  10757.        to shrink its memory allocation, so that the maximum possible space
  10758.        will be available for COMMAND.COM if it is run as an overlay. (This is
  10759.        explicit in the assembly-language version only. To keep the example
  10760.        code simple, the number of paragraphs to be reserved is coded as a
  10761.        generous literal value, rather than being figured out at runtime from
  10762.        the size and location of the various program segments.)
  10763.  
  10764.    2.  The program searches the environment for the COMSPEC variable, which
  10765.        defines the location of an executable copy of COMMAND.COM. If it can't
  10766.        find the COMSPEC variable, it prints an error message and exits.
  10767.  
  10768.    3.  The program puts the address of its own handler in the Ctrl-C vector
  10769.        (Int 23H) so that it won't lose control if the user enters a Ctrl-C
  10770.        or a Ctrl-Break.
  10771.  
  10772.    4.  The program issues a prompt to the standard output device.
  10773.  
  10774.    5.  The program reads a buffered line from the standard input device to
  10775.        get the user's command.
  10776.  
  10777.    6.  The program matches the first blank-delimited token in the line
  10778.        against its table of intrinsic commands. If it finds a match, it
  10779.        executes the associated procedure.
  10780.  
  10781.    7.  If the program does not find a match in the table of intrinsic
  10782.        commands, it synthesizes a command-line tail by appending the user's
  10783.        input to the /C switch and then EXECs a copy of COMMAND.COM, passing
  10784.        the address of the synthesized command tail in the EXEC parameter
  10785.        block.
  10786.  
  10787.    8.  The program repeats steps 4 through 7 until the user enters the
  10788.        command EXIT, which is one of the intrinsic commands, and which causes
  10789.        SHELL to terminate execution.
  10790.  
  10791.    In its present form, SHELL allows COMMAND.COM to inherit a full copy of
  10792.    the current environment. However, in some applications it may be helpful,
  10793.    or safer, to pass a modified copy of the environment block so that the
  10794.    secondary copy of COMMAND.COM will not have access to certain information.
  10795.  
  10796.  
  10797.  Using EXEC to Load Overlays
  10798.  
  10799.    Loading overlays with the EXEC function is much less complex than using
  10800.    EXEC to run another program. The overlay can be constructed as either a
  10801.    memory image (.COM) or relocatable (.EXE) file and need not be the same
  10802.    type as the program that loads it. The main program, called the root
  10803.    segment, must carry out the following steps to load and execute an
  10804.    overlay:
  10805.  
  10806.    1.  Make a memory block available to receive the overlay. The program that
  10807.        calls EXEC must own the memory block for the overlay.
  10808.  
  10809.    2.  Set up the overlay parameter block to be passed to the EXEC function.
  10810.        This block contains the segment address of the block that will receive
  10811.        the overlay, plus a segment relocation value to be applied to the
  10812.        contents of the overlay file (if it is a .EXE file). These are
  10813.        normally the same value.
  10814.  
  10815.    3.  Call the MS-DOS EXEC function to load the overlay by issuing an Int
  10816.        21H with the registers set up as follows:
  10817.  
  10818.        AH = 4BH
  10819.        AL = 03H (EXEC subfunction to load overlay)
  10820.        DS:DX = segment:offset of overlay file pathname
  10821.        ES:BX = segment:offset of overlay parameter block
  10822.  
  10823.        Upon return from the EXEC function, the carry flag is clear if the
  10824.        overlay was found and loaded. The carry flag is set if the file could
  10825.        not be found or if some other error occurred.
  10826.  
  10827.    4.  Execute the code within the overlay by transferring to it with a far
  10828.        call. The overlay should be designed so that either the entry point or
  10829.        a pointer to the entry point is at the beginning of the module after
  10830.        it is loaded. This technique allows you to maintain the root and
  10831.        overlay modules separately, because the root module does not contain
  10832.        any "magical" knowledge of addresses within the overlay segment.
  10833.  
  10834.    To prevent users from inadvertently running an overlay directly from the
  10835.    command line, you should assign overlay files an extension other than .COM
  10836.    or .EXE. It is most convenient to relate overlays to their root segment by
  10837.    assigning them the same filename but a different extension, such as .OVL
  10838.    or .OV1, .OV2, and so on.
  10839.  
  10840.    Figure 12-6 shows the use of EXEC to load and execute an overlay.
  10841.  
  10842.    ──────────────────────────────────────────────────────────────────────────
  10843.            .
  10844.            .
  10845.            .
  10846.                                    ; allocate memory for overlay
  10847.            mov     bx,1000h        ; get 64 KB (4096 paragraphs)
  10848.            mov     ah,48h          ; function 48h = allocate block
  10849.            int     21h             ; transfer to MS-DOS
  10850.            jc      error           ; jump if allocation failed
  10851.  
  10852.            mov     pars,ax         ; set load address for overlay
  10853.            mov     pars+2,ax       ; set relocation segment for overlay
  10854.  
  10855.                                    ; set segment of entry point
  10856.            mov     word ptr entry+2,ax
  10857.  
  10858.            mov     stkseg,ss       ; save root's stack pointer
  10859.            mov     stkptr,sp
  10860.  
  10861.            mov     ax,ds           ; set ES = DS
  10862.            mov     es,ax
  10863.  
  10864.            mov     dx,offset oname ; DS:DX = overlay pathname
  10865.            mov     bx,offset pars  ; ES:BX = parameter block
  10866.            mov     ax,4b03h        ; function 4bh, subfunction 03h
  10867.            int     21h             ; transfer to MS-DOS
  10868.  
  10869.            mov     ax,_DATA        ; make our data segment
  10870.            mov     ds,ax           ; addressable again
  10871.            mov     es,ax
  10872.  
  10873.            cli                     ; (for bug in some early 8088s)
  10874.            mov     ss,stkseg       ; restore stack pointer
  10875.            mov     sp,stkptr
  10876.            sti                     ; (for bug in some early 8088s)
  10877.  
  10878.            jc      error           ; jump if EXEC failed
  10879.  
  10880.                                    ; otherwise EXEC succeeded...
  10881.            push    ds              ; save our data segment
  10882.            call    dword ptr entry ; now call the overlay
  10883.            pop     ds              ; restore our data segment
  10884.            .
  10885.            .
  10886.            .
  10887.  
  10888.    oname   db      'OVERLAY.OVL',0 ; pathname of overlay file
  10889.  
  10890.    pars    dw      0               ; load address (segment) for file
  10891.            dw      0               ; relocation (segment) for file
  10892.  
  10893.    entry   dd      0               ; entry point for overlay
  10894.  
  10895.    stkseg  dw      0               ; save SS register
  10896.    stkptr  dw      0               ; save SP register
  10897.    ──────────────────────────────────────────────────────────────────────────
  10898.  
  10899.    Figure 12-6.  A code skeleton for loading and executing an overlay with
  10900.    the EXEC function. The overlay file may be in either .COM or .EXE format.
  10901.  
  10902.  
  10903.  
  10904.  ────────────────────────────────────────────────────────────────────────────
  10905.  Chapter 13  Interrupt Handlers
  10906.  
  10907.    Interrupts are signals that cause the computer's central processing unit
  10908.    to suspend what it is doing and transfer to a program called an interrupt
  10909.    handler. Special hardware mechanisms that are designed for maximum speed
  10910.    force the transfer. The interrupt handler determines the cause of the
  10911.    interrupt, takes the appropriate action, and then returns control to the
  10912.    original process that was suspended.
  10913.  
  10914.    Interrupts are typically caused by events external to the central
  10915.    processor that require immediate attention, such as the following:
  10916.  
  10917.    ■  Completion of an I/O operation
  10918.  
  10919.    ■  Detection of a hardware failure
  10920.  
  10921.    ■  "Catastrophes" (power failures, for example)
  10922.  
  10923.    In order to service interrupts more efficiently, most modern processors
  10924.    support multiple interrupt types, or levels. Each type usually has a
  10925.    reserved location in memory, called an interrupt vector, that specifies
  10926.    where the interrupt-handler program for that interrupt type is located.
  10927.    This design speeds processing of an interrupt because the computer can
  10928.    transfer control directly to the appropriate routine; it does not need a
  10929.    central routine that wastes precious machine cycles determining the cause
  10930.    of the interrupt. The concept of interrupt types also allows interrupts to
  10931.    be prioritized, so that if several interrupts occur simultaneously, the
  10932.    most important one can be processed first.
  10933.  
  10934.    CPUs that support interrupts must also have the capability to block
  10935.    interrupts while they are executing critical sections of code. Sometimes
  10936.    the CPU can block interrupt levels selectively, but more frequently the
  10937.    effect is global. While an interrupt is being serviced, the CPU masks all
  10938.    other interrupts of the same or lower priority until the active handler
  10939.    has completed its execution; similarly, it can preempt the execution of a
  10940.    handler if a different interrupt with higher priority requires service.
  10941.    Some CPUs can even draw a distinction between selectively masking
  10942.    interrupts (they are recognized, but their processing is deferred) and
  10943.    simply disabling them (the interrupt is thrown away).
  10944.  
  10945.    The creation of interrupt handlers has traditionally been considered one
  10946.    of the most arcane of programming tasks, suitable only for the elite cadre
  10947.    of system hackers. In reality, writing an interrupt handler is, in itself,
  10948.    straightforward. Although the exact procedure must, of course, be
  10949.    customized for the characteristics of the particular CPU and operating
  10950.    system, the guidelines on the following page are applicable to almost any
  10951.    computer system.
  10952.  
  10953.    A program preparing to handle interrupts must do the following:
  10954.  
  10955.    1.  Disable interrupts, if they were previously enabled, to prevent them
  10956.        from occurring while interrupt vectors are being modified.
  10957.  
  10958.    2.  Initialize the vector for the interrupt of interest to point to the
  10959.        program's interrupt handler.
  10960.  
  10961.    3.  Ensure that, if interrupts were previously disabled, all other vectors
  10962.        point to some valid handler routine.
  10963.  
  10964.    4.  Enable interrupts again.
  10965.  
  10966.    The interrupt handler itself must follow a simple but rigid sequence of
  10967.    steps:
  10968.  
  10969.    1.  Save the system context (registers, flags, and anything else that the
  10970.        handler will modify and that wasn't saved automatically by the CPU).
  10971.  
  10972.    2.  Block any interrupts that might cause interference if they were
  10973.        allowed to occur during this handler's processing. (This is often done
  10974.        automatically by the computer hardware.)
  10975.  
  10976.    3.  Enable any interrupts that should still be allowed to occur during
  10977.        this handler's processing.
  10978.  
  10979.    4.  Determine the cause of the interrupt.
  10980.  
  10981.    5.  Take the appropriate action for the interrupt: receive and store data
  10982.        from the serial port, set a flag to indicate the completion of a
  10983.        disk-sector transfer, and so forth.
  10984.  
  10985.    6.  Restore the system context.
  10986.  
  10987.    7.  Reenable any interrupt levels that were blocked during this handler's
  10988.        execution.
  10989.  
  10990.    8.  Resume execution of the interrupted process.
  10991.  
  10992.    As in writing any other program, the key to success in writing an
  10993.    interrupt handler is to program defensively and cover all the bases. The
  10994.    main reason interrupt handlers have acquired such a mystical reputation is
  10995.    that they are so difficult to debug when they contain obscure errors.
  10996.    Because interrupts can occur asynchronously──that is, because they can be
  10997.    caused by external events without regard to the state of the currently
  10998.    executing process──bugs in interrupt handlers can cause the system as a
  10999.    whole to behave quite unpredictably.
  11000.  
  11001.  
  11002.  Interrupts and the Intel 80x86 Family
  11003.  
  11004.    The Intel 80x86 family of microprocessors supports 256 levels of
  11005.    prioritized interrupts, which can be triggered by three types of events:
  11006.  
  11007.    ■  Internal hardware interrupts
  11008.  
  11009.    ■  External hardware interrupts
  11010.  
  11011.    ■  Software interrupts
  11012.  
  11013.  Internal Hardware Interrupts
  11014.  
  11015.    Internal hardware interrupts, sometimes called faults, are generated by
  11016.    certain events encountered during program execution, such as an attempt to
  11017.    divide by zero. The assignment of such events to certain interrupt numbers
  11018.    is wired into the processor and is not modifiable (Figure 13-1).
  11019.  
  11020. ╓┌─┌──────────┌─────────┌────────────────────┌──────────┌─────────┌──────────╖
  11021.    Interrupt  Vector    Interrupt            8086/88    80286     80386
  11022.    level      address   trigger
  11023.    ──────────────────────────────────────────────────────────────────────────
  11024.    00H        00H─03H   Divide-by-zero       x          x         x
  11025.    01H        04H─07H   Single step          x          x         x
  11026.    02H        08H─0BH   Nonmaskable          x          x         x
  11027.    Interrupt  Vector    Interrupt            8086/88    80286     80386
  11028.    level      address   trigger
  11029.    ──────────────────────────────────────────────────────────────────────────
  11030.   02H        08H─0BH   Nonmaskable          x          x         x
  11031.                         interrupt (NMI)
  11032.    03H        0CH─0FH   Breakpoint           x          x         x
  11033.    04H        10H─13H   Overflow             x          x         x
  11034.    05H        14H─17H   BOUND exceeded                  x         x
  11035.    06H        18H─1BH   Invalid opcode                  x         x
  11036.    07H        1CH─1FH   Processor extension             x         x
  11037.                         not available
  11038.    08H        20H─23H   Double fault                    x         x
  11039.    09H        24H─27H   Segment overrun                 x         x
  11040.    0AH        28H─2BH   Invalid task-state              x         x
  11041.                         segment
  11042.    0BH        2CH─2FH   Segment not present             x         x
  11043.    0CH        30H─33H   Stack segment                   x         x
  11044.                         overrun
  11045.    0DH        34H─37H   General protection              x         x
  11046.                         fault
  11047.    0EH        38H─3BH   Page fault                                x
  11048.    Interrupt  Vector    Interrupt            8086/88    80286     80386
  11049.    level      address   trigger
  11050.    ──────────────────────────────────────────────────────────────────────────
  11051.   0EH        38H─3BH   Page fault                                x
  11052.    0FH        3CH─3FH   Reserved
  11053.    10H        40H─43H   Numeric coprocessor             x         x
  11054.                         error
  11055.    11H─1FH    44H─7FH   Reserved
  11056.    ──────────────────────────────────────────────────────────────────────────
  11057.  
  11058.  
  11059.    Figure 13-1.  Internal interrupts (faults) on the Intel 8086/88, 80286,
  11060.    and 80386 microprocessors.
  11061.  
  11062.  External Hardware Interrupts
  11063.  
  11064.    External hardware interrupts are triggered by peripheral device
  11065.    controllers or by coprocessors such as the 8087/80287. These can be tied
  11066.    to either the CPU's nonmaskable-interrupt (NMI) pin or its
  11067.    maskable-interrupt (INTR) pin. The NMI line is usually reserved for
  11068.    interrupts caused by such catastrophic events as a memory parity error or
  11069.    a power failure.
  11070.  
  11071.    Instead of being wired directly to the CPU, the interrupts from external
  11072.    devices can be channeled through a device called the Intel 8259A
  11073.    Programmable Interrupt Controller (PIC). The CPU controls the PIC through
  11074.    a set of I/O ports, and the PIC, in turn, signals the CPU through the INTR
  11075.    pin. The PIC allows the interrupts from specific devices to be enabled and
  11076.    disabled, and their priorities to be adjusted, under program control.
  11077.  
  11078.    A single PIC can handle only eight levels of interrupts. However, PICs can
  11079.    be cascaded together in a treelike structure to handle as many levels as
  11080.    desired. For example, 80286- and 80386-based machines with a
  11081.    PC/AT-compatible architecture use two PICs wired together to obtain 16
  11082.    individually configurable levels of interrupts.
  11083.  
  11084.    INTR interrupts can be globally enabled and disabled with the CPU's STI
  11085.    and CLI instructions. As you would expect, these instructions have no
  11086.    effect on interrupts received on the CPU's NMI pin.
  11087.  
  11088.    The manufacturer of the computer system and/or the manufacturer of the
  11089.    peripheral device assigns external devices to specific 8259A PIC interrupt
  11090.    levels. These assignments are realized as physical electrical connections
  11091.    and cannot be modified by software.
  11092.  
  11093.  Software Interrupts
  11094.  
  11095.    Any program can trigger software interrupts synchronously simply by
  11096.    executing an INT instruction. MS-DOS uses Interrupts 20H through 3FH to
  11097.    communicate with its modules and with application programs. (For instance,
  11098.    the MS-DOS function dispatcher is reached by executing an Int 21H.) The
  11099.    IBM PC ROM BIOS and application software use other interrupts, with either
  11100.    higher or lower numbers, for various purposes (Figure 13-2). These
  11101.    assignments are simply conventions and are not wired into the hardware in
  11102.    any way.
  11103.  
  11104. ╓┌─┌──────────────────┌────────────────────────────────────┌─────────────────╖
  11105.    Interrupt          Usage                                Machine
  11106.    ──────────────────────────────────────────────────────────────────────────
  11107.    00H                Divide-by-zero                       PC, AT, PS/2
  11108.    01H                Single step                          PC, AT, PS/2
  11109.    02H                NMI                                  PC, AT, PS/2
  11110.    03H                Breakpoint                           PC, AT, PS/2
  11111.    Interrupt          Usage                                Machine
  11112.    ──────────────────────────────────────────────────────────────────────────
  11113.   03H                Breakpoint                           PC, AT, PS/2
  11114.    04H                Overflow                             PC, AT, PS/2
  11115.    05H                ROM BIOS PrintScreen                 PC, AT, PS/2
  11116.                       BOUND exceeded                       AT, PS/2
  11117.    06H                Reserved                             PC
  11118.                       Invalid opcode                       AT, PS/2
  11119.    07H                Reserved                             PC
  11120.                       80287/80387 not present              AT, PS/2
  11121.    08H                IRQ0 timer tick                      PC, AT, PS/2
  11122.                       Double fault                         AT, PS/2
  11123.    09H                IRQ1 keyboard                        PC, AT, PS/2
  11124.                       80287/80387 segment overrun          AT, PS/2
  11125.    0AH                IRQ2 reserved                        PC
  11126.                       IRQ2 cascade from slave 8259A PIC    AT, PS/2
  11127.                       Invalid task-state segment (TSS)     AT, PS/2
  11128.    0BH                IRQ3 serial communications (COM2)    PC, AT, PS/2
  11129.                       Segment not present                  AT, PS/2
  11130.    0CH                IRQ4 serial communications (COM1)    PC, AT, PS/2
  11131.                       Stack segment overflow               AT, PS/2
  11132.    Interrupt          Usage                                Machine
  11133.    ──────────────────────────────────────────────────────────────────────────
  11134.                      Stack segment overflow               AT, PS/2
  11135.    0DH                IRQ5 fixed disk                      PC
  11136.                       IRQ5 parallel printer (LPT2)         AT
  11137.                       Reserved                             PS/2
  11138.                       General protection fault             AT, PS/2
  11139.    0EH                IRQ6 floppy disk                     PC, AT, PS/2
  11140.                       Page fault                           AT, PS/2
  11141.    0FH                IRQ7 parallel printer (LPT1)         PC, AT, PS/2
  11142.    10H                ROM BIOS video driver                PC, AT, PS/2
  11143.                       Numeric coprocessor fault            AT, PS/2
  11144.    11H                ROM BIOS equipment check             PC, AT, PS/2
  11145.    12H                ROM BIOS conventional-memory size    PC, AT, PS/2
  11146.    13H                ROM BIOS disk driver                 PC, AT, PS/2
  11147.    14H                ROM BIOS communications driver       PC, AT, PS/2
  11148.    15H                ROM BIOS cassette driver             PC
  11149.                       ROM BIOS I/O system extensions       AT, PS/2
  11150.    16H                ROM BIOS keyboard driver             PC, AT, PS/2
  11151.    17H                ROM BIOS printer driver              PC, AT, PS/2
  11152.    18H                ROM BASIC                            PC, AT, PS/2
  11153.    Interrupt          Usage                                Machine
  11154.    ──────────────────────────────────────────────────────────────────────────
  11155.   18H                ROM BASIC                            PC, AT, PS/2
  11156.    19H                ROM BIOS bootstrap                   PC, AT, PS/2
  11157.    1AH                ROM BIOS time of day                 AT, PS/2
  11158.    1BH                ROM BIOS Ctrl-Break                  PC, AT, PS/2
  11159.    1CH                ROM BIOS timer tick                  PC, AT, PS/2
  11160.    1DH                ROM BIOS video parameter table       PC, AT, PS/2
  11161.    1EH                ROM BIOS floppy-disk parameters      PC, AT, PS/2
  11162.    1FH                ROM BIOS font (characters 80H─FFH)   PC, AT, PS/2
  11163.    20H                MS-DOS terminate process
  11164.    21H                MS-DOS function dispatcher
  11165.    22H                MS-DOS terminate address
  11166.    23H                MS-DOS Ctrl-C handler address
  11167.    24H                MS-DOS critical-error handler
  11168.                       address
  11169.    25H                MS-DOS absolute disk read
  11170.    26H                MS-DOS absolute disk write
  11171.    27H                MS-DOS terminate and stay resident
  11172.    28H                MS-DOS idle interrupt
  11173.    29H                MS-DOS reserved
  11174.    Interrupt          Usage                                Machine
  11175.    ──────────────────────────────────────────────────────────────────────────
  11176.   29H                MS-DOS reserved
  11177.    2AH                MS-DOS network redirector
  11178.    2BH─2EH            MS-DOS reserved
  11179.    2FH                MS-DOS multiplex interrupt
  11180.    30H─3FH            MS-DOS reserved
  11181.    40H                ROM BIOS floppy-disk driver (if      PC, AT, PS/2
  11182.                       fixed disk installed)
  11183.    41H                ROM BIOS fixed-disk parameters       PC
  11184.                       ROM BIOS fixed-disk parameters       AT, PS/2
  11185.                       (drive 0)
  11186.    42H                ROM BIOS default video driver (if    PC, AT, PS/2
  11187.                       EGA installed)
  11188.    43H                EGA, MCGA, VGA character table       PC, AT, PS/2
  11189.    44H                ROM BIOS font (characters 00H─7FH)   PCjr
  11190.    46H                ROM BIOS fixed-disk parameters       AT, PS/2
  11191.                       (drive 1)
  11192.    4AH                ROM BIOS alarm handler               AT, PS/2
  11193.    5AH                Cluster adapter                      PC, AT
  11194.    5BH                Used by cluster program              PC, AT
  11195.    Interrupt          Usage                                Machine
  11196.    ──────────────────────────────────────────────────────────────────────────
  11197.   5BH                Used by cluster program              PC, AT
  11198.    60H─66H            User interrupts                      PC, AT, PS/2
  11199.    67H                LIM EMS driver                       PC, AT, PS/2
  11200.    68H─6FH            Unassigned
  11201.    70H                IRQ8 CMOS real-time clock            AT, PS/2
  11202.    71H                IRQ9 software diverted to IRQ2       AT, PS/2
  11203.    72H                IRQ10 reserved                       AT, PS/2
  11204.    73H                IRQ11 reserved                       AT, PS/2
  11205.    74H                IRQ12 reserved                       AT
  11206.                       IRQ12 mouse                          PS/2
  11207.    75H                IRQ13 numeric coprocessor            AT, PS/2
  11208.    76H                IRQ14 fixed-disk controller          AT, PS/2
  11209.    77H                IRQ15 reserved                       AT, PS/2
  11210.    78H─7FH            Unassigned
  11211.    80H─F0H            BASIC                                PC, AT, PS/2
  11212.    F1H─FFH            Not used                             PC, AT, PS/2
  11213.    ──────────────────────────────────────────────────────────────────────────
  11214.  
  11215.  
  11216.    Figure 13-2.  Interrupts with special significance on the IBM PC, PC/AT,
  11217.    and PS/2 and compatible computers. Note that the IBM ROM BIOS uses several
  11218.    interrupts in the range 00H─1FH, even though they were reserved by Intel
  11219.    for CPU faults. IRQ numbers refer to Intel 8259A PIC priority levels.
  11220.  
  11221.  The Interrupt-Vector Table
  11222.  
  11223.    The bottom 1024 bytes of system memory are called the interrupt-vector
  11224.    table. Each 4-byte position in the table corresponds to an interrupt type
  11225.    (0 through 0FFH) and contains the segment and offset of the interrupt
  11226.    handler for that level. Interrupts 0 through 1FH (the lowest levels) are
  11227.    used for internal hardware interrupts; MS-DOS uses Interrupts 20H through
  11228.    3FH; all the other interrupts are available for use by either external
  11229.    hardware devices or system drivers and application software.
  11230.  
  11231.    When an 8259A PIC or other device interrupts the CPU by means of the INTR
  11232.    pin, it must also place the interrupt type as an 8-bit number (0 through
  11233.    0FFH) on the system bus, where the CPU can find it. The CPU then
  11234.    multiplies this number by 4 to find the memory address of the interrupt
  11235.    vector to be used.
  11236.  
  11237.  Servicing an Interrupt
  11238.  
  11239.    When the CPU senses an interrupt, it pushes the program status word (which
  11240.    defines the various CPU flags), the code segment (CS) register, and the
  11241.    instruction pointer (IP) onto the machine stack and disables the interrupt
  11242.    system. It then uses the 8-bit number that was jammed onto the system bus
  11243.    by the interrupting device to fetch the address of the handler from the
  11244.    vector table and resumes execution at that address.
  11245.  
  11246.    Usually the handler immediately reenables the interrupt system (to allow
  11247.    higher-priority interrupts to occur), saves any registers it is going to
  11248.    use, and then processes the interrupt as quickly as possible. Some
  11249.    external devices also require a special acknowledgment signal so that they
  11250.    will know the interrupt has been recognized.
  11251.  
  11252.    If the interrupt was funneled through an 8259A PIC, the handler must send
  11253.    a special code called end of interrupt (EOI) to the PIC through its
  11254.    control port to tell it when interrupt processing is completed. (The EOI
  11255.    has no effect on the CPU itself.) Finally, the handler executes the
  11256.    special IRET (INTERRUPT RETURN) instruction that restores the original
  11257.    state of the CPU flags, the CS register, and the instruction pointer
  11258.    (Figure 13-3).
  11259.  
  11260.    Whether an interrupt was triggered by an external device or forced by
  11261.    software execution of an INT instruction, there is no discernible
  11262.    difference in the system state at the time the interrupt handler receives
  11263.    control. This fact is convenient when you are writing and testing external
  11264.    interrupt handlers because you can debug them to a large extent simply by
  11265.    invoking them with software drivers.
  11266.  
  11267.    ──────────────────────────────────────────────────────────────────────────
  11268.    pic_ctl         equ  20h                     ; control port for 8259A
  11269.                                                 ; interrupt controller
  11270.                    .
  11271.                    .
  11272.                    .
  11273.                    sti                          ; turn interrupts back on,
  11274.                    push  ax                     ; save registers
  11275.                    push  bx
  11276.                    push  cx
  11277.                    push  dx
  11278.                    push  si
  11279.                    push  di
  11280.                    push  bp
  11281.                    push  ds
  11282.                    push  es
  11283.  
  11284.                    mov   ax,cs                  ; make local data addressable
  11285.                    mov   ds,ax
  11286.                    .                            ; do some stuff appropriate
  11287.                    .                            ; for this interrupt here
  11288.                    .
  11289.                    mov   al,20h                 ; send EOI to 8259A PIC
  11290.                    mov   dx,pic_ctl
  11291.                    out   dx,al
  11292.  
  11293.                    pop   es                     ; restore registers
  11294.                    pop   ds
  11295.                    pop   bp
  11296.                    pop   di
  11297.                    pop   si
  11298.                    pop   dx
  11299.                    pop   cx
  11300.                    pop   bx
  11301.                    pop   ax
  11302.                    iret                         ; resume previous processing
  11303.    ──────────────────────────────────────────────────────────────────────────
  11304.  
  11305.    Figure 13-3.  Typical handler for hardware interrupts on the 80x86 family
  11306.    of microprocessors. In real life, the interrupt handler would need to save
  11307.    and restore only the registers that it actually modified. Also, if the
  11308.    handler made extensive use of the machine stack, it would need to save and
  11309.    restore the SS and SP registers of the interrupted process and use its own
  11310.    local stack.
  11311.  
  11312.  
  11313.  Interrupt Handlers and MS-DOS
  11314.  
  11315.    The introduction of an interrupt handler into your program brings with it
  11316.    considerable hardware dependence. It goes without saying (but I am saying
  11317.    it again here anyway) that you should avoid such hardware dependence in
  11318.    MS-DOS applications whenever possible, to ensure that your programs will
  11319.    be portable to any machine running current versions of MS-DOS and that
  11320.    they will run properly under future versions of the operating system.
  11321.  
  11322.    Valid reasons do exist, however, for writing your own interrupt handler
  11323.    for use under MS-DOS:
  11324.  
  11325.    ■  To supersede the MS-DOS default handler for an internal hardware
  11326.       interrupt (such as divide-by-zero, BOUND exceeded, and so forth).
  11327.  
  11328.    ■  To supersede the MS-DOS default handler for a defined system exception,
  11329.       such as the critical-error handler or Ctrl-C handler.
  11330.  
  11331.    ■  To chain your own interrupt handler onto the default system handler for
  11332.       a hardware device, so that both the system's actions and your own will
  11333.       occur on an interrupt. (A typical example of this is the "clock-tick"
  11334.       interrupt.)
  11335.  
  11336.    ■  To service interrupts not supported by the default MS-DOS device
  11337.       drivers (such as the serial communications port, which can be used at
  11338.       much higher speeds with interrupts than with polling).
  11339.  
  11340.    ■  To provide a path of communication between a program that terminates
  11341.       and stays resident and other application software.
  11342.  
  11343.    MS-DOS provides the following facilities to enable you to install
  11344.    well-behaved interrupt handlers in a manner that does not interfere with
  11345.    operating-system functions or other interrupt handlers:
  11346.  
  11347.    Function                             Action
  11348.    ──────────────────────────────────────────────────────────────────────────
  11349.    Int 21H Function 25H                Set interrupt vector.
  11350.    Int 21H Function 35H                Get interrupt vector.
  11351.    Int 21H Function 31H                Terminate and stay resident.
  11352.    ──────────────────────────────────────────────────────────────────────────
  11353.  
  11354.    These functions allow you to examine or modify the contents of the system
  11355.    interrupt-vector table and to reserve memory for the use of a handler
  11356.    without running afoul of other processes in the system or causing memory
  11357.    use conflicts. Section 2 of this book, "MS-DOS Functions Reference,"
  11358.    describes each of these functions in detail, with programming examples.
  11359.  
  11360.    Handlers for external hardware interrupts under MS-DOS must operate under
  11361.    some fairly severe restrictions:
  11362.  
  11363.    ■  Because the current versions of MS-DOS are not reentrant, a hardware
  11364.       interrupt handler should never call the MS-DOS functions during the
  11365.       actual interrupt processing.
  11366.  
  11367.    ■  The handler must reenable interrupts as soon as it gets control, to
  11368.       avoid crippling other devices or destroying the accuracy of the system
  11369.       clock.
  11370.  
  11371.    ■  A program should access the 8259A PIC with great care. The program
  11372.       should not access the PIC unless that program is known to be the only
  11373.       process in the system concerned with that particular interrupt level.
  11374.       And it is vital that the handler issue an end-of-interrupt code to the
  11375.       8259A PIC before performing the IRET; otherwise, the processing of
  11376.       further interrupts for that priority level or lower priority levels
  11377.       will be blocked.
  11378.  
  11379.    Restrictions on handlers that replace the MS-DOS default handlers for
  11380.    internal hardware interrupts or system exceptions (such as Ctrl-C or
  11381.    critical errors) are not quite so stringent, but you must still program
  11382.    the handlers with extreme care to avoid destroying system tables or
  11383.    leaving the operating system in an unstable state.
  11384.  
  11385.    The following are a few rules to keep in mind when you are writing an
  11386.    interrupt driver:
  11387.  
  11388.    ■  Use Int 21H Function 25H (Set Interrupt Vector) to modify the
  11389.       interrupt vector; do not write directly to the interrupt-vector table.
  11390.  
  11391.    ■  If your program is not the only process in the system that uses this
  11392.       interrupt level, chain back to the previous handler after performing
  11393.       your own processing on an interrupt.
  11394.  
  11395.    ■  If your program is not going to stay resident, fetch and save the
  11396.       current contents of the interrupt vector before modifying it and then
  11397.       restore the original contents when your program exits.
  11398.  
  11399.    ■  If your program is going to stay resident, use one of the terminate-
  11400.       and-stay-resident functions (preferably Int 21H Function 31H) to
  11401.       reserve the proper amount of memory for your handler.
  11402.  
  11403.    ■  If you are going to process hardware interrupts, keep the time that
  11404.       interrupts are disabled and the total length of the service routine to
  11405.       an absolute minimum. Remember that even after interrupts are reenabled
  11406.       with an STI instruction, interrupts of the same or lower priority
  11407.       remain blocked if the interrupt was received through the 8259A PIC.
  11408.  
  11409.  
  11410.  ZERODIV, an Example Interrupt Handler
  11411.  
  11412.    The listing ZERODIV.ASM (Figure 13-4) illustrates some of the principles
  11413.    and guidelines on the previous pages. It is an interrupt handler for the
  11414.    divide-by-zero internal interrupt (type 0). ZERODIV is loaded as a .COM
  11415.    file (usually by a command in the system's AUTOEXEC file) but makes itself
  11416.    permanently resident in memory as long as the system is running.
  11417.  
  11418.    The ZERODIV program has two major portions: the initialization portion and
  11419.    the interrupt handler.
  11420.  
  11421.    The initialization procedure (called init in the program listing) is
  11422.    executed only once, when the ZERODIV program is executed from the MS-DOS
  11423.    level. The init procedure takes over the type 0 interrupt vector, prints a
  11424.    sign-on message, then performs a terminate-and-stay-resident exit to
  11425.    MS-DOS. This special exit reserves the memory occupied by the ZERODIV
  11426.    program, so that it is not overwritten by subsequent application programs.
  11427.  
  11428.    The interrupt handler (called zdiv in the program listing) receives
  11429.    control when a divide-by-zero interrupt occurs. The handler preserves all
  11430.    registers and then prints a message to the user asking whether to continue
  11431.    or to abort the program. We can use the MS-DOS console I/O functions
  11432.    within this particular interrupt handler because we can safely presume
  11433.    that the application was in control when the interrupt occurred; thus,
  11434.    there should be no chance of accidentally making overlapping calls upon
  11435.    the operating system.
  11436.  
  11437.    If the user enters a C to continue, the handler simply restores all the
  11438.    registers and performs an IRET (INTERRUPT RETURN) to return control to the
  11439.    application. (Of course, the results of the divide operation will be
  11440.    useless.) If the user enters Q to quit, the handler exits to MS-DOS. Int
  11441.    21H Function 4CH is particularly convenient in this case because it
  11442.    allows the program to pass a return code and at the same time is the only
  11443.    termination function that does not rely on the contents of any of the
  11444.    segment registers.
  11445.  
  11446.    For an example of an interrupt handler for external (communications port)
  11447.    interrupts, see the TALK terminal-emulator program in Chapter 7. You may
  11448.    also want to look again at the discussions of Ctrl-C and critical-error
  11449.    exception handlers in Chapters 5 and 8.
  11450.  
  11451.    ──────────────────────────────────────────────────────────────────────────
  11452.             name      zdivide
  11453.             page      55,132
  11454.             title     ZERODIV--Divide-by-zero handler
  11455.  
  11456.    ;
  11457.    ; ZERODIV.ASM--Terminate-and-stay-resident handler
  11458.    ;              for divide-by-zero interrupts
  11459.    ;
  11460.    ; Copyright 1988 Ray Duncan
  11461.    ;
  11462.    ; Build:        C>MASM ZERODIV;
  11463.    ;               C>LINK ZERODIV;
  11464.    ;               C>EXE2BIN ZERODIV.EXE ZERODIV.COM
  11465.    ;               C>DEL ZERODIV.EXE
  11466.    ;
  11467.    ; Usage:        C>ZERODIV
  11468.    ;
  11469.  
  11470.    cr      equ     0dh             ; ASCII carriage return
  11471.    lf      equ     0ah             ; ASCII linefeed
  11472.    beep    equ     07h             ; ASCII bell code
  11473.    backsp  equ     08h             ; ASCII backspace code
  11474.  
  11475.    _TEXT   segment word public 'CODE'
  11476.  
  11477.            org     100H
  11478.  
  11479.            assume  cs:_TEXT,ds:_TEXT,es:_TEXT,ss:_TEXT
  11480.  
  11481.    init    proc    near            ; entry point at load time
  11482.  
  11483.                                    ; capture vector for
  11484.                                    ; interrupt zero...
  11485.            mov     dx,offset zdiv  ; DS:DX = handler address
  11486.            mov     ax,2500h        ; function 25h = set vector
  11487.                                    ; interrupt type = 0
  11488.            int     21h             ; transfer to MS-DOS
  11489.  
  11490.                                    ; print sign-on message
  11491.            mov     dx,offset msg1  ; DS:DX = message address
  11492.            mov     ah,9            ; function 09h = display string
  11493.            int     21h             ; transfer to MS-DOS
  11494.  
  11495.                                    ; DX = paragraphs to reserve
  11496.            mov     dx,((offset pgm_len+15)/16)+10h
  11497.            mov     ax,3100h        ; function 31h = terminate and
  11498.                                    ; stay resident
  11499.            int     21h             ; transfer to MS-DOS
  11500.  
  11501.    init    endp
  11502.  
  11503.  
  11504.    zdiv    proc    far             ; this is the divide-by-
  11505.                                    ; zero interrupt handler
  11506.  
  11507.            sti                     ; enable interrupts
  11508.  
  11509.            push    ax              ; save registers
  11510.            push    bx
  11511.            push    cx
  11512.            push    dx
  11513.            push    si
  11514.            push    di
  11515.            push    bp
  11516.            push    ds
  11517.            push    es
  11518.  
  11519.            mov     ax,cs           ; make data addressable
  11520.            mov     ds,ax
  11521.  
  11522.                                    ; display message
  11523.                                    ; "Continue or Quit?"
  11524.            mov     dx,offset msg2  ; DS:DX = message address
  11525.            mov     ah,9            ; function 09h = display string
  11526.            int     21h             ; transfer to MS-DOS
  11527.  
  11528.    zdiv1:  mov     ah,1            ; function 01h = read keyboard
  11529.            int     21h             ; transfer to MS-DOS
  11530.  
  11531.            or      al,20h          ; fold char to lowercase
  11532.  
  11533.            cmp     al,'c'          ; is it C or Q?
  11534.            je      zdiv3           ; jump, it's a C
  11535.  
  11536.            cmp     al,'q'
  11537.            je      zdiv2           ; jump, it's a Q
  11538.  
  11539.                                    ; illegal entry, send beep
  11540.                                    ; and erase the character
  11541.            mov     dx,offset msg3  ; DS:DX = message address
  11542.            mov     ah,9            ; function 09h = display string
  11543.            int     21h             ; transfer to MS-DOS
  11544.  
  11545.            jmp     zdiv1           ; try again
  11546.  
  11547.    zdiv2:                          ; user chose "Quit"
  11548.            mov     ax,4cffh        ; terminate current program
  11549.            int     21h             ; with return code = 255
  11550.  
  11551.    zdiv3:                          ; user chose "Continue"
  11552.                                    ; send CR-LF pair
  11553.            mov     dx,offset msg4  ; DS:DX = message address
  11554.            mov     ah,9            ; function 09h = print string
  11555.            int     21h             ; transfer to MS-DOS
  11556.  
  11557.                                    ; what CPU type is this?
  11558.            xor     ax,ax           ; to find out, we'll put
  11559.            push    ax              ; zero in the CPU flags
  11560.            popf                    ; and see what happens
  11561.            pushf
  11562.            pop     ax
  11563.            and     ax,0f000h       ; 8086/8088 forces
  11564.            cmp     ax,0f000h       ; bits 12-15 true
  11565.            je      zdiv5           ; jump if 8086/8088
  11566.  
  11567.                                    ; otherwise we must adjust
  11568.                                    ; return address to bypass
  11569.                                    ; the divide instruction...
  11570.            mov     bp,sp           ; make stack addressable
  11571.  
  11572.            lds     bx,[bp+18]      ; get address of the
  11573.                                    ; faulting instruction
  11574.  
  11575.            mov     bl,[bx+1]       ; get addressing byte
  11576.            and     bx,0c7h         ; isolate mod & r/m fields
  11577.  
  11578.            cmp     bl,6            ; mod 0, r/m 6 = direct
  11579.            jne     zdiv4           ; not direct, jump
  11580.  
  11581.            add     word ptr [bp+18],4
  11582.            jmp     zdiv5
  11583.  
  11584.    zdiv4:  mov     cl,6            ; otherwise isolate mod
  11585.            shr     bx,cl           ; field and get instruction
  11586.            mov     bl,cs:[bx+itab] ; size from table
  11587.            add     [bp+18],bx
  11588.  
  11589.    zdiv5:  pop     es              ; restore registers
  11590.            pop     ds
  11591.            pop     bp
  11592.            pop     di
  11593.            pop     si
  11594.            pop     dx
  11595.            pop     cx
  11596.            pop     bx
  11597.            pop     ax
  11598.            iret                    ; return from interrupt
  11599.  
  11600.    zdiv    endp
  11601.  
  11602.  
  11603.    msg1    db      cr,lf           ; load-time sign-on message
  11604.            db      'Divide by Zero Interrupt '
  11605.            db      'Handler installed.'
  11606.            db      cr,lf,'$'
  11607.  
  11608.    msg2    db      cr,lf,lf        ; interrupt-time message
  11609.            db      'Divide by Zero detected: '
  11610.            db      cr,lf,'Continue or Quit (C/Q) ? '
  11611.            db      '$'
  11612.  
  11613.    msg3    db      beep            ; used if bad entry
  11614.            db      backsp,' ',backsp,'$'
  11615.  
  11616.    msg4    db      cr,lf,'$'       ; carriage return-linefeed
  11617.  
  11618.                                    ; instruction size table
  11619.    itab    db      2               ; mod = 0
  11620.            db      3               ; mod = 1
  11621.            db      4               ; mod = 2
  11622.            db      2               ; mod = 3
  11623.  
  11624.    pgm_len equ     $-init          ; program length
  11625.  
  11626.    _TEXT   ends
  11627.  
  11628.            end     init
  11629.    ──────────────────────────────────────────────────────────────────────────
  11630.  
  11631.    Figure 13-4.  A simple example of an interrrupt handler for use within the
  11632.    MS-DOS environment. ZERODIV makes itself permanently resident in memory
  11633.    and handles the CPU's internal divide-by-zero interrupt.
  11634.  
  11635.  
  11636.  
  11637.  ────────────────────────────────────────────────────────────────────────────
  11638.  Chapter 14  Installable Device Drivers
  11639.  
  11640.    Device drivers are the modules of an operating system that control the
  11641.    hardware. They isolate the operating-system kernel from the specific
  11642.    characteristics and idiosyncrasies of the peripheral devices interfaced to
  11643.    the central processor. Thus, the driver's relationship to the kernel is
  11644.    analogous to the operating system's relationship to application programs.
  11645.  
  11646.    The installable device drivers that were introduced in MS-DOS version 2
  11647.    give the user great flexibility. They allow the user to customize and
  11648.    configure the computer for a wide range of peripheral devices, with a
  11649.    minimum of troublesome interactions and without having to "patch" the
  11650.    operating system. Even the most inexperienced user can install a new
  11651.    device into a system by plugging in a card, copying a driver file to the
  11652.    boot disk, and editing the system configuration file.
  11653.  
  11654.    For those inclined to do their own programming, the MS-DOS installable
  11655.    device drivers are interfaced to the hardware-independent kernel through a
  11656.    simple and clearly defined scheme of function codes and data structures.
  11657.    Given adequate information about the hardware, any competent assembly-
  11658.    language programmer can expect to successfully interface even the most
  11659.    bizarre device to MS-DOS without altering the operating system in the
  11660.    slightest and without acquiring any special or proprietary knowledge about
  11661.    its innards.
  11662.  
  11663.    In retrospect, installable device drivers have proven to be one of the key
  11664.    usability features of MS-DOS. I feel that they have been largely
  11665.    responsible for the rapid proliferation and competitive pricing of
  11666.    high-speed mass-storage devices for MS-DOS machines, and for the growing
  11667.    confidence of the average user toward "tampering with" (upgrading) his or
  11668.    her machine.
  11669.  
  11670.  
  11671.  MS-DOS Device-Driver Types
  11672.  
  11673.    Drivers written for MS-DOS fall into two distinct classes:
  11674.  
  11675.    ■  Block-device drivers
  11676.  
  11677.    ■  Character-device drivers
  11678.  
  11679.    A driver's class determines what functions it must support, how it is
  11680.    viewed by MS-DOS, and how it makes the associated physical device appear
  11681.    to behave when an application program makes a request for I/O.
  11682.  
  11683.  Character-Device Drivers
  11684.  
  11685.    Character-device drivers control peripheral devices that perform input and
  11686.    output one character (or byte) at a time, such as a terminal or printer. A
  11687.    single character-device driver ordinarily supports a single hardware unit.
  11688.    Each character device has a one-to-eight-character logical name, and an
  11689.    application program can use this name to open the device for input or
  11690.    output, as though it were a file. The logical name is strictly a means of
  11691.    identification for MS-DOS and has no physical equivalent on the device.
  11692.  
  11693.    MS-DOS's built-in character-device drivers for the console, serial port,
  11694.    and printer are unique in that an application program can access them in
  11695.    three different ways:
  11696.  
  11697.    ■  It can open them by name (CON, AUX, PRN, etc.) for input and output,
  11698.       like any other character device.
  11699.  
  11700.    ■  It can use the special-purpose MS-DOS function calls (Int 21H Functions
  11701.       01-0CH).
  11702.  
  11703.    ■  It can use the default handles (standard input, standard output,
  11704.       standard error, standard auxiliary, and standard printer), which do not
  11705.       need to be opened to be used.
  11706.  
  11707.    The number of additional character-device drivers that can be installed is
  11708.    limited only by available memory and by the requirement that each driver
  11709.    have a unique logical name. If more than one driver uses the same logical
  11710.    name, the last driver to be loaded will supersede any others and will
  11711.    receive all I/O requests addressed to that logical name. This fact can
  11712.    occasionally be turned to advantage; for example, it allows the user to
  11713.    replace the system's default CON driver, which does not support cursor
  11714.    positioning or character attributes, with the more powerful ANSI.SYS
  11715.    driver.
  11716.  
  11717.    ASCII vs Binary Mode
  11718.  
  11719.    MS-DOS regards a handle associated with a character device to be in either
  11720.    ASCII (cooked) mode or binary (raw) mode. The mode affects MS-DOS's
  11721.    buffering of data for read and write requests. The driver itself is not
  11722.    aware of the mode, and the mode does not affect its operation. An
  11723.    application can select the mode of a handle with the IOCTL function (Int
  11724.    21H Function 44H).
  11725.  
  11726.    During ASCII-mode input, MS-DOS requests characters one at a time from the
  11727.    driver and places them into its own internal buffer, echoing each to the
  11728.    screen (if the input device is the keyboard) and checking each character
  11729.    for a Ctrl-C (03H). When the number of characters requested by the
  11730.    application program has been received, when a Ctrl-Z is detected, or when
  11731.    the Enter key is pressed (in the case of the keyboard), MS-DOS terminates
  11732.    the input and copies the data from its internal buffer into the requesting
  11733.    program's buffer. Similarly, during ASCII-mode output, MS-DOS passes the
  11734.    characters to the device driver one at a time and checks for a Ctrl-C
  11735.    pending at the keyboard between each character. When a Ctrl-C is detected,
  11736.    MS-DOS aborts the input or output operation and transfers to the routine
  11737.    whose address is stored in the Int 23H vector.
  11738.  
  11739.    In binary mode, MS-DOS reads or writes the exact number of bytes requested
  11740.    by the application program, without regard to any control characters such
  11741.    as Enter or Ctrl-C. MS-DOS passes the entire request through to the driver
  11742.    in a single operation, instead of breaking it into single-character reads
  11743.    or writes, and transfers the characters directly to or from the requesting
  11744.    program's buffer.
  11745.  
  11746.  Block-Device drivers
  11747.  
  11748.    Block-device drivers usually control random-access mass-storage devices
  11749.    such as floppy-disk drives and fixed disks, although they can also be used
  11750.    to control non-random-access devices such as magnetic-tape drives. Block
  11751.    devices transfer data in chunks, rather than one byte at a time. The size
  11752.    of the blocks may be either fixed (disk drives) or variable (tape drives).
  11753.  
  11754.    A block driver can support more than one hardware unit, map a single
  11755.    physical unit onto two or more logical units, or both. Block devices do
  11756.    not have file-like logical names, as character devices do. Instead, MS-DOS
  11757.    assigns drive designators to the block-device units or logical drives in
  11758.    an alphabetic sequence: A, B, and so forth. Each logical drive contains a
  11759.    file system: boot block, file allocation table, root directory, and so
  11760.    forth. (See Chapter 10.)
  11761.  
  11762.    A block-device driver's position in the chain of all drivers determines
  11763.    the first letter assigned to that driver. The number of logical drive
  11764.    units that the driver supports determines the total number of letters
  11765.    assigned to it.
  11766.  
  11767.    Block-device drivers always read or write exactly the number of sectors
  11768.    requested (barring hardware or addressing errors) and never filter or
  11769.    otherwise manipulate the contents of the blocks being transferred.
  11770.  
  11771.  
  11772.  Structure of an MS-DOS Device Driver
  11773.  
  11774.    A device driver consists of three major parts (Figure 14-1):
  11775.  
  11776.    ■  A device header
  11777.  
  11778.    ■  A strategy (strat) routine
  11779.  
  11780.    ■  An interrupt (intr) routine
  11781.  
  11782.    We'll discuss each of these in more detail as we work through this
  11783.    chapter.
  11784.  
  11785.    ┌────────────────────────┬─────────────────────────┐
  11786.    │                        │     Initialization      │
  11787.    │                        ├─────────────────────────┤
  11788.    │                        │       Media check       │
  11789.    │                        ├─────────────────────────┤
  11790.    │                        │        Build BPB        │
  11791.    │                        ├─────────────────────────┤
  11792.    │                        │  IOCTL read and write   │
  11793.    │                        ├─────────────────────────┤
  11794.    │                        │         Status          │
  11795.    │                        ├─────────────────────────┤
  11796.    │                        │          Read           │
  11797.    │                        ├─────────────────────────┤
  11798.    │                        │   Write, write/verify   │
  11799.    │                        ├─────────────────────────┤
  11800.    │   Interrupt routine    │    Output until busy    │
  11801.    │                        ├─────────────────────────┤
  11802.    │                        │      Flush buffers      │
  11803.    │                        ├─────────────────────────┤
  11804.    │                        │       Device open       │
  11805.    │                        ├─────────────────────────┤
  11806.    │                        │      Device close       │
  11807.    │                        ├─────────────────────────┤
  11808.    │                        │ Check whether removable │
  11809.    │                        ├─────────────────────────┤
  11810.    │                        │     Generic IOCTL       │
  11811.    │                        ├─────────────────────────┤
  11812.    │                        │ Get/Set logical device  │
  11813.    │                        └─────────────────────────┤
  11814.    ├──────────────────────────────────────────────────┤
  11815.    │                 Strategy routine                 │
  11816.    ├──────────────────────────────────────────────────┤
  11817.    │               Device-driver header               │
  11818.    └──────────────────────────────────────────────────┘
  11819.  
  11820.    Figure 14-1.  General structure of an MS-DOS installable device driver.
  11821.  
  11822.  The Device Header
  11823.  
  11824.    The device header (Figure 14-2) lies at the beginning of the driver. It
  11825.    contains a link to the next driver in the chain, a set of attribute flags
  11826.    for the device (Figure 14-3), offsets to the executable strategy and
  11827.    interrupt routines for the device, and the logical-device name (if it is a
  11828.    character device such as PRN or COM1) or the number of logical units (if
  11829.    it is a block device).
  11830.  
  11831.    Byte offset
  11832.  
  11833.    00H ┌──────────────────────────────────────────────┐
  11834.        │         Link to next driver, offset          │
  11835.    02H ├──────────────────────────────────────────────┤
  11836.        │         Link to next driver, segment         │
  11837.    04H ├──────────────────────────────────────────────┤
  11838.        │            Device attribute word             │
  11839.    06H ├──────────────────────────────────────────────┤
  11840.        │         Strategy entry point, offset         │
  11841.    08H ├──────────────────────────────────────────────┤
  11842.        │        Interrupt entry point, offset         │
  11843.    0AH ├──────────────────────────────────────────────┤
  11844.        │  Logical name (8 bytes) if character device  │
  11845.        │  Number of units (1 byte) if block device,   │
  11846.        │    followed by 7 bytes of reserved space     │
  11847.        └──────────────────────────────────────────────┘
  11848.  
  11849.    Figure 14-2.  Device-driver header. The offsets to the strat and intr
  11850.    routines are offsets from the same segment used to point to the device
  11851.    header.
  11852.  
  11853. ╓┌─┌──────────────┌──────────────────────────────────────────────────────────╖
  11854.    Bit            Significance
  11855.    ──────────────────────────────────────────────────────────────────────────
  11856.    15             1 if character device, 0 if block device
  11857.    14             1 if IOCTL read and write supported
  11858.    13             for block devices:
  11859.                   1 if BIOS parameter block in boot sector should be used to
  11860.                   determine media characteristics, 0 if media ID byte should
  11861.                   be used
  11862.                   for character devices:
  11863.                   1 if output until busy supported
  11864.    12             Reserved (should be 0)
  11865.    11             1 if open/close/removable media supported (MS-DOS 3.0 and
  11866.                   later)
  11867.    Bit            Significance
  11868.    ──────────────────────────────────────────────────────────────────────────
  11869.                  later)
  11870.    7─10           Reserved (should be 0)
  11871.    6              1 if generic IOCTL and get/set logical drive supported
  11872.                   (MS-DOS 3.2 and later)
  11873.    5              Reserved (should be 0)
  11874.    4              1 if CON driver and Int 29H fast-output function supported
  11875.    3              1 if current CLOCK$ device
  11876.    2              1 if current NUL device
  11877.    1              for block devices:
  11878.                   1 if driver supports 32-bit sector addressing (MS-DOS 4.0)
  11879.                   for character devices:
  11880.                   1 if standard output device (stdout)
  11881.    0              1 if current standard input device (stdin)
  11882.    ──────────────────────────────────────────────────────────────────────────
  11883.  
  11884.  
  11885.    Figure 14-3.  Device attribute word in device header. In block-device
  11886.    drivers, only bits 6, 11, and 13─15 (and bit 1 in MS-DOS version 4.0) have
  11887.    significance; the remainder should always be zero.
  11888.  
  11889.  The Strategy Routine
  11890.  
  11891.    MS-DOS calls the strategy routine (strat) for the device when the driver
  11892.    is first loaded and installed, and again whenever an application program
  11893.    issues an I/O request for the device. MS-DOS passes the strategy routine a
  11894.    double-word pointer to a data structure called a request header. This
  11895.    structure contains information about the type of operation to be
  11896.    performed. In current versions of MS-DOS, the strategy routine never
  11897.    actually performs any I/O operation but simply saves the pointer to the
  11898.    request header. The strat routine must not make any Int 21H function
  11899.    calls.
  11900.  
  11901.    The first 13 bytes of the request header are the same for all
  11902.    device-driver functions and are therefore referred to as the static
  11903.    portion of the header. The number and contents of the subsequent bytes
  11904.    vary according to the type of function being requested (Figure 14-4).
  11905.    Both MS-DOS and the driver read and write information in the request
  11906.    header.
  11907.  
  11908.    The request header's most important component is a command code, or
  11909.    function number, passed in its third byte to select a driver subfunction
  11910.    such as read, write, or status. Other information passed to the driver in
  11911.    the header includes unit numbers, transfer addresses, and sector or byte
  11912.    counts.
  11913.  
  11914.    ──────────────────────────────────────────────────────────────────────────
  11915.    ;
  11916.    ; MS-DOS request header structure definition
  11917.    ;
  11918.    Request         struc                  ; request header template structure
  11919.  
  11920.    Rlength         db    ?                ; 0  length of request header
  11921.    Unit            db    ?                ; 1  unit number for this request
  11922.    Command         db    ?                ; 2  request header's command code
  11923.    Status          dw    ?                ; 3  driver's return status word
  11924.    Reserve         db    8 dup (?)        ; 5  reserved area
  11925.    Media           db    ?                ; 13 media descriptor byte
  11926.    Address         dd    ?                ; 14 memory address for transfer
  11927.    Count           dw    ?                ; 18 byte/sector count value
  11928.    Sector          dw    ?                ; 20 starting sector value
  11929.  
  11930.    Request         ends                   ; end of request header template
  11931.    ──────────────────────────────────────────────────────────────────────────
  11932.  
  11933.    Figure 14-4.  Format of request header. Only the first 13 bytes are common
  11934.    to all driver functions; the number and definition of the subsequent bytes
  11935.    vary, depending upon the function type. The structure shown here is the
  11936.    one used by the read and write subfunctions of the driver.
  11937.  
  11938.  The Interrupt Routine
  11939.  
  11940.    The last and most complex part of a device driver is the interrupt routine
  11941.    (intr), which MS-DOS calls immediately after it calls the strategy
  11942.    routine. The interrupt routine implements the device driver proper; it
  11943.    performs (or calls other resident routines to perform) the actual input or
  11944.    output operations, based on the information passed in the request header.
  11945.    The strat routine may not make any Int 21H function calls, except for a
  11946.    restricted set during driver initialization.
  11947.  
  11948.    When an I/O function is completed, the interrupt routine uses the status
  11949.    field in the request header to inform the DOS kernel about the outcome of
  11950.    the requested I/O operation. It can use other fields in the request header
  11951.    to pass back such useful information as counts of the actual sectors or
  11952.    bytes transferred.
  11953.  
  11954.    The interrupt routine usually consists of the following elements:
  11955.  
  11956.    ■  A collection of subroutines to implement the various function types
  11957.       that may be requested by MS-DOS (sometimes called the command-code
  11958.       routines)
  11959.  
  11960.    ■  A centralized entry point that saves all affected registers, extracts
  11961.       the desired function code from the request header, and branches to the
  11962.       appropriate command-code routine (typically accomplished with a jump
  11963.       table)
  11964.  
  11965.    ■  A centralized exit point that stores status and error codes into the
  11966.       request header (Figures 14-5 and 14-6) and restores the previous
  11967.       contents of the affected registers
  11968.  
  11969.    The command-code routines that implement the various functions supported
  11970.    by an installable device driver are discussed in detail in the following
  11971.    pages.
  11972.  
  11973.    Bit(s)         Significance
  11974.    ──────────────────────────────────────────────────────────────────────────
  11975.    15             Error
  11976.    12─14          Reserved
  11977.    9              Busy
  11978.    8              Done
  11979.    0─7            Error code if bit 15 = 1
  11980.    ──────────────────────────────────────────────────────────────────────────
  11981.  
  11982.    Figure 14-5.  Values for the return status word of the request header.
  11983.  
  11984.    Code           Meaning
  11985.    ──────────────────────────────────────────────────────────────────────────
  11986.    0              Write-protect violation
  11987.    1              Unknown unit
  11988.    2              Drive not ready
  11989.    3              Unknown command
  11990.    4              Data error (CRC)
  11991.    5              Bad request-structure length
  11992.    6              Seek error
  11993.    7              Unknown medium
  11994.    8              Sector not found
  11995.    9              Printer out of paper
  11996.    0AH            Write fault
  11997.    0BH            Read fault
  11998.    0CH            General failure
  11999.    0D─0EH         Reserved
  12000.    0FH            Invalid disk change (MS-DOS versions 3.0 and later)
  12001.    ──────────────────────────────────────────────────────────────────────────
  12002.  
  12003.    Figure 14-6.  Driver error codes returned in bits 0 through 7 of the
  12004.    return status word of the request header.
  12005.  
  12006.    Although its name suggests otherwise, the interrupt routine is never
  12007.    entered asynchronously (on an I/O completion interrupt, for example).
  12008.    Thus, the division of function between strategy and interrupt routines is
  12009.    completely artificial in the current versions of MS-DOS.
  12010.  
  12011.  
  12012.  The Command-Code Routines
  12013.  
  12014.    A total of 20 command codes are defined for MS-DOS device drivers. The
  12015.    command codes (which are not consecutive), the names of the associated
  12016.    driver-interrupt routines, and the MS-DOS versions in which they are first
  12017.    supported are as follows:
  12018.  
  12019. ╓┌─┌───────────┌────────────────────────┌───────────┌────────────┌───────────╖
  12020.    Command     Function                 Character   Block        MS-DOS
  12021.    code                                 driver      driver       version
  12022.    ──────────────────────────────────────────────────────────────────────────
  12023.    0           Init (Initialization)    X           X            2.0
  12024.    1           Media Check                          X            2.0
  12025.    2           Build BPB                            X            2.0
  12026.    3           IOCTL Read               X           X            2.0
  12027.    4           Read                     X           X            2.0
  12028.    5           Nondestructive Read      X                        2.0
  12029.    6           Input Status             X                        2.0
  12030.    7           Flush Input Buffers      X                        2.0
  12031.    8           Write                    X           X            2.0
  12032.    9           Write with Verify                    X            2.0
  12033.    10          Output Status            X                        2.0
  12034.    11          Flush Output Buffers     X                        2.0
  12035.    Command     Function                 Character   Block        MS-DOS
  12036.    code                                 driver      driver       version
  12037.    ──────────────────────────────────────────────────────────────────────────
  12038.   11          Flush Output Buffers     X                        2.0
  12039.    12          IOCTL Write              X           X            2.0
  12040.    13          Device Open              X           X            3.0
  12041.    14          Device Close             X           X            3.0
  12042.    15          Removable Media                      X            3.0
  12043.    16          Output Until Busy        X                        3.0
  12044.    19          Generic IOCTL            X           X            3.2
  12045.    23          Get Logical Device                   X            3.2
  12046.    24          Set Logical Device                   X            3.2
  12047.    ──────────────────────────────────────────────────────────────────────────
  12048.  
  12049.  
  12050.    As you can see from the preceding table, a driver's interrupt section must
  12051.    support functions 0 through 12 under all versions of MS-DOS. Drivers
  12052.    tailored for MS-DOS 3.0 and 3.1 can optionally support an additional four
  12053.    functions, and MS-DOS drivers for versions 3.2 and later can support three
  12054.    more (for a total of 20). MS-DOS inspects the bits in the attribute word
  12055.    of the device-driver header to determine which of the optional functions a
  12056.    driver supports, if any.
  12057.  
  12058.    Some of the functions are relevant only for character-device drivers and
  12059.    some only for block-device drivers; a few have meaning to both types. In
  12060.    any case, both driver types should have an executable routine present for
  12061.    each function, even if it does nothing except set the done flag in the
  12062.    status word of the request header.
  12063.  
  12064.    In the command-code descriptions that follow, RH refers to the request
  12065.    header whose address was passed to the strategy routine in ES:BX, BYTE is
  12066.    an 8-bit parameter, WORD is a 16-bit parameter, and DWORD is a far pointer
  12067.    (a 16-bit offset followed by a 16-bit segment).
  12068.  
  12069.  Function 00H (0): Driver Initialization
  12070.  
  12071.    MS-DOS requests the driver's initialization function (init) only once,
  12072.    when the driver is first loaded. This function performs any necessary
  12073.    device hardware initialization, setup of interrupt vectors, and so forth.
  12074.    The initialization routine must return the address of the position where
  12075.    free memory begins after the driver code (the break address), so that
  12076.    MS-DOS knows where it can build certain control structures and then load
  12077.    the next installable driver. If this is a block-device driver, init must
  12078.    also return the number of units and the address of a BPB pointer array.
  12079.  
  12080.    MS-DOS uses the number of units returned by a block driver in the request
  12081.    header to assign drive identifiers. For example, if the current maximum
  12082.    drive is D and the driver being initialized supports four units, MS-DOS
  12083.    will assign it the drive letters E, F, G, and H. Although the
  12084.    device-driver header also has a field for number of units, MS-DOS does not
  12085.    inspect it.
  12086.  
  12087.    The BPB pointer array is an array of word offsets to BIOS parameter blocks
  12088.    (Figure 14-7). Each unit defined by the driver must have one entry in the
  12089.    array, although the entries can all point to the same BPB to conserve
  12090.    memory. During the operating-system boot sequence, MS-DOS scans all the
  12091.    BPBs defined by all the units in all the block-device drivers to determine
  12092.    the largest sector size that exists on any device in the system and uses
  12093.    this information to set its cache buffer size.
  12094.  
  12095.    The operating-system services that the initialization code can invoke at
  12096.    load time are very limited only Int 21H Functions 01H through 0CH and
  12097.    30H. These are just adequate to check the MS-DOS version number and
  12098.    display a driver-identification or error message.
  12099.  
  12100.    Many programmers position the initialization code at the end of the driver
  12101.    and return that address as the location of the first free memory, so that
  12102.    MS-DOS will reclaim the memory occupied by the initialization routine
  12103.    after the routine is finished with its work. If the initialization routine
  12104.    finds that the device is missing or defective and wants to abort the
  12105.    installation of the driver completely so that it does not occupy any
  12106.    memory, it should return number of units as zero and set the free memory
  12107.    address to CS:0000H. (A character-device driver that wants to abort its
  12108.    installation should clear bit 15 of the attribute word in the driver
  12109.    header and then set the units field and free memory address as though it
  12110.    were a block-device driver.)
  12111.  
  12112.    Byte(s)                  Contents
  12113.    ──────────────────────────────────────────────────────────────────────────
  12114.    00─01H                   Bytes per sector
  12115.    02H                      Sectors per allocation unit (power of 2)
  12116.    03H─04H                  Number of reserved sectors (starting at sector 0)
  12117.    05H                      Number of file allocation tables
  12118.    06H─07H                  Maximum number of root-directory entries
  12119.    08H─09H                  Total number of sectors in medium
  12120.    0AH                      Media descriptor byte
  12121.    0BH─0CH                  Number of sectors occupied by a single FAT
  12122.    0DH─0EH                  Sectors per track (versions 3.0 and later)
  12123.    0FH─10H                  Number of heads (versions 3.0 and later)
  12124.    11H─12H                  Number of hidden sectors (versions 3.0 and later)
  12125.    13H─14H                  High-order word of number of hidden sectors
  12126.                             (version 4.0)
  12127.    15H─18H                  If bytes 8─9 are zero, total number of sectors in
  12128.                             medium (version 4.0)
  12129.    19H─1EH                  Reserved, should be zero (version 4.0)
  12130.    ──────────────────────────────────────────────────────────────────────────
  12131.  
  12132.    Figure 14-7.  Structure of a BIOS parameter block (BPB). Every formatted
  12133.    disk contains a copy of its BPB in the boot sector. (See Chapter 10.)
  12134.  
  12135.    The initialization function is called with
  12136.  
  12137.    ──────────────────────────────────────────────────────────────────────────
  12138.    RH + 2             BYTE              Command code = 0
  12139.  
  12140.    RH + 18            DWORD             Pointer to character after equal sign
  12141.                                         on CONFIG.SYS line that loaded driver
  12142.                                         (this information is read-only)
  12143.  
  12144.    RH + 22            BYTE              Drive number for first unit of this
  12145.                                         block driver (0 = A, 1 = B, and so
  12146.                                         forth) (MS-DOS version 3 only)
  12147.    ──────────────────────────────────────────────────────────────────────────
  12148.  
  12149.    It returns:
  12150.  
  12151.    ──────────────────────────────────────────────────────────────────────────
  12152.    RH + 3             WORD              Status
  12153.  
  12154.    RH + 13            BYTE              Number of units (block devices only)
  12155.  
  12156.    RH + 14            DWORD             Address of first free memory above
  12157.                                         driver (break address)
  12158.  
  12159.    RH + 18            DWORD             BPB pointer array (block devices
  12160.                                         only)
  12161.    ──────────────────────────────────────────────────────────────────────────
  12162.  
  12163.  Function 01H (1): Media Check
  12164.  
  12165.    The media-check function applies only to block devices, and in
  12166.    character-device drivers it should do nothing except set the done flag.
  12167.    This function is called when a drive-access call other than a simple file
  12168.    read or write is pending. MS-DOS passes to the function the media
  12169.    descriptor byte for the disk that it assumes is in the drive (Figure
  12170.    14-8). If feasible, the media-check routine returns a code indicating
  12171.    whether the disk has been changed since the last transfer. If the
  12172.    media-check routine can assert that the disk has not been changed, MS-DOS
  12173.    can bypass rereading the FAT before a directory access, which improves
  12174.    overall performance.
  12175.  
  12176.    Code                     Meaning
  12177.    ──────────────────────────────────────────────────────────────────────────
  12178.    0F0H                     3.5", 2-sided, 18-sector
  12179.    0F8H                     fixed disk
  12180.    0F9H                     3.5", 2-sided, 9-sector
  12181.    0F9H                     5.25", 2-sided, 15-sector
  12182.    0FCH                     5.25", 1-sided, 9-sector
  12183.    0FDH                     5.25", 2-sided, 9-sector
  12184.    0FEH                     5.25", 1-sided, 8-sector
  12185.    0FFH                     5.25", 2-sided, 8-sector
  12186.    ──────────────────────────────────────────────────────────────────────────
  12187.  
  12188.    Figure 14-8.  Current valid MS-DOS codes for the media descriptor byte of
  12189.    the request header, assuming bit 13 in the attribute word of the driver
  12190.    header is zero.
  12191.  
  12192.    MS-DOS responds to the results of the media-check function in the
  12193.    following ways:
  12194.  
  12195.    ■  If the disk has not been changed, MS-DOS proceeds with the disk access.
  12196.  
  12197.    ■  If the disk has been changed, MS-DOS invalidates all buffers associated
  12198.       with this unit, including buffers containing data waiting to be written
  12199.       (this data is simply lost), performs a BUILD BPB call, and then reads
  12200.       the disk's FAT and directory.
  12201.  
  12202.    ■  If the disk-change status is unknown, the action taken by MS-DOS
  12203.       depends upon the state of its internal buffers. If data that needs to
  12204.       be written out is present in the buffers, MS-DOS assumes no disk change
  12205.       has occurred and writes the data (taking the risk that, if the disk
  12206.       really was changed, the file structure on the new disk may be damaged).
  12207.       If the buffers are empty or have all been previously flushed to the
  12208.       disk, MS-DOS assumes that the disk was changed, and then proceeds as
  12209.       described above for the disk-changed return code.
  12210.  
  12211.    If bit 11 of the device-header attribute word is set (that is, the driver
  12212.    supports the optional open/close/removable-media functions), the host
  12213.    system is MS-DOS version 3.0 or later, and the function returns the
  12214.    disk-changed code (-1), the function must also return the segment and
  12215.    offset of the ASCIIZ volume label for the previous disk in the drive. (If
  12216.    the driver does not have the volume label, it can return a pointer to the
  12217.    ASCIIZ string NO NAME.) If MS-DOS determines that the disk was changed
  12218.    with unwritten data still present in its buffers, it issues a
  12219.    critical-error 0FH (invalid disk change). Application programs can trap
  12220.    this critical error and prompt the user to replace the original disk.
  12221.  
  12222.    The media-check function is called with
  12223.  
  12224.    ──────────────────────────────────────────────────────────────────────────
  12225.    RH + 1             BYTE              Unit code
  12226.  
  12227.    RH + 2             BYTE              Command code = 1
  12228.  
  12229.    RH + 13            BYTE              Media descriptor byte
  12230.    ──────────────────────────────────────────────────────────────────────────
  12231.  
  12232.    It returns
  12233.  
  12234.    ──────────────────────────────────────────────────────────────────────────
  12235.    RH + 3             WORD              Status
  12236.  
  12237.    RH + 14            BYTE              Media-change code:
  12238.  
  12239.                                         -1 if disk changed
  12240.  
  12241.                                         0 if don't know whether disk changed
  12242.  
  12243.                                         1 if disk not changed
  12244.  
  12245.    RH + 15            DWORD             Pointer to previous volume label, if
  12246.                                         device attribute bit 11 = 1 and disk
  12247.                                         has been changed (MS-DOS versions 3.0
  12248.                                         and later)
  12249.    ──────────────────────────────────────────────────────────────────────────
  12250.  
  12251.  Function 02H (2): Build BIOS Parameter Block (BPB)
  12252.  
  12253.    The build BPB function applies only to block devices, and in
  12254.    character-device drivers should do nothing except set the done flag. The
  12255.    kernel uses this function to get a pointer to the valid BPB (see Figure
  12256.    14-7) for the current disk and calls it when the disk-changed code is
  12257.    returned by the media-check routine or the don't-know code is returned and
  12258.    there are no dirty buffers (buffers with changed data that have not yet
  12259.    been written to disk). Thus, a call to this function indicates that the
  12260.    disk has been legally changed.
  12261.  
  12262.    The build BPB function receives a pointer to a one-sector buffer in the
  12263.    request header. If bit 13 in the driver header's attribute word is zero,
  12264.    the buffer contains the first sector of the FAT (which includes the media
  12265.    identification byte) and should not be altered by the driver. If bit 13 is
  12266.    set, the driver can use the buffer as scratch space.
  12267.  
  12268.    The build BPB function is called with
  12269.  
  12270.    ──────────────────────────────────────────────────────────────────────────
  12271.    RH + 1             BYTE              Unit code
  12272.  
  12273.    RH + 2             BYTE              Command code = 2
  12274.  
  12275.    RH + 13            BYTE              Media descriptor byte
  12276.  
  12277.    RH + 14            DWORD             Buffer address
  12278.    ──────────────────────────────────────────────────────────────────────────
  12279.  
  12280.    It returns
  12281.  
  12282.    ──────────────────────────────────────────────────────────────────────────
  12283.    RH + 3             WORD              Status
  12284.  
  12285.    RH + 18            DWORD             Pointer to new BPB
  12286.    ──────────────────────────────────────────────────────────────────────────
  12287.  
  12288.    Under MS-DOS versions 3.0 and later, if bit 11 of the header's device
  12289.    attribute word is set, this routine should also read the volume label off
  12290.    the disk and save it.
  12291.  
  12292.  Function 03H (3): I/O-Control Read
  12293.  
  12294.    The IOCTL read function allows the device driver to pass information
  12295.    directly to the application program. This function is called only if bit
  12296.    14 is set in the device attribute word. MS-DOS performs no error check on
  12297.    IOCTL I/O calls.
  12298.  
  12299.    The IOCTL read function is called with
  12300.  
  12301.    ──────────────────────────────────────────────────────────────────────────
  12302.    RH + 1             BYTE              Unit code (block devices)
  12303.  
  12304.    RH + 2             BYTE              Command code = 3
  12305.  
  12306.    RH + 13            BYTE              Media descriptor byte
  12307.  
  12308.    RH + 14            DWORD             Transfer address
  12309.  
  12310.    RH + 18            WORD              Byte/sector count
  12311.  
  12312.    RH + 20            WORD              Starting sector number (block
  12313.                                         devices)
  12314.    ──────────────────────────────────────────────────────────────────────────
  12315.  
  12316.    It returns
  12317.  
  12318.    ──────────────────────────────────────────────────────────────────────────
  12319.    RH + 3             WORD              Status
  12320.  
  12321.    RH + 18            WORD              Actual bytes or sectors transferred
  12322.    ──────────────────────────────────────────────────────────────────────────
  12323.  
  12324.  Function 04H (4): Read
  12325.  
  12326.    The read function transfers data from the device into the specified memory
  12327.    buffer. If an error is encountered during the read, the function must set
  12328.    the error status and, in addition, report the number of bytes or sectors
  12329.    successfully transferred; it is not sufficient to simply report an error.
  12330.  
  12331.    The read function is called with
  12332.  
  12333.    ──────────────────────────────────────────────────────────────────────────
  12334.    RH + 1             BYTE              Unit code (block devices)
  12335.  
  12336.    RH + 2             BYTE              Command code = 4
  12337.  
  12338.    RH + 13            BYTE              Media descriptor byte
  12339.  
  12340.    RH + 14            DWORD             Transfer address
  12341.  
  12342.    RH + 18            WORD              Byte/sector count
  12343.  
  12344.    RH + 20            WORD              Starting sector number (block
  12345.                                         devices)
  12346.    ──────────────────────────────────────────────────────────────────────────
  12347.  
  12348.    For block-device read operations in MS-DOS version 4, if the logical unit
  12349.    is larger than 32 MB and bit 1 of the driver's attribute word is set, the
  12350.    following request structure is used instead:
  12351.  
  12352.    ──────────────────────────────────────────────────────────────────────────
  12353.    RH + 1             BYTE              Unit code
  12354.  
  12355.    RH + 2             BYTE              Command code = 4
  12356.  
  12357.    RH + 13            BYTE              Media descriptor byte
  12358.  
  12359.    RH + 14            DWORD             Transfer address
  12360.  
  12361.    RH + 18            WORD              Sector count
  12362.  
  12363.    RH + 20            WORD              Contains -1 to signal use of 32-bit
  12364.                                         sector number
  12365.  
  12366.    RH + 26            DWORD             32-bit starting sector number
  12367.    ──────────────────────────────────────────────────────────────────────────
  12368.  
  12369.    The read function returns
  12370.  
  12371.    ──────────────────────────────────────────────────────────────────────────
  12372.    RH + 3             WORD              Status
  12373.  
  12374.    RH + 18            WORD              Actual bytes or sectors transferred
  12375.  
  12376.    RH + 22            DWORD             Pointer to volume label if error 0FH
  12377.                                         is returned (MS-DOS versions 3.0 and
  12378.                                         later)
  12379.    ──────────────────────────────────────────────────────────────────────────
  12380.  
  12381.    Under MS-DOS versions 3.0 and later, this routine can use the count of
  12382.    open files maintained by the open and close functions (0DH and 0EH) and
  12383.    the media descriptor byte to determine whether the disk has been illegally
  12384.    changed.
  12385.  
  12386.  Function 05H (5): Nondestructive Read
  12387.  
  12388.    The nondestructive read function applies only to character devices, and in
  12389.    block devices it should do nothing except set the done flag. It returns
  12390.    the next character that would be obtained with a read function (command
  12391.    code 4), without removing that character from the driver's internal
  12392.    buffer. MS-DOS uses this function to check the console driver for pending
  12393.    Control-C characters during other operations.
  12394.  
  12395.    The nondestructive read function is called with
  12396.  
  12397.    ──────────────────────────────────────────────────────────────────────────
  12398.    RH + 2             BYTE              Command code = 5
  12399.    ──────────────────────────────────────────────────────────────────────────
  12400.  
  12401.    It returns
  12402.  
  12403.    ──────────────────────────────────────────────────────────────────────────
  12404.    RH + 3             WORD              Status
  12405.  
  12406.                                         If busy bit = 0, at least one
  12407.                                         character is waiting
  12408.  
  12409.                                         If busy bit = 1, no characters are
  12410.                                         waiting
  12411.  
  12412.    RH + 13            BYTE              Character (if busy bit = 0)
  12413.    ──────────────────────────────────────────────────────────────────────────
  12414.  
  12415.  Function 06H (6): Input Status
  12416.  
  12417.    The input-status function applies only to character devices, and in
  12418.    block-device drivers it should do nothing except set the done flag. This
  12419.    function returns the current input status for the device, allowing MS-DOS
  12420.    to test whether characters are waiting in a type-ahead buffer. If the
  12421.    character device does not have a type-ahead buffer, the input-status
  12422.    routine should always return the busy bit equal to zero, so that MS-DOS
  12423.    will not wait forever to call the read (04H) or nondestructive read (05H)
  12424.    function.
  12425.  
  12426.    The input-status function is called with
  12427.  
  12428.    ──────────────────────────────────────────────────────────────────────────
  12429.    RH + 2             BYTE              Command code = 6
  12430.    ──────────────────────────────────────────────────────────────────────────
  12431.  
  12432.    It returns
  12433.  
  12434.    ──────────────────────────────────────────────────────────────────────────
  12435.    RH + 3             WORD              Status:
  12436.  
  12437.                                         If busy bit = 1, read request goes to
  12438.                                         physical device.
  12439.  
  12440.                                         If busy bit = 0, characters already
  12441.                                         in device buffer and read request
  12442.                                         returns quickly.
  12443.    ──────────────────────────────────────────────────────────────────────────
  12444.  
  12445.  Function 07H (7): Flush Input Buffers
  12446.  
  12447.    The flush-input-buffers function applies only to character devices, and in
  12448.    block-device drivers it should do nothing except set the done flag. This
  12449.    function causes any data waiting in the input buffer to be discarded.
  12450.  
  12451.    The flush-input-buffers function is called with
  12452.  
  12453.    ──────────────────────────────────────────────────────────────────────────
  12454.    RH + 2             BYTE              Command code = 7
  12455.    ──────────────────────────────────────────────────────────────────────────
  12456.  
  12457.    It returns
  12458.  
  12459.    ──────────────────────────────────────────────────────────────────────────
  12460.    RH + 3             WORD              Status
  12461.  
  12462.    ──────────────────────────────────────────────────────────────────────────
  12463.  
  12464.  Function 08H (8): Write
  12465.  
  12466.    The write function transfers data from the specified memory buffer to the
  12467.    device. If an error is encountered during the write, the write function
  12468.    must set the error status and, in addition, report the number of bytes or
  12469.    sectors successfully transferred; it is not sufficient to simply report an
  12470.    error.
  12471.  
  12472.    The write function is called with
  12473.  
  12474.    ──────────────────────────────────────────────────────────────────────────
  12475.    RH + 1             BYTE              Unit code (block devices)
  12476.  
  12477.    RH + 2             BYTE              Command code = 8
  12478.  
  12479.    RH + 13            BYTE              Media descriptor byte
  12480.  
  12481.    RH + 14            DWORD             Transfer address
  12482.  
  12483.    RH + 18            WORD              Byte/sector count
  12484.  
  12485.    RH + 20            WORD              Starting sector number (block
  12486.                                         devices)
  12487.    ──────────────────────────────────────────────────────────────────────────
  12488.  
  12489.    For block-device write operations in MS-DOS version 4, if the logical unit
  12490.    is larger than 32 MB and bit 1 of the driver's attribute word is set, the
  12491.    following request structure is used instead:
  12492.  
  12493.    ──────────────────────────────────────────────────────────────────────────
  12494.    RH + 1             BYTE              Unit code
  12495.  
  12496.    RH + 2             BYTE              Command code = 8
  12497.  
  12498.    RH + 13            BYTE              Media descriptor byte
  12499.  
  12500.    RH + 14            DWORD             Transfer address
  12501.  
  12502.    RH + 18            WORD              Sector count
  12503.  
  12504.    RH + 20            WORD              Contains -1 to signal use of 32-bit
  12505.                                         sector number
  12506.  
  12507.    RH + 26            DWORD             32-bit starting sector number
  12508.    ──────────────────────────────────────────────────────────────────────────
  12509.  
  12510.    The write function returns
  12511.  
  12512.    ──────────────────────────────────────────────────────────────────────────
  12513.    RH + 3             WORD              Status
  12514.  
  12515.    RH + 18            WORD              Actual bytes or sectors transferred
  12516.  
  12517.    RH + 22            DWORD             Pointer to volume label if error 0FH
  12518.                                         returned (MS-DOS versions 3.0 and
  12519.                                         later)
  12520.    ──────────────────────────────────────────────────────────────────────────
  12521.  
  12522.    Under MS-DOS versions 3.0 and later, this routine can use the reference
  12523.    count of open files maintained by the open and close functions (0DH and
  12524.    0EH) and the media descriptor byte to determine whether the disk has been
  12525.    illegally changed.
  12526.  
  12527.  Function 09H (9): Write with Verify
  12528.  
  12529.    The write-with-verify function transfers data from the specified memory
  12530.    buffer to the device. If feasible, it should perform a read-after-write
  12531.    verification of the data to confirm that the data was written correctly.
  12532.    Otherwise, Function 09H is exactly like Function 08H.
  12533.  
  12534.  Function 0AH (10): Output Status
  12535.  
  12536.    The output-status function applies only to character devices, and in
  12537.    block-device drivers it should do nothing except set the done flag. This
  12538.    function returns the current output status for the device.
  12539.  
  12540.    The output-status function is called with
  12541.  
  12542.    ──────────────────────────────────────────────────────────────────────────
  12543.    RH + 2             BYTE              Command code = 10 (0AH)
  12544.    ──────────────────────────────────────────────────────────────────────────
  12545.  
  12546.    It returns
  12547.  
  12548.    ──────────────────────────────────────────────────────────────────────────
  12549.    RH + 3             WORD              Status:
  12550.  
  12551.                                         If busy bit = 1, write request waits
  12552.                                         for completion of current request.
  12553.  
  12554.                                         If busy bit = 0, device idle and
  12555.                                         write request starts immediately.
  12556.    ──────────────────────────────────────────────────────────────────────────
  12557.  
  12558.  Function 0BH (11): Flush Output Buffers
  12559.  
  12560.    The flush-output-buffers function applies only to character devices, and
  12561.    in block-device drivers it should do nothing except set the done flag.
  12562.    This function empties the output buffer, if any, and discards any pending
  12563.    output requests.
  12564.  
  12565.    The flush-output-buffers function is called with
  12566.  
  12567.    ──────────────────────────────────────────────────────────────────────────
  12568.    RH + 2             BYTE              Command code = 11 (0BH)
  12569.    ──────────────────────────────────────────────────────────────────────────
  12570.  
  12571.    It returns
  12572.  
  12573.    ──────────────────────────────────────────────────────────────────────────
  12574.    RH + 3             WORD              Status
  12575.  
  12576.    ──────────────────────────────────────────────────────────────────────────
  12577.  
  12578.  Function 0CH (12): I/O-Control Write
  12579.  
  12580.    The IOCTL write function allows an application program to pass control
  12581.    information directly to the driver. This function is called only if bit 14
  12582.    is set in the device attribute word. MS-DOS performs no error check on
  12583.    IOCTL I/O calls.
  12584.  
  12585.    The IOCTL write function is called with
  12586.  
  12587.    ──────────────────────────────────────────────────────────────────────────
  12588.    RH + 1             BYTE              Unit code (block devices)
  12589.  
  12590.    RH + 2             BYTE              Command code = 12 (0CH)
  12591.  
  12592.    RH + 13            BYTE              Media descriptor byte
  12593.  
  12594.    RH + 14            DWORD             Transfer address
  12595.  
  12596.    RH + 18            WORD              Byte/sector count
  12597.  
  12598.    RH + 20            WORD              Starting sector number (block
  12599.                                         devices)
  12600.    ──────────────────────────────────────────────────────────────────────────
  12601.  
  12602.    It returns
  12603.  
  12604.    ──────────────────────────────────────────────────────────────────────────
  12605.    RH + 3             WORD              Status
  12606.  
  12607.    RH + 18            WORD              Actual bytes or sectors transferred
  12608.    ──────────────────────────────────────────────────────────────────────────
  12609.  
  12610.  Function 0DH (13): Device Open
  12611.  
  12612.    The device-open function is supported only under MS-DOS versions 3.0 and
  12613.    later and is called only if bit 11 is set in the device attribute word of
  12614.    the device header.
  12615.  
  12616.    On block devices, the device-open function can be used to manage local
  12617.    buffering and to increment a reference count of the number of open files
  12618.    on the device. This capability must be used with care, however, because
  12619.    programs that access files through FCBs frequently fail to close them,
  12620.    thus invalidating the open-files count. One way to protect against this
  12621.    possibility is to reset the open-files count to zero, without flushing the
  12622.    buffers, whenever the answer to a media-change call is yes and a
  12623.    subsequent build BPB call is made to the driver.
  12624.  
  12625.    On character devices, the device-open function can be used to send a
  12626.    device-initialization string (which can be set into the driver by an
  12627.    application program by means of an IOCTL write function) or to deny
  12628.    simultaneous access to a character device by more than one process. Note
  12629.    that the predefined handles for the CON, AUX, and PRN devices are always
  12630.    open.
  12631.  
  12632.    The device-open function is called with
  12633.  
  12634.    ──────────────────────────────────────────────────────────────────────────
  12635.    RH + 1             BYTE              Unit code (block devices)
  12636.  
  12637.    RH + 2             BYTE              Command code = 13 (0DH)
  12638.    ──────────────────────────────────────────────────────────────────────────
  12639.  
  12640.    It returns
  12641.  
  12642.    ──────────────────────────────────────────────────────────────────────────
  12643.    RH + 3             WORD              Status
  12644.    ──────────────────────────────────────────────────────────────────────────
  12645.  
  12646.  Function 0EH (14): Device Close
  12647.  
  12648.    The device-close function is supported only under MS-DOS versions 3.0 and
  12649.    later and is called only if bit 11 is set in the device attribute word of
  12650.    the device header.
  12651.  
  12652.    On block devices, this function can be used to manage local buffering and
  12653.    to decrement a reference count of the number of open files on the device;
  12654.    when the count reaches zero, all files have been closed and the driver
  12655.    should flush buffers because the user may change disks.
  12656.  
  12657.    On character devices, the device-close function can be used to send a
  12658.    device-dependent post-I/O string such as a formfeed. (This string can be
  12659.    set into the driver by an application program by means of an IOCTL write
  12660.    function.) Note that the predefined handles for the CON, PRN, and AUX
  12661.    devices are never closed.
  12662.  
  12663.    The device-close function is called with
  12664.  
  12665.    ──────────────────────────────────────────────────────────────────────────
  12666.    RH + 1             BYTE              Unit code (block devices)
  12667.  
  12668.    RH + 2             BYTE              Command code = 14 (0EH)
  12669.    ──────────────────────────────────────────────────────────────────────────
  12670.  
  12671.    It returns
  12672.  
  12673.    ──────────────────────────────────────────────────────────────────────────
  12674.    RH + 3             WORD              Status
  12675.    ──────────────────────────────────────────────────────────────────────────
  12676.  
  12677.  Function 0FH (15): Removable Media
  12678.  
  12679.    The removable-media function is supported only under MS-DOS versions 3.0
  12680.    and later and only on block devices; in character-device drivers it should
  12681.    do nothing except set the done flag. This function is called only if bit
  12682.    11 is set in the device attribute word in the device header.
  12683.  
  12684.    The removable-media function is called with
  12685.  
  12686.    ──────────────────────────────────────────────────────────────────────────
  12687.    RH + 1             BYTE              Unit code
  12688.  
  12689.    RH + 2             BYTE              Command code = 15 (0FH)
  12690.    ──────────────────────────────────────────────────────────────────────────
  12691.  
  12692.    It returns
  12693.  
  12694.    ──────────────────────────────────────────────────────────────────────────
  12695.    RH + 3             WORD              Status:
  12696.  
  12697.                                         If busy bit = 1, medium nonremovable
  12698.  
  12699.                                         If busy bit = 0, medium removable
  12700.    ──────────────────────────────────────────────────────────────────────────
  12701.  
  12702.  Function 10H (16): Output Until Busy
  12703.  
  12704.    The output-until-busy function is supported only under MS-DOS versions 3.0
  12705.    and later, and only on character devices; in block-device drivers it
  12706.    should do nothing except set the done flag. This function transfers data
  12707.    from the specified memory buffer to a device, continuing to transfer bytes
  12708.    until the device is busy. It is called only if bit 13 of the device
  12709.    attribute word is set in the device header.
  12710.  
  12711.    This function is an optimization included specifically for the use of
  12712.    print spoolers. It is not an error for this function to return a number of
  12713.    bytes transferred that is less than the number of bytes requested.
  12714.  
  12715.    The output-until-busy function is called with
  12716.  
  12717.    ──────────────────────────────────────────────────────────────────────────
  12718.    RH + 2             BYTE              Command code = 16 (10H)
  12719.  
  12720.    RH + 14            DWORD             Transfer address
  12721.  
  12722.    RH + 18            WORD              Byte count
  12723.    ──────────────────────────────────────────────────────────────────────────
  12724.  
  12725.    It returns
  12726.  
  12727.    ──────────────────────────────────────────────────────────────────────────
  12728.    RH + 3             WORD              Status
  12729.  
  12730.    RH + 18            WORD              Actual bytes transferred
  12731.    ──────────────────────────────────────────────────────────────────────────
  12732.  
  12733.  Function 13H (19) Generic IOCTL
  12734.  
  12735.    The generic IOCTL function is supported only under MS-DOS versions 3.2 and
  12736.    later and is called only if bit 6 is set in the device attribute word of
  12737.    the device header. This function corresponds to the MS-DOS generic IOCTL
  12738.    service supplied to application programs by Int 21H Function 44H
  12739.    Subfunctions 0CH and 0DH.
  12740.  
  12741.    The generic IOCTL function is passed a category (major) code, a function
  12742.    (minor) code, the contents of the SI and DI registers at the point of the
  12743.    IOCTL call, and the segment and offset of a data buffer. This buffer in
  12744.    turn contains other information whose format depends on the major and
  12745.    minor IOCTL codes passed in the request header. The driver must interpret
  12746.    the major and minor codes in the request header and the contents of the
  12747.    additional buffer to determine which operation it will carry out, then set
  12748.    the done flag in the request-header status word, and return any other
  12749.    applicable information in the request header or the data buffer.
  12750.  
  12751.    Services that the generic IOCTL function may invoke, if the driver
  12752.    supports them, include configuration of the driver for nonstandard disk
  12753.    formats, reading and writing entire disk tracks of data, and formatting
  12754.    and verifying tracks. The generic IOCTL function has been designed to be
  12755.    open-ended, so that it can be used to easily extend the device-driver
  12756.    definition under future versions of MS-DOS.
  12757.  
  12758.    The generic IOCTL function is called with
  12759.  
  12760.    ──────────────────────────────────────────────────────────────────────────
  12761.    RH + 1             BYTE              Unit number (block devices)
  12762.  
  12763.    RH + 2             BYTE              Command code = 19 (13H)
  12764.  
  12765.    RH + 13            BYTE              Category (major) code
  12766.  
  12767.    RH + 14            BYTE              Function (minor) code
  12768.  
  12769.    RH + 15            WORD              SI register contents
  12770.  
  12771.    RH + 17            WORD              DI register contents
  12772.  
  12773.    RH + 19            DWORD             Address of generic IOCTL data packet
  12774.    ──────────────────────────────────────────────────────────────────────────
  12775.  
  12776.    It returns
  12777.  
  12778.    ──────────────────────────────────────────────────────────────────────────
  12779.    RH + 3             WORD              Status
  12780.    ──────────────────────────────────────────────────────────────────────────
  12781.  
  12782.  Function 17H (23): Get Logical Device
  12783.  
  12784.    The get-logical-device function is supported only under MS-DOS versions
  12785.    3.2 and later and only on block devices; in character-device drivers it
  12786.    should do nothing except set the done bit in the status word. This
  12787.    function is called only if bit 6 is set in the device attribute word of
  12788.    the device header. It corresponds to the get-logical-device-map service
  12789.    supplied to application programs through Int 21H Function 44H Subfunction
  12790.    0EH.
  12791.  
  12792.    The get-logical-device function returns a code for the last drive letter
  12793.    used to reference the device; if only one drive letter is assigned to the
  12794.    device, the returned unit code should be zero. Thus, this function can be
  12795.    used to determine whether more than one drive letter is assigned to the
  12796.    same physical device.
  12797.  
  12798.    The get-logical-device function is called with
  12799.  
  12800.    ──────────────────────────────────────────────────────────────────────────
  12801.    RH + 1             BYTE              Unit code
  12802.  
  12803.    RH + 2             BYTE              Command code = 23 (17H)
  12804.    ──────────────────────────────────────────────────────────────────────────
  12805.  
  12806.    It returns
  12807.  
  12808.    ──────────────────────────────────────────────────────────────────────────
  12809.    RH + 1             BYTE              Last unit referenced, or zero
  12810.  
  12811.    RH + 3             WORD              Status
  12812.    ──────────────────────────────────────────────────────────────────────────
  12813.  
  12814.  Function 18H (24): Set Logical Device
  12815.  
  12816.    The set-logical-device function is supported only under MS-DOS versions
  12817.    3.2 and later and only on block devices; in character-device drivers it
  12818.    should do nothing except set the done bit in the status word. This
  12819.    function is called only if bit 6 is set in the device attribute word of
  12820.    the device header. It corresponds to the set-logical-device-map service
  12821.    supplied to application programs by MS-DOS through Int 21H Function 44H
  12822.    Subfunction 0FH.
  12823.  
  12824.    The set-logical-device function informs the driver of the next
  12825.    logical-drive identifier that will be used to reference the physical
  12826.    device. The unit code passed by the MS-DOS kernel in this case is
  12827.    zero-based relative to the number of logical drives supported by this
  12828.    particular driver. For example, if the driver supports two floppy-disk
  12829.    units (A and B), only one physical floppy-disk drive exists in the system,
  12830.    and the set-logical-device function is called with a unit number of 1, the
  12831.    driver is being informed that the next read or write request from the
  12832.    kernel will be directed to drive B.
  12833.  
  12834.    The set-logical-device function is called with
  12835.  
  12836.    ──────────────────────────────────────────────────────────────────────────
  12837.    RH + 1             BYTE              Unit code
  12838.  
  12839.    RH + 2             BYTE              Command code = 24 (18H)
  12840.    ──────────────────────────────────────────────────────────────────────────
  12841.  
  12842.    It returns
  12843.  
  12844.    ──────────────────────────────────────────────────────────────────────────
  12845.    RH + 3             WORD              Status
  12846.    ──────────────────────────────────────────────────────────────────────────
  12847.  
  12848.  
  12849.  The Processing of a Typical I/O Request
  12850.  
  12851.    An application program requests an I/O operation from MS-DOS by loading
  12852.    registers with the appropriate values and executing an Int 21H. This
  12853.    results in the following sequence of actions:
  12854.  
  12855.    1.  MS-DOS inspects its internal tables and determines which device driver
  12856.        should receive the I/O request.
  12857.  
  12858.    2.  MS-DOS creates a request-header data packet in a reserved area of
  12859.        memory. (Disk I/O requests are transformed from file and record
  12860.        information into logical-sector requests by MS-DOS's interpretation of
  12861.        the disk directory and FAT.)
  12862.  
  12863.    3.  MS-DOS calls the device driver's strat entry point, passing the
  12864.        address of the request header in the ES:BX registers.
  12865.  
  12866.    4.  The device driver saves the address of the request header in a local
  12867.        variable and performs a FAR RETURN.
  12868.  
  12869.    5.  MS-DOS calls the device driver's intr entry point.
  12870.  
  12871.    6.  The interrupt routine saves all registers, retrieves the address of
  12872.        the request header that was saved by the strategy routine, extracts
  12873.        the function code, and branches to the appropriate command-code
  12874.        subroutine to perform the function.
  12875.  
  12876.    7.  If a data transfer on a block device was requested, the driver's read
  12877.        or write subroutine translates the logical-sector number into a head,
  12878.        track, and physical-sector address for the requested unit and then
  12879.        performs the I/O operation. Because a multiple-sector transfer can be
  12880.        requested in a single request header, a single request by MS-DOS to
  12881.        the driver can result in multiple read or write commands to the disk
  12882.        controller.
  12883.  
  12884.    8.  When the requested function is complete, the interrupt routine sets
  12885.        the status word and any other required information into the request
  12886.        header, restores all registers to their state at entry, and performs a
  12887.        FAR RETURN.
  12888.  
  12889.    9.  MS-DOS translates the driver's return status into the appropriate
  12890.        return code and carry-flag status for the MS-DOS Int 21H function that
  12891.        was requested and returns control to the application program.
  12892.  
  12893.    Note that a single request by an application program can result in MS-DOS
  12894.    passing many request headers to the driver. For example, attempting to
  12895.    open a file in a subdirectory on a previously unaccessed disk drive might
  12896.    require the following actions:
  12897.  
  12898.    ■  Reading the disk's boot sector to get the BPB
  12899.  
  12900.    ■  Reading from one to many sectors of the root directory to find the
  12901.       entry for the subdirectory and obtain its starting-cluster number
  12902.  
  12903.    ■  Reading from one to many sectors of both the FAT and the subdirectory
  12904.       itself to find the entry for the desired file
  12905.  
  12906.  
  12907.  The CLOCK Driver: A Special Case
  12908.  
  12909.    MS-DOS uses the CLOCK device for marking file control blocks and directory
  12910.    entries with the date and time, as well as for providing the date and time
  12911.    services to application programs. This device has a unique type of
  12912.    interaction with MS-DOS──a 6-byte sequence is read from or written to the
  12913.    driver that obtains or sets the current date and time. The sequence has
  12914.    the following format:
  12915.  
  12916.    ┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
  12917.    │    0    │    1    │    2    │    3    │   4     │    5    │
  12918.    │  Days   │  Days   │ Minutes │  Hours  │Seconds/ │ Seconds │
  12919.    │low byte │high byte│         │         │  100    │         │
  12920.    └─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
  12921.  
  12922.    The value passed for days is a 16-bit integer representing the number of
  12923.    days elapsed since January 1, 1980.
  12924.  
  12925.    The clock driver can have any logical-device name because MS-DOS uses the
  12926.    CLOCK bit in the device attribute word of the driver's device header to
  12927.    identify the device, rather than its name. On IBM PC systems, the clock
  12928.    device has the logical-device name CLOCK$.
  12929.  
  12930.  
  12931.  Writing and Installing a Device Driver
  12932.  
  12933.    Now that we have discussed the structure and capabilities of installable
  12934.    device drivers for the MS-DOS environment, we can discuss the mechanical
  12935.    steps of assembling and linking them.
  12936.  
  12937.  Assembly
  12938.  
  12939.    Device drivers for MS-DOS always have an origin of zero but are otherwise
  12940.    assembled, linked, and converted into an executable module as though they
  12941.    were .COM files. (Although MS-DOS is also capable of loading installable
  12942.    drivers in the .EXE file format, this introduces unnecessary complexity
  12943.    into writing and debugging drivers and offers no significant advantages.
  12944.    In addition, it is not possible to use .EXE-format drivers with some IBM
  12945.    versions of MS-DOS because the .EXE loader is located in COMMAND.COM,
  12946.    which is not present when the installable device drivers are being
  12947.    loaded.) The driver should not have a declared stack segment and must, in
  12948.    general, follow the other restrictions outlined in Chapter 3 for
  12949.    memory-image (.COM) programs. A driver can be loaded anywhere, so beware
  12950.    that you do not make any assumptions in your code about the driver's
  12951.    location in physical memory. Figure 14-9 presents a skeleton example that
  12952.    you can follow as you read the next few pages.
  12953.  
  12954.    ──────────────────────────────────────────────────────────────────────────
  12955.            name    driver
  12956.            page    55,132
  12957.            title   DRIVER.ASM Device-Driver Skeleton
  12958.  
  12959.    ;
  12960.    ; DRIVER.ASM   MS-DOS device-driver skeleton
  12961.    ;
  12962.    ; The driver command-code routines are stubs only and have
  12963.    ; no effect but to return a nonerror "done" status.
  12964.    ;
  12965.    ; Copyright 1988 Ray Duncan
  12966.    ;
  12967.  
  12968.    _TEXT   segment word public 'CODE'
  12969.  
  12970.            assume  cs:_TEXT,ds:_TEXT,es:NOTHING
  12971.  
  12972.            org     0
  12973.  
  12974.    MaxCmd  equ     24              ; maximum allowed command code:
  12975.                                    ; 12 for MS-DOS 2
  12976.                                    ; 16 for MS-DOS 3.0-3.1
  12977.                                    ; 24 for MS-DOS 3.2-3.3
  12978.    cr      equ     0dh             ; ASCII carriage return
  12979.    lf      equ     0ah             ; ASCII linefeed
  12980.    eom     equ     '$'             ; end-of-message signal
  12981.  
  12982.  
  12983.    Header:                         ; device-driver header
  12984.            dd      -1              ; link to next device driver
  12985.            dw      0c840h          ; device attribute word
  12986.            dw      Strat           ; "strategy" routine entry point
  12987.            dw      Intr            ; "interrupt" routine entry point
  12988.            db      'SKELETON'      ; logical-device name
  12989.  
  12990.  
  12991.    RHPtr   dd      ?               ; pointer to request header, passed
  12992.                                    ; by MS-DOS kernel to strategy routine
  12993.  
  12994.  
  12995.    Dispatch:                       ; interrupt-routine command-code
  12996.                                    ; dispatch table:
  12997.            dw      Init            ; 0  = initialize driver
  12998.            dw      MediaChk        ; 1  = media check
  12999.            dw      BuildBPB        ; 2  = build BPB
  13000.            dw      IoctlRd         ; 3  = IOCTL read
  13001.            dw      Read            ; 4  = read
  13002.            dw      NdRead          ; 5  = nondestructive read
  13003.            dw      InpStat         ; 6  = input status
  13004.            dw      InpFlush        ; 7  = flush input buffers
  13005.            dw      Write           ; 8  = write
  13006.            dw      WriteVfy        ; 9  = write with verify
  13007.            dw      OutStat         ; 10 = output status
  13008.            dw      OutFlush        ; 11 = flush output buffers
  13009.            dw      IoctlWt         ; 12 = IOCTL write
  13010.            dw      DevOpen         ; 13 = device open       (MS-DOS 3.0+)
  13011.            dw      DevClose        ; 14 = device close      (MS-DOS 3.0+)
  13012.            dw      RemMedia        ; 15 = removable media  (MS-DOS 3.0+)
  13013.            dw      OutBusy         ; 16 = output until busy (MS-DOS 3.0+)
  13014.            dw      Error           ; 17 = not used
  13015.            dw      Error           ; 18 = not used
  13016.            dw      GenIOCTL        ; 19 = generic IOCTL     (MS-DOS 3.2+)
  13017.            dw      Error           ; 20 = not used
  13018.            dw      Error           ; 21 = not used
  13019.            dw      Error           ; 22 = not used
  13020.            dw      GetLogDev       ; 23 = get logical device (MS-DOS 3.2+)
  13021.            dw      SetLogDev       ; 24 = set logical device (MS-DOS 3.2+)
  13022.    Strat   proc    far             ; device-driver strategy routine,
  13023.                                    ; called by MS-DOS kernel with
  13024.                                    ; ES:BX = address of request header
  13025.  
  13026.                                    ; save pointer to request header
  13027.            mov     word ptr cs:[RHPtr],bx
  13028.            mov     word ptr cs:[RHPtr+2],es
  13029.  
  13030.            ret                     ; back to MS-DOS kernel
  13031.  
  13032.    Strat   endp
  13033.  
  13034.  
  13035.    Intr    proc  far               ; device-driver interrupt routine,
  13036.                                    ; called by MS-DOS kernel immediately
  13037.                                    ; after call to strategy routine
  13038.  
  13039.            push    ax              ; save general registers
  13040.            push    bx
  13041.            push    cx
  13042.            push    dx
  13043.            push    ds
  13044.            push    es
  13045.            push    di
  13046.            push    si
  13047.            push    bp
  13048.  
  13049.            push    cs              ; make local data addressable
  13050.            pop     ds              ; by setting DS = CS
  13051.  
  13052.            les     di,[RHPtr]      ; let ES:DI = request header
  13053.  
  13054.                                    ; get BX = command code
  13055.            mov     bl,es:[di+2]
  13056.            xor     bh,bh
  13057.            cmp     bx,MaxCmd       ; make sure it's legal
  13058.            jle     Intr1           ; jump, function code is ok
  13059.            call    Error           ; set error bit, "unknown command" code
  13060.            jmp     Intr2
  13061.  
  13062.    Intr1:  shl     bx,1            ; form index to dispatch table
  13063.                                    ; and branch to command-code routine
  13064.            call    word ptr [bx+Dispatch]
  13065.  
  13066.            les     di,[RHPtr]      ; ES:DI = addr of request header
  13067.  
  13068.    Intr2:  or      ax,0100h        ; merge 'done' bit into status and
  13069.            mov     es:[di+3],ax    ; store status into request header
  13070.  
  13071.            pop     bp              ; restore general registers
  13072.            pop     si
  13073.            pop     di
  13074.            pop     es
  13075.            pop     ds
  13076.            pop     dx
  13077.            pop     cx
  13078.            pop     bx
  13079.            pop     ax
  13080.            ret                     ; back to MS-DOS kernel
  13081.  
  13082.  
  13083.    ; Command-code routines are called by the interrupt routine
  13084.    ; via the dispatch table with ES:DI pointing to the request
  13085.    ; header.  Each routine should return AX = 0 if function was
  13086.    ; completed successfully or AX = (8000h + error code) if
  13087.    ; function failed.
  13088.  
  13089.  
  13090.    MediaChk proc   near            ; function 1 = media check
  13091.  
  13092.            xor     ax,ax
  13093.            ret
  13094.  
  13095.    MediaChk endp
  13096.  
  13097.  
  13098.    BuildBPB proc   near            ; function 2 = build BPB
  13099.  
  13100.            xor     ax,ax
  13101.            ret
  13102.  
  13103.    BuildBPB endp
  13104.  
  13105.  
  13106.    IoctlRd proc    near            ; function 3 = IOCTL read
  13107.  
  13108.            xor     ax,ax
  13109.            ret
  13110.  
  13111.    IoctlRd endp
  13112.    Read    proc    near            ; function 4 = read (input)
  13113.  
  13114.            xor     ax,ax
  13115.            ret
  13116.  
  13117.    Read    endp
  13118.  
  13119.  
  13120.    NdRead  proc    near            ; function 5 = nondestructive read
  13121.  
  13122.            xor     ax,ax
  13123.            ret
  13124.  
  13125.    NdRead  endp
  13126.  
  13127.  
  13128.    InpStat proc    near            ; function 6 = input status
  13129.  
  13130.            xor     ax,ax
  13131.            ret
  13132.  
  13133.    InpStat endp
  13134.  
  13135.  
  13136.    InpFlush proc   near            ; function 7 = flush input buffers
  13137.  
  13138.            xor     ax,ax
  13139.            ret
  13140.  
  13141.    InpFlush endp
  13142.  
  13143.  
  13144.    Write   proc    near            ; function 8 = write (output)
  13145.  
  13146.            xor     ax,ax
  13147.            ret
  13148.  
  13149.    Write   endp
  13150.  
  13151.  
  13152.    WriteVfy proc   near            ; function 9 = write with verify
  13153.  
  13154.            xor     ax,ax
  13155.            ret
  13156.    endp
  13157.  
  13158.  
  13159.    OutStat proc    near            ; function 10 = output status
  13160.  
  13161.            xor     ax,ax
  13162.            ret
  13163.  
  13164.    OutStat endp
  13165.  
  13166.  
  13167.    OutFlush proc   near            ; function 11 = flush output buffers
  13168.  
  13169.            xor     ax,ax
  13170.            ret
  13171.  
  13172.    OutFlush endp
  13173.  
  13174.  
  13175.    IoctlWt proc    near            ; function 12 = IOCTL write
  13176.  
  13177.            xor     ax,ax
  13178.            ret
  13179.  
  13180.    IoctlWt endp
  13181.  
  13182.  
  13183.    DevOpen proc    near            ; function 13 = device open
  13184.  
  13185.            xor     ax,ax
  13186.            ret
  13187.  
  13188.    DevOpen endp
  13189.  
  13190.  
  13191.    DevClose proc   near            ; function 14 = device close
  13192.  
  13193.            xor     ax,ax
  13194.            ret
  13195.  
  13196.    DevClose endp
  13197.    RemMedia proc   near            ; function 15 = removable media
  13198.  
  13199.            xor     ax,ax
  13200.            ret
  13201.  
  13202.    RemMedia endp
  13203.  
  13204.  
  13205.    OutBusy proc    near            ; function 16 = output until busy
  13206.  
  13207.            xor     ax,ax
  13208.            ret
  13209.  
  13210.    OutBusy endp
  13211.  
  13212.  
  13213.    GenIOCTL proc   near            ; function 19 = generic IOCTL
  13214.  
  13215.            xor     ax,ax
  13216.            ret
  13217.  
  13218.    GenIOCTL endp
  13219.  
  13220.  
  13221.    GetLogDev proc  near            ; function 23 = get logical device
  13222.  
  13223.            xor     ax,ax
  13224.            ret
  13225.  
  13226.    GetLogDev endp
  13227.  
  13228.  
  13229.    SetLogDev proc  near            ; function 24 = set logical device
  13230.  
  13231.            xor     ax,ax
  13232.            ret
  13233.  
  13234.    SetLogDev endp
  13235.  
  13236.  
  13237.    Error   proc    near            ; bad command code in request header
  13238.  
  13239.            mov     ax,8003h        ; error bit + "unknown command" code
  13240.            ret
  13241.      endp
  13242.  
  13243.  
  13244.    Init    proc    near            ; function 0 = initialize driver
  13245.  
  13246.            push    es              ; save address of request header
  13247.            push    di
  13248.  
  13249.            mov     ax,cs           ; convert load address to ASCII
  13250.            mov     bx,offset Ident1
  13251.            call    hexasc
  13252.  
  13253.            mov     ah,9            ; display driver sign-on message
  13254.            mov     dx,offset Ident
  13255.            int     21h
  13256.  
  13257.            pop     di              ; restore request-header address
  13258.            pop     es
  13259.  
  13260.                                    ; set address of free memory
  13261.                                    ; above driver (break address)
  13262.            mov     word ptr es:[di+14],offset Init
  13263.            mov     word ptr es:[di+16],cs
  13264.  
  13265.            xor     ax,ax           ; return status
  13266.            ret
  13267.  
  13268.    Init    endp
  13269.  
  13270.  
  13271.    hexasc  proc    near            ; converts word to hex ASCII
  13272.                                    ; call with AX = value,
  13273.                                    ; DS:BX = address for string
  13274.                                    ; returns AX, BX destroyed
  13275.  
  13276.            push    cx              ; save registers
  13277.            push    dx
  13278.  
  13279.            mov     dx,4            ; initialize character counter
  13280.            mov     cx,4            ; isolate next four bits
  13281.            rol     ax,cl
  13282.            mov     cx,ax
  13283.            and     cx,0fh
  13284.            add     cx,'0'          ; convert to ASCII
  13285.            cmp     cx,'9'          ; is it 0-9?
  13286.            jbe     hexasc2         ; yes, jump
  13287.            add     cx,'A'-'9'-1    ; add fudge factor for A-F
  13288.  
  13289.    hexasc2:                        ; store this character
  13290.            mov     [bx],cl
  13291.            inc     bx              ; bump string pointer
  13292.  
  13293.            dec     dx              ; count characters converted
  13294.            jnz     hexasc1         ; loop, not four yet
  13295.  
  13296.            pop     dx              ; restore registers
  13297.            pop     cx
  13298.            ret                     ; back to caller
  13299.  
  13300.    hexasc  endp
  13301.  
  13302.  
  13303.    Ident   db      cr,lf,lf
  13304.            db      'Advanced MS-DOS Example Device Driver'
  13305.            db      cr,lf
  13306.            db      'Device driver header at: '
  13307.    Ident1  db      'XXXX:0000'
  13308.            db      cr,lf,lf,eom
  13309.  
  13310.    Intr    endp
  13311.  
  13312.    _TEXT   ends
  13313.  
  13314.            end
  13315.    ──────────────────────────────────────────────────────────────────────────
  13316.  
  13317.    Figure 14-9.  DRIVER.ASM: A functional skeleton from which you can
  13318.    implement your own working device driver.
  13319.  
  13320.    The driver's device header must be located at the beginning of the file
  13321.    (offset 0000H). Both words in the link field in the header should be set
  13322.    to -1. The attribute word must be set up correctly for the device type and
  13323.    other options. The offsets to the strategy and interrupt routines must be
  13324.    relative to the same segment base as the device header itself. If the
  13325.    driver is for a character device, the name field should be filled in
  13326.    properly with the device's logical name. The logical name can be any legal
  13327.    8-character filename, padded with spaces and without a colon. Beware of
  13328.    accidentally duplicating the names of existing character devices, unless
  13329.    you are intentionally superseding a resident driver.
  13330.  
  13331.    MS-DOS calls the strategy and interrupt routines for the device by means
  13332.    of an intersegment call (CALL FAR) when the driver is first loaded and
  13333.    installed and again whenever an application program issues an I/O request
  13334.    for the device. MS-DOS uses the ES:BX registers to pass the strat routine
  13335.    a double-word pointer to the request header; this address should be saved
  13336.    internally in the driver so that it is available for use during the
  13337.    subsequent call to the intr routine.
  13338.  
  13339.    The command-code routines for function codes 0 through 12 (0CH) must be
  13340.    present in every installable device driver, regardless of device type.
  13341.    Functions 13 (0DH) and above are optional for drivers used with MS-DOS
  13342.    versions 3.0 and later and can be handled in one of the following ways:
  13343.  
  13344.    ■  Don't implement them, and leave the associated bits in the device
  13345.       header cleared. The resulting driver will work in either version 2 or
  13346.       version 3 but does not take full advantage of the augmented
  13347.       functionality of version 3.
  13348.  
  13349.    ■  Implement them, and test the MS-DOS version during the initialization
  13350.       sequence, setting bits 6 and 11 of the device header appropriately.
  13351.       Write all command-code routines so that they test this bit and adjust
  13352.       to accommodate the host version of MS-DOS. Such a driver requires more
  13353.       work and testing but will take full advantage of both the version 2 and
  13354.       the version 3 environments.
  13355.  
  13356.    ■  Implement them, and assume that all the version 3 facilities are
  13357.       available. With this approach, the resulting driver may not work
  13358.       properly under version 2.
  13359.  
  13360.    Remember that device drivers must preserve the integrity of MS-DOS. The
  13361.    driver must preserve all registers, including flags (especially the
  13362.    direction flag and interrupt enable bits), and if the driver makes heavy
  13363.    use of the stack, it should switch to an internal stack of adequate depth
  13364.    (the MS-DOS stack has room for only 40 to 50 bytes when a driver is
  13365.    called).
  13366.  
  13367.    If you install a new CON driver, be sure to set the bits for standard
  13368.    input and standard output in the device attribute word in the device
  13369.    header.
  13370.  
  13371.    You'll recall that one file can contain multiple drivers. In this case,
  13372.    the device-header link field of each driver should point to the segment
  13373.    offset of the next, all using the same segment base, and the link field
  13374.    for the last driver in the file should be set to -1,-1. The initialization
  13375.    routines for all the drivers in the file should return the same break
  13376.    address.
  13377.  
  13378.  Linking
  13379.  
  13380.    Use the standard MS-DOS linker to transform the .OBJ file that is output
  13381.    from the assembler into a relocatable .EXE module. Then, use the EXE2BIN
  13382.    utility (see Chapter 4) to convert the .EXE file into a memory-image
  13383.    program. The extension on the final driver file can be anything, but .BIN
  13384.    and .SYS are most commonly used in MS-DOS systems, and it is therefore
  13385.    wise to follow one of these conventions.
  13386.  
  13387.  Installation
  13388.  
  13389.    After the driver is assembled, linked, and converted to a .BIN or .SYS
  13390.    file, copy it to the root directory of a bootable disk. If it is a
  13391.    character-device driver, do not use the same name for the file as you used
  13392.    for the logical device listed in the driver's header, or you will not be
  13393.    able to delete, copy, or rename the file after the driver is loaded.
  13394.  
  13395.    Use your favorite text editor to add the line
  13396.  
  13397.      DEVICE=[D:][PATH]FILENAME.EXT
  13398.  
  13399.    to the CONFIG.SYS file on the bootable disk. (In this line, D: is an
  13400.    optional drive designator and FILENAME.EXT is the name of the file
  13401.    containing your new device driver. You can include a path specification in
  13402.    the entry if you prefer not to put the driver file in your root
  13403.    directory.) Now restart your computer system to load the modified
  13404.    CONFIG.SYS file.
  13405.  
  13406.    During the MS-DOS boot sequence, the SYSINIT module (which is part of
  13407.    IO.SYS) reads and processes the CONFIG.SYS file. It loads the driver into
  13408.    memory and inspects the device header. If the driver is a character-device
  13409.    driver, SYSINIT links it into the device chain ahead of the other
  13410.    character devices; if it is a block-device driver, SYSINIT places it
  13411.    behind all previously linked block devices and the resident block devices
  13412.    (Figures 14-10, 14-11, and 14-12). It accomplishes the linkage by
  13413.    updating the link field in the device header to point to the segment and
  13414.    offset of the next driver in the chain. The link field of the last driver
  13415.    in the chain contains -1,-1.
  13416.  
  13417.    Next, SYSINIT calls the strat routine with a request header that contains
  13418.    a command code of zero, and then it calls the intr routine. The driver
  13419.    executes its initialization routine and returns the break address, telling
  13420.    MS-DOS how much memory to reserve for this driver. Now MS-DOS can proceed
  13421.    to the next entry in the CONFIG.SYS file.
  13422.  
  13423.    You cannot supersede a built-in block-device driver──you can only add
  13424.    supplemental block devices. However, you can override the default system
  13425.    driver for a character device (such as CON) with an installed driver by
  13426.    giving it the same logical-device name in the device header. When
  13427.    processing a character I/O request, MS-DOS always scans the list of
  13428.    installed drivers before it scans the list of default devices and takes
  13429.    the first match.
  13430.  
  13431.               NUL
  13432.                │
  13433.                
  13434.               CON
  13435.                │
  13436.                
  13437.               AUX
  13438.                │
  13439.                
  13440.               PRN
  13441.                │
  13442.                
  13443.              CLOCK
  13444.                │
  13445.                
  13446.    Any other resident block
  13447.      or character devices
  13448.  
  13449.    Figure 14-10.  MS-DOS device-driver chain before any installable device
  13450.    drivers have been loaded.
  13451.  
  13452.               NUL
  13453.                │
  13454.                
  13455.     Installable character-
  13456.         device drivers
  13457.                │
  13458.                
  13459.               CON
  13460.                │
  13461.                
  13462.               AUX
  13463.                │
  13464.                
  13465.               PRN
  13466.                │
  13467.                
  13468.              CLOCK
  13469.                │
  13470.                
  13471.    Any other resident block
  13472.      or character devices
  13473.                │
  13474.                
  13475.       Installable block-
  13476.         device drivers
  13477.  
  13478.    Figure 14-11.  MS-DOS device-driver chain after installable device drivers
  13479.    have been loaded.
  13480.  
  13481.    Address          Attribute  Strategy    Interrupt  Type  Units Name
  13482.                                routine     routine
  13483.    ──────────────────────────────────────────────────────────────────────────
  13484.    00E3:0111        8004       0FD5        0FE0       C           NUL
  13485.    0070:0148        8013       008E        0099       C           CON
  13486.    0070:01DD        8000       008E        009F       C           AUX
  13487.    0070:028E        8000       008E        00AE       C           PRN
  13488.    0070:0300        8008       008E        00C3       C           CLOCK
  13489.    0070:03CC        0000       008E        00C9       B     02
  13490.    0070:01EF        8000       008E        009F       C           COM1
  13491.    0070:02A0        8000       008E        00AE       C           LPT1
  13492.    0070:06F0        8000       008E        00B4       C           LPT2
  13493.    0070:0702        8000       008E        00BA       C           LPT3
  13494.    0070:0714        8000       008E        00A5       C           COM2
  13495.    End of
  13496.    device chain
  13497.    ──────────────────────────────────────────────────────────────────────────
  13498.  
  13499.    Figure 14-12.  Example listing of device chain under MS-DOS version 2.1,
  13500.    "plain vanilla" IBM PC with no fixed disks or user device drivers.
  13501.    (C=character device, B=block device)
  13502.  
  13503.  
  13504.  Debugging a Device Driver
  13505.  
  13506.    The most important thing to remember when testing new device drivers is to
  13507.    maintain adequate backups and a viable fallback position. Don't modify the
  13508.    CONFIG.SYS file and install the new driver on your fixed disk before it is
  13509.    proven! Be prudent──create a bootable floppy disk and put the modified
  13510.    CONFIG.SYS file and the new driver on that for debugging. When everything
  13511.    is working properly, copy the finished product to its permanent storage
  13512.    medium.
  13513.  
  13514.    The easiest way to test a new device driver is to write a simple
  13515.    assembly-language front-end routine that sets up a simulated request
  13516.    packet and then performs FAR CALLs to the strat and intr entry points,
  13517.    exactly as MS-DOS would. You can then link the driver and the front end
  13518.    together into a .COM or .EXE file that can be run under the control of
  13519.    CodeView or another debugger. This arrangement makes it easy to trace each
  13520.    of the command-code routines individually, to observe the results of the
  13521.    I/O, and to examine the status codes returned in the request header.
  13522.  
  13523.    Tracing the installed driver when it is linked into the MS-DOS system in
  13524.    the normal manner is more difficult. Breakpoints must be chosen carefully,
  13525.    to yield the maximum possible information per debugging run. Because
  13526.    current versions of MS-DOS maintain only one request header internally,
  13527.    the request header that was being used by the driver you are tracing will
  13528.    be overwritten as soon as your debugger makes an output request to display
  13529.    information. You will find it helpful to add a routine to your
  13530.    initialization subroutine that displays the driver's load address on the
  13531.    console when you boot MS-DOS; you can then use this address to inspect the
  13532.    device-driver header and set breakpoints within the body of the driver.
  13533.  
  13534.    Debugging a device driver can also be somewhat sticky when interrupt
  13535.    handling is involved, especially if the device uses the same
  13536.    interrupt-request priority level (IRQ level) as other peripherals in the
  13537.    system. Cautious, conservative programming is needed to avoid unexpected
  13538.    and unreproducible interactions with other device drivers and interrupt
  13539.    handlers. If possible, prove out the basic logic of the driver using
  13540.    polled I/O, rather than interrupt-driven I/O, and introduce interrupt
  13541.    handling only when you know the rest of the driver's logic to be solid.
  13542.  
  13543.    Typical device-driver errors or problems that can cause system crashes or
  13544.    strange system behavior include the following:
  13545.  
  13546.    ■  Failure to set the linkage address of the last driver in a file to -1
  13547.  
  13548.    ■  Overflow of the MS-DOS stack by driver-initialization code, corrupting
  13549.       the memory image of MS-DOS (can lead to unpredictable behavior during
  13550.       boot; remedy is to use a local stack)
  13551.  
  13552.    ■  Incorrect break-address reporting by the initialization routine (can
  13553.       lead to a system crash if the next driver loaded overwrites vital parts
  13554.       of the driver)
  13555.  
  13556.    ■  Improper BPBs supplied by the build BPB routine, or incorrect BPB
  13557.       pointer array supplied by the initialization routine (can lead to many
  13558.       confusing problems, ranging from out-of-memory errors to system boot
  13559.       failure)
  13560.  
  13561.    ■  Incorrect reporting of the number of bytes or sectors successfully
  13562.       transferred at the time an I/O error occurs (can manifest itself as a
  13563.       system crash after you enter R to the Abort, Retry, Ignore? prompt)
  13564.  
  13565.    Although the interface between the DOS kernel and the device driver is
  13566.    fairly simple, it is also quite strict. The command-code routines must
  13567.    perform exactly as they are defined, or the system will behave
  13568.    erratically. Even a very subtle discrepancy in the action of a
  13569.    command-code routine can have unexpectedly large global effects.
  13570.  
  13571.  
  13572.  
  13573.  ────────────────────────────────────────────────────────────────────────────
  13574.  Chapter 15  Filters
  13575.  
  13576.    A filter is, essentially, a program that operates on a stream of
  13577.    characters. The source and destination of the character stream can be
  13578.    files, another program, or almost any character device. The transformation
  13579.    applied by the filter to the character stream can range from an operation
  13580.    as simple as character substitution to one as elaborate as generating
  13581.    splines from sets of coordinates.
  13582.  
  13583.    The standard MS-DOS package includes three simple filters: SORT, which
  13584.    alphabetically sorts text on a line-by-line basis; FIND, which searches a
  13585.    text stream to match a specified string; and MORE, which displays text one
  13586.    screenful at a time.
  13587.  
  13588.  
  13589.  System Support for Filters
  13590.  
  13591.    The operation of a filter program relies on two MS-DOS features that first
  13592.    appeared in version 2.0: standard devices and redirectable I/O.
  13593.  
  13594.    The standard devices are represented by five handles that are originally
  13595.    established by COMMAND.COM. Each process inherits these handles from its
  13596.    immediate parent. Thus, the standard device handles are already open when
  13597.    a process acquires control of the system, and it can use them with
  13598.    Interrupt 21H Functions 3FH and 40H for read and write operations
  13599.    without further preliminaries. The default assignments of the standard
  13600.    device handles are as follows:
  13601.  
  13602.    Handle             Name                                 Default device
  13603.    ──────────────────────────────────────────────────────────────────────────
  13604.    0                  stdin (standard input)               CON
  13605.    1                  stdout (standard output)             CON
  13606.    2                  stderr (standard error)              CON
  13607.    3                  stdaux (standard auxiliary)          AUX
  13608.    4                  stdprn (standard printer)            PRN
  13609.    ──────────────────────────────────────────────────────────────────────────
  13610.  
  13611.    The CON device is assigned by default to the system's keyboard and video
  13612.    display. AUX and PRN are respectively associated by default with COM1 (the
  13613.    first physical serial port) and LPT1 (the first parallel printer port).
  13614.    You can use the MODE command to redirect LPT1 to one of the serial ports;
  13615.    the MODE command will also redirect PRN.
  13616.  
  13617.    When executing a program by entering its name at the COMMAND.COM prompt,
  13618.    you can redirect the standard input, the standard output, or both from
  13619.    their default device (CON) to another file, a character device, or a
  13620.    process. You do this by including one of the special characters <, >, >>,
  13621.    and | in the command line, in the form shown on the following page.
  13622.  
  13623.    Symbol             Effect
  13624.    ──────────────────────────────────────────────────────────────────────────
  13625.    < file             Takes standard input from the specified file instead of
  13626.                       the keyboard.
  13627.  
  13628.    < device           Takes standard input from the named device instead of
  13629.                       the keyboard.
  13630.  
  13631.    > file             Sends standard output to the specified file instead of
  13632.                       the display.
  13633.  
  13634.    >> file            Appends standard output to the current contents of the
  13635.                       specified file instead of sending it to the display.
  13636.  
  13637.    > device           Sends standard output to the named device instead of
  13638.                       the display.
  13639.  
  13640.    p1 | p2            Routes standard output of program p1 to become the
  13641.                       standard input of program p2. (Output of p1 is said to
  13642.                       be piped to p2.)
  13643.    ──────────────────────────────────────────────────────────────────────────
  13644.  
  13645.    For example, the command
  13646.  
  13647.    C>SORT <MYFILE.TXT >PRN <Enter>
  13648.  
  13649.    causes the SORT filter to read its input from the file MYFILE.TXT, sort
  13650.    the lines alphabetically, and write the resulting text to the character
  13651.    device PRN (the logical name for the system's list device).
  13652.  
  13653.    The redirection requested by the <, >, >>, and | characters takes place at
  13654.    the level of COMMAND.COM and is invisible to the program it affects. Any
  13655.    other process can achieve a similar effect by redirecting the standard
  13656.    input and standard output with Int 21H Function 46H before calling the
  13657.    EXEC function (Int 21H Function 4BH) to run a child process.
  13658.  
  13659.    Note that if a program circumvents MS-DOS to perform its input and output,
  13660.    either by calling ROM BIOS functions or by manipulating the keyboard or
  13661.    video controller directly, redirection commands placed in the program's
  13662.    command line do not have the expected effect.
  13663.  
  13664.  
  13665.  How Filters Work
  13666.  
  13667.    By convention, a filter program reads its text from the standard input
  13668.    device and writes the results of its operations to the standard output
  13669.    device. When it reaches the end of the input stream, the filter simply
  13670.    terminates. As a result, filters are both flexible and simple.
  13671.  
  13672.    Filter programs are flexible because they do not know, and do not care
  13673.    about, the source of the data they process or the destination of their
  13674.    output. Thus, any character device that has a logical name within the
  13675.    system (CON, AUX, COM1, COM2, PRN, LPT1, LPT2, LPT3, and so on), any file
  13676.    on any block device (local or network) known to the system, or any other
  13677.    program can supply a filter's input or accept its output. If necessary,
  13678.    you can concatenate several functionally simple filters with pipes to
  13679.    perform very complex operations.
  13680.  
  13681.    Although flexible, filters are also simple because they rely on their
  13682.    parent processes to supply standard input and standard output handles that
  13683.    have already been appropriately redirected. The parent must open or create
  13684.    any necessary files, check the validity of logical character-device names,
  13685.    and load and execute the preceding or following process in a pipe. The
  13686.    filter concerns itself only with the transformation it applies to the
  13687.    data.
  13688.  
  13689.  
  13690.  Building a Filter
  13691.  
  13692.    Creating a new filter for MS-DOS is a straightforward process. In its
  13693.    simplest form, a filter need only use the handle-oriented read (Interrupt
  13694.    21H Function 3FH) and write (Interrupt 21H Function 40H) functions to
  13695.    get characters or lines from standard input and send them to standard
  13696.    output, performing any desired alterations on the text stream on a
  13697.    character-by-character or line-by-line basis.
  13698.  
  13699.    Figures 15-1 and 15-2 contain prototype character-oriented filters in
  13700.    both assembly language and C. In these examples, the translate routine,
  13701.    which is called for each character transferred from the standard input to
  13702.    the standard output, does nothing at all. As a result, both filters
  13703.    function rather like a very slow COPY command. You can quickly turn these
  13704.    primitive filters into useful programs by substituting your own translate
  13705.    routine.
  13706.  
  13707.    If you try out these programs, you'll notice that the C prototype filter
  13708.    runs much faster than its MASM equivalent. This is because the C runtime
  13709.    library is performing hidden blocking and deblocking of the input and
  13710.    output stream, whereas the MASM filter is doing exactly what it appears to
  13711.    be doing: making two calls to MS-DOS for each character processed. You can
  13712.    easily restore the MASM filter's expected speed advantage by adapting it
  13713.    to read and write lines instead of single characters.
  13714.  
  13715.    ──────────────────────────────────────────────────────────────────────────
  13716.             name      proto
  13717.             page      55,132
  13718.             title     PROTO.ASM--prototype filter
  13719.    ;
  13720.    ; PROTO.ASM:  prototype character-oriented filter
  13721.    ;
  13722.    ; Copyright 1988 Ray Duncan
  13723.    ;
  13724.  
  13725.    stdin   equ     0               ; standard input handle
  13726.    stdout  equ     1               ; standard output handle
  13727.    stderr  equ     2               ; standard error handle
  13728.  
  13729.    cr      equ     0dh             ; ASCII carriage return
  13730.    lf      equ     0ah             ; ASCII linefeed
  13731.  
  13732.    _TEXT   segment word public 'CODE'
  13733.  
  13734.            assume  cs:_TEXT,ds:_DATA,ss:STACK
  13735.  
  13736.    main    proc    far             ; entry point from MS-DOS
  13737.  
  13738.            mov     ax,_DATA        ; set DS = our data segment
  13739.            mov     ds,ax
  13740.  
  13741.    main1:                          ; read char from stdin...
  13742.            mov     dx,offset char  ; DS:DX = buffer address
  13743.            mov     cx,1            ; CX = length to read
  13744.            mov     bx,stdin        ; BX = standard input handle
  13745.            mov     ah,3fh          ; function 3fh = read
  13746.            int     21h             ; transfer to MS-DOS
  13747.            jc      main3           ; if error, terminate
  13748.  
  13749.            cmp     ax,1            ; any character read?
  13750.            jne     main2           ; if end of file, terminate
  13751.  
  13752.            call    translate       ; translate character
  13753.  
  13754.                                    ; write char to stdout...
  13755.            mov     dx,offset char  ; DS:DX = buffer address
  13756.            mov     cx,1            ; CX = length to write
  13757.            mov     bx,stdout       ; BX = standard output handle
  13758.            mov     ah,40h          ; function 40h = write
  13759.            int     21h             ; transfer to MS-DOS
  13760.            jc      main3           ; if error, terminate
  13761.            cmp     ax,1            ; was character written?
  13762.            jne     main3           ; if disk full, terminate
  13763.  
  13764.            jmp     main1           ; get another character
  13765.  
  13766.    main2:                          ; end of file reached
  13767.            mov     ax,4c00h        ; function 4ch = terminate
  13768.                                    ; return code = 0
  13769.            int     21h             ; transfer to MS-DOS
  13770.  
  13771.    main3:                          ; error or disk full
  13772.            mov     ax,4c01h        ; function 4ch = terminate
  13773.                                    ; return code = 1
  13774.            int     21h             ; transfer to MS-DOS
  13775.  
  13776.    main    endp
  13777.  
  13778.    ;
  13779.    ; Perform any necessary translation on character
  13780.    ; from standard input stored in variable 'char'.
  13781.    ; This example simply leaves character unchanged.
  13782.    ;
  13783.    translate proc  near
  13784.  
  13785.            ret                     ; does nothing
  13786.  
  13787.    translate endp
  13788.  
  13789.    _TEXT   ends
  13790.  
  13791.  
  13792.    _DATA   segment word public 'DATA'
  13793.  
  13794.    char    db      0               ; storage for input character
  13795.  
  13796.    _DATA   ends
  13797.  
  13798.  
  13799.    STACK   segment para stack 'STACK'
  13800.  
  13801.            dw      64 dup (?)
  13802.  
  13803.    STACK   ends
  13804.  
  13805.            end     main            ; defines program entry point
  13806.    ──────────────────────────────────────────────────────────────────────────
  13807.  
  13808.    Figure 15-1.  PROTO.ASM, the source code for a prototype
  13809.    character-oriented MASM filter.
  13810.  
  13811.    ──────────────────────────────────────────────────────────────────────────
  13812.    /*
  13813.        PROTO.C:  prototype character-oriented filter
  13814.  
  13815.        Copyright 1988 Ray Duncan
  13816.    */
  13817.  
  13818.    #include <stdio.h>
  13819.  
  13820.    main(int argc, char *argv[])
  13821.    {
  13822.        char ch;
  13823.  
  13824.        while((ch=getchar()) != EOF)    /* read a character            */
  13825.        {
  13826.            ch = translate(ch);         /* translate it if necessary   */
  13827.  
  13828.            putchar(ch);                /* write the character         */
  13829.        }
  13830.        exit(0);                        /* terminate at end of file    */
  13831.    }
  13832.  
  13833.  
  13834.    /*
  13835.        Perform any necessary translation on character
  13836.        from input file. This example simply returns
  13837.        the same character.
  13838.    */
  13839.  
  13840.    int translate(char ch)
  13841.    {
  13842.        return (ch);
  13843.    }
  13844.    ──────────────────────────────────────────────────────────────────────────
  13845.  
  13846.    Figure 15-2.  PROTO.C, the source code for a prototype character-oriented
  13847.    C filter.
  13848.  
  13849.  
  13850.  The CLEAN Filter
  13851.  
  13852.    As a more practical example of MS-DOS filters, let's look at a simple but
  13853.    very useful filter called CLEAN. Figures 15-3 and 15-4 show the
  13854.    assembly-language and C source code for this filter. CLEAN processes a
  13855.    text stream by stripping the high bit from all characters, expanding tabs
  13856.    to spaces, and throwing away all control codes except carriage returns,
  13857.    linefeeds, and formfeeds. Consequently, CLEAN can transform almost any
  13858.    kind of word-processed document file into a plain ASCII text file.
  13859.  
  13860.    ──────────────────────────────────────────────────────────────────────────
  13861.            name    clean
  13862.            page    55,132
  13863.            title   CLEAN--Text-file filter
  13864.    ;
  13865.    ; CLEAN.ASM     Filter to turn document files into
  13866.    ;               normal text files.
  13867.    ;
  13868.    ; Copyright 1988 Ray Duncan
  13869.    ;
  13870.    ; Build:        C>MASM CLEAN;
  13871.    ;               C>LINK CLEAN;
  13872.    ;
  13873.    ; Usage:        C>CLEAN  <infile  >outfile
  13874.    ;
  13875.    ; All text characters are passed through with high
  13876.    ; bit stripped off. Formfeeds, carriage returns,
  13877.    ; and linefeeds are passed through. Tabs are expanded
  13878.    ; to spaces. All other control codes are discarded.
  13879.    ;
  13880.  
  13881.    tab     equ     09h             ; ASCII tab code
  13882.    lf      equ     0ah             ; ASCII linefeed
  13883.    ff      equ     0ch             ; ASCII formfeed
  13884.    cr      equ     0dh             ; ASCII carriage return
  13885.    blank   equ     020h            ; ASCII space code
  13886.    eof     equ     01ah            ; Ctrl-Z end-of-file
  13887.  
  13888.    tabsiz  equ     8               ; width of tab stop
  13889.  
  13890.    bufsiz  equ     128             ; size of input and
  13891.                                    ; output buffers
  13892.  
  13893.    stdin   equ     0000            ; standard input handle
  13894.    stdout  equ     0001            ; standard output handle
  13895.    stderr  equ     0002            ; standard error handle
  13896.  
  13897.  
  13898.    _TEXT   segment word public 'CODE'
  13899.  
  13900.            assume  cs:_TEXT,ds:_DATA,es:_DATA,ss:STACK
  13901.  
  13902.    clean   proc    far             ; entry point from MS-DOS
  13903.  
  13904.            push    ds              ; save DS:0000 for final
  13905.            xor     ax,ax           ; return to MS-DOS, in case
  13906.            push    ax              ; function 4ch can't be used
  13907.            mov     ax,_DATA        ; make data segment addressable
  13908.            mov     ds,ax
  13909.            mov     es,ax
  13910.  
  13911.            mov     ah,30h          ; check version of MS-DOS
  13912.            int     21h
  13913.            cmp     al,2            ; MS-DOS 2.0 or later?
  13914.            jae     clean1          ; jump if version OK
  13915.  
  13916.                                    ; MS-DOS 1, display error
  13917.                                    ; message and exit...
  13918.            mov     dx,offset msg1  ; DS:DX = message address
  13919.            mov     ah,9            ; function 9 = display string
  13920.            int     21h             ; transfer to MS-DOS
  13921.            ret                     ; then exit the old way
  13922.  
  13923.    clean1: call    init            ; initialize input buffer
  13924.  
  13925.    clean2: call    getc            ; get character from input
  13926.            jc      clean9          ; exit if end of stream
  13927.  
  13928.            and     al,07fh         ; strip off high bit
  13929.  
  13930.            cmp     al,blank        ; is it a control char?
  13931.            jae     clean4          ; no, write it
  13932.  
  13933.            cmp     al,eof          ; is it end of file?
  13934.            je      clean8          ; yes, write EOF and exit
  13935.  
  13936.            cmp     al,tab          ; is it a tab?
  13937.            je      clean6          ; yes, expand it to spaces
  13938.  
  13939.            cmp     al,cr           ; is it a carriage return?
  13940.            je      clean3          ; yes, go process it
  13941.  
  13942.            cmp     al,lf           ; is it a linefeed?
  13943.            je      clean3          ; yes, go process it
  13944.  
  13945.            cmp     al,ff           ; is it a formfeed?
  13946.            jne     clean2          ; no, discard it
  13947.  
  13948.    clean3: mov     column,0        ; if CR, LF, or FF,
  13949.            jmp     clean5          ; reset column to zero
  13950.  
  13951.    clean4: inc     column          ; if non-control character,
  13952.                                    ; increment column counter
  13953.    clean5: call    putc            ; write char to stdout
  13954.            jnc     clean2          ; if disk not full,
  13955.                                    ; get another character
  13956.  
  13957.                                    ; write failed...
  13958.            mov     dx,offset msg2  ; DS:DX = error message
  13959.            mov     cx,msg2_len     ; CX = message length
  13960.            mov     bx,stderr       ; BX = standard error handle
  13961.            mov     ah,40h          ; function 40h = write
  13962.            int     21h             ; transfer to MS-DOS
  13963.  
  13964.            mov     ax,4c01h        ; function 4ch = terminate
  13965.                                    ; return code = 1
  13966.            int     21h             ; transfer to MS-DOS
  13967.  
  13968.    clean6: mov     ax,column       ; tab code detected
  13969.            cwd                     ; tabsiz - (column MOD tabsiz)
  13970.            mov     cx,tabsiz       ; is number of spaces needed
  13971.            idiv    cx              ; to move to next tab stop
  13972.            sub     cx,dx
  13973.  
  13974.            add     column,cx       ; also update column counter
  13975.  
  13976.    clean7: push    cx              ; save spaces counter
  13977.  
  13978.            mov     al,blank        ; write an ASCII space
  13979.            call    putc
  13980.  
  13981.            pop     cx              ; restore spaces counter
  13982.            loop    clean7          ; loop until tab stop
  13983.  
  13984.            jmp     clean2          ; get another character
  13985.  
  13986.    clean8: call    putc            ; write EOF mark
  13987.  
  13988.    clean9: call    flush           ; write last output buffer
  13989.            mov     ax,4c00h        ; function 4ch = terminate
  13990.                                    ; return code = 0
  13991.            int     21h             ; transfer to MS-DOS
  13992.  
  13993.    clean   endp
  13994.  
  13995.  
  13996.    getc    proc    near            ; get character from stdin
  13997.                                    ; returns carry = 1 if
  13998.                                    ; end of input, else
  13999.                                    ; AL = char, carry = 0
  14000.            mov     bx,iptr         ; get input buffer pointer
  14001.            cmp     bx,ilen         ; end of buffer reached?
  14002.            jne     getc1           ; not yet, jump
  14003.  
  14004.                                    ; more data is needed...
  14005.            mov     bx,stdin        ; BX = standard input handle
  14006.            mov     cx,bufsiz       ; CX = length to read
  14007.            mov     dx,offset ibuff ; DS:DX = buffer address
  14008.            mov     ah,3fh          ; function 3fh = read
  14009.            int     21h             ; transfer to MS-DOS
  14010.            jc      getc2           ; jump if read failed
  14011.  
  14012.            or      ax,ax           ; was anything read?
  14013.            jz      getc2           ; jump if end of input
  14014.  
  14015.            mov     ilen,ax         ; save length of data
  14016.            xor     bx,bx           ; reset buffer pointer
  14017.  
  14018.    getc1:  mov     al,[ibuff+bx]   ; get character from buffer
  14019.            inc     bx              ; bump buffer pointer
  14020.  
  14021.            mov     iptr,bx         ; save updated pointer
  14022.            clc                     ; return character in AL
  14023.            ret                     ; and carry = 0 (clear)
  14024.  
  14025.    getc2:  stc                     ; end of input stream
  14026.            ret                     ; return carry = 1 (set)
  14027.  
  14028.    getc    endp
  14029.  
  14030.  
  14031.    putc    proc    near            ; send character to stdout,
  14032.                                    ; returns carry = 1 if
  14033.                                    ; error, else carry = 0
  14034.  
  14035.            mov     bx,optr         ; store character into
  14036.            mov     [obuff+bx],al   ; output buffer
  14037.  
  14038.            inc     bx              ; bump buffer pointer
  14039.            cmp     bx,bufsiz       ; buffer full?
  14040.            jne     putc1           ; no, jump
  14041.  
  14042.  
  14043.            mov     bx,stdout       ; BX = standard output handle
  14044.            mov     cx,bufsiz       ; CX = length to write
  14045.            mov     dx,offset obuff ; DS:DX = buffer address
  14046.            mov     ah,40h          ; function 40h = write
  14047.            int     21h             ; transfer to MS-DOS
  14048.            jc      putc2           ; jump if write failed
  14049.  
  14050.            cmp     ax,cx           ; was write complete?
  14051.            jne     putc2           ; jump if disk full
  14052.  
  14053.            xor     bx,bx           ; reset buffer pointer
  14054.  
  14055.    putc1:  mov     optr,bx         ; save buffer pointer
  14056.            clc                     ; write successful,
  14057.            ret                     ; return carry = 0 (clear)
  14058.  
  14059.    putc2:  stc                     ; write failed or disk full,
  14060.            ret                     ; return carry = 1 (set)
  14061.  
  14062.    putc    endp
  14063.  
  14064.  
  14065.    init    proc    near            ; initialize input buffer
  14066.  
  14067.            mov     bx,stdin        ; BX = standard input handle
  14068.            mov     cx,bufsiz       ; CX = length to read
  14069.            mov     dx,offset ibuff ; DS:DX = buffer address
  14070.            mov     ah,3fh          ; function 3fh = read
  14071.            int     21h             ; transfer to MS-DOS
  14072.            jc      init1           ; jump if read failed
  14073.            mov     ilen,ax         ; save actual bytes read
  14074.    init1:  ret
  14075.  
  14076.    init    endp
  14077.  
  14078.  
  14079.    flush   proc    near            ; flush output buffer
  14080.  
  14081.            mov     cx,optr         ; CX = bytes to write
  14082.            jcxz    flush1          ; exit if buffer empty
  14083.            mov     dx,offset obuff ; DS:DX = buffer address
  14084.            mov     bx,stdout       ; BX = standard output handle
  14085.            mov     ah,40h          ; function 40h = write
  14086.            int     21h             ; transfer to MS-DOS
  14087.    flush1: ret
  14088.  
  14089.    flush   endp
  14090.  
  14091.    _TEXT   ends
  14092.    _DATA   segment word public 'DATA'
  14093.  
  14094.    ibuff   db      bufsiz dup (0)  ; input buffer
  14095.    obuff   db      bufsiz dup (0)  ; output buffer
  14096.  
  14097.    iptr    dw      0               ; ibuff pointer
  14098.    ilen    dw      0               ; bytes in ibuff
  14099.    optr    dw      0               ; obuff pointer
  14100.  
  14101.    column  dw      0               ; current column counter
  14102.  
  14103.    msg1    db      cr,lf
  14104.            db      'clean: need MS-DOS version 2 or greater.'
  14105.            db      cr,lf,'$'
  14106.  
  14107.    msg2    db      cr,lf
  14108.            db      'clean: disk is full.'
  14109.            db      cr,lf
  14110.    msg2_len equ    $-msg2
  14111.  
  14112.    _DATA   ends
  14113.  
  14114.  
  14115.    STACK   segment para stack 'STACK'
  14116.  
  14117.            dw      64 dup (?)
  14118.  
  14119.    STACK   ends
  14120.  
  14121.            end     clean
  14122.    ──────────────────────────────────────────────────────────────────────────
  14123.  
  14124.    Figure 15-3.  CLEAN.ASM, the source code for the MASM version of the CLEAN
  14125.    filter.
  14126.  
  14127.    ──────────────────────────────────────────────────────────────────────────
  14128.    /*
  14129.        CLEAN.C     Filter to turn document files into
  14130.                    normal text files.
  14131.  
  14132.        Copyright 1988 Ray Duncan
  14133.  
  14134.        Compile:    C>CL CLEAN.C
  14135.  
  14136.        Usage:      C>CLEAN  <infile >outfile
  14137.  
  14138.        All text characters are passed through with high bit stripped
  14139.        off. Formfeeds, carriage returns, and linefeeds are passed
  14140.        through. Tabs are expanded to spaces. All other control codes
  14141.        are discarded.
  14142.    */
  14143.  
  14144.    #include <stdio.h>
  14145.  
  14146.    #define TAB_WIDTH   8              /* width of a tab stop     */
  14147.    #define TAB     '\x09'             /* ASCII tab character     */
  14148.    #define LF      '\x0A'             /* ASCII linefeed          */
  14149.    #define FF      '\x0C'             /* ASCII formfeed          */
  14150.    #define CR      '\x0D'             /* ASCII carriage return   */
  14151.    #define BLANK   '\x20'             /* ASCII space code        */
  14152.    #define EOFMK   '\x1A'             /* Ctrl-Z end of file      */
  14153.  
  14154.  
  14155.    main(int argc, char *argv[])
  14156.    {
  14157.        char c;                        /* character from stdin    */
  14158.        int col = 0;                   /* column counter          */
  14159.  
  14160.        while((c = getchar()) != EOF)  /* read input character    */
  14161.        {
  14162.            c &= 0x07F;                /* strip high bit          */
  14163.  
  14164.            switch(c)                  /* decode character        */
  14165.            {
  14166.                case LF:               /* if linefeed or          */
  14167.                case CR:               /* carriage return,        */
  14168.                    col=0;             /* reset column count      */
  14169.  
  14170.                case FF:               /* if formfeed, carriage   */
  14171.                    wchar(c);          /* return, or linefeed,    */
  14172.                    break;             /* pass character through  */
  14173.  
  14174.                case TAB:              /* if tab, expand to spaces*/
  14175.                    do wchar(BLANK);
  14176.                    while((++col % TAB_WIDTH) != 0);
  14177.                    break;
  14178.  
  14179.                default:               /* discard other control   */
  14180.                    if(c >= BLANK)     /* characters, pass text   */
  14181.                    {                  /* characters through      */
  14182.                        wchar(c);
  14183.                        col++;         /* bump column counter     */
  14184.                    }
  14185.                    break;
  14186.            }
  14187.        }
  14188.        wchar(EOFMK);                  /* write end-of-file mark  */
  14189.        exit(0);
  14190.    }
  14191.  
  14192.  
  14193.    /*
  14194.        Write a character to the standard output. If
  14195.        write fails, display error message and terminate.
  14196.    */
  14197.  
  14198.    wchar(char c)
  14199.    {
  14200.        if((putchar(c) == EOF) && (c != EOFMK))
  14201.        {
  14202.            fputs("clean: disk full",stderr);
  14203.            exit(1);
  14204.        }
  14205.    }
  14206.    ──────────────────────────────────────────────────────────────────────────
  14207.  
  14208.    Figure 15-4.  CLEAN.C, the source code for the C version of the CLEAN
  14209.    filter.
  14210.  
  14211.    When using the CLEAN filter, you must specify the source and destination
  14212.    files with redirection parameters in the command line; otherwise, CLEAN
  14213.    will simply read the keyboard and write to the display. For example, to
  14214.    filter the document file MYFILE.DOC and leave the result in the file
  14215.    MYFILE.TXT, you would enter the following command:
  14216.  
  14217.    C>CLEAN <MYFILE.DOC >MYFILE.TXT  <Enter>
  14218.  
  14219.    (Note that the original file, MYFILE.DOC, is unchanged.)
  14220.  
  14221.    One valuable application of this filter is to rescue assembly-language
  14222.    source files. If you accidentally edit such a source file in document
  14223.    mode, the resulting file may cause the assembler to generate spurious or
  14224.    confusing error messages. CLEAN lets you turn the source file back into
  14225.    something the assembler can cope with, without losing the time you spent
  14226.    to edit it.
  14227.  
  14228.    Another handy application for CLEAN is to list a word-processed document
  14229.    in raw form on the printer, using a command such as
  14230.  
  14231.    C>CLEAN <MYFILE.DOC >PRN  <Enter>
  14232.  
  14233.    Contrasting the C and assembly-language versions of this filter provides
  14234.    some interesting statistics. The C version contains 79 lines and compiles
  14235.    to a 5889-byte .EXE file, whereas the assembly-language version contains
  14236.    265 lines and builds an 1107-byte .EXE file. The size and execution-speed
  14237.    advantages of implementing such tools in assembly language is obvious,
  14238.    even compared with such an excellent compiler as the Microsoft C
  14239.    Optimizing Compiler. However, you must balance performance considerations
  14240.    against the time and expense required for programming, particularly when a
  14241.    program will not be used very often.
  14242.  
  14243.  
  14244.  
  14245.  ────────────────────────────────────────────────────────────────────────────
  14246.  Chapter 16  Compatibility and Portability
  14247.  
  14248.    At the beginning of this book, we surveyed the history of MS-DOS and saw
  14249.    that new versions come along nearly every year, loosely coupled to the
  14250.    introduction of new models of personal computers. We then focused on each
  14251.    of the mainstream issues of MS-DOS applications programming: the user
  14252.    interface; mass storage; memory management; control of "child" processes;
  14253.    and special classes of programs, such as filters, interrupt handlers, and
  14254.    device drivers.
  14255.  
  14256.    It's now time to close the circle and consider two global concerns of
  14257.    MS-DOS programming: compatibility and portability. For your programs to
  14258.    remain useful in a constantly evolving software and hardware environment,
  14259.    you must design them so that they perform reliably on any reasonable
  14260.    machine configuration and exploit available system resources; in addition,
  14261.    you should be able to upgrade them easily for new versions of MS-DOS, for
  14262.    new machines, and, for that matter, for completely new environments such
  14263.    as MS OS/2.
  14264.  
  14265.  
  14266.  Degrees of Compatibility
  14267.  
  14268.    If we look at how existing MS-DOS applications use the operating system
  14269.    and hardware, we find that we can assign them to one of four categories:
  14270.  
  14271.    ■  MS-DOS─compatible applications
  14272.  
  14273.    ■  ROM BIOS─compatible applications
  14274.  
  14275.    ■  Hardware-compatible applications
  14276.  
  14277.    ■  "Ill-behaved" applications
  14278.  
  14279.    MS-DOS─compatible applications use only the documented MS-DOS function
  14280.    calls and do not call the ROM BIOS or access the hardware directly. They
  14281.    use ANSI escape sequences for screen control, and their input and output
  14282.    is redirectable. An MS-DOS─compatible application will run on any machine
  14283.    that supports MS-DOS, regardless of the machine configuration. Because of
  14284.    the relatively poor performance of MS-DOS's built-in display and serial
  14285.    port drivers, few popular programs other than compilers, assemblers, and
  14286.    linkers fall into this category.
  14287.  
  14288.    ROM BIOS─compatible applications use the documented MS-DOS and ROM BIOS
  14289.    function calls but do not access the hardware directly. As recently as
  14290.    three years ago, this strategy might have significantly limited a
  14291.    program's potential market. Today, the availability of high-quality
  14292.    IBM-compatible ROM BIOSes from companies such as Phoenix has ensured the
  14293.    dominance of the IBM ROM BIOS standard; virtually no machines are being
  14294.    sold in which a program cannot rely as much on the ROM BIOS interface as
  14295.    it might on the MS-DOS interface. However, as we noted in Chapters 6 and
  14296.    7, the ROM BIOS display and serial drivers are still not adequate to the
  14297.    needs of high-performance interactive applications, so the popular
  14298.    programs that fall into this category are few.
  14299.  
  14300.    Hardware-compatible applications generally use MS-DOS functions for mass
  14301.    storage, memory management, and the like, and use a mix of MS-DOS and ROM
  14302.    BIOS function calls and direct hardware access for their user interfaces.
  14303.    The amount of hardware dependence in such programs varies widely. For
  14304.    example, some programs only write characters and attributes into the video
  14305.    controller's regen buffer and use the ROM BIOS to switch modes and
  14306.    position the cursor; others bypass the ROM BIOS video driver altogether
  14307.    and take complete control of the video adapter. As this book is written,
  14308.    the vast majority of the popular MS-DOS "productivity" applications (word
  14309.    processors, databases, telecommunications programs, and so on) can be
  14310.    placed somewhere in this category.
  14311.  
  14312.    "Ill-behaved" applications are those that rely on undocumented MS-DOS
  14313.    function calls or data structures, interception of MS-DOS or ROM BIOS
  14314.    interrupts, or direct access to mass storage devices (bypassing the MS-DOS
  14315.    file system). These programs tend to be extremely sensitive to their
  14316.    environment and typically must be "adjusted" in order to work with each
  14317.    new MS-DOS version or PC model. Virtually all popular terminate-
  14318.    and-stay-resident (TSR) utilities, network programs, and disk
  14319.    repair/optimization packages are in this category.
  14320.  
  14321.  Writing Well-Behaved MS-DOS Applications
  14322.  
  14323.    Your choice of MS-DOS functions, ROM BIOS functions, or direct hardware
  14324.    access to solve a particular problem must always be balanced against
  14325.    performance needs; and, of course, the user is the final judge of a
  14326.    program's usefulness and reliability. Nevertheless, you can follow some
  14327.    basic guidelines, outlined below, to create well-behaved applications that
  14328.    are likely to run properly under future versions of MS-DOS and under
  14329.    multitasking program managers that run on top of MS-DOS, such as Microsoft
  14330.    Windows.
  14331.  
  14332.    Program structure
  14333.  
  14334.    Design your programs as .EXE files with separate code, data, and stack
  14335.    segments; shun the use of .COM files. Use the Microsoft conventions for
  14336.    segment names and attributes discussed in Chapter 3. Inspect the
  14337.    environment block at runtime to locate your program's overlays or data
  14338.    files; don't "hard-wire" a directory location into the program.
  14339.  
  14340.    Check host capabilities
  14341.  
  14342.    Obtain the MS-DOS version number with Int 21H Function 30H during your
  14343.    program's initialization and be sure that all of the functions your
  14344.    program requires are actually available. If you find that the host MS-DOS
  14345.    version is inadequate, be careful about which functions you call to
  14346.    display an error message and to terminate.
  14347.  
  14348.    Use the enhanced capabilities of MS-DOS versions 3 and 4 when your program
  14349.    is running under those versions. For example, you can specify a sharing
  14350.    mode when opening a file with Int 21H Function 3DH, you can create
  14351.    temporary or unique files with Int 21H Functions 5AH and 5BH, and you
  14352.    can obtain extended error information (including a recommended recovery
  14353.    strategy) with Int 21H Function 59H. Section 2 of this book contains
  14354.    version-dependency information for each MS-DOS function.
  14355.  
  14356.    Input and output
  14357.  
  14358.    Use the handle file functions exclusively and extend full path support
  14359.    throughout your application (being sure to allow for the maximum possible
  14360.    path length during user input of filenames). Use buffered I/O whenever
  14361.    possible. The device drivers in MS-DOS versions 2.0 and later can handle
  14362.    strings as long as 64 KB, and performance will be improved if you write
  14363.    fewer, larger records as opposed to many short ones.
  14364.  
  14365.    Avoid the use of FCBs, the Int 25H or Int 26H functions, or the ROM BIOS
  14366.    disk driver. If you must use FCBs, close them when you are done with them
  14367.    and don't move them around while they are open. Avoid reopening FCBs that
  14368.    are already open or reclosing FCBs that have already been closed──these
  14369.    seemingly harmless practices can cause problems when network software is
  14370.    running.
  14371.  
  14372.    Memory management
  14373.  
  14374.    During your program's initialization, release any memory that is not
  14375.    needed by the program. (This is especially important for .COM programs.)
  14376.    If your program requires extra memory for buffers or tables, allocate that
  14377.    memory dynamically when it is needed and release it as soon as it is no
  14378.    longer required. Use expanded memory, when it is available, to minimize
  14379.    your program's demands on conventional memory.
  14380.  
  14381.    As a general rule, don't touch any memory that is not owned by your
  14382.    program. To set or inspect interrupt vectors, use Int 21H Functions 25H
  14383.    and 35H rather than editing the interrupt vector table directly. If you
  14384.    alter the contents of interrupt vectors, save their original values and
  14385.    restore them before the program exits.
  14386.  
  14387.    Process management
  14388.  
  14389.    To isolate your program from dependencies on PSP structure and relocation
  14390.    information, use the EXEC function (Int 21H Function 4BH) when loading
  14391.    overlays or other programs. Terminate your program with Int 21H Function
  14392.    4CH, passing a zero return code if the program executes successfully and
  14393.    a nonzero code if an error is encountered. Your program's parent can then
  14394.    test this return code with Int 21H Function 4DH or, in a batch file, with
  14395.    the IF ERRORLEVEL statement.
  14396.  
  14397.    Exception handling
  14398.  
  14399.    Install Ctrl-C (Int 23H) and critical-error (Int 24H) handlers so that
  14400.    your program cannot be terminated unexpectedly by the user's entry of
  14401.    Ctrl-C or Ctrl-Break or by a hardware I/O failure. This is particularly
  14402.    important if your program uses expanded memory or installs its own
  14403.    interrupt handlers.
  14404.  
  14405.  ROM BIOS and Hardware-Compatible Applications
  14406.  
  14407.    When you feel the need to introduce ROM BIOS or hardware dependence for
  14408.    performance reasons, keep it isolated to small, well-documented procedures
  14409.    that can be easily modified when the hardware changes. Use macros and
  14410.    equates to hide hardware characteristics and to avoid spreading "magic
  14411.    numbers" throughout your program.
  14412.  
  14413.    Check host capabilities
  14414.  
  14415.    If you use ROM BIOS functions in your program, you must check the machine
  14416.    model at runtime to be sure that the functions your program needs are
  14417.    actually available. There is a machine ID byte at F000:FFFEH whose value
  14418.    is interpreted as follows:
  14419.  
  14420.    ──────────────────────────────────────────────────────────────────────────
  14421.    F8H                      PS/2 Models 70 and 80
  14422.  
  14423.    F9H                      PC Convertible
  14424.  
  14425.    FAH                      PS/2 Model 30
  14426.  
  14427.    FBH                      PC/XT (later models)
  14428.  
  14429.    FCH                      PC/AT, PC/XT-286, PS/2 Models 50 and 60
  14430.  
  14431.    FDH                      PCjr
  14432.  
  14433.    FEH                      PC/XT (early models)
  14434.  
  14435.    FFH                      PC "Classic"
  14436.    ──────────────────────────────────────────────────────────────────────────
  14437.  
  14438.    In some cases, submodels can be identified; see Int 15H Function C0H on
  14439.    page 573. Section 3 of this book contains version-dependency information
  14440.    for each ROM BIOS function.
  14441.  
  14442.    When writing your own direct video drivers, you must determine the type
  14443.    and capabilities of the video adapter by a combination of Int 10H calls,
  14444.    reading ports, and inspection of the ROM BIOS data area at 0040:0000H and
  14445.    the memory reserved for the EGA or VGA ROM BIOS, among other things. The
  14446.    techniques required are beyond the scope of this book but are well
  14447.    explained in Programmer's Guide to PC and PS/2 Video Systems (Microsoft
  14448.    Press, 1987).
  14449.  
  14450.    Avoid unstable hardware
  14451.  
  14452.    Some areas of IBM personal computer architecture have remained remarkably
  14453.    stable from the original IBM PC, based on a 4.77 MHz 8088, to today's PS/2
  14454.    Model 80, based on a 20 MHz 80386. IBM's track record for upward
  14455.    compatibility in its video and serial communications controllers has been
  14456.    excellent; in many cases, the same hardware-dependent code that was
  14457.    written for the original IBM PC runs perfectly well on an IBM PS/2 Model
  14458.    80. Other areas of relative hardware stability are:
  14459.  
  14460.    ■  Sound control via port 61H
  14461.  
  14462.    ■  The 8253 timer chip's channels 0 and 2 (ports 40H, 42H, and 43H)
  14463.  
  14464.    ■  The game adapter at port 201H
  14465.  
  14466.    ■  Control of the interrupt system via the 8259 PIC's mask register at
  14467.       port 21H
  14468.  
  14469.    However, direct sound generation and manipulation of the 8253 timer or
  14470.    8259 PIC are quite likely to cause problems if your program is run under a
  14471.    multitasking program manager such as Microsoft Windows or DesqView.
  14472.  
  14473.    Keyboard mapping, the keyboard controller, and the floppy and fixed disk
  14474.    controllers are areas of relative hardware instability. Programs that
  14475.    bypass MS-DOS for keyboard or disk access are much less likely to function
  14476.    properly across the different PC models and are also prone to interfere
  14477.    with each other and with well-behaved applications.
  14478.  
  14479.  
  14480.  OS/2 Compatibility
  14481.  
  14482.    MS-DOS is upwardly compatible in several respects with OS/2, Microsoft's
  14483.    multitasking protected-mode virtual memory operating system for 80286 and
  14484.    80386 computers. The OS/2 graphical user interface (the Presentation
  14485.    Manager) is nearly identical to Microsoft Windows 2.0. OS/2 versions 1.0
  14486.    and 1.1 use exactly the same disk formats as MS-DOS so that files may
  14487.    easily be moved between MS-DOS and OS/2 systems. Most important, OS/2
  14488.    includes a module called the "DOS Compatibility Environment" or "3.x Box,"
  14489.    which can run one MS-DOS application at a time alongside protected-mode
  14490.    OS/2 applications.
  14491.  
  14492.    The 3.x Box traps Int 21H function calls and remaps them into OS/2
  14493.    function calls, emulating an MS-DOS 3.3 environment with the file-sharing
  14494.    module (SHARE.EXE) loaded but returning a major version number of 10
  14495.    instead of 3 for Int 21H Function 30H. The 3.x Box also supports most ROM
  14496.    BIOS calls, either by emulating their function or by interlocking the
  14497.    device and then calling the original ROM BIOS routine. In addition, the
  14498.    3.x Box maintains the ROM BIOS data area, provides timer ticks to
  14499.    applications via Int 1CH, and supports certain undocumented MS-DOS
  14500.    services and data structures so that most TSR utilities can function
  14501.    properly. Nevertheless, the 3.x Box's emulation of MS-DOS is not perfect,
  14502.    and you must be aware of certain constraints on MS-DOS applications
  14503.    running under OS/2.
  14504.  
  14505.    The most significant restriction on an MS-DOS application is that it does
  14506.    not receive any CPU cycles when it is in the background. That is, when a
  14507.    protected-mode application has been "selected," so that the user can
  14508.    interact with it, the MS-DOS application is frozen. If the MS-DOS
  14509.    application has captured any interrupt vectors (such as the serial port or
  14510.    timer tick), these interrupts will not be serviced until the application
  14511.    is again selected and in the foreground. OS/2 must freeze MS-DOS
  14512.    applications when they are in the background because they execute in real
  14513.    mode and are thus not subject to hardware memory protection; nothing else
  14514.    ensures that they will not interfere with a protected-mode process that
  14515.    has control of the screen and keyboard.
  14516.  
  14517.    Use of FCBs is restricted in the 3.x Box, as it is under MS-DOS 3 or 4
  14518.    with SHARE.EXE loaded. A file cannot be opened with an FCB if any other
  14519.    process is using it. The number of FCBs that can be simultaneously opened
  14520.    is limited to 16 or to the number specified in a CONFIG.SYS FCBS=
  14521.    directive. Even when the handle file functions are used, these functions
  14522.    may fail unexpectedly due to the activity of other processes (for example,
  14523.    if a protected-mode process has already opened the file with "deny all"
  14524.    sharing mode); most MS-DOS applications are not written with file sharing
  14525.    in mind, and they do not handle such errors gracefully.
  14526.  
  14527.    Direct writes to a fixed disk using Int 26H or Int 13H are not allowed.
  14528.    This prevents the file system from being corrupted, because protected-mode
  14529.    applications running concurrently with the MS-DOS application may also be
  14530.    writing to the same disk. Imagine the mess if a typical MS-DOS unerase
  14531.    utility were to alter the root directory and FAT at the same time that a
  14532.    protected-mode database program was updating its file and indexes!
  14533.  
  14534.    MS-DOS applications that attempt to reprogram the 8259 to move the
  14535.    interrupt vector table or that modify interrupt vectors already belonging
  14536.    to an OS/2 device driver are terminated by the operating system. MS-DOS
  14537.    applications can change the 8259's interrupt-mask register, disable and
  14538.    reenable interrupts at their discretion, and read or write any I/O port.
  14539.    The obvious corollary is that an MS-DOS program running in the 3.x Box can
  14540.    crash the entire OS/2 system at any time; this is the price for allowing
  14541.    real-mode applications to run at all.
  14542.  
  14543.  Porting MS-DOS Applications to OS/2
  14544.  
  14545.    The application program interface (API) provided by OS/2 to protected-mode
  14546.    programs is quite different from the familiar Int 21H interface of MS-DOS
  14547.    and the OS/2 3.x Box. However, the OS/2 API is functionally a proper
  14548.    superset of MS-DOS. This makes it easy to convert well-behaved MS-DOS
  14549.    applications to run in OS/2 protected mode, whence they can be enhanced to
  14550.    take advantage of OS/2's virtual memory, multitasking, and interprocess
  14551.    communication capabilities.
  14552.  
  14553.    To give you a feeling for both the nature of the OS/2 API and the
  14554.    practices that should be avoided in MS-DOS programming if portability to
  14555.    OS/2 is desired, I will outline my own strategy for converting existing
  14556.    MS-DOS assembly-language programs to OS/2. For the purposes of discussion,
  14557.    I have divided the conversion process into five steps and have assigned
  14558.    each an easily remembered buzzword:
  14559.  
  14560.    1.  Segmentation
  14561.  
  14562.    2.  Rationalization
  14563.  
  14564.    3.  Encapsulation
  14565.  
  14566.    4.  Conversion
  14567.  
  14568.    5.  Optimization
  14569.  
  14570.    The first three stages can (and should) be performed and tested in the
  14571.    MS-DOS environment; only the last two require OS/2 and the protected-mode
  14572.    programming tools. As you read on, you may notice that an MS-DOS program
  14573.    that follows the compatibility guidelines presented earlier in this
  14574.    chapter requires relatively little work to make it run in protected mode.
  14575.    This is the natural benefit of working with the operating system instead
  14576.    of against it.
  14577.  
  14578.    Segmentation
  14579.  
  14580.    Most of the 80286's protected-mode capabilities revolve around a change in
  14581.    the way memory is addressed. In real mode, the 80286 essentially emulates
  14582.    an 8088/86 processor, and the value in a segment register corresponds
  14583.    directly to a physical memory address. MS-DOS runs on the 80286 in real
  14584.    mode.
  14585.  
  14586.    When an 80286 is running in protected mode, as it does under OS/2, an
  14587.    additional level of indirection is added to memory addressing. A segment
  14588.    register holds a selector, which is an index to a table of descriptors. A
  14589.    descriptor defines the physical address and length of a memory segment,
  14590.    its characteristics (executable, read-only data, or read/write data) and
  14591.    access rights, and whether the segment is currently resident in RAM or has
  14592.    been swapped out to disk. Each time a program loads a segment register or
  14593.    accesses memory, the 80286 hardware checks the associated descriptor and
  14594.    the program's privilege level, generating a fault if the selector or
  14595.    memory operation is not valid. The fault acts like a hardware interrupt,
  14596.    allowing the operating system to regain control and take the appropriate
  14597.    action.
  14598.  
  14599.    This scheme of memory addressing in protected mode has two immediate
  14600.    consequences for application programs. The first is that application
  14601.    programs can no longer perform arithmetic on the contents of segment
  14602.    registers (because selectors are magic numbers and have no direct
  14603.    relationship to physical memory addresses) or use segment registers for
  14604.    storage of temporary values. A program must not load a segment register
  14605.    with anything but a legitimate selector provided by the OS/2 loader or
  14606.    resulting from an OS/2 memory allocation function call. The second
  14607.    consequence is that a program must strictly segregate machine code
  14608.    ("text") from data, placing them in separate segments with distinct
  14609.    selectors (because a selector that is executable is not writable, and vice
  14610.    versa).
  14611.  
  14612.    Accordingly, the first step in converting a program for OS/2 is to turn it
  14613.    into a .EXE-type program that uses the Microsoft segment, class, and group
  14614.    conventions described in Chapter 3. At minimum, the program must have one
  14615.    code segment and one data segment, and should declare a group──with the
  14616.    special name DGROUP──that contains the "near" data segment, stack, and
  14617.    local heap (if any). At the same time, you should remove or rewrite any
  14618.    code that performs direct manipulation of segment values.
  14619.  
  14620.    After restructuring and segmentation, reassemble and link your program and
  14621.    check to be sure it still works as expected under MS-DOS. Changing or
  14622.    adding segmentation often uncovers hidden addressing assumptions in the
  14623.    code, so it is best to track these problems down before making other
  14624.    substantive changes to the program.
  14625.  
  14626.    Rationalization
  14627.  
  14628.    Once you've successfully segmented your program so that it can be linked
  14629.    and executed as a .EXE file under MS-DOS, the next step is to rationalize
  14630.    your code. By rationalization I mean converting your program into a
  14631.    completely well-behaved MS-DOS application.
  14632.  
  14633.    First, you must ruthlessly eliminate any elements that manipulate the
  14634.    peripheral device adapters directly, alter interrupt priorities, edit the
  14635.    system interrupt-vector table, or depend on CPU speed or characteristics
  14636.    (such as timing loops). In protected mode, control of the interrupt system
  14637.    is completely reserved to the operating system and its device drivers, I/O
  14638.    ports may be read or written by an application only under very specific
  14639.    conditions, and timing loops burn up CPU cycles that can be used by other
  14640.    processes.
  14641.  
  14642.    As I mentioned earlier in this chapter, display routines constitute the
  14643.    most common area of hardware dependence in an MS-DOS application. Direct
  14644.    manipulation of the video adapter and its regen buffer poses obvious
  14645.    difficulties in a multitasking, protected-memory environment such as OS/2.
  14646.    For porting purposes, you must convert all routines that write text to the
  14647.    display, modify character attributes, or affect cursor shape or position
  14648.    into Int 21H Function 40H calls using ANSI escape sequences or into ROM
  14649.    BIOS Int 10H calls. Similarly, you must convert all hardware-dependent
  14650.    keyboard operations to Int 21H Function 3FH or ROM BIOS Int 16H calls.
  14651.  
  14652.    Once all hardware dependence has been expunged from your program, your
  14653.    next priority is to make it well-behaved in its use of system memory.
  14654.    Under MS-DOS an application is typically handed all remaining memory in
  14655.    the system to do with as it will; under OS/2 the converse is true: A
  14656.    process is initially allocated only enough memory to hold its code,
  14657.    declared data storage, and stack. You can make the MS-DOS loader behave
  14658.    like the OS/2 loader by linking your application with the /CPARMAXALLOC
  14659.    switch. Alternatively, your program can give up all extra memory during
  14660.    its initialization with Int 21H Function 4AH, as recommended earlier in
  14661.    this chapter.
  14662.  
  14663.    After your program completes its initialization sequence, it should
  14664.    dynamically obtain and release any additional memory it may require for
  14665.    buffers and tables with MS-DOS Int 21H Functions 48H and 49H. To ensure
  14666.    compatibility with protected mode, limit the size of any single allocated
  14667.    block to 65,536 bytes or less, even though MS-DOS allows larger blocks to
  14668.    be allocated.
  14669.  
  14670.    Finally, you must turn your attention to file and device handling. Replace
  14671.    any calls to FCB file functions with their handle-based equivalents,
  14672.    because OS/2 does not support FCBs in protected mode at all. Check
  14673.    pathnames for validity within the application; although MS-DOS and the 3.x
  14674.    Box silently truncate a name or extension, OS/2 refuses to open or create
  14675.    a file in protected mode if the name or extension is too long and returns
  14676.    an error instead. Replace any use of the predefined handles for the
  14677.    standard auxiliary and standard list devices with explicit opens of COM1,
  14678.    PRN, LPT1, and so on, using the resulting handle for read and write
  14679.    operations. OS/2 does not supply processes with standard handles for the
  14680.    serial communications port or printer.
  14681.  
  14682.    Encapsulation
  14683.  
  14684.    When you reach this point, with a well-behaved, segmented MS-DOS
  14685.    application in hand, the worst of a port to OS/2 is behind you. You are
  14686.    now ready to prepare your program for true conversion to protected-mode
  14687.    operation by encapsulating, in individual subroutines, every part of the
  14688.    program that is specific to the host operating system. The objective here
  14689.    is to localize the program's "knowledge" of the environment into small
  14690.    procedures that can be subsequently modified without affecting the
  14691.    remainder of the program.
  14692.  
  14693.    As an example of encapsulation, consider a typical call by an MS-DOS
  14694.    application to write a string to the standard output device (Figure
  14695.    16-1). In order to facilitate conversion to OS/2, you would replace every
  14696.    instance of such a write to a file or device with a call to a small
  14697.    subroutine that "hides" the mechanics of the actual operating-system
  14698.    function call, as illustrated in Figure 16-2.
  14699.  
  14700.    Another candidate for encapsulation, which does not necessarily involve an
  14701.    operating-system function call, is the application's code to gain access
  14702.    to command-line parameters, environment-block variables, and the name of
  14703.    the file it was loaded from. Under MS-DOS, this information is divided
  14704.    between the program segment prefix (PSP) and the environment block, as we
  14705.    saw in Chapters 3 and 12; under OS/2, there is no such thing as a PSP,
  14706.    and the program filename and command-line information are appended to the
  14707.    environment block.
  14708.  
  14709.    ──────────────────────────────────────────────────────────────────────────
  14710.    stdin   equ     0               ; standard input handle
  14711.    stdout  equ     1               ; standard output handle
  14712.    stderr  equ     2               ; standard error handle
  14713.  
  14714.    msg     db      'This is a sample message'
  14715.    msg_len equ     $-msg
  14716.  
  14717.            .
  14718.            .
  14719.            .
  14720.            mov     dx,seg msg      ; DS:DX = message address
  14721.            mov     ds,dx
  14722.            mov     dx,offset DGROUP:msg
  14723.            mov     cx,msg_len      ; CX = message length
  14724.            mov     bx,stdout       ; BX = handle
  14725.            mov     ah,40h          ; AH = function 40h write
  14726.            int     21h             ; transfer to MS-DOS
  14727.            jc      error           ; jump if error
  14728.            cmp     ax,msg_len      ; all characters written?
  14729.            jne     diskfull        ; no, device is full
  14730.            .
  14731.            .
  14732.            .
  14733.    ──────────────────────────────────────────────────────────────────────────
  14734.  
  14735.    Figure 16-1.  Typical in-line code for an MS-DOS function call. This
  14736.    particular sequence writes a string to the standard output device. Since
  14737.    the standard output might be redirected to a file without the program's
  14738.    knowledge, it must also check that all of the requested characters were
  14739.    actually written; if the returned length is less than the requested
  14740.    length, this usually indicates that the standard output has been
  14741.    redirected to a disk file and that the disk is full.
  14742.  
  14743.    ──────────────────────────────────────────────────────────────────────────
  14744.    stdin   equ     0               ; standard input handle
  14745.    stdout  equ     1               ; standard output handle
  14746.    stderr  equ     2               ; standard error handle
  14747.  
  14748.    msg     db      'This is a sample message'
  14749.    msg_len equ     $-msg
  14750.  
  14751.            .
  14752.            .
  14753.            .
  14754.            mov     dx,seg msg      ; DS:DX = message address
  14755.            mov     ds,dx
  14756.            mov     dx,offset DGROUP:msg
  14757.            mov     cx,msg_len      ; CX = message length
  14758.            mov     bx,stdout       ; BX = handle
  14759.            call    write           ; perform the write
  14760.            jc      error           ; jump if error
  14761.            cmp     ax,msg_len      ; all characters written?
  14762.            jne     diskfull        ; no, device is full
  14763.            .
  14764.            .
  14765.            .
  14766.  
  14767.    write   proc    near            ; write to file or device
  14768.                                    ; Call with:
  14769.                                    ; BX = handle
  14770.                                    ; CX = length of data
  14771.                                    ; DS:DX = address of data
  14772.                                    ; returns:
  14773.                                    ; if successful, carry clear
  14774.                                    ; and AX = bytes written
  14775.                                    ; if error, carry set
  14776.                                    ; and AX = error code
  14777.  
  14778.            mov     ah,40h          ; function 40h = write
  14779.            int     21h             ; transfer to MS-DOS
  14780.            ret                     ; return status in CY and AX
  14781.  
  14782.    write   endp
  14783.  
  14784.            .
  14785.            .
  14786.            .
  14787.    ──────────────────────────────────────────────────────────────────────────
  14788.  
  14789.    Figure 16-2.  Code from Figure 16-1 after "encapsulation." The portion of
  14790.    the code that is operating-system dependent has been isolated inside a
  14791.    subroutine that is called from other points within the application.
  14792.  
  14793.    When you have completed the encapsulation of system services and access to
  14794.    the PSP and environment, subject your program once more to thorough
  14795.    testing under MS-DOS. This is your last chance, while you are still
  14796.    working in a familiar milieu and have access to your favorite debugging
  14797.    tools, to detect any subtle errors you may have introduced during the
  14798.    three conversion steps discussed thus far.
  14799.  
  14800.    Conversion
  14801.  
  14802.    Next, you must rewrite each system-dependent procedure you created during
  14803.    the encapsulation stage to conform to the OS/2 protected-mode API. In
  14804.    contrast to MS-DOS functions, which are actuated through software
  14805.    interrupts and pass parameters in registers, OS/2 API functions are
  14806.    requested through a far call to a named entry point. Parameters are passed
  14807.    on the stack, along with the addresses of variables within the calling
  14808.    program's data segment that will receive any results returned by the
  14809.    function. The status of an operation is returned in register AX──zero if
  14810.    the function succeeded, an error code otherwise. All other registers are
  14811.    preserved.
  14812.  
  14813.    Although it is not my intention here to provide a detailed introduction to
  14814.    OS/2 programming, Figure 16-3 illustrates the final form of our previous
  14815.    example, after conversion for OS/2. Note especially the addition of the
  14816.    extrn statement, the wlen variable, and the simulation of the MS-DOS
  14817.    function status. This code may not be elegant, but it serves the purpose
  14818.    of limiting the necessary changes to a very small portion of the source
  14819.    file. Some OS/2 functions (such as DosOpen) require parameters that have
  14820.    no counterpart under MS-DOS; you can usually select reasonable values for
  14821.    these extra parameters that will make their existence temporarily
  14822.    invisible to the remainder of the application.
  14823.  
  14824.    ──────────────────────────────────────────────────────────────────────────
  14825.    stdin   equ     0               ; standard input handle
  14826.    stdout  equ     1               ; standard output handle
  14827.    stderr  equ     2               ; standard error handle
  14828.  
  14829.            extrn   DosWrite:far
  14830.  
  14831.    msg     db      'This is a sample message'
  14832.    msg_len equ     $-msg
  14833.  
  14834.    wlen    dw      ?               ; receives actual number
  14835.                                    ; of bytes written
  14836.  
  14837.            .
  14838.            .
  14839.            .
  14840.            mov     dx,seg msg      ; DS:DX = message address
  14841.            mov     ds,dx
  14842.            mov     dx,offset DGROUP:msg
  14843.            mov     cx,msg_len      ; CX = message length
  14844.            mov     bx,stdout       ; BX = handle
  14845.            call    write           ; perform the write
  14846.            jc      error           ; jump if error
  14847.            cmp     ax,msg_len      ; all characters written?
  14848.            jne     diskfull        ; no, device is full
  14849.            .
  14850.            .
  14851.            .
  14852.  
  14853.    write   proc    near            ; write to file or device
  14854.                                    ; call with:
  14855.                                    ; BX = handle
  14856.                                    ; CX = length of data
  14857.                                    ; DS:DX = address of data
  14858.                                    ; returns:
  14859.                                    ; if successful, carry clear
  14860.                                    ; and AX = bytes written
  14861.                                    ; if error, carry set
  14862.                                    ; and AX = error code
  14863.  
  14864.            push    bx              ; handle
  14865.            push    ds              ; address of data
  14866.            push    dx
  14867.            push    cx              ; length of data
  14868.            push    ds              ; receives length written
  14869.            mov     ax,offset DGROUP:wlen
  14870.            push    ax
  14871.            call    DosWrite        ; transfer to OS/2
  14872.            or      ax,ax           ; did write succeed?
  14873.            jnz     write1          ; jump, write failed
  14874.            mov     ax,wlen         ; no error, OR cleared CY
  14875.            ret                     ; and AX := bytes written
  14876.  
  14877.    write1: stc                     ; write error, return CY set
  14878.            ret                     ; and AX = error number
  14879.  
  14880.    write   endp
  14881.  
  14882.            .
  14883.            .
  14884.            .
  14885.    ──────────────────────────────────────────────────────────────────────────
  14886.  
  14887.    Figure 16-3.  Code from Figure 16-2 after "conversion." The MS-DOS
  14888.    function call has been replaced with the equivalent OS/2 function call.
  14889.    Since the knowledge of the operating system has been hidden inside the
  14890.    subroutine by the previous encapsulation step, the surrounding program's
  14891.    requests for write operations should run unchanged. Note that the OS/2
  14892.    function had to be declared as an external name with the "far" attribute,
  14893.    and that a variable named wlen was added to the data segment of the
  14894.    application to receive the actual number of bytes written.
  14895.  
  14896.    Figures 16-4, 16-5, and 16-6 list the OS/2 services that are equivalent
  14897.    to selected MS-DOS and ROM BIOS Int 21H, Int 10H, and Int 16H calls.
  14898.    MS-DOS functions related to FCBs and PSPs are not included in these tables
  14899.    because OS/2 does not support either of these structures. The MS-DOS
  14900.    terminate-and-stay-resident functions are also omitted. Because OS/2 is a
  14901.    true multitasking system, a process doesn't need to terminate in order to
  14902.    stay resident while another process is running.
  14903.  
  14904. ╓┌─┌───────────────────┌────────────────────────────────┌────────────────────╖
  14905.    MS-DOS               Description                     OS/2 function
  14906.    ──────────────────────────────────────────────────────────────────────────
  14907.    Int 21H Function
  14908.    0                   Terminate process               DosExit
  14909.    1                   Character input with echo       KbdCharIn
  14910.    2                   Character output                VioWrtTTY
  14911.    3                   Auxiliary input                 DosRead
  14912.    MS-DOS               Description                     OS/2 function
  14913.    ──────────────────────────────────────────────────────────────────────────
  14914.   3                   Auxiliary input                 DosRead
  14915.    4                   Auxiliary output                DosWrite
  14916.    5                   Printer output                  DosWrite
  14917.    6                   Direct console I/O              KbdCharIn,
  14918.                                                         VioWrtTTY
  14919.    7                   Unfiltered input without echo   KbdCharIn
  14920.    8                   Character input without echo    KbdCharIn
  14921.    9                   Display string                  VioWrtTTY
  14922.    0AH (10)            Buffered keyboard input         KbdStringIn
  14923.    0BH (11)            Check input status              KbdPeek
  14924.    0CH (12)            Reset buffer and input          KbdFlushBuffer,
  14925.                                                         KbdCharIn
  14926.    0DH (13)            Disk reset                      DosBufReset
  14927.    0EH (14)            Select disk                     DosSelectDisk
  14928.    19H (25)            Get current disk                DosQCurDisk
  14929.    1BH (27)            Get default drive data          DosQFSInfo
  14930.    1CH (28)            Get drive data                  DosQFSInfo
  14931.    2AH (42)            Get date                        DosGetDateTime
  14932.    2BH (43)            Set date                        DosSetDateTime
  14933.    MS-DOS               Description                     OS/2 function
  14934.    ──────────────────────────────────────────────────────────────────────────
  14935.   2BH (43)            Set date                        DosSetDateTime
  14936.    2CH (44)            Get time                        DosGetDateTime
  14937.    2DH (45)            Set time                        DosSetDateTime
  14938.    2EH (46)            Set verify flag                 DosSetVerify
  14939.    30H (48)            Get MS-DOS version              DosGetVersion
  14940.    36H (54)            Get drive allocation            DosQFSInfo
  14941.                         information
  14942.    38H (56)            Get or set country              DosGetCtryInfo
  14943.                         information
  14944.    39H (57)            Create directory                DosMkdir
  14945.    3AH (58)            Delete directory                DosRmdir
  14946.    3BH (59)            Set current directory           DosChdir
  14947.    3CH (60)            Create file                     DosOpen
  14948.    3DH (61)            Open file                       DosOpen
  14949.    3EH (62)            Close file                      DosClose
  14950.    3FH (63)            Read file or device             DosRead
  14951.    40H (64)            Write file or device            DosWrite
  14952.    41H (65)            Delete file                     DosDelete
  14953.    42H (66)            Set file pointer                DosChgFilePtr
  14954.    MS-DOS               Description                     OS/2 function
  14955.    ──────────────────────────────────────────────────────────────────────────
  14956.   42H (66)            Set file pointer                DosChgFilePtr
  14957.    43H (67)            Get or set file attributes      DosQFileMode,
  14958.                                                         DosSetFileMode
  14959.    44H (68)            I/O control (IOCTL)             DosDevIOCtl
  14960.    45H (69)            Duplicate handle                DosDupHandle
  14961.    46H (70)            Redirect handle                 DosDupHandle
  14962.    47H (71)            Get current directory           DosQCurDir
  14963.    48H (72)            Allocate memory block           DosAllocSeg
  14964.    49H (73)            Release memory block            DosFreeSeg
  14965.    4AH (74)            Resize memory block             DosReAllocSeg
  14966.    4BH (75)            Execute program                 DosExecPgm
  14967.    4CH (76)            Terminate process with          DosExit
  14968.                         return code
  14969.    4DH (77)            Get return code                 DosCWait
  14970.    4EH (78)            Find first file                 DosFindFirst
  14971.    4FH (79)            Find next file                  DosFindNext
  14972.    54H (84)            Get verify flag                 DosQVerify
  14973.    56H (86)            Rename file                     DosMove
  14974.    57H (87)            Get or set file date and time   DosQFileInfo,
  14975.    MS-DOS               Description                     OS/2 function
  14976.    ──────────────────────────────────────────────────────────────────────────
  14977.   57H (87)            Get or set file date and time   DosQFileInfo,
  14978.                                                         DosSetFileInfo
  14979.    59H (89)            Get extended error              DosErrClass
  14980.                         information
  14981.    5BH (91)            Create new file                 DosOpen
  14982.    5CH (92)            Lock or unlock file region      DosFileLocks
  14983.    65H (101)           Get extended country            DosGetCtryInfo
  14984.                         information
  14985.    66H (102)           Get or set code page            DosGetCp,
  14986.                                                         DosSetCp
  14987.    67H (103)           Set handle count                DosSetMaxFH
  14988.    68H (104)           Commit file                     DosBufReset
  14989.    6CH (108)           Extended open file              DosOpen
  14990.    ──────────────────────────────────────────────────────────────────────────
  14991.  
  14992.  
  14993.    Figure 16-4.  Table of selected MS-DOS function calls and their OS/2
  14994.    counterparts. Note that OS/2 functions are typically more powerful and
  14995.    flexible than the corresponding MS-DOS functions, and that this is not a
  14996.    complete list of OS/2 services.
  14997.  
  14998.    ROM BIOS           Description                         OS/2 function
  14999.    ──────────────────────────────────────────────────────────────────────────
  15000.    Int 10H Function
  15001.    0                 Select display mode                 VioSetMode
  15002.    1                 Set cursor type                     VioSetCurType
  15003.    2                 Set cursor position                 VioSetCurPos
  15004.    3                 Get cursor position                 VioGetCurPos
  15005.    6                 Initialize or scroll window up      VioScrollUp
  15006.    7                 Initialize or scroll window down    VioScrollDn
  15007.    8                 Read character and attribute        VioReadCellStr
  15008.    9                 Write character and attribute       VioWrtNCell
  15009.    0AH (10)          Write character                     VioWrtNChar
  15010.    0EH (14)          Write character in teletype mode    VioWrtTTY
  15011.    0FH (15)          Get display mode                    VioGetMode
  15012.    10H (16)          Set palette, border color, etc.     VioSetState
  15013.    13H (19)          Write string in teletype mode       VioWrtTTY
  15014.    ──────────────────────────────────────────────────────────────────────────
  15015.  
  15016.    Figure 16-5.  Table of ROM BIOS Int 10H video-display driver functions
  15017.    used by MS-DOS applications and their OS/2 equivalents. This is not a
  15018.    complete list of OS/2 video services.
  15019.  
  15020.    ROM BIOS           Description                         OS/2 function
  15021.    ──────────────────────────────────────────────────────────────────────────
  15022.    Int 16H Function
  15023.    0                 Read keyboard character             KbdCharIn
  15024.    1                 Get keyboard status                 KbdPeek
  15025.    2                 Get keyboard flags                  KbdGetStatus
  15026.    ──────────────────────────────────────────────────────────────────────────
  15027.  
  15028.    Figure 16-6.  Table of ROM BIOS Int 16H keyboard driver functions used by
  15029.    MS-DOS applications and their OS/2 equivalents. This is not a complete
  15030.    list of OS/2 keyboard services.
  15031.  
  15032.    Optimization
  15033.  
  15034.    Once your program is running in protected mode, it is time to unravel some
  15035.    of the changes made for purposes of conversion and to introduce various
  15036.    optimizations. Three obvious categories should be considered:
  15037.  
  15038.    1.  Modifying the program's user-interface code for the more powerful OS/2
  15039.        keyboard and display API functions.
  15040.  
  15041.    2.  Incorporating 80286-specific machine instructions where appropriate.
  15042.  
  15043.    3.  Revamping the application to exploit the OS/2 facilities that are
  15044.        unique to protected mode. (Of course, the application benefits from
  15045.        OS/2's virtual memory capabilities automatically; it can allocate
  15046.        memory until physical memory and disk swapping space are exhausted.)
  15047.  
  15048.    Modifying subroutines that encapsulate user input and output to take
  15049.    advantage of the additional functionality available under OS/2 is
  15050.    straight-forward, and the resulting performance improvements can be quite
  15051.    dramatic. For example, the OS/2 video driver offers a variety of services
  15052.    that are far superior to the screen support in MS-DOS and the ROM BIOS,
  15053.    including high-speed display of strings and attributes at any screen
  15054.    position, "reading back" selected areas of the display into a buffer, and
  15055.    scrolling in all four directions.
  15056.  
  15057.    The 80286-specific machine instructions can be very helpful in reducing
  15058.    code size and increasing execution speed. The most useful instructions are
  15059.    the shifts and rotates by an immediate count other than one, the
  15060.    three-operand multiply where one of the operands is an immediate (literal)
  15061.    value, and the push immediate value instruction (particularly handy for
  15062.    setting up OS/2 function calls). For example, in Figure 16-3, the
  15063.    sequence
  15064.  
  15065.    mov     ax,offset DGROUP:wlen
  15066.    push    ax
  15067.  
  15068.    could be replaced by the single instruction
  15069.  
  15070.    push    offset DGROUP:wlen
  15071.  
  15072.    Restructuring an application to take full advantage of OS/2's
  15073.    protected-mode capabilities requires close study of both the application
  15074.    and the OS/2 API, but such study can pay off with sizable benefits in
  15075.    performance, ease of maintenance, and code sharing. Often, for instance,
  15076.    different parts of an application are concerned with I/O devices of vastly
  15077.    different speeds, such as the keyboard, disk, and video display. It both
  15078.    simplifies and enhances the application to separate these elements into
  15079.    subprocesses (called threads in OS/2) that execute asynchronously,
  15080.    communicate through shared data structures, and synchronize with each
  15081.    other, when necessary, using semaphores.
  15082.  
  15083.    As another example, when several applications are closely related and
  15084.    contain many identical or highly similar procedures, OS/2 allows you to
  15085.    centralize those procedures in a dynamic link library. Routines in a
  15086.    dynamic link library are bound to a program at its load time (rather than
  15087.    by LINK, as in the case of traditional runtime libraries) and are shared
  15088.    by all the processes that need them. This reduces the size of each
  15089.    application .EXE file and allows more efficient use of memory. Best of
  15090.    all, dynamic link libraries drastically simplify code maintenance; the
  15091.    routines in the libraries can be debugged or improved at any time, and the
  15092.    applications that use them will automatically benefit the next time they
  15093.    are executed.
  15094.  
  15095.  
  15096.  
  15097.  ────────────────────────────────────────────────────────────────────────────
  15098.  SECTION 2  MS-DOS FUNCTIONS REFERENCE
  15099.  ────────────────────────────────────────────────────────────────────────────
  15100.  
  15101.  
  15102.  Notes to the Reader
  15103.  
  15104.    This section documents the services that the MS-DOS kernel provides to
  15105.    application programs via software interrupts 20H─2FH. Each MS-DOS function
  15106.    is described in the same format:
  15107.  
  15108.    ■ A heading containing the function's name, software interrupt and
  15109.      function number, and an icon indicating the MS-DOS version in which the
  15110.      function was first supported. You can assume that the function is
  15111.      available in all subsequent MS-DOS versions unless explicitly noted
  15112.      otherwise.
  15113.  
  15114.    ■ A synopsis of the actions performed by the function and the
  15115.      circumstances under which it would be used.
  15116.  
  15117.    ■ A summary of the function's arguments.
  15118.  
  15119.    ■ The results and/or error indicators returned by the function. A
  15120.      comprehensive list of error codes can be found in the entry for Int 21H
  15121.      Function 59H.
  15122.  
  15123.    ■ Notes describing special uses or dependencies of the function.
  15124.  
  15125.    ■ A skeleton example of the function's use, written in assembly language.
  15126.  
  15127.    Version icons used in the synopsis, arguments, results, or Notes sections
  15128.    refer to specific minor or major versions, unless they include a + sign to
  15129.    indicate a version and all subsequent versions.
  15130.  
  15131.    For purposes of clarity, the examples may include instructions that would
  15132.    not be necessary if the code were inserted into a working program. For
  15133.    example, most of the examples explicitly set the segment registers when
  15134.    passing the address of a filename or buffer to MS-DOS; in real
  15135.    applications, the segment registers are usually initialized once at entry
  15136.    to the program and left alone thereafter.
  15137.  
  15138.  
  15139.  Int 21H Function Summary by Number
  15140.  
  15141. ╓┌─┌───────┌────────┌───────────────────────────────────────┌───────┌────────╖
  15142.    Hex      Dec     Function name                           Vers    F/H
  15143.    Hex      Dec     Function name                           Vers    F/H
  15144.    ──────────────────────────────────────────────────────────────────────────
  15145.    00H        0    Terminate Process                       1.0+
  15146.    01H        1    Character Input with Echo               1.0+
  15147.    02H        2    Character Output                        1.0+
  15148.    03H        3    Auxiliary Input                         1.0+
  15149.    04H        4    Auxiliary Output                        1.0+
  15150.    05H        5    Printer Output                          1.0+
  15151.    06H        6    Direct Console I/O                      1.0+
  15152.    07H        7    Unfiltered Character Input Without Echo 1.0+
  15153.    08H        8    Character Input Without Echo            1.0+
  15154.    09H        9    Display String                          1.0+
  15155.    0AH       10    Buffered Keyboard Input                 1.0+
  15156.    0BH       11    Check Input Status                      1.0+
  15157.    0CH       12    Flush Input Buffer and Then Input       1.0+
  15158.    0DH       13    Disk Reset                              1.0+
  15159.    0EH       14    Select Disk                             1.0+
  15160.    0FH       15    Open File                               1.0+    F
  15161.    10H       16    Close File                              1.0+    F
  15162.    11H       17    Find First File                         1.0+    F
  15163.    12H       18    Find Next File                          1.0+    F
  15164.    Hex      Dec     Function name                           Vers    F/H
  15165.    ──────────────────────────────────────────────────────────────────────────
  15166.   12H       18    Find Next File                          1.0+    F
  15167.    13H       19    Delete File                             1.0+    F
  15168.    14H       20    Sequential Read                         1.0+    F
  15169.    15H       21    Sequential Write                        1.0+    F
  15170.    16H       22    Create File                             1.0+    F
  15171.    17H       23    Rename File                             1.0+    F
  15172.    18H        24    Reserved
  15173.    19H       25    Get Current Disk                        1.0+
  15174.    1AH       26    Set DTA Address                         1.0+
  15175.    1BH       27    Get Default Drive Data                  1.0+
  15176.    1CH       28    Get Drive Data                          2.0+
  15177.    1DH        29    Reserved
  15178.    1EH        30    Reserved
  15179.    1FH        31    Reserved
  15180.    20H        32    Reserved
  15181.    21H       33    Random Read                             1.0+    F
  15182.    22H       34    Random Write                            1.0+    F
  15183.    23H       35    Get File Size                           1.0+    F
  15184.    24H       36    Set Relative Record Number              1.0+    F
  15185.    Hex      Dec     Function name                           Vers    F/H
  15186.    ──────────────────────────────────────────────────────────────────────────
  15187.   24H       36    Set Relative Record Number              1.0+    F
  15188.    25H       37    Set Interrupt Vector                    1.0+
  15189.    26H       38    Create New PSP                          1.0+
  15190.    27H       39    Random Block Read                       1.0+    F
  15191.    28H       40    Random Block Write                      1.0+    F
  15192.    29H       41    Parse Filename                          1.0+
  15193.    2AH       42    Get Date                                1.0+
  15194.    2BH       43    Set Date                                1.0+
  15195.    2CH       44    Get Time                                1.0+
  15196.    2DH       45    Set Time                                1.0+
  15197.    2EH       46    Set Verify Flag                         1.0+
  15198.    2FH       47    Get DTA Address                         2.0+
  15199.    30H       48    Get MS-DOS Version Number               2.0+
  15200.    31H       49    Terminate and Stay Resident             2.0+
  15201.    32H        50    Reserved
  15202.    33H       51    Get or Set Break Flag, Get Boot Drive   2.0+
  15203.    34H        52    Reserved
  15204.    35H       53    Get Interrupt Vector                    2.0+
  15205.    36H       54    Get Drive Allocation Information        2.0+
  15206.    Hex      Dec     Function name                           Vers    F/H
  15207.    ──────────────────────────────────────────────────────────────────────────
  15208.   36H       54    Get Drive Allocation Information        2.0+
  15209.    37H        55    Reserved
  15210.    38H       56    Get or Set Country Information          2.0+
  15211.    39H       57    Create Directory                        2.0+
  15212.    3AH       58    Delete Directory                        2.0+
  15213.    3BH       59    Set Current Directory                   2.0+
  15214.    3CH       60    Create File                             2.0+    H
  15215.    3DH       61    Open File                               2.0+    H
  15216.    3EH       62    Close File                              2.0+    H
  15217.    3FH       63    Read File or Device                     2.0+    H
  15218.    40H       64    Write File or Device                    2.0+    H
  15219.    41H       65    Delete File                             2.0+    H
  15220.    42H       66    Set File Pointer                        2.0+    H
  15221.    43H       67    Get or Set File Attributes              2.0+
  15222.    44H       68    IOCTL (I/O Control)                     2.0+
  15223.    45H       69    Duplicate Handle                        2.0+
  15224.    46H       70    Redirect Handle                         2.0+
  15225.    47H       71    Get Current Directory                   2.0+
  15226.    48H       72    Allocate Memory Block                   2.0+
  15227.    Hex      Dec     Function name                           Vers    F/H
  15228.    ──────────────────────────────────────────────────────────────────────────
  15229.   48H       72    Allocate Memory Block                   2.0+
  15230.    49H       73    Release Memory Block                    2.0+
  15231.    4AH       74    Resize Memory Block                     2.0+
  15232.    4BH       75    Execute Program (EXEC)                  2.0+
  15233.    4CH       76    Terminate Process with Return Code      2.0+
  15234.    4DH       77    Get Return Code                         2.0+
  15235.    4EH       78    Find First File                         2.0+    H
  15236.    4FH       79    Find Next File                          2.0+    H
  15237.    50H        80    Reserved
  15238.    51H        81    Reserved
  15239.    52H        82    Reserved
  15240.    53H        83    Reserved
  15241.    54H       84    Get Verify Flag                         2.0+
  15242.    55H        85    Reserved
  15243.    56H       86    Rename File                             2.0+
  15244.    57H       87    Get or Set File Date and Time           2.0+    H
  15245.    58H       88    Get or Set Allocation Strategy          3.0+
  15246.    59H       89    Get Extended Error Information          3.0+
  15247.    5AH       90    Create Temporary File                   3.0+    H
  15248.    Hex      Dec     Function name                           Vers    F/H
  15249.    ──────────────────────────────────────────────────────────────────────────
  15250.   5AH       90    Create Temporary File                   3.0+    H
  15251.    5BH       91    Create New File                         3.0+    H
  15252.    5CH       92    Lock or Unlock File Region              3.0+    H
  15253.    5DH        93    Reserved
  15254.    5EH       94    Get Machine Name, Get or Set Printer    3.1+
  15255.                     Setup
  15256.    5FH       95    Device Redirection                      3.1+
  15257.    60H        96    Reserved
  15258.    61H        97    Reserved
  15259.    62H       98    Get PSP Address                         3.0+
  15260.    63H       99    Get DBCS Lead Byte Table                2.25
  15261.                                                             only
  15262.    64H       100    Reserved
  15263.    65H      101    Get Extended Country Information        3.3+
  15264.    66H      102    Get or Set Code Page                    3.3+
  15265.    67H      103    Set Handle Count                        3.3+
  15266.    68H      104    Commit File                             3.3+    H
  15267.    69H       105    Reserved
  15268.    6AH       106    Reserved
  15269.    Hex      Dec     Function name                           Vers    F/H
  15270.    ──────────────────────────────────────────────────────────────────────────
  15271.   6AH       106    Reserved
  15272.    6BH       107    Reserved
  15273.    6CH      108    Extended Open File                      4.0+    H
  15274.    ──────────────────────────────────────────────────────────────────────────
  15275.  
  15276.  
  15277.  
  15278.  Int 21H Function Summary by Category
  15279.  
  15280. ╓┌─┌───────┌────────┌───────────────────────────────────────┌───────┌────────╖
  15281.    Hex      Dec     Function name                           Vers    F/H
  15282.    ──────────────────────────────────────────────────────────────────────────
  15283.    Character I/O
  15284.    01H        1    Character Input with Echo               1.0+
  15285.    02H        2    Character Output                        1.0+
  15286.    03H        3    Auxiliary Input                         1.0+
  15287.    04H        4    Auxiliary Output                        1.0+
  15288.    05H        5    Printer Output                          1.0+
  15289.    06H        6    Direct Console I/O                      1.0+
  15290.    Hex      Dec     Function name                           Vers    F/H
  15291.    ──────────────────────────────────────────────────────────────────────────
  15292.   06H        6    Direct Console I/O                      1.0+
  15293.    07H        7    Unfiltered Character Input Without Echo 1.0+
  15294.    08H        8    Character Input Without Echo            1.0+
  15295.    09H        9    Display String                          1.0+
  15296.    0AH       10    Buffered Keyboard Input                 1.0+
  15297.    0BH       11    Check Input Status                      1.0+
  15298.    0CH       12    Flush Input Buffer and Then Input       1.0+
  15299.  
  15300.    File Operations
  15301.    0FH       15    Open File                               1.0+    F
  15302.    10H       16    Close File                              1.0+    F
  15303.    11H       17    Find First File                         1.0+    F
  15304.    12H       18    Find Next File                          1.0+    F
  15305.    13H       19    Delete File                             1.0+    F
  15306.    16H       22    Create File                             1.0+    F
  15307.    17H       23    Rename File                             1.0+    F
  15308.    23H       35    Get File Size                           1.0+    F
  15309.    29H       41    Parse Filename                          1.0+    F
  15310.    3CH       60    Create File                             2.0+    H
  15311.    Hex      Dec     Function name                           Vers    F/H
  15312.    ──────────────────────────────────────────────────────────────────────────
  15313.   3CH       60    Create File                             2.0+    H
  15314.    3DH       61    Open File                               2.0+    H
  15315.    3EH       62    Close File                              2.0+    H
  15316.    41H       65    Delete File                             2.0+    H
  15317.    43H       67    Get or Set File Attributes              2.0+
  15318.    45H       69    Duplicate Handle                        2.0+
  15319.    46H       70    Redirect Handle                         2.0+
  15320.    4EH       78    Find First File                         2.0+    H
  15321.    4FH       79    Find Next File                          2.0+    H
  15322.    56H       86    Rename File                             2.0+
  15323.    57H       87    Get or Set File Date and Time           2.0+    H
  15324.    5AH       90    Create Temporary File                   3.0+    H
  15325.    5BH       91    Create New File                         3.0+    H
  15326.    67H      103    Set Handle Count                        3.3+
  15327.    68H      104    Commit File                             3.3+    H
  15328.    6CH      108    Extended Open File                      4.0+    H
  15329.  
  15330.    Record Operations
  15331.    14H       20    Sequential Read                         1.0+    F
  15332.    Hex      Dec     Function name                           Vers    F/H
  15333.    ──────────────────────────────────────────────────────────────────────────
  15334.   14H       20    Sequential Read                         1.0+    F
  15335.    15H       21    Sequential Write                        1.0+    F
  15336.    1AH       26    Set DTA Address                         1.0+
  15337.    21H       33    Random Read                             1.0+    F
  15338.    22H       34    Random Write                            1.0+    F
  15339.    24H       36    Set Relative Record Number              1.0+    F
  15340.    27H       39    Random Block Read                       1.0+    F
  15341.    28H       40    Random Block Write                      1.0+    F
  15342.    2FH       47    Get DTA Address                         2.0+
  15343.    3FH       63    Read File or Device                     2.0+    H
  15344.    40H       64    Write File or Device                    2.0+    H
  15345.    42H       66    Set File Pointer                        2.0+    H
  15346.    5CH       92    Lock or Unlock File Region              3.0+    H
  15347.  
  15348.    Directory Operations
  15349.    39H       57    Create Directory                        2.0+
  15350.    3AH       58    Delete Directory                        2.0+
  15351.    3BH       59    Set Current Directory                   2.0+
  15352.    47H       71    Get Current Directory                   2.0+
  15353.    Hex      Dec     Function name                           Vers    F/H
  15354.    ──────────────────────────────────────────────────────────────────────────
  15355.   47H       71    Get Current Directory                   2.0+
  15356.  
  15357.    Disk Management
  15358.    0DH       13    Disk Reset                              1.0+
  15359.    0EH       14    Select Disk                             1.0+
  15360.    19H       25    Get Current Disk                        1.0+
  15361.    1BH       27    Get Default Drive Data                  1.0+
  15362.    1CH       28    Get Drive Data                          2.0+
  15363.    2EH       46    Set Verify Flag                         1.0+
  15364.    36H       54    Get Drive Allocation Information        2.0+
  15365.    54H       84    Get Verify Flag                         2.0+
  15366.  
  15367.    Process Management
  15368.    00H        0    Terminate Process                       1.0+
  15369.    26H       38    Create New PSP                          1.0+
  15370.    31H       49    Terminate and Stay Resident             2.0+
  15371.    4BH       75    Execute Program (EXEC)                  2.0+
  15372.    4CH       76    Terminate Process with Return Code      2.0+
  15373.    4DH       77    Get Return Code                         2.0+
  15374.    Hex      Dec     Function name                           Vers    F/H
  15375.    ──────────────────────────────────────────────────────────────────────────
  15376.   4DH       77    Get Return Code                         2.0+
  15377.    62H       98    Get PSP Address                         3.0+
  15378.  
  15379.    Memory Management
  15380.    48H       72    Allocate Memory Block                   2.0+
  15381.    49H       73    Release Memory Block                    2.0+
  15382.    4AH       74    Resize Memory Block                     2.0+
  15383.    58H       88    Get or Set Allocation Strategy          3.0+
  15384.  
  15385.    Network Functions
  15386.    5EH       94    Get Machine Name, Get or Set Printer    3.1+
  15387.                     Setup
  15388.    5FH       95    Device Redirection                      3.1+
  15389.  
  15390.    Time and Date
  15391.    2AH       42    Get Date                                1.0+
  15392.    2BH       43    Set Date                                1.0+
  15393.    2CH       44    Get Time                                1.0+
  15394.    2DH       45    Set Time                                1.0+
  15395.    Hex      Dec     Function name                           Vers    F/H
  15396.    ──────────────────────────────────────────────────────────────────────────
  15397.   2DH       45    Set Time                                1.0+
  15398.  
  15399.    Miscellaneous System Functions
  15400.    25H       37    Set Interrupt Vector                    1.0+
  15401.    30H       48    Get MS-DOS Version Number               2.0+
  15402.    33H       51    Get or Set Break Flag, Get Boot Drive   2.0+
  15403.    35H       53    Get Interrupt Vector                    2.0+
  15404.    38H       56    Get or Set Country Information          2.0+
  15405.    44H       68    IOCTL (I/O Control)                     2.0+
  15406.    59H       89    Get Extended Error Information          3.0+
  15407.    63H       99    Get Lead Byte Table                     2.25
  15408.                                                             only
  15409.    65H      101    Get Extended Country Information        3.3+
  15410.    66H      102    Get or Set Code Page                    3.3+
  15411.  
  15412.    Reserved Functions
  15413.    18H        24    Reserved
  15414.    1DH        29    Reserved
  15415.    1EH        30    Reserved
  15416.    Hex      Dec     Function name                           Vers    F/H
  15417.    ──────────────────────────────────────────────────────────────────────────
  15418.   1EH        30    Reserved
  15419.    1FH        31    Reserved
  15420.    20H        32    Reserved
  15421.    32H        50    Reserved
  15422.    34H        52    Reserved
  15423.    37H        55    Reserved
  15424.    50H        80    Reserved
  15425.    51H        81    Reserved
  15426.    52H        82    Reserved
  15427.    53H        83    Reserved
  15428.    55H        85    Reserved
  15429.    5DH        93    Reserved
  15430.    60H        96    Reserved
  15431.    61H        97    Reserved
  15432.    64H       100    Reserved
  15433.    69H       105    Reserved
  15434.    6AH       106    Reserved
  15435.    6BH       107    Reserved
  15436.    ──────────────────────────────────────────────────────────────────────────
  15437.    Hex      Dec     Function name                           Vers    F/H
  15438.    ──────────────────────────────────────────────────────────────────────────
  15439.   ──────────────────────────────────────────────────────────────────────────
  15440.  
  15441.  
  15442.  
  15443.  ────────────────────────────────────────────────────────────────────────────
  15444.  Int 20H                                                                [1.0]
  15445.  Terminate process
  15446.  ────────────────────────────────────────────────────────────────────────────
  15447.  
  15448.    Terminates the current process. This is one of several methods that a
  15449.    program can use to perform a final exit. MS-DOS then takes the following
  15450.    actions:
  15451.  
  15452.    ■ All memory belonging to the process is released.
  15453.  
  15454.    ■ File buffers are flushed and any open handles for files or devices owned
  15455.      by the process are closed.
  15456.  
  15457.    ■ The termination handler vector (Int 22H) is restored from PSP:000AH.
  15458.  
  15459.    ■ The Ctrl-C handler vector (Int 23H) is restored from PSP:000EH.
  15460.  
  15461.    ■ [2.0+] The critical-error handler vector (Int 24H) is restored from
  15462.      PSP:0012H.
  15463.  
  15464.    ■ Control is transferred to the termination handler.
  15465.  
  15466.    If the program is returning to COMMAND.COM, control transfers to the
  15467.    resident portion, and the transient portion is reloaded if necessary. If a
  15468.    batch file is in progress, the next line of the file is fetched and
  15469.    interpreted; otherwise, a prompt is issued for the next user command.
  15470.  
  15471.  Call with:
  15472.  
  15473.    CS            = segment address of program segment prefix
  15474.  
  15475.  Returns:
  15476.  
  15477.    Nothing
  15478.  
  15479.  Notes:
  15480.  
  15481.    ■ Any files that have been written to using FCBs should be closed before
  15482.      performing this exit call; otherwise, data may be lost.
  15483.  
  15484.    ■ Other methods of performing a final exit are:
  15485.  
  15486.      ∙ Int 21H Function 00H
  15487.  
  15488.      ∙ Int 21H Function 31H
  15489.  
  15490.      ∙ Int 21H Function 4CH
  15491.  
  15492.      ∙ Int 27H
  15493.  
  15494.    ■ [2.0+] Int 21H Functions 31H and 4CH are the preferred methods for
  15495.      termination, since they allow a return code to be passed to the parent
  15496.      process.
  15497.  
  15498.    ■ [3.0+] If the program is running on a network, it should remove all
  15499.      locks it has placed on file regions before terminating.
  15500.  
  15501.  Example:
  15502.  
  15503.    Terminate the current program, returning control to the program's parent.
  15504.  
  15505.            .
  15506.            .
  15507.            .
  15508.            int     20h             ; transfer to MS-DOS
  15509.  
  15510.  
  15511.  ────────────────────────────────────────────────────────────────────────────
  15512.  Int 21H                                                                [1.0]
  15513.  Function 00H
  15514.  Terminate process
  15515.  ────────────────────────────────────────────────────────────────────────────
  15516.  
  15517.    Terminates the current process. This is one of several methods that a
  15518.    program can use to perform a final exit. MS-DOS then takes the following
  15519.    actions:
  15520.  
  15521.    ■ All memory belonging to the process is released.
  15522.  
  15523.    ■ File buffers are flushed and any open handles for files or devices owned
  15524.      by the process are closed.
  15525.  
  15526.    ■ The termination handler vector (Int 22H) is restored from PSP:000AH.
  15527.  
  15528.    ■ The Ctrl-C handler vector (Int 23H) is restored from PSP:000EH.
  15529.  
  15530.    ■ [2.0+] The critical-error handler vector (Int 24H) is restored from
  15531.      PSP:0012H.
  15532.  
  15533.    ■ Control is transferred to the termination handler.
  15534.  
  15535.    If the program is returning to COMMAND.COM, control transfers to the
  15536.    resident portion, and the transient portion is reloaded if necessary. If a
  15537.    batch file is in progress, the next line of the file is fetched and
  15538.    interpreted; otherwise, a prompt is issued for the next user command.
  15539.  
  15540.  Call with:
  15541.  
  15542.    AH            = 00H
  15543.    CS            = segment address of program segment prefix
  15544.  
  15545.  Returns:
  15546.  
  15547.    Nothing
  15548.  
  15549.  Notes:
  15550.  
  15551.    ■ Any files that have been written to using FCBs should be closed before
  15552.      performing this exit call; otherwise, data may be lost.
  15553.  
  15554.    ■ Other methods of performing a final exit are:
  15555.  
  15556.      ∙ Int 20H
  15557.  
  15558.      ∙ Int 21H Function 31H
  15559.  
  15560.      ∙ Int 21H Function 4CH<21H4CH>
  15561.  
  15562.      ∙ Int 27H
  15563.  
  15564.    ■ [2.0+] Int 21H Functions 31H and 4CH are the preferred methods for
  15565.      termination, since they allow a return code to be passed to the parent
  15566.      process.
  15567.  
  15568.    ■ [3.0+] If the program is running on a network, it should remove all
  15569.      locks it has placed on file regions before terminating.
  15570.  
  15571.  Example:
  15572.  
  15573.    Terminate the current program, returning control to the program's parent.
  15574.  
  15575.            .
  15576.            .
  15577.            .
  15578.            mov     ah,0            ; function number
  15579.            int     21h             ; transfer to MS-DOS
  15580.  
  15581.  
  15582.  ────────────────────────────────────────────────────────────────────────────
  15583.  Int 21H                                                                [1.0]
  15584.  Function 01H
  15585.  Character input with echo
  15586.  ────────────────────────────────────────────────────────────────────────────
  15587.  
  15588.    [1] Inputs a character from the keyboard, then echoes it to the display.
  15589.    If no character is ready, waits until one is available.
  15590.  
  15591.    [2.0+] Reads a character from the standard input device and echoes it to
  15592.    the standard output device. If no character is ready, waits until one is
  15593.    available. Input can be redirected. (If input has been redirected, there
  15594.    is no way to detect EOF.)
  15595.  
  15596.  Call with:
  15597.  
  15598.    AH            = 01H
  15599.  
  15600.  Returns:
  15601.  
  15602.    AL            = 8-bit input data
  15603.  
  15604.  Notes:
  15605.  
  15606.    ■ If the standard input is not redirected, and the character read is a
  15607.      Ctrl-C, an Int 23H is executed. If the standard input is redirected, a
  15608.      Ctrl-C is detected at the console, and BREAK is ON, an Int 23H is
  15609.      executed.
  15610.  
  15611.    ■ To read extended ASCII codes (such as the special function keys F1 to
  15612.      F10) on the IBM PC and compatibles, you must call this function twice.
  15613.      The first call returns the value 00H to signal the presence of an
  15614.      extended code.
  15615.  
  15616.    ■ See also Int 21H Functions 06H, 07H, and 08H, which provide character
  15617.      input with various combinations of echo and/or Ctrl-C sensing.
  15618.  
  15619.    ■ [2.0+] You can also read the keyboard by issuing a read (Int 21H
  15620.      Function 3FH) using the predefined handle for the standard input
  15621.      (0000H), if input has not been redirected, or a handle obtained by
  15622.      opening the logical device CON.
  15623.  
  15624.  Example:
  15625.  
  15626.    Read one character from the keyboard into register AL, echo it to the
  15627.    display, and store it in the variable char.
  15628.  
  15629.    char    db      0               ; input character
  15630.            .
  15631.            .
  15632.            .
  15633.            mov     ah,1            ; function number
  15634.            int     21h             ; transfer to MS-DOS
  15635.            mov     char,al         ; save character
  15636.            .
  15637.            .
  15638.            .
  15639.  
  15640.  
  15641.  ────────────────────────────────────────────────────────────────────────────
  15642.  Int 21H                                                                [1.0]
  15643.  Function 02H
  15644.  Character output
  15645.  ────────────────────────────────────────────────────────────────────────────
  15646.  
  15647.    [1] Outputs a character to the currently active video display.
  15648.  
  15649.    [2.0+] Outputs a character to the standard output device. Output can be
  15650.    redirected. (If output is redirected, there is no way to detect disk
  15651.    full.)
  15652.  
  15653.  Call with:
  15654.  
  15655.    AH            = 02H
  15656.    DL            = 8-bit data for output
  15657.  
  15658.  Returns:
  15659.  
  15660.    Nothing
  15661.  
  15662.  Notes:
  15663.  
  15664.    ■ If a Ctrl-C is detected at the keyboard after the requested character is
  15665.      output, an Int 23H is executed.
  15666.  
  15667.    ■ If the standard output has not been redirected, a backspace code (08H)
  15668.      causes the cursor to move left one position. If output has been
  15669.      redirected, the backspace code does not receive any special treatment.
  15670.  
  15671.    ■ [2.0+] You can also send strings to the display by performing a write
  15672.      (Int 21H Function 40H) using the predefined handle for the standard
  15673.      output (0001H), if output has not been redirected, or a handle obtained
  15674.      by opening the logical device CON.
  15675.  
  15676.  Example:
  15677.  
  15678.    Send the character "*" to the standard output device.
  15679.  
  15680.            .
  15681.            .
  15682.            .
  15683.            mov     ah,2            ; function number
  15684.            mov     dl,'*'          ; character to output
  15685.            int     21h             ; transfer to MS-DOS
  15686.            .
  15687.            .
  15688.            .
  15689.  
  15690.  
  15691.  ────────────────────────────────────────────────────────────────────────────
  15692.  Int 21H                                                                [1.0]
  15693.  Function 03H
  15694.  Auxiliary input
  15695.  ────────────────────────────────────────────────────────────────────────────
  15696.  
  15697.    [1] Reads a character from the first serial port.
  15698.  
  15699.    [2.0+] Reads a character from the standard auxiliary device. The default
  15700.    is the first serial port (COM1).
  15701.  
  15702.  Call with:
  15703.  
  15704.    AH            = 03H
  15705.  
  15706.  Returns:
  15707.  
  15708.    AL            = 8-bit input data
  15709.  
  15710.  Notes:
  15711.  
  15712.    ■ In most MS-DOS systems, the serial device is unbuffered and is not
  15713.      interrupt-driven. If the auxiliary device sends data faster than your
  15714.      program can process it, characters may be lost.
  15715.  
  15716.    ■ At startup on the IBM PC, PC-DOS initializes the first serial port to
  15717.      2400 baud, no parity, 1 stop bit, and 8 data bits. Other implementations
  15718.      of MS-DOS may initialize the serial device differently.
  15719.  
  15720.    ■ There is no way for a user program to read the status of the auxiliary
  15721.      device or to detect I/O errors (such as lost characters) through this
  15722.      function call. On the IBM PC, more precise control can be obtained by
  15723.      calling ROM BIOS Int 14H or by driving the communications controller
  15724.      directly.
  15725.  
  15726.    ■ If a Ctrl-C is detected at the keyboard, an Int 23H is executed.
  15727.  
  15728.    ■ [2.0+] You can also input from the auxiliary device by requesting a read
  15729.      (Int 21H Function 3FH) using the predefined handle for the standard
  15730.      auxiliary device (0003H) or using a handle obtained by opening the
  15731.      logical device AUX.
  15732.  
  15733.  Example:
  15734.  
  15735.    Read a character from the standard auxiliary input and store it in the
  15736.    variable char.
  15737.  
  15738.    char    db      0               ; input character
  15739.            .
  15740.            .
  15741.            .
  15742.            mov     ah,3            ; function number
  15743.            int     21h             ; transfer to MS-DOS
  15744.            mov     char,al         ; save character
  15745.            .
  15746.            .
  15747.            .
  15748.  
  15749.  
  15750.  ────────────────────────────────────────────────────────────────────────────
  15751.  Int 21H                                                                [1.0]
  15752.  Function 04H
  15753.  Auxiliary output
  15754.  ────────────────────────────────────────────────────────────────────────────
  15755.  
  15756.    [1] Outputs a character to the first serial port.
  15757.  
  15758.    [2.0+] Outputs a character to the standard auxiliary device. The default
  15759.    is the first serial port (COM1).
  15760.  
  15761.  Call with:
  15762.  
  15763.    AH            = 04H
  15764.    DL            = 8-bit data for output
  15765.  
  15766.  Returns:
  15767.  
  15768.    Nothing
  15769.  
  15770.  Notes:
  15771.  
  15772.    ■ If the output device is busy, this function waits until the device is
  15773.      ready to accept a character.
  15774.  
  15775.    ■ There is no way to poll the status of the auxiliary device using this
  15776.      function. On the IBM PC, more precise control can be obtained by calling
  15777.      ROM BIOS Int 14H or by driving the communications controller directly.
  15778.  
  15779.    ■ If a Ctrl-C is detected at the keyboard, an Int 23H is executed.
  15780.  
  15781.    ■ [2.0+] You can also send strings to the auxiliary device by performing a
  15782.      write (Int 21H Function 40H) using the predefined handle for the
  15783.      standard auxiliary device (0003H) or using a handle obtained by opening
  15784.      the logical device AUX.
  15785.  
  15786.  Example:
  15787.  
  15788.    Output a "*'' character to the auxiliary device.
  15789.  
  15790.            .
  15791.            .
  15792.            .
  15793.            mov     ah,4            ; function number
  15794.            mov     dl,'*'          ; character to output
  15795.            int     21h             ; transfer to MS-DOS
  15796.            .
  15797.            .
  15798.            .
  15799.  
  15800.  
  15801.  ────────────────────────────────────────────────────────────────────────────
  15802.  Int 21H                                                                [1.0]
  15803.  Function 05H
  15804.  Printer output
  15805.  ────────────────────────────────────────────────────────────────────────────
  15806.  
  15807.    [1] Sends a character to the first list device (PRN or LPT1).
  15808.  
  15809.    [2.0+] Sends a character to the standard list device. The default device
  15810.    is the printer on the first parallel port (LPT1), unless explicitly
  15811.    redirected by the user with the MODE command.
  15812.  
  15813.  Call with:
  15814.  
  15815.    AH            = 05H
  15816.    DL            = 8-bit data for output
  15817.  
  15818.  Returns:
  15819.  
  15820.    Nothing
  15821.  
  15822.  Notes:
  15823.  
  15824.    ■ If the printer is busy, this function waits until the printer is ready
  15825.      to accept the character.
  15826.  
  15827.    ■ There is no standardized way to poll the status of the printer under
  15828.      MS-DOS.
  15829.  
  15830.    ■ If a Ctrl-C is detected at the keyboard, an Int 23H is executed.
  15831.  
  15832.    ■ [2.0+] You can also send strings to the printer by performing a write
  15833.      (Int 21H Function 40H) using the predefined handle for the standard
  15834.      printer device (0004H) or using a handle obtained by opening the logical
  15835.      device PRN or LPT1.
  15836.  
  15837.  Example:
  15838.  
  15839.    Output the character "*'' to the list device.
  15840.  
  15841.            .
  15842.            .
  15843.            .
  15844.            mov     ah,5            ; function number
  15845.            mov     dl,'*'          ; character to output
  15846.            int     21h             ; transfer to MS-DOS
  15847.            .
  15848.            .
  15849.            .
  15850.  
  15851.  
  15852.  ────────────────────────────────────────────────────────────────────────────
  15853.  Int 21H                                                                [1.0]
  15854.  Function 06H
  15855.  Direct console I/O
  15856.  ────────────────────────────────────────────────────────────────────────────
  15857.  
  15858.    Used by programs that need to read and write all possible characters and
  15859.    control codes without any interference from the operating system.
  15860.  
  15861.    [1] Reads a character from the keyboard or writes a character to the
  15862.    display.
  15863.  
  15864.    [2.0+] Reads a character from the standard input device or writes a
  15865.    character to the standard output device. I/O may be redirected. (If I/O
  15866.    has been redirected, there is no way to detect EOF or disk full.)
  15867.  
  15868.  Call with:
  15869.  
  15870.    AH            = 06H
  15871.    DL            = function requested
  15872.  
  15873.                    00H─FEH   if output request
  15874.                    0FFH      if input request
  15875.  
  15876.  Returns:
  15877.  
  15878.    If called with DL = 00H─0FEH
  15879.  
  15880.    Nothing
  15881.  
  15882.    If called with DL = FFH and a character is ready
  15883.  
  15884.    Zero flag     = clear
  15885.    AL            = 8-bit input data
  15886.  
  15887.    If called with DL = FFH and no character is ready
  15888.  
  15889.    Zero flag     = set
  15890.  
  15891.  Notes:
  15892.  
  15893.    ■ No special action is taken upon entry of a Ctrl-C when this service is
  15894.      used.
  15895.  
  15896.    ■ To read extended ASCII codes (such as the special function keys F1 to
  15897.      F10) on the IBM PC and compatibles, you must call this function twice.
  15898.      The first call returns the value 00H to signal the presence of an
  15899.      extended code.
  15900.  
  15901.    ■ See also Int 21H Functions 01H, 07H, and 08H, which provide character
  15902.      input with various combinations of echo and/or Ctrl-C sensing, and
  15903.      Functions 02H and 09H, which may be used to write characters to the
  15904.      standard output.
  15905.  
  15906.    ■ [2.0+] You can also read the keyboard by issuing a read (Int 21H
  15907.      Function 3FH) using the predefined handle for the standard input
  15908.      (0000H), if input has not been redirected, or a handle obtained by
  15909.      opening the logical device CON.
  15910.  
  15911.    ■ [2.0+] You can also send characters to the display by issuing a write
  15912.      (Int 21H Function 40H) using the predefined handle for the standard
  15913.      output (0001H), if output has not been redirected, or a handle obtained
  15914.      by opening the logical device CON.
  15915.  
  15916.  Examples:
  15917.  
  15918.    Send the character "*" to the standard output device.
  15919.  
  15920.            .
  15921.            .
  15922.            .
  15923.            mov     ah,6            ; function number
  15924.            mov     dl,'*'          ; character to output
  15925.            int     21h             ; transfer to MS-DOS
  15926.            .
  15927.            .
  15928.            .
  15929.  
  15930.    Read a character from the standard input device and save it in the
  15931.    variable char. If no character is ready, wait until one is available.
  15932.  
  15933.    char    db      0               ; input character
  15934.            .
  15935.            .
  15936.            .
  15937.    wait:   mov     ah,6            ; function number
  15938.            mov     dl,0ffh         ; parameter for read
  15939.            int     21h             ; transfer to MS-DOS
  15940.            jz      wait            ; wait until char ready
  15941.            mov     char,al         ; save the character
  15942.            .
  15943.            .
  15944.            .
  15945.  
  15946.  
  15947.  ────────────────────────────────────────────────────────────────────────────
  15948.  Int 21H                                                                [1.0]
  15949.  Function 07H
  15950.  Unfiltered character input without echo
  15951.  ────────────────────────────────────────────────────────────────────────────
  15952.  
  15953.    [1] Reads a character from the keyboard without echoing it to the display.
  15954.    If no character is ready, waits until one is available.
  15955.  
  15956.    [2.0+] Reads a character from the standard input device without echoing it
  15957.    to the standard output device. If no character is ready, waits until one
  15958.    is available. Input may be redirected. (If input has been redirected,
  15959.    there is no way to detect EOF.)
  15960.  
  15961.  Call with:
  15962.  
  15963.    AH            = 07H
  15964.  
  15965.  Returns:
  15966.  
  15967.    AL            = 8-bit input data
  15968.  
  15969.  Notes:
  15970.  
  15971.    ■ No special action is taken upon entry of a Ctrl-C when this function is
  15972.      used. If Ctrl-C checking is required, use Int 21H Function 08H instead.
  15973.  
  15974.    ■ To read extended ASCII codes (such as the special function keys F1 to
  15975.      F10) on the IBM PC and compatibles, you must call this function twice.
  15976.      The first call returns the value 00H to signal the presence of an
  15977.      extended code.
  15978.  
  15979.    ■ See also Int 21H Functions 01H, 06H, and 08H, which provide character
  15980.      input with various combinations of echo and/or Ctrl-C sensing.
  15981.  
  15982.    ■ [2.0+] You can also read the keyboard by issuing a read (Int 21H
  15983.      Function 3FH) using the predefined handle for the standard input
  15984.      (0000H), if input has not been redirected, or a handle obtained by
  15985.      opening the logical device CON.
  15986.  
  15987.  Example:
  15988.  
  15989.    Read a character from the standard input without echoing it to the
  15990.    display, and store it in the variable char.
  15991.  
  15992.    char    db      0               ; input character
  15993.            .
  15994.            .
  15995.            .
  15996.            mov     ah,7            ; function number
  15997.            int     21h             ; transfer to MS-DOS
  15998.            mov     char,al         ; save character
  15999.            .
  16000.            .
  16001.            .
  16002.  
  16003.  
  16004.  ────────────────────────────────────────────────────────────────────────────
  16005.  Int 21H                                                                [1.0]
  16006.  Function 08H
  16007.  Character input without echo
  16008.  ────────────────────────────────────────────────────────────────────────────
  16009.  
  16010.    [1] Reads a character from the keyboard without echoing it to the display.
  16011.    If no character is ready, waits until one is available.
  16012.  
  16013.    [2.0+] Reads a character from the standard input device without echoing it
  16014.    to the standard output device. If no character is ready, waits until one
  16015.    is available. Input may be redirected. (If input has been redirected,
  16016.    there is no way to detect EOF.)
  16017.  
  16018.  Call with:
  16019.  
  16020.    AH            = 08H
  16021.  
  16022.  Returns:
  16023.  
  16024.    AL            = 8-bit input data
  16025.  
  16026.  Notes:
  16027.  
  16028.    ■ If the standard input is not redirected, and the character read is a
  16029.      Ctrl-C, an Int 23H is executed. If the standard input is redirected, a
  16030.      Ctrl-C is detected at the console, and BREAK is ON, an Int 23H is
  16031.      executed. To avoid possible interruption by a Ctrl-C, use Int 21H
  16032.      Function 07H instead.
  16033.  
  16034.    ■ To read extended ASCII codes (such as the special function keys F1 to
  16035.      F10) on the IBM PC and compatibles, you must call this function twice.
  16036.      The first call returns the value 00H to signal the presence of an
  16037.      extended code.
  16038.  
  16039.    ■ See also Int 21H Functions 01H, 06H, and 07H, which provide character
  16040.      input with various combinations of echo and/or Ctrl-C sensing.
  16041.  
  16042.    ■ [2.0+] You can also read the keyboard by issuing a read (Int 21H
  16043.      Function 3FH) using the predefined handle for the standard input
  16044.      (0000H), if input has not been redirected, or a handle obtained by
  16045.      opening the logical device CON.
  16046.  
  16047.  Example:
  16048.  
  16049.    Read a character from the standard input without echoing it to the
  16050.    display, allowing possible detection of Ctrl-C, and store the character in
  16051.    the variable char.
  16052.  
  16053.    char    db      0
  16054.            .
  16055.            .
  16056.            .
  16057.            mov     ah,8            ; function number
  16058.            int     21h             ; transfer to MS-DOS
  16059.            mov     char,al         ; save character
  16060.            .
  16061.            .
  16062.            .
  16063.  
  16064.  
  16065.  ────────────────────────────────────────────────────────────────────────────
  16066.  Int 21H                                                                [1.0]
  16067.  Function 09H
  16068.  Display string
  16069.  ────────────────────────────────────────────────────────────────────────────
  16070.  
  16071.    [1] Sends a string of characters to the display.
  16072.  
  16073.    [2.0+] Sends a string of characters to the standard output device. Output
  16074.    may be redirected. (If output has been redirected, there is no way to
  16075.    detect disk full.)
  16076.  
  16077.  Call with:
  16078.  
  16079.    AH            = 09H
  16080.    DS:DX         = segment:offset of string
  16081.  
  16082.  Returns:
  16083.  
  16084.    Nothing
  16085.  
  16086.  Notes:
  16087.  
  16088.    ■ The string must be terminated with the character $ (24H), which is not
  16089.      transmitted. Any other ASCII codes, including control codes, can be
  16090.      embedded in the string.
  16091.  
  16092.    ■ See Int 21H Functions 02H and 06H for single-character output to the
  16093.      video display or standard output device.
  16094.  
  16095.    ■ If a Ctrl-C is detected at the keyboard, an Int 23H is executed.
  16096.  
  16097.    ■ [2.0+] You can also send strings to the display by performing a write
  16098.      (Int 21H Function 40H) using the predefined handle for the standard
  16099.      output (0001H), if it has not been redirected, or a handle obtained by
  16100.      opening the logical device CON.
  16101.  
  16102.  Example:
  16103.  
  16104.    Send the string Hello World, followed by a carriage return and line feed,
  16105.    to the standard output device.
  16106.  
  16107.    cr      equ     0dh
  16108.    lf      equ     0ah
  16109.  
  16110.    msg     db      'Hello World',cr,lf,'$'
  16111.            .
  16112.            .
  16113.            .
  16114.            mov     ah,9            ; function number
  16115.            mov     dx,seg msg      ; address of string
  16116.            mov     ds,dx
  16117.            mov     dx,offset msg
  16118.            int     21h             ; transfer to MS-DOS
  16119.            .
  16120.            .
  16121.            .
  16122.  
  16123.  
  16124.  ────────────────────────────────────────────────────────────────────────────
  16125.  Int 21H                                                                [1.0]
  16126.  Function 0AH (10)
  16127.  Buffered keyboard input
  16128.  ────────────────────────────────────────────────────────────────────────────
  16129.  
  16130.    [1] Reads a line from the keyboard and places it in a user-designated
  16131.    buffer. The characters are echoed to the display.
  16132.  
  16133.    [2.0+] Reads a string of bytes from the standard input device, up to and
  16134.    including an ASCII carriage return (0DH), and places them in a
  16135.    user-designated buffer. The characters are echoed to the standard output
  16136.    device. Input may be redirected. (If input has been redirected, there is
  16137.    no way to detect EOF.)
  16138.  
  16139.  Call with:
  16140.  
  16141.    AH            = 0AH
  16142.    DS:DX         = segment:offset of buffer
  16143.  
  16144.  Returns:
  16145.  
  16146.    Nothing (data placed in buffer)
  16147.  
  16148.  Notes:
  16149.  
  16150.    ■ The buffer used by this function has the following format:
  16151.  
  16152.      Byte          Contents
  16153.      ────────────────────────────────────────────────────────────────────────
  16154.      0             maximum number of characters to read, set by program
  16155.      1             number of characters actually read (excluding carriage
  16156.                    return), set
  16157.                    by MS-DOS
  16158.      2+            string read from keyboard or standard input, terminated by
  16159.                    a carriage return (0DH)
  16160.      ────────────────────────────────────────────────────────────────────────
  16161.  
  16162.    ■ If the buffer fills to one fewer than the maximum number of characters
  16163.      it can hold, subsequent input is ignored and the bell is sounded until a
  16164.      carriage return is detected.
  16165.  
  16166.    ■ This input function is buffered with type-ahead capability, and all of
  16167.      the standard keyboard editing commands are active.
  16168.  
  16169.    ■ If the standard input is not redirected, and a Ctrl-C is detected at the
  16170.      console, an Int 23H is executed. If the standard input is redirected, a
  16171.      Ctrl-C is detected at the console, and BREAK is ON, an Int 23H is
  16172.      executed.
  16173.  
  16174.    ■ See Int 21H Functions 01H, 06H, 07H, and 08H for single-character input
  16175.      from the keyboard or standard input device.
  16176.  
  16177.    ■ [2.0+] You can also read strings from the keyboard by performing a read
  16178.      (Int 21H Function 3FH) using the predefined handle for the standard
  16179.      input (0000H), if it has not been redirected, or a handle obtained by
  16180.      opening the logical device CON.
  16181.  
  16182.  Example:
  16183.  
  16184.    Read a string that is a maximum of 80 characters long from the standard
  16185.    input device, placing it in the buffer named buff.
  16186.  
  16187.    buff    db      81              ; maximum length of input
  16188.            db      0               ; actual length of input
  16189.            db      81 dup (0)      ; actual input placed here
  16190.            .
  16191.            .
  16192.            .
  16193.            mov     ah,0ah          ; function number
  16194.            mov     dx,seg buff     ; input buffer address
  16195.            mov     ds,dx
  16196.            mov     dx,offset buff
  16197.            int     21h             ; transfer to MS-DOS
  16198.            .
  16199.            .
  16200.            .
  16201.  
  16202.  
  16203.  ────────────────────────────────────────────────────────────────────────────
  16204.  Int 21H                                                                [1.0]
  16205.  Function 0BH (11)
  16206.  Check input status
  16207.  ────────────────────────────────────────────────────────────────────────────
  16208.  
  16209.    [1] Checks whether a character is available from the keyboard.
  16210.  
  16211.    [2.0+] Checks whether a character is available from the standard input
  16212.    device. Input can be redirected.
  16213.  
  16214.  Call with:
  16215.  
  16216.    AH            = 0BH
  16217.  
  16218.  Returns:
  16219.  
  16220.    AL            = 00H if no character is available
  16221.                    FFH if at least one character is available
  16222.  
  16223.  Notes:
  16224.  
  16225.    ■ [1] If a Ctrl-C is detected, an Int 23H is executed.
  16226.  
  16227.    ■ [2.0+] If the standard input is not redirected, and a Ctrl-C is detected
  16228.      at the console, an Int 23H is executed. If the standard input is
  16229.      redirected, a Ctrl-C is detected at the console, and BREAK is ON, an Int
  16230.      23H is executed.
  16231.  
  16232.    ■ If a character is waiting, this function will continue to return a true
  16233.      flag until the character is consumed with a call to Int 21H Function
  16234.      01H, 06H, 07H, 08H, 0AH, or 3FH.
  16235.  
  16236.    ■ This function is equivalent to IOCTL Int 21H Function 44H Subfunction
  16237.      06H.
  16238.  
  16239.  Example:
  16240.  
  16241.    Test whether a character is available from the standard input.
  16242.  
  16243.            .
  16244.            .
  16245.            .
  16246.            mov     ah,0bh          ; function number
  16247.            int     21h             ; transfer to MS-DOS
  16248.            or      al,al           ; character waiting?
  16249.            jnz     ready           ; jump if char ready
  16250.            .
  16251.            .
  16252.            .
  16253.  
  16254.  
  16255.  ────────────────────────────────────────────────────────────────────────────
  16256.  Int 21H                                                                [1.0]
  16257.  Function 0CH (12)
  16258.  Flush input buffer and then input
  16259.  ────────────────────────────────────────────────────────────────────────────
  16260.  
  16261.    [1] Clears the type-ahead buffer and then invokes one of the keyboard
  16262.    input functions.
  16263.  
  16264.    [2.0+] Clears the standard input buffer and then invokes one of the
  16265.    character input functions. Input can be redirected.
  16266.  
  16267.  Call with:
  16268.  
  16269.    AH            = 0CH
  16270.    AL            = number of input function to be invoked after resetting
  16271.                    buffer (must be 01H, 06H, 07H, 08H, or 0AH)
  16272.  
  16273.    (if AL = 0AH)
  16274.  
  16275.    DS:DX         = segment:offset of input buffer
  16276.  
  16277.  Returns:
  16278.  
  16279.    (if called with AL = 01H, 06H, 07H, or 08H)
  16280.  
  16281.    AL            = 8-bit input data
  16282.  
  16283.    (if called with AL = 0AH)
  16284.  
  16285.    Nothing (data placed in buffer)
  16286.  
  16287.  Notes:
  16288.  
  16289.    ■ The function exists to allow a program to defeat MS-DOS's type-ahead
  16290.      feature. It discards any characters that are waiting in MS-DOS's
  16291.      internal type-ahead buffer, forcing the specified input function to wait
  16292.      for a character (usually a keyboard entry) that is truly entered after
  16293.      the program's request.
  16294.  
  16295.    ■ The presence or absence of Ctrl-C checking during execution of this
  16296.      function depends on the function number placed in register AL.
  16297.  
  16298.    ■ A function number in AL other than 01H, 06H, 07H, 08H, or 0AH simply
  16299.      flushes the input buffer and returns control to the calling program.
  16300.  
  16301.  Example:
  16302.  
  16303.    Clear the type-ahead buffer, then wait for a character to be entered,
  16304.    echoing it and then returning it in AL. Store the character in the
  16305.    variable char.
  16306.  
  16307.    char    db      0
  16308.            .
  16309.            .
  16310.            .
  16311.            mov     ah,0ch          ; function number
  16312.            mov     al,1            ; subfunction = input char
  16313.            int     21h             ; transfer to MS-DOS
  16314.            mov     char,al         ; save character
  16315.            .
  16316.            .
  16317.            .
  16318.  
  16319.  
  16320.  ────────────────────────────────────────────────────────────────────────────
  16321.  Int 21H                                                                [1.0]
  16322.  Function 0DH (13)
  16323.  Disk reset
  16324.  ────────────────────────────────────────────────────────────────────────────
  16325.  
  16326.    Flushes all file buffers. All data that has been logically written by user
  16327.    programs, but has been temporarily buffered within MS-DOS, is physically
  16328.    written to the disk.
  16329.  
  16330.  Call with:
  16331.  
  16332.    AH            = 0DH
  16333.  
  16334.  Returns:
  16335.  
  16336.    Nothing
  16337.  
  16338.  Notes:
  16339.  
  16340.    ■ This function does not update the disk directory for any files that are
  16341.      still open. If your program fails to properly close all files before the
  16342.      disk is removed, and files have changed size, the data forced out to the
  16343.      disk by this function may still be inaccessible because the directory
  16344.      entries will not be correct.
  16345.  
  16346.    ■ [3.3+] Int 21H Function 68H (Commit File) should be used in preference
  16347.      to this function, since it also updates the disk directory.
  16348.  
  16349.  Example:
  16350.  
  16351.    Flush all MS-DOS internal disk buffers.
  16352.  
  16353.            .
  16354.            .
  16355.            .
  16356.            mov     ah,0dh          ; function number
  16357.            int     21h             ; transfer to MS-DOS
  16358.            .
  16359.            .
  16360.            .
  16361.  
  16362.  
  16363.  ────────────────────────────────────────────────────────────────────────────
  16364.  Int 21H                                                                [1.0]
  16365.  Function 0EH (14)
  16366.  Select disk
  16367.  ────────────────────────────────────────────────────────────────────────────
  16368.  
  16369.    Selects the specified drive to be the current, or default, disk drive and
  16370.    returns the total number of logical drives in the system.
  16371.  
  16372.  Call with:
  16373.  
  16374.    AH            = 0EH
  16375.    DL            = drive code (0 = A, 1 = B, etc.)
  16376.  
  16377.  Returns:
  16378.  
  16379.    AL            = number of logical drives in system
  16380.  
  16381.  Notes:
  16382.  
  16383.    ■ [1] 16 drive designators (0 through 0FH) are available.
  16384.  
  16385.    ■ [2] 63 drive designators (0 through 3FH) are available.
  16386.  
  16387.    ■ [3.0+] 26 drive designators (0 through 19H) are available.
  16388.  
  16389.    ■ To preserve upward compatibility, new applications should limit
  16390.      themselves to the drive letters A─Z (0 = A, 1 = B, etc.).
  16391.  
  16392.    ■ Logical drives means the total number of block devices: floppy disks,
  16393.      simulated disk drives (RAMdisks), and hard-disk drives. A single
  16394.      physical hard-disk drive is frequently partitioned into two or more
  16395.      logical drives.
  16396.  
  16397.    ■ [1] [2] In single-drive IBM PC─compatible systems, the value 2 is
  16398.      returned in AL, because PC-DOS supports two logical drives (A: and B:)
  16399.      on the single physical floppy-disk drive. The actual number of physical
  16400.      drives in the system can be determined with ROM BIOS Int 11H.
  16401.  
  16402.    ■ [3.0+] The value returned in AL is either 5 or the drive code
  16403.      corresponding to the LASTDRIVE entry (if any) in CONFIG.SYS, whichever
  16404.      is greater.
  16405.  
  16406.  Example:
  16407.  
  16408.    Make drive B the current (default) disk drive. Save the total number of
  16409.    logical drives in the system in the variable drives.
  16410.  
  16411.    drives  db      0
  16412.            .
  16413.            .
  16414.            .
  16415.            mov     ah,0eh          ; function number
  16416.            mov     dl,1            ; drive 1 = B
  16417.            int     21h             ; transfer to MS-DOS
  16418.            mov     drives,al       ; save total drives
  16419.            .
  16420.            .
  16421.            .
  16422.  
  16423.  
  16424.  ────────────────────────────────────────────────────────────────────────────
  16425.  Int 21H                                                                [1.0]
  16426.  Function 0FH (15)
  16427.  Open file
  16428.  ────────────────────────────────────────────────────────────────────────────
  16429.  
  16430.    Opens a file and makes it available for subsequent read/write operations.
  16431.  
  16432.  Call with:
  16433.  
  16434.    AH            = 0FH
  16435.    DS:DX         = segment:offset of file control block
  16436.  
  16437.  Returns:
  16438.  
  16439.    If function successful (file found)
  16440.  
  16441.    AL            = 00H
  16442.  
  16443.    and FCB filled in by MS-DOS as follows:
  16444.  
  16445.    drive field (offset 00H)              = 1 for drive A, 2 for drive B, etc.
  16446.    current block field (offset 0CH)      = 00H
  16447.    record size field (offset 0EH)        = 0080H
  16448.    [2.0+] size field (offset 10H)        = file size from directory
  16449.    [2.0+] date field (offset 14H)        = date stamp from directory
  16450.    [2.0+] time field (offset 16H)        = time stamp from directory
  16451.  
  16452.    If function unsuccessful (file not found)
  16453.  
  16454.    AL            = 0FFH
  16455.  
  16456.  Notes:
  16457.  
  16458.    ■ If your program is going to use a record size other than 128 bytes, it
  16459.      should set the record-size field at FCB offset 0EH after the file is
  16460.      successfully opened and before any other disk operation.
  16461.  
  16462.    ■ If random access is to be performed, the calling program must also set
  16463.      the FCB relative-record field (offset 21H) after successfully opening
  16464.      the file.
  16465.  
  16466.    ■ For format of directory time and date, see Int 21H Function 57H.
  16467.  
  16468.    ■ [2.0+] Int 21H Function 3DH, which allows full access to the
  16469.      hierarchical directory structure, should be used in preference to this
  16470.      function.
  16471.  
  16472.    ■ [3.0+] If the program is running on a network, the file is opened for
  16473.      read/write access in compatibility sharing mode.
  16474.  
  16475.  Example:
  16476.  
  16477.    Attempt to open the file named QUACK.DAT on the default disk drive.
  16478.  
  16479.    myfcb   db      0               ; drive = default
  16480.            db      'QUACK   '      ; filename, 8 characters
  16481.            db      'DAT'           ; extension, 3 characters
  16482.            db      25 dup (0)      ; remainder of FCB
  16483.            .
  16484.            .
  16485.            .
  16486.            mov     ah,0fh          ; function number
  16487.            mov     dx,seg myfcb    ; address of FCB
  16488.            mov     ds,dx
  16489.            mov     dx,offset myfcb
  16490.            int     21h             ; transfer to MS-DOS
  16491.            or      al,al           ; check status
  16492.            jnz     error           ; jump if open failed
  16493.            .
  16494.            .
  16495.            .
  16496.  
  16497.  
  16498.  ────────────────────────────────────────────────────────────────────────────
  16499.  Int 21H                                                                [1.0]
  16500.  Function 10H (16)
  16501.  Close file
  16502.  ────────────────────────────────────────────────────────────────────────────
  16503.  
  16504.    Closes a file, flushes all MS-DOS internal disk buffers associated with
  16505.    the file to disk, and updates the disk directory if the file has been
  16506.    modified or extended.
  16507.  
  16508.  Call with:
  16509.  
  16510.    AH            = 10H
  16511.    DS:DX         = segment:offset of file control block
  16512.  
  16513.  Returns:
  16514.  
  16515.    If function successful (directory update successful)
  16516.  
  16517.    AL            = 00H
  16518.  
  16519.    If function unsuccessful (file not found in directory)
  16520.  
  16521.    AL            = FFH
  16522.  
  16523.  Notes:
  16524.  
  16525.    ■ [1] [2] MS-DOS versions 1 and 2 do not reliably detect a floppy-disk
  16526.      change, and an error can occur if the user changes disks while a file is
  16527.      still open on that drive. In the worst case, the directory and file
  16528.      allocation table of the newly inserted disk can be damaged or destroyed.
  16529.  
  16530.    ■ [2.0+] Int 21H Function 3EH should be used in preference to this
  16531.      function.
  16532.  
  16533.  Example:
  16534.  
  16535.    Close the file that was previously opened using the file control block
  16536.    named myfcb.
  16537.  
  16538.    myfcb   db      0               ; drive = default
  16539.            db      'QUACK   '      ; filename, 8 characters
  16540.            db      'DAT'           ; extension, 3 characters
  16541.            db      25 dup (0)      ; remainder of FCB
  16542.            .
  16543.            .
  16544.            .
  16545.            mov     ah,10h          ; function number
  16546.            mov     dx,seg myfcb    ; address of FCB
  16547.            mov     ds,dx
  16548.            mov     dx,offset myfcb
  16549.            int     21h             ; transfer to MS-DOS
  16550.            or      al,al           ; check status
  16551.            jnz     error           ; jump if close failed
  16552.            .
  16553.            .
  16554.            .
  16555.  
  16556.  
  16557.  ────────────────────────────────────────────────────────────────────────────
  16558.  Int 21H                                                                [1.0]
  16559.  Function 11H (17)
  16560.  Find first file
  16561.  ────────────────────────────────────────────────────────────────────────────
  16562.  
  16563.    Searches the current directory on the designated drive for a matching
  16564.    filename.
  16565.  
  16566.  Call with:
  16567.  
  16568.    AH            = 11H
  16569.    DS:DX         = segment:offset of file control block
  16570.  
  16571.  Returns:
  16572.  
  16573.    If function successful (matching filename found)
  16574.  
  16575.    AL            = 00H
  16576.  
  16577.    and buffer at current disk transfer area (DTA) address filled in as an
  16578.    unopened normal FCB or extended FCB, depending on which type of FCB was
  16579.    input to function
  16580.  
  16581.    If function unsuccessful (no matching filename found)
  16582.  
  16583.    AL            = FFH
  16584.  
  16585.  Notes:
  16586.  
  16587.    ■ Use Int 21H Function 1AH to set the DTA to point to a buffer of
  16588.      adequate size before calling this function.
  16589.  
  16590.    ■ The wildcard character ? is allowed in the filename in all versions of
  16591.      MS-DOS. In versions 3.0 and later, the wildcard character * may also be
  16592.      used in a filename. If ? or * is used, this function returns the first
  16593.      matching filename.
  16594.  
  16595.    ■ An extended FCB must be used to search for files that have the system,
  16596.      hidden, read-only, directory, or volume-label attributes.
  16597.  
  16598.    ■ If an extended FCB is used, its attribute byte determines the type of
  16599.      search that will be performed. If the attribute byte contains 00H, only
  16600.      ordinary files are found. If the volume-label attribute bit is set, only
  16601.      volume labels will be returned (if any are present). If any other
  16602.      attribute or combination of attributes is set (such as hidden, system,
  16603.      or read-only), those files and all ordinary files will be matched.
  16604.  
  16605.    ■ [2.0+] Int 21H Function 4EH, which allows full access to the
  16606.      hierarchical directory structure, should be used in preference to this
  16607.      function.
  16608.  
  16609.  Example:
  16610.  
  16611.    Search for the first file with the extension .COM in the current
  16612.    directory.
  16613.  
  16614.    buff    db      37 dup (0)      ; receives search result
  16615.  
  16616.    myfcb   db      0               ; drive = default
  16617.            db      '????????'      ; wildcard filename
  16618.            db      'COM'           ; extension = COM
  16619.            db      25 dup (0)      ; remainder of FCB
  16620.            .
  16621.            .
  16622.            .
  16623.                                    ; set DTA address
  16624.            mov     ah,1ah          ; function number
  16625.            mov     dx,seg buff     ; buffer address
  16626.            mov     ds,dx
  16627.            mov     dx,offset buff
  16628.            int     21h             ; transfer to MS-DOS
  16629.  
  16630.                                    ; search for first match
  16631.            mov     ah,11h          ; function number
  16632.            mov     dx,seg myfcb    ; address of FCB
  16633.            mov     ds,dx
  16634.            mov     dx,offset myfcb
  16635.            int     21h             ; transfer to MS-DOS
  16636.            or      al,al           ; check status
  16637.            jnz     error           ; jump if no match
  16638.            .
  16639.            .
  16640.            .
  16641.  
  16642.  
  16643.  ────────────────────────────────────────────────────────────────────────────
  16644.  Int 21H                                                                [1.0]
  16645.  Function 12H (18)
  16646.  Find next file
  16647.  ────────────────────────────────────────────────────────────────────────────
  16648.  
  16649.    Given that a previous call to Int 21H Function 11H has been successful,
  16650.    returns the next matching filename (if any).
  16651.  
  16652.  Call with:
  16653.  
  16654.    AH            = 12H
  16655.    DS:DX         = segment:offset of file control block
  16656.  
  16657.  Returns:
  16658.  
  16659.    If function successful (matching filename found)
  16660.  
  16661.    AL            = 00H
  16662.  
  16663.    and buffer at current disk transfer area (DTA) address set up as an
  16664.    unopened normal FCB or extended FCB, depending on which type of FCB was
  16665.    originally input to Int 21H Function 11H
  16666.  
  16667.    If function unsuccessful (no more matching filenames found)
  16668.  
  16669.    AL            = FFH
  16670.  
  16671.  Notes:
  16672.  
  16673.    ■ This function assumes that the FCB used as input has been properly
  16674.      initialized by a previous call to Int 21H Function 11H (and possible
  16675.      subsequent calls to Int 21H Function 12H) and that the filename or
  16676.      extension being searched for contained at least one wildcard character.
  16677.  
  16678.    ■ As with Int 21H Function 11H, it is important to use Int 21H Function
  16679.      1AH to set the DTA to a buffer of adequate size before calling this
  16680.      function.
  16681.  
  16682.    ■ [2.0+] Int 21H Functions 4EH and 4FH, which allow full access to the
  16683.      hierarchical directory structure, should be used in preference to this
  16684.      function.
  16685.  
  16686.  Example:
  16687.  
  16688.    Assuming a previous successful call to function 11H, search for the next
  16689.    file with the extension .COM in the current directory. If the DTA has not
  16690.    been changed since the previous search, another call to Function 1AH is
  16691.    not necessary.
  16692.  
  16693.    buff    db      37 dup (0)      ; receives search result
  16694.  
  16695.    my_fcb  db      0               ; drive = default
  16696.            db      '????????'      ; wildcard filename
  16697.            db      'COM'           ; extension = COM
  16698.            db      25 dup (0)      ; remainder of FCB
  16699.            .
  16700.            .
  16701.            .
  16702.                                    ; set DTA address
  16703.            mov     ah,1ah          ; function number
  16704.            mov     dx,seg buff     ; buffer address
  16705.            mov     ds,dx
  16706.            mov     dx,offset buff
  16707.            int     21h             ; transfer to MS-DOS
  16708.  
  16709.                                    ; search for next match
  16710.            mov     ah,12h          ; function number
  16711.            mov     dx,seg myfcb    ; address of FCB
  16712.            mov     ds,dx
  16713.            mov     dx,offset myfcb
  16714.            int     21h             ; transfer to MS-DOS
  16715.            or      al,al           ; check status
  16716.            jnz     error           ; jump if no match
  16717.            .
  16718.            .
  16719.            .
  16720.  
  16721.  
  16722.  ────────────────────────────────────────────────────────────────────────────
  16723.  Int 21H                                                                [1.0]
  16724.  Function 13H (19)
  16725.  Delete file
  16726.  ────────────────────────────────────────────────────────────────────────────
  16727.  
  16728.    Deletes all matching files from the current directory on the default or
  16729.    specified disk drive.
  16730.  
  16731.  Call with:
  16732.  
  16733.    AH            = 13H
  16734.    DS:DX         = segment:offset of file control block
  16735.  
  16736.  Returns:
  16737.  
  16738.    If function successful (file or files deleted)
  16739.  
  16740.    AL            = 00H
  16741.  
  16742.    If function unsuccessful (no matching files were found, or at least one
  16743.    matching file was read-only)
  16744.  
  16745.    AL            = FFH
  16746.  
  16747.  Notes:
  16748.  
  16749.    ■ The wildcard character ? is allowed in the filename; if ? is present and
  16750.      there is more than one matching filename, all matching files will be
  16751.      deleted.
  16752.  
  16753.    ■ [2.0+] Int 21H Function 41H, which allows full access to the
  16754.      hierarchical directory structure, should be used in preference to this
  16755.      function.
  16756.  
  16757.    ■ [3.0+] If the program is running on a network, the user must have Create
  16758.      rights to the directory containing the file to be deleted.
  16759.  
  16760.  Example:
  16761.  
  16762.    Delete the file MYFILE.DAT from the current disk drive and directory.
  16763.  
  16764.    myfcb   db      0               ; drive = default
  16765.            db      'MYFILE  '      ; filename, 8 chars
  16766.            db      'DAT'           ; extension, 3 chars
  16767.            db      25 dup (0)      ; remainder of FCB
  16768.            .
  16769.            .
  16770.            .
  16771.            mov     ah,13h          ; function number
  16772.            mov     dx,seg myfcb    ; address of FCB
  16773.            mov     ds,dx
  16774.            mov     dx,offset myfcb
  16775.            int     21h             ; transfer to MS-DOS
  16776.            or      al,al           ; check status
  16777.            jnz     error           ; jump, delete failed
  16778.            .
  16779.            .
  16780.            .
  16781.  
  16782.  
  16783.  ────────────────────────────────────────────────────────────────────────────
  16784.  Int 21H                                                                [1.0]
  16785.  Function 14H (20)
  16786.  Sequential read
  16787.  ────────────────────────────────────────────────────────────────────────────
  16788.  
  16789.    Reads the next sequential block of data from a file, then increments the
  16790.    file pointer appropriately.
  16791.  
  16792.  Call with:
  16793.  
  16794.    AH            = 14H
  16795.    DS:DX         = segment:offset of previously opened file control block
  16796.  
  16797.  Returns:
  16798.  
  16799.    AL            = 00H       if read successful
  16800.                    01H       if end of file
  16801.                    02H       if segment wrap
  16802.                    03H       if partial record read at end of file
  16803.  
  16804.  Notes:
  16805.  
  16806.    ■ The record is read into memory at the current disk transfer area (DTA)
  16807.      address, specified by the most recent call to Int 21H Function 1AH. If
  16808.      the size of the record and the location of the buffer are such that a
  16809.      segment overflow or wraparound would occur, the function fails with a
  16810.      return code of 02H.
  16811.  
  16812.    ■ The number of bytes of data to be read is specified by the record-size
  16813.      field (offset 0EH) of the file control block (FCB).
  16814.  
  16815.    ■ The file location of the data that will be read is specified by the
  16816.      combination of the current block field (offset 0CH) and current record
  16817.      field (offset 20H) of the file control block (FCB). These fields are
  16818.      also automatically incremented by this function.
  16819.  
  16820.    ■ If a partial record is read at the end of file, it is padded to the
  16821.      requested record length with zeros.
  16822.  
  16823.    ■ [3.0+] If the program is running on a network, the user must have Read
  16824.      access rights to the directory containing the file to be read.
  16825.  
  16826.  Example:
  16827.  
  16828.    Read 1024 bytes of data from the file specified by the previously opened
  16829.    file control block myfcb.
  16830.  
  16831.    myfcb   db      0               ; drive = default
  16832.            db      'QUACK   '      ; filename, 8 chars
  16833.            db      'DAT'           ; extension, 3 chars
  16834.            db      25 dup (0)      ; remainder of FCB
  16835.            .
  16836.            .
  16837.            .
  16838.            mov     ah,14h          ; function number
  16839.            mov     dx,seg myfcb    ; address of FCB
  16840.            mov     ds,dx
  16841.            mov     dx,offset myfcb
  16842.                                    ; set record size
  16843.            mov     word ptr myfcb+0eH,1024
  16844.            int     21h             ; transfer to MS-DOS
  16845.            or      al,al           ; check status
  16846.            jnz     error           ; jump if read failed
  16847.            .
  16848.            .
  16849.            .
  16850.  
  16851.  
  16852.  ────────────────────────────────────────────────────────────────────────────
  16853.  Int 21H                                                                [1.0]
  16854.  Function 15H (21)
  16855.  Sequential write
  16856.  ────────────────────────────────────────────────────────────────────────────
  16857.  
  16858.    Writes the next sequential block of data into a file, then increments the
  16859.    file pointer appropriately.
  16860.  
  16861.  Call with:
  16862.  
  16863.    AH            = 15H
  16864.    DS:DX         = segment:offset of previously opened file control block
  16865.  
  16866.  Returns:
  16867.  
  16868.    AL            = 00H       if write successful
  16869.                    01H       if disk is full
  16870.                    02H       if segment wrap
  16871.  
  16872.  Notes:
  16873.  
  16874.    ■ The record is written (logically, not necessarily physically) to the
  16875.      disk from memory at the current disk transfer area (DTA) address,
  16876.      specified by the most recent call to Int 21H Function 1AH. If the size
  16877.      of the record and the location of the buffer are such that a segment
  16878.      overflow or wraparound would occur, the function fails with a return
  16879.      code of 02H.
  16880.  
  16881.    ■ The number of bytes of data to be written is specified by the
  16882.      record-size field (offset 0EH) of the file control block (FCB).
  16883.  
  16884.    ■ The file location of the data that will be written is specified by the
  16885.      combination of the current block field (offset 0CH) and current record
  16886.      field (offset 20H) of the file control block (FCB). These fields are
  16887.      also automatically incremented by this function.
  16888.  
  16889.    ■ [3.0+] If the program is running on a network, the user must have Write
  16890.      access rights to the directory containing the file to be written.
  16891.  
  16892.  Example:
  16893.  
  16894.    Write 1024 bytes of data to the file specified by the previously opened
  16895.    file control block myfcb.
  16896.  
  16897.    myfcb   db      0               ; drive = default
  16898.            db      'QUACK   '      ; filename, 8 chars
  16899.            db      'DAT'           ; extension, 3 chars
  16900.            db      25 dup (0)      ; remainder of FCB
  16901.            .
  16902.            .
  16903.            .
  16904.            mov     ah,15h          ; function number
  16905.            mov     dx,seg myfcb    ; address of FCB
  16906.            mov     ds,dx
  16907.            mov     dx,offset myfcb
  16908.                                    ; set record size
  16909.            mov     word ptr myfcb+0eh,1024
  16910.            int     21h             ; transfer to MS-DOS
  16911.            or      al,al           ; check status
  16912.            jnz     error           ; jump if write failed
  16913.            .
  16914.            .
  16915.            .
  16916.  
  16917.  
  16918.  ────────────────────────────────────────────────────────────────────────────
  16919.  Int 21H                                                                [1.0]
  16920.  Function 16H (22)
  16921.  Create file
  16922.  ────────────────────────────────────────────────────────────────────────────
  16923.  
  16924.    Creates a new directory entry in the current directory or truncates any
  16925.    existing file with the same name to zero length. Opens the file for
  16926.    subsequent read/write operations.
  16927.  
  16928.  Call with:
  16929.  
  16930.    AH            = 16H
  16931.    DS:DX         = segment:offset of unopened file control block
  16932.  
  16933.  Returns:
  16934.  
  16935.    If function successful (file was created or truncated)
  16936.  
  16937.    AL            = 00H
  16938.  
  16939.    and FCB filled in by MS-DOS as follows:
  16940.  
  16941.    drive field (offset 00H)              = 1 for drive A, 2 for drive B, etc.
  16942.    current block field (offset 0CH)      = 00H
  16943.    record size field (offset 0EH)        = 0080H
  16944.    [2.0+] size field (offset 10H)        = file size from directory
  16945.    [2.0+] date field (offset 14H)        = date stamp from directory
  16946.    [2.0+] time field (offset 16H)        = time stamp from directory
  16947.  
  16948.    If function unsuccessful (directory full)
  16949.  
  16950.    AL            = FFH
  16951.  
  16952.  Notes:
  16953.  
  16954.    ■ Since an existing file with the specified name is truncated to zero
  16955.      length (i.e., all data in that file is irretrievably lost), this
  16956.      function must be used with caution.
  16957.  
  16958.    ■ If this function is called with an extended file control block (FCB),
  16959.      the new file may be assigned a special attribute, such as hidden or
  16960.      system, during its creation by setting the appropriate bit in the
  16961.      extended FCB's attribute byte.
  16962.  
  16963.    ■ Since this function also opens the file, a subsequent call to Int 21H
  16964.      Function 0FH is not required.
  16965.  
  16966.    ■ For format of directory time and date, see Int 21H Function 57H.
  16967.  
  16968.    ■ [2.0+] Int 21H Functions 3CH, 5AH, 5BH, and 6CH, which provide full
  16969.      access to the hierarchical directory structure, should be used in
  16970.      preference to this function.
  16971.  
  16972.    ■ [3.0+] If the program is running on a network, the user must have Create
  16973.      rights to the directory that will contain the new file.
  16974.  
  16975.  Example:
  16976.  
  16977.    Create a file in the current directory using the name in the file control
  16978.    block myfcb.
  16979.  
  16980.    myfcb   db      0               ; drive = default
  16981.            db      'QUACK   '      ; filename, 8 chars
  16982.            db      'DAT'           ; extension, 3 chars
  16983.            db      25 dup (0)      ; remainder of FCB
  16984.            .
  16985.            .
  16986.            .
  16987.            mov     ah,16h          ; function number
  16988.            mov     dx,seg myfcb    ; address of FCB
  16989.            mov     ds,dx
  16990.            mov     dx,offset myfcb
  16991.            int     21h             ; transfer to MS-DOS
  16992.            or      al,al           ; check status
  16993.            jnz     error           ; jump if create failed
  16994.            .
  16995.            .
  16996.            .
  16997.  
  16998.  
  16999.  ────────────────────────────────────────────────────────────────────────────
  17000.  Int 21H                                                                [1.0]
  17001.  Function 17H (23)
  17002.  Rename file
  17003.  ────────────────────────────────────────────────────────────────────────────
  17004.  
  17005.    Alters the name of all matching files in the current directory on the disk
  17006.    in the specified drive.
  17007.  
  17008.  Call with:
  17009.  
  17010.    AH            = 17H
  17011.    DS:DX         = segment:offset of "special" file control block
  17012.  
  17013.  Returns:
  17014.  
  17015.    If function successful (one or more files renamed)
  17016.  
  17017.    AL            = 00H
  17018.  
  17019.    If function unsuccessful (no matching files, or new filename matched an
  17020.    existing file)
  17021.  
  17022.    AL            = FFH
  17023.  
  17024.  Notes:
  17025.  
  17026.    ■ The special file control block has a drive code, filename, and extension
  17027.      in the usual position (bytes 0 through 0BH) and a second filename
  17028.      starting 6 bytes after the first (offset 11H).
  17029.  
  17030.    ■ The ? wildcard character can be used in the first filename. Every file
  17031.      matching the first file specification will be renamed to match the
  17032.      second file specification.
  17033.  
  17034.    ■ If the second file specification contains any ? wildcard characters, the
  17035.      corresponding letters in the first filename are left unchanged.
  17036.  
  17037.    ■ The function terminates if the new name to be assigned to a file matches
  17038.      that of an existing file.
  17039.  
  17040.    ■ [2.0+] An extended FCB can be used with this function to rename a
  17041.      directory.
  17042.  
  17043.    ■ [2.0+] Int 21H Function 56H, which allows full access to the
  17044.      hierarchical directory structure, should be used in preference to this
  17045.      function.
  17046.  
  17047.  Example:
  17048.  
  17049.    Rename the file OLDNAME.DAT to NEWNAME.DAT.
  17050.  
  17051.    myfcb   db      0               ; drive = default
  17052.            db      'OLDNAME '      ; old file name, 8 chars
  17053.            db      'DAT'           ; old extension, 3 chars
  17054.            db      6 dup (0)       ; reserved area
  17055.            db      'NEWNAME '      ; new file name, 8 chars
  17056.            db      'DAT'           ; new extension, 3 chars
  17057.            db      14 dup (0)      ; reserved area
  17058.            .
  17059.            .
  17060.            .
  17061.            mov     ah,17h          ; function number
  17062.            mov     dx,seg myfcb    ; address of FCB
  17063.            mov     ds,dx
  17064.            mov     dx,offset myfcb
  17065.            int     21h             ; transfer to MS-DOS
  17066.            or      al,al           ; check status
  17067.            jnz     error           ; jump if rename failed
  17068.            .
  17069.            .
  17070.            .
  17071.  
  17072.  
  17073.  ────────────────────────────────────────────────────────────────────────────
  17074.  Int 21H
  17075.  Function 18H (24)
  17076.  Reserved
  17077.  ────────────────────────────────────────────────────────────────────────────
  17078.  
  17079.  
  17080.  ────────────────────────────────────────────────────────────────────────────
  17081.  Int 21H                                                                [1.0]
  17082.  Function 19H (25)
  17083.  Get current disk
  17084.  ────────────────────────────────────────────────────────────────────────────
  17085.  
  17086.    Returns the drive code of the current, or default, disk drive.
  17087.  
  17088.  Call with:
  17089.  
  17090.    AH            = 19H
  17091.  
  17092.  Returns:
  17093.  
  17094.    AL            = drive code (0 = A, 1 = B, etc.)
  17095.  
  17096.  Notes:
  17097.  
  17098.    ■ To set the default drive, use Int 21H Function 0EH.
  17099.  
  17100.    ■ Some other Int 21H functions use drive codes beginning at 1 (that is,
  17101.      1 = A, 2 = B, etc.) and reserve drive code zero for the default drive.
  17102.  
  17103.  Example:
  17104.  
  17105.    Get the current disk drive and save the code in the variable cdrive.
  17106.  
  17107.    cdrive  db      0               ; current drive code
  17108.            .
  17109.            .
  17110.            .
  17111.            mov     ah,19h          ; function number
  17112.            int     21h             ; transfer to MS-DOS
  17113.            mov     cdrive,al       ; save drive code
  17114.            .
  17115.            .
  17116.            .
  17117.  
  17118.  
  17119.  ────────────────────────────────────────────────────────────────────────────
  17120.  Int 21H                                                                [1.0]
  17121.  Function 1AH (26)
  17122.  Set DTA address
  17123.  ────────────────────────────────────────────────────────────────────────────
  17124.  
  17125.    Specifies the address of the disk transfer area (DTA) to be used for
  17126.    subsequent FCB-related function calls.
  17127.  
  17128.  Call with:
  17129.  
  17130.    AH            = 1AH
  17131.    DS:DX         = segment:offset of disk transfer area
  17132.  
  17133.  Returns:
  17134.  
  17135.    Nothing
  17136.  
  17137.  Notes:
  17138.  
  17139.    ■ If this function is never called by the program, the DTA defaults to a
  17140.      128-byte buffer at offset 0080H in the program segment prefix.
  17141.  
  17142.    ■ In general, it is the programmer's responsibility to ensure that the
  17143.      buffer area specified is large enough for any disk operation that will
  17144.      use it. The only exception to this is that MS-DOS will detect and abort
  17145.      disk transfers that would cause a segment wrap.
  17146.  
  17147.    ■ Int 21H Function 2FH can be used to determine the current disk transfer
  17148.      address.
  17149.  
  17150.    ■ The only handle-type operations that rely on the DTA address are the
  17151.      directory search functions, Int 21H Functions 4EH and 4FH.
  17152.  
  17153.  Example:
  17154.  
  17155.    Set the current disk transfer area address to the buffer labeled buff.
  17156.  
  17157.    buff    db      128 dup (?)
  17158.            .
  17159.            .
  17160.            .
  17161.            mov     ah,1ah          ; function number
  17162.            mov     dx,seg buff     ; address of disk
  17163.            mov     ds,dx           ; transfer area
  17164.            mov     dx,offset buff
  17165.            int     21h             ; transfer to MS-DOS
  17166.            .
  17167.            .
  17168.            .
  17169.  
  17170.  
  17171.  ────────────────────────────────────────────────────────────────────────────
  17172.  Int 21H                                                                [1.0]
  17173.  Function 1BH (27)
  17174.  Get default drive data
  17175.  ────────────────────────────────────────────────────────────────────────────
  17176.  
  17177.    Obtains selected information about the default disk drive and a pointer to
  17178.    the media identification byte from its file allocation table.
  17179.  
  17180.  Call with:
  17181.  
  17182.    AH            = 1BH
  17183.  
  17184.  Returns:
  17185.  
  17186.    If function successful
  17187.  
  17188.    AL            = sectors per cluster
  17189.    DS:BX         = segment:offset of media ID byte
  17190.    CX            = size of physical sector (bytes)
  17191.    DX            = number of clusters for default drive
  17192.  
  17193.    If function unsuccessful (invalid drive or critical error)
  17194.  
  17195.    AL            = FFH
  17196.  
  17197.  Notes:
  17198.  
  17199.    ■ The media ID byte has the following meanings:
  17200.  
  17201.    0F0H           3.5-inch double-sided, 18 sectors
  17202.                   or "other"
  17203.    0F8H           fixed disk
  17204.    0F9H           5.25-inch double-sided, 15 sectors
  17205.                   or 3.5-inch double-sided, 9 sectors
  17206.    0FCH           5.25-inch single-sided, 9 sectors
  17207.    0FDH           5.25-inch double-sided, 9 sectors
  17208.    0FEH           5.25-inch single-sided, 8 sectors
  17209.    0FFH           5.25-inch double-sided, 8 sectors
  17210.  
  17211.    ■ To obtain information about disks other than the one in the default
  17212.      drive, use Int 21H Function 1CH or 36H.
  17213.  
  17214.    ■ [1] The address returned in DS:BX points to a copy of the first sector
  17215.      of the actual FAT, with the media ID byte in the first byte.
  17216.  
  17217.    ■ [2.0+] The address returned in DS:BX points only to a copy of the media
  17218.      ID byte from the disk's FAT; the memory above that address cannot be
  17219.      assumed to contain the FAT or any other useful information. If direct
  17220.      access to the FAT is required, use Int 25H to read it into memory.
  17221.  
  17222.  Example:
  17223.  
  17224.    Determine whether the current disk drive is fixed or removable.
  17225.  
  17226.            .
  17227.            .
  17228.            .
  17229.            mov     ah,1bh          ; function number
  17230.            int     21h             ; transfer to MS-DOS
  17231.  
  17232.                                    ; check media ID byte
  17233.            cmp     byte ptr [bx],0f8h
  17234.            je      fixed           ; jump if fixed disk
  17235.            jmp     floppy          ; else assume floppy
  17236.            .
  17237.            .
  17238.            .
  17239.  
  17240.  
  17241.  ────────────────────────────────────────────────────────────────────────────
  17242.  Int 21H                                                                [2.0]
  17243.  Function 1CH (28)
  17244.  Get drive data
  17245.  ────────────────────────────────────────────────────────────────────────────
  17246.  
  17247.    Obtains allocation information about the specified disk drive and a
  17248.    pointer to the media identification byte from its file allocation table.
  17249.  
  17250.  Call with:
  17251.  
  17252.    AH            = 1CH
  17253.    DL            = drive code (0 = default, 1 = A, etc.)
  17254.  
  17255.  Returns:
  17256.  
  17257.    If function successful
  17258.  
  17259.    AL            = sectors per cluster
  17260.    DS:BX         = segment:offset of media ID byte
  17261.    CX            = size of physical sector (bytes)
  17262.    DX            = number of clusters for default or specified drive
  17263.  
  17264.    If function unsuccessful (invalid drive or critical error)
  17265.  
  17266.    AL            = FFH
  17267.  
  17268.  Notes:
  17269.  
  17270.    ■ The media ID byte has the following meanings:
  17271.  
  17272.    0F0H           3.5-inch double-sided, 18 sectors
  17273.                   or "other"
  17274.    0F8H           fixed disk
  17275.    0F9H           5.25-inch double-sided, 15 sectors
  17276.                   or 3.5-inch double-sided, 9 sectors
  17277.    0FCH           5.25-inch single-sided, 9 sectors
  17278.    0FDH           5.25-inch double-sided, 9 sectors
  17279.    0FEH           5.25-inch single-sided, 8 sectors
  17280.    0FFH           5.25-inch double-sided, 8 sectors
  17281.  
  17282.    ■ In general, this call is identical to Int 21H Function 1BH, except for
  17283.      the ability to designate a specific disk drive. See also Int 21H
  17284.      Function 36H, which returns similar information.
  17285.  
  17286.    ■ [1] The address returned in DS:BX points to a copy of the first sector
  17287.      of the actual FAT, with the media ID byte in the first byte.
  17288.  
  17289.    ■ [2.0+] The address returned in DS:BX points only to a copy of the media
  17290.      ID byte from the disk's FAT; the memory above that address cannot be
  17291.      assumed to contain the FAT or any other useful information. If direct
  17292.      access to the FAT is required, use Int 25H to read it into memory.
  17293.  
  17294.  Example:
  17295.  
  17296.    Determine whether disk drive C is fixed or removable.
  17297.  
  17298.            .
  17299.            .
  17300.            .
  17301.            mov     ah,1ch          ; function number
  17302.            mov     dl,3            ; drive code 3 = C
  17303.            int     21h             ; transfer to MS-DOS
  17304.  
  17305.                                    ; check media ID byte
  17306.            cmp     byte ptr ds:[bx],0f8h
  17307.            je      fixed           ; jump if fixed disk
  17308.            jmp     floppy          ; else assume floppy
  17309.            .
  17310.            .
  17311.            .
  17312.  
  17313.  
  17314.  ────────────────────────────────────────────────────────────────────────────
  17315.  Int 21H
  17316.  Function 1DH (29)
  17317.  Reserved
  17318.  ────────────────────────────────────────────────────────────────────────────
  17319.  
  17320.  
  17321.  ────────────────────────────────────────────────────────────────────────────
  17322.  Int 21H
  17323.  Function 1EH (30)
  17324.  Reserved
  17325.  ────────────────────────────────────────────────────────────────────────────
  17326.  
  17327.  
  17328.  ────────────────────────────────────────────────────────────────────────────
  17329.  Int 21H
  17330.  Function 1FH (31)
  17331.  Reserved
  17332.  ────────────────────────────────────────────────────────────────────────────
  17333.  
  17334.  
  17335.  ────────────────────────────────────────────────────────────────────────────
  17336.  Int 21H
  17337.  Function 20H (32)
  17338.  Reserved
  17339.  ────────────────────────────────────────────────────────────────────────────
  17340.  
  17341.  
  17342.  ────────────────────────────────────────────────────────────────────────────
  17343.  Int 21H                                                                [1.0]
  17344.  Function 21H (33)
  17345.  Random read
  17346.  ────────────────────────────────────────────────────────────────────────────
  17347.  
  17348.    Reads a selected record from a file into memory.
  17349.  
  17350.  Call with:
  17351.  
  17352.    AH            = 21H
  17353.    DS:DX         = segment:offset of previously opened file control block
  17354.  
  17355.  Returns:
  17356.  
  17357.    AL            = 00H       if read successful
  17358.                    01H       if end of file
  17359.                    02H       if segment wrap, read canceled
  17360.                    03H       if partial record read at end of file
  17361.  
  17362.  Notes:
  17363.  
  17364.    ■ The record is read into memory at the current disk transfer area
  17365.      address, specified by the most recent call to Int 21H Function 1AH. It
  17366.      is the programmer's responsibility to ensure that this area is large
  17367.      enough for any record that will be transferred. If the size and location
  17368.      of the buffer are such that a segment overflow or wraparound would
  17369.      occur, the function fails with a return code of 02H.
  17370.  
  17371.    ■ The file location of the data to be read is determined by the
  17372.      combination of the relative-record field (offset 21H) and the
  17373.      record-size field (offset 0EH) of the FCB. The default record size is
  17374.      128 bytes.
  17375.  
  17376.    ■ The current block field (offset 0CH) and current record field (offset
  17377.      20H) are updated to agree with the relative-record field as a side
  17378.      effect of the function.
  17379.  
  17380.    ■ The relative-record field of the FCB is not incremented by this
  17381.      function; it is the responsibility of the application to update the FCB
  17382.      appropriately if it wishes to read successive records. Compare with Int
  17383.      21H Function 27H, which can read multiple records with one function
  17384.      call and automatically increments the relative-record field.
  17385.  
  17386.    ■ If a partial record is read at end of file, it is padded to the
  17387.      requested record length with zeros.
  17388.  
  17389.    ■ [3.0+] If the program is running on a network, the user must have Read
  17390.      access rights to the directory containing the file to be read.
  17391.  
  17392.  Example:
  17393.  
  17394.    Open the file MYFILE.DAT, set the record length to 1024 bytes, then read
  17395.    record number 4 from the file into the buffer named buff.
  17396.  
  17397.    myfcb   db      0               ; drive = default
  17398.            db      'MYFILE  '      ; filename, 8 chars
  17399.            db      'DAT'           ; extension, 3 chars
  17400.            db      25 dup (0)      ; remainder of FCB
  17401.  
  17402.    buff    db      1024 dup (?)    ; receives read data
  17403.            .
  17404.            .
  17405.            .
  17406.                                    ; open the file
  17407.            mov     ah,0fh          ; function number
  17408.            mov     dx,seg myfcb    ; address of FCB
  17409.            mov     ds,dx
  17410.            mov     dx,offset myfcb
  17411.            int     21h             ; transfer to MS-DOS
  17412.            or      al,al           ; check open status
  17413.            jnz     error           ; jump if no file
  17414.  
  17415.                                    ; set DTA address
  17416.            mov     ah,1ah          ; function number
  17417.            mov     dx,offset buff  ; read buffer address
  17418.            int     21h             ; transfer to MS-DOS
  17419.  
  17420.                                    ; set record size
  17421.            mov     word ptr myfcb+0eh,1024
  17422.  
  17423.                                    ; set record number
  17424.            mov     word ptr myfcb+21h,4
  17425.            mov     word ptr myfcb+23h,0
  17426.  
  17427.                                    ; read the record
  17428.            mov     ah,21h          ; function number
  17429.            mov     dx,offset myfcb ; address of FCB
  17430.            int     21h             ; transfer to MS-DOS
  17431.            or      al,al           ; check status
  17432.            jnz     error           ; jump if read failed
  17433.            .
  17434.            .
  17435.            .
  17436.  
  17437.  
  17438.  ────────────────────────────────────────────────────────────────────────────
  17439.  Int 21H                                                                [1.0]
  17440.  Function 22H (34)
  17441.  Random write
  17442.  ────────────────────────────────────────────────────────────────────────────
  17443.  
  17444.    Writes data from memory into a selected record in a file.
  17445.  
  17446.  Call with:
  17447.  
  17448.    AH            = 22H
  17449.    DS:DX         = segment:offset of previously opened file control block
  17450.  
  17451.  Returns:
  17452.  
  17453.    AL            = 00H       if write successful
  17454.                    01H       if disk full
  17455.                    02H       if segment wrap, write canceled
  17456.  
  17457.  Notes:
  17458.  
  17459.    ■ The record is written (logically, not necessarily physically) to the
  17460.      file from memory at the current disk transfer address, specified by the
  17461.      most recent call to Int 21H Function 1AH. If the size and location of
  17462.      the buffer are such that a segment overflow or wraparound would occur,
  17463.      the function fails with a return code of 02H.
  17464.  
  17465.    ■ The file location of the data to be written is determined by the
  17466.      combination of the relative-record field (offset 21H) and the
  17467.      record-size field (offset 0EH) of the FCB. The default record size is
  17468.      128 bytes.
  17469.  
  17470.    ■ The current block field (offset 0CH) and current record field (offset
  17471.      20H) are updated to agree with the relative-record field as a side
  17472.      effect of the function.
  17473.  
  17474.    ■ The relative-record field of the FCB is not incremented by this
  17475.      function; it is the responsibility of the application to update the FCB
  17476.      appropriately if it wishes to write successive records. Compare with Int
  17477.      21H Function 28H, which can write multiple records with one function
  17478.      call and automatically increments the relative-record field.
  17479.  
  17480.    ■ If a record is written beyond the current end of file, the space between
  17481.      the old end of file and the new record is allocated but not initialized.
  17482.  
  17483.    ■ [3.0+] If the program is running on a network, the user must have Write
  17484.      access rights to the directory containing the file to be written.
  17485.  
  17486.  Example:
  17487.  
  17488.    Open the file MYFILE.DAT, set the record length to 1024 bytes, write
  17489.    record number 4 into the file from the buffer named buff, then close the
  17490.    file.
  17491.  
  17492.    myfcb   db      0               ; drive = default
  17493.            db      'MYFILE  '      ; filename, 8 chars
  17494.            db      'DAT'           ; extension, 3 chars
  17495.            db      25 dup (0)      ; remainder of FCB
  17496.  
  17497.    buff    db      1024 dup (?)    ; buffer for write
  17498.            .
  17499.            .
  17500.            .
  17501.                                    ; open the file
  17502.            mov     ah,0fh          ; function number
  17503.            mov     dx,seg myfcb    ; address of FCB
  17504.            mov     ds,dx
  17505.            mov     dx,offset myfcb
  17506.            int     21h             ; transfer to MS-DOS
  17507.            or      al,al           ; check status
  17508.            jnz     error           ; jump if no file
  17509.  
  17510.                                    ; set DTA address
  17511.            mov     dx,offset buff  ; buffer address
  17512.            mov     ah,1ah          ; function number
  17513.            int     21h             ; transfer to MS-DOS
  17514.  
  17515.                                    ; set record size
  17516.            mov     word ptr myfcb+0eh,1024
  17517.  
  17518.                                    ; set record number
  17519.            mov     word ptr myfcb+21h,4
  17520.            mov     word ptr myfcb+23h,0
  17521.  
  17522.                                    ; write the record
  17523.            mov     ah,22h          ; function number
  17524.            mov     dx,offset myfcb ; address of FCB
  17525.            int     21h             ; transfer to MS-DOS
  17526.            or      al,al           ; check status
  17527.            jnz     error           ; jump if write failed
  17528.  
  17529.                                    ; close the file
  17530.            mov     ah,10h          ; function number
  17531.            mov     dx,offset myfcb ; address of FCB
  17532.            int     21h             ; transfer to MS-DOS
  17533.            or      al,al           ; check status
  17534.            jnz     error           ; jump if close failed
  17535.            .
  17536.            .
  17537.            .
  17538.  
  17539.  
  17540.  ────────────────────────────────────────────────────────────────────────────
  17541.  Int 21H                                                                [1.0]
  17542.  Function 23H (35)
  17543.  Get file size
  17544.  ────────────────────────────────────────────────────────────────────────────
  17545.  
  17546.    Searches for a matching file in the current directory; if one is found,
  17547.    updates the FCB with the file's size in terms of number of records.
  17548.  
  17549.  Call with:
  17550.  
  17551.    AH            = 23H
  17552.    DS:DX         = segment:offset of unopened file control block
  17553.  
  17554.  Returns:
  17555.  
  17556.    If function successful (matching file found)
  17557.  
  17558.    AL            = 00H
  17559.  
  17560.    and FCB relative-record field (offset 21H) set to the number of records in
  17561.    the file, rounded up if necessary to the next complete record
  17562.  
  17563.    If function unsuccessful (no matching file found)
  17564.  
  17565.    AL            = FFH
  17566.  
  17567.  Notes:
  17568.  
  17569.    ■ An appropriate value must be placed in the FCB record-size field (offset
  17570.      0EH) before calling this function. There is no default record size for
  17571.      this function. Compare with the FCB-related open and create functions
  17572.      (Int 21H Functions 0FH and 16H), which initialize the FCB for a
  17573.      default record size of 128 bytes.
  17574.  
  17575.    ■ The record-size field can be set to 1 to find the size of the file in
  17576.      bytes.
  17577.  
  17578.    ■ Because record numbers are zero based, this function can be used to
  17579.      position the FCB's file pointer to the end of file.
  17580.  
  17581.  Example:
  17582.  
  17583.    Determine the size in bytes of the file MYFILE.DAT and leave the result in
  17584.    registers DX:AX.
  17585.  
  17586.    myfcb   db      0               ; drive = default
  17587.            db      'MYFILE  '      ; filename, 8 chars
  17588.            db      'DAT'           ; extension, 3 chars
  17589.            db      25 dup (0)      ; remainder of FCB
  17590.            .
  17591.            .
  17592.            .
  17593.            mov     ah,23h          ; function number
  17594.            mov     dx,seg myfcb    ; address of FCB
  17595.            mov     ds,dx
  17596.            mov     dx,offset myfcb
  17597.                                    ; record size = 1 byte
  17598.            mov     word ptr myfcb+0eh,1
  17599.            int     21h             ; transfer to MS-DOS
  17600.            or      al,al           ; check status
  17601.            jnz     error           ; jump if no file
  17602.  
  17603.                                    ; get file size in bytes
  17604.            mov     ax,word ptr myfcb+21h
  17605.            mov     dx,word ptr myfcb+23h
  17606.            .
  17607.            .
  17608.            .
  17609.  
  17610.  
  17611.  ────────────────────────────────────────────────────────────────────────────
  17612.  Int 21H                                                                [1.0]
  17613.  Function 24H (36)
  17614.  Set relative record number
  17615.  ────────────────────────────────────────────────────────────────────────────
  17616.  
  17617.    Sets the relative-record-number field of a file control block (FCB) to
  17618.    correspond to the current file position as recorded in the opened FCB.
  17619.  
  17620.  Call with:
  17621.  
  17622.    AH            = 24H
  17623.    DS:DX         = segment:offset of previously opened file control block
  17624.  
  17625.  Returns:
  17626.  
  17627.    AL is destroyed (other registers not affected)
  17628.  
  17629.    FCB relative-record field (offset 21H) updated
  17630.  
  17631.  Notes:
  17632.  
  17633.    ■ This function is used when switching from sequential to random I/O
  17634.      within a file. The contents of the relative-record field (offset 21H)
  17635.      are derived from the record size (offset 0EH), current block (offset
  17636.      0CH), and current record (offset 20H) fields of the file control block.
  17637.  
  17638.    ■ All four bytes of the FCB relative-record field (offset 21H) should be
  17639.      initialized to zero before calling this function.
  17640.  
  17641.  Example:
  17642.  
  17643.    After a series of sequential record transfers have been performed using
  17644.    the file control block myfcb, obtain the current relative-record position
  17645.    in the file and leave the record number in DX.
  17646.  
  17647.    myfcb   db      0               ; drive = default
  17648.            db      'MYFILE  '      ; filename, 8 chars
  17649.            db      'DAT'           ; extension, 3 chars
  17650.            db      25 dup (0)      ; remainder of FCB
  17651.            .
  17652.            .
  17653.            .
  17654.            mov     dx,seg myfcb    ; make FCB addressable
  17655.            mov     ds,dx
  17656.  
  17657.                                    ; initialize relative
  17658.                                    ; record field to zero
  17659.            mov     word ptr myfcb+21h,0
  17660.            mov     word ptr myfcb+23h,0
  17661.  
  17662.                                    ; now set record number
  17663.            mov     ah,24h          ; function number
  17664.            mov     dx,offset myfcb ; address of FCB
  17665.            int     21h             ; transfer to MS-DOS
  17666.  
  17667.                                    ; load record number in DX
  17668.            mov     dx,word ptr myfcb+21h
  17669.            .
  17670.            .
  17671.            .
  17672.  
  17673.  
  17674.  ────────────────────────────────────────────────────────────────────────────
  17675.  Int 21H                                                                [1.0]
  17676.  Function 25H (37)
  17677.  Set interrupt vector
  17678.  ────────────────────────────────────────────────────────────────────────────
  17679.  
  17680.    Initializes a CPU interrupt vector to point to an interrupt handling
  17681.    routine.
  17682.  
  17683.  Call with:
  17684.  
  17685.    AH            = 25H
  17686.    AL            = interrupt number
  17687.    DS:DX         = segment:offset of interrupt handling routine
  17688.  
  17689.  Returns:
  17690.  
  17691.    Nothing
  17692.  
  17693.  Notes:
  17694.  
  17695.    ■ This function should be used in preference to direct editing of the
  17696.      interrupt-vector table by well-behaved applications.
  17697.  
  17698.    ■ Before an interrupt vector is modified, its original value should be
  17699.      obtained with Int 21H Function 35H and saved, so that it can be restored
  17700.      using this function before program termination.
  17701.  
  17702.  Example:
  17703.  
  17704.    Install a new interrupt handler, named zdiv, for "divide by zero" CPU
  17705.    exceptions.
  17706.  
  17707.            .
  17708.            .
  17709.            .
  17710.            mov     ah,25h          ; function number
  17711.            mov     al,0            ; interrupt number
  17712.            mov     dx,seg zdiv     ; address of handler
  17713.            mov     ds,dx
  17714.            mov     dx,offset zdiv
  17715.            int     21h             ; transfer to MS-DOS
  17716.            .
  17717.            .
  17718.            .
  17719.    zdiv:                           ; int 00h handler
  17720.            iret                    ; (does nothing)
  17721.  
  17722.  
  17723.  ────────────────────────────────────────────────────────────────────────────
  17724.  Int 21H                                                                [1.0]
  17725.  Function 26H (38)
  17726.  Create new PSP
  17727.  ────────────────────────────────────────────────────────────────────────────
  17728.  
  17729.    Copies the program segment prefix (PSP) of the currently executing program
  17730.    to a specified segment address in free memory, then updates the new PSP to
  17731.    make it usable by another program.
  17732.  
  17733.  Call with:
  17734.  
  17735.    AH            = 26H
  17736.    DX            = segment of new program segment prefix
  17737.  
  17738.  Returns:
  17739.  
  17740.    Nothing
  17741.  
  17742.  Notes:
  17743.  
  17744.    ■ After the executing program's PSP is copied into the new segment, the
  17745.      memory size information in the new PSP is updated appropriately and the
  17746.      current contents of the termination (Int 22H), Ctrl-C handler (Int 23H),
  17747.      and critical-error handler (Int 24H) vectors are saved starting at
  17748.      offset 0AH.
  17749.  
  17750.    ■ This function does not load another program or in itself cause one to be
  17751.      executed.
  17752.  
  17753.    ■ [2.0+] Int 21H Function 4BH (EXEC), which can be used to load and
  17754.      execute programs or overlays in either .COM or .EXE format, should be
  17755.      used in preference to this function.
  17756.  
  17757.  Example:
  17758.  
  17759.    Create a new program segment prefix 64 KB above the currently executing
  17760.    program. This example assumes that the running program was loaded as a
  17761.    .COM file so that the CS register points to its PSP throughout its
  17762.    execution. If the running program was loaded as a .EXE file, the address
  17763.    of the PSP must be obtained with Int 21H Function 62H (under MS-DOS 3.0
  17764.    or later) or by saving the original contents of the DS or ES registers at
  17765.    entry.
  17766.  
  17767.            .
  17768.            .
  17769.            .
  17770.            mov     ah,26h          ; function number
  17771.            mov     dx,cs           ; PSP segment of
  17772.                                    ; this program
  17773.            add     dx,1000h        ; add 64 KB as
  17774.                                    ; paragraph address
  17775.            int     21h             ; transfer to MS-DOS
  17776.            .
  17777.            .
  17778.            .
  17779.  
  17780.  
  17781.  ────────────────────────────────────────────────────────────────────────────
  17782.  Int 21H                                                                [1.0]
  17783.  Function 27H (39)
  17784.  Random block read
  17785.  ────────────────────────────────────────────────────────────────────────────
  17786.  
  17787.    Reads one or more sequential records from a file into memory, starting at
  17788.    a designated file location.
  17789.  
  17790.  Call with:
  17791.  
  17792.    AH            = 27H
  17793.    CX            = number of records to read
  17794.    DS:DX         = segment:offset of previously opened file control block
  17795.  
  17796.  Returns:
  17797.  
  17798.    AL            = 00H       if all requested records read
  17799.                    01H       if end of file
  17800.                    02H       if segment wrap
  17801.                    03H       if partial record read at end of file
  17802.  
  17803.    CX            = actual number of records read
  17804.  
  17805.  Notes:
  17806.  
  17807.    ■ The records are read into memory at the current disk transfer area
  17808.      address, specified by the most recent call to Int 21H Function 1AH. It
  17809.      is the programmer's responsibility to ensure that this area is large
  17810.      enough for the group of records that will be transferred. If the size
  17811.      and location of the buffer are such that a segment overflow or
  17812.      wraparound would occur, the function fails with a return code of 02H.
  17813.  
  17814.    ■ The file location of the data to be read is determined by the
  17815.      combination of the relative-record field (offset 21H) and the
  17816.      record-size field (offset 0EH) of the FCB. The default record size is
  17817.      128 bytes.
  17818.  
  17819.    ■ After the disk transfer is performed, the current block (offset 0CH),
  17820.      current record (offset 20H), and relative-record (offset 21H) fields of
  17821.      the FCB are updated to point to the next record in the file.
  17822.  
  17823.    ■ If a partial record is read at the end of file, the remainder of the
  17824.      record is padded with zeros.
  17825.  
  17826.    ■ Compare with Int 21H Function 21H, which transfers only one record per
  17827.      function call and does not update the FCB relative-record field.
  17828.  
  17829.    ■ [3.0+] If the program is running on a network, the user must have Read
  17830.      access rights to the directory containing the file to be read.
  17831.  
  17832.  Example:
  17833.  
  17834.    Read four 1024-byte records starting at record number 8 into the buffer
  17835.    named buff, using the file control block myfcb.
  17836.  
  17837.    myfcb   db      0               ; drive = default
  17838.            db      'MYFILE  '      ; filename, 8 chars
  17839.            db      'DAT'           ; extension, 3 chars
  17840.            db      25 dup (0)      ; remainder of FCB
  17841.  
  17842.    buff    db      4096 dup (?)    ; buffer for data
  17843.            .
  17844.            .
  17845.            .
  17846.                                    ; set DTA address
  17847.            mov     ah,1ah          ; function number
  17848.            mov     dx,seg buff     ; address of buffer
  17849.            mov     ds,dx
  17850.            mov     dx,offset buff
  17851.            int     21h             ; transfer to MS-DOS
  17852.  
  17853.                                    ; set relative-record number
  17854.            mov     word ptr myfcb+21h,8
  17855.            mov     word ptr myfcb+23h,0
  17856.  
  17857.                                    ; set record size
  17858.            mov     word ptr myfcb+0eh,1024
  17859.  
  17860.                                    ; read the records
  17861.            mov     ah,27h          ; function number
  17862.            mov     cx,4            ; number of records
  17863.            mov     dx,offset myfcb ; address of FCB
  17864.            int     21h             ; transfer to MS-DOS
  17865.            or      al,al           ; check status
  17866.            jnz     error           ; jump if read error
  17867.            .
  17868.            .
  17869.            .
  17870.  
  17871.  
  17872.  ────────────────────────────────────────────────────────────────────────────
  17873.  Int 21H                                                                [1.0]
  17874.  Function 28H (40)
  17875.  Random block write
  17876.  ────────────────────────────────────────────────────────────────────────────
  17877.  
  17878.    Writes one or more sequential records from memory to a file, starting at a
  17879.    designated file location.
  17880.  
  17881.  Call with:
  17882.  
  17883.    AH            = 28H
  17884.    CX            = number of records to write
  17885.    DS:DX         = segment:offset of previously opened file control block
  17886.  
  17887.  Returns:
  17888.  
  17889.    AL            = 00H       if all requested records written
  17890.                    01H       if disk full
  17891.                    02H       if segment wrap
  17892.  
  17893.    CX            = actual number of records written
  17894.  
  17895.  Notes:
  17896.  
  17897.    ■ The records are written (logically, not necessarily physically) to disk
  17898.      from memory at the current disk transfer area address, specified by the
  17899.      most recent call to Int 21H Function 1AH. If the size and location of
  17900.      the buffer are such that a segment overflow or wraparound would occur,
  17901.      the function fails with a return code of 02H.
  17902.  
  17903.    ■ The file location of the data to be written is determined by the
  17904.      combination of the relative-record field (offset 21H) and the
  17905.      record-size field (offset 0EH) of the FCB. The default record size is
  17906.      128 bytes.
  17907.  
  17908.    ■ After the disk transfer is performed, the current block (offset 0CH),
  17909.      current record (offset 20H), and relative-record (offset 21H) fields of
  17910.      the FCB are updated to point to the next record in the file.
  17911.  
  17912.    ■ If this function is called with CX = 0, no data is written to the disk
  17913.      but the file is extended or truncated to the length specified by
  17914.      combination of the record-size (offset 0EH) and the relative-record
  17915.      (offset 21H) fields of the FCB.
  17916.  
  17917.    ■ Compare with Int 21H Function 22H, which transfers only one record per
  17918.      function call and does not update the FCB relative-record field.
  17919.  
  17920.    ■ [3.0+] If the program is running on a network, the user must have Write
  17921.      access rights to the directory containing the file to be written.
  17922.  
  17923.  Example:
  17924.  
  17925.    Write four 1024-byte records, starting at record number 8, to disk from
  17926.    the buffer named buff, using the file control block myfcb.
  17927.  
  17928.    myfcb   db      0               ; drive = default
  17929.            db      'MYFILE  '      ; filename, 8 chars
  17930.            db      'DAT'           ; extension, 3 chars
  17931.            db      25 dup (0)      ; remainder of FCB
  17932.  
  17933.    buff    db      4096 dup (?)    ; buffer for data
  17934.            .
  17935.            .
  17936.            .
  17937.                                    ; set DTA address
  17938.            mov     ah,1ah          ; function number
  17939.            mov     dx,seg buff     ; address of buffer
  17940.            mov     ds,dx
  17941.            mov     dx,offset buff
  17942.            int     21h             ; transfer to MS-DOS
  17943.  
  17944.                                    ; set relative-record number
  17945.            mov     word ptr myfcb+21h,8
  17946.            mov     word ptr myfcb+23h,0
  17947.  
  17948.                                    ; set record size
  17949.            mov     word ptr myfcb+0eh,1024
  17950.  
  17951.                                    ; write the records
  17952.            mov     ah,28h          ; function number
  17953.            mov     cx,4            ; number of records
  17954.            mov     dx,offset myfcb ; address of FCB
  17955.            int     21h             ; transfer to MS-DOS
  17956.            or      al,al           ; check status
  17957.            jnz     error           ; jump if write error
  17958.            .
  17959.            .
  17960.            .
  17961.  
  17962.  
  17963.  ────────────────────────────────────────────────────────────────────────────
  17964.  Int 21H                                                                [1.0]
  17965.  Function 29H (41)
  17966.  Parse filename
  17967.  ────────────────────────────────────────────────────────────────────────────
  17968.  
  17969.    Parses a text string into the various fields of a file control block
  17970.    (FCB).
  17971.  
  17972.  Call with:
  17973.  
  17974.    AH            = 29H
  17975.    AL            = flags to control parsing
  17976.  
  17977.                   Bit 3        = 1    if extension field in FCB will be
  17978.                                       modified only if an extension is
  17979.                                       specified in the string being parsed.
  17980.                                = 0    if extension field in FCB will be
  17981.                                       modified regardless; if no extension is
  17982.                                       present in the parsed string, FCB
  17983.                                       extension is set to ASCII blanks.
  17984.                   Bit 2        = 1    if filename field in FCB will be
  17985.                                       modified only if a filename is
  17986.                                       specified in the string being parsed.
  17987.                                = 0    if filename field in FCB will be
  17988.                                       modified regardless; if no filename is
  17989.                                       present in the parsed string, FCB
  17990.                                       filename is set to ASCII blanks.
  17991.                   Bit 1        = 1    if drive ID byte in FCB will be
  17992.                                       modified only if a drive was specified
  17993.                                       in the string being parsed.
  17994.                                = 0    if the drive ID byte in FCB will be
  17995.                                       modified regardless; if no drive
  17996.                                       specifier is present in the parsed
  17997.                                       string, FCB drive-code field is set to
  17998.                                       0 (default).
  17999.                   Bit 0        = 1    if leading separators will be scanned
  18000.                                       off (ignored).
  18001.                                = 0    if leading separators will not be
  18002.                                       scanned off.
  18003.  
  18004.    DS:SI         = segment:offset of string
  18005.    ES:DI         = segment:offset of file control block
  18006.  
  18007.  Returns:
  18008.  
  18009.    AL            = 00H if no wildcard characters encountered
  18010.                    01H if parsed string contained wildcard characters
  18011.                    FFH if drive specifier invalid
  18012.    DS:SI         = segment:offset of first character after parsed filename
  18013.    ES:DI         = segment:offset of formatted unopened file control block
  18014.  
  18015.  Notes:
  18016.  
  18017.    ■ This function regards the following as separator characters:
  18018.  
  18019.    [1]        : . ; ,      = + tab space / " [ ]
  18020.    [2.0+]     : . ; ,      = + tab space
  18021.  
  18022.    ■ This function regards all control characters and the following as
  18023.      terminator characters:
  18024.  
  18025.               : . ; ,      = + tab space < > | / " [ ]
  18026.  
  18027.    ■ If no valid filename is present in the string to be parsed, upon return
  18028.      ES:DI + 1 points to an ASCII blank.
  18029.  
  18030.    ■ If the * wildcard character occurs in a filename or extension, it and
  18031.      all remaining characters in the corresponding field in the FCB are set
  18032.      to ?.
  18033.  
  18034.    ■ This function (and file control blocks in general) cannot be used with
  18035.      file specifications that include a path.
  18036.  
  18037.  Example:
  18038.  
  18039.    Parse the string fname into the file control block myfcb.
  18040.  
  18041.    fname   db      'D:QUACK.DAT',0 ; filename to be parsed
  18042.  
  18043.    myfcb   db      37 dup (0)      ; becomes file control block
  18044.            .
  18045.            .
  18046.            .
  18047.            mov     ah,29h          ; function number
  18048.            mov     al,01h          ; skip leading separators
  18049.            mov     si,seg fname    ; address of filename
  18050.            mov     ds,si
  18051.            mov     si,offset fname
  18052.            mov     di,seg myfcb    ; address of FCB
  18053.            mov     es,di
  18054.            mov     di,offset myfcb
  18055.            int     21h             ; transfer to MS-DOS
  18056.            cmp     al,0ffh         ; check status
  18057.            je      error           ; jump, drive invalid
  18058.            .
  18059.            .
  18060.            .
  18061.  
  18062.  
  18063.  ────────────────────────────────────────────────────────────────────────────
  18064.  Int 21H                                                                [1.0]
  18065.  Function 2AH (42)
  18066.  Get date
  18067.  ────────────────────────────────────────────────────────────────────────────
  18068.  
  18069.    Obtains the system day of the month, day of the week, month, and year.
  18070.  
  18071.  Call with:
  18072.  
  18073.    AH            = 2AH
  18074.  
  18075.  Returns:
  18076.  
  18077.    CX            = year (1980 through 2099)
  18078.    DH            = month (1 through 12)
  18079.    DL            = day (1 through 31)
  18080.  
  18081.    Under MS-DOS versions 1.1 and later
  18082.  
  18083.    AL            = day of the week (0 = Sunday, 1 = Monday, etc.)
  18084.  
  18085.  Notes:
  18086.  
  18087.    ■ This function's register format is the same as that required for Int 21H
  18088.      Function 2BH (Set Date).
  18089.  
  18090.    ■ This function can be used together with Int 21H Function 2BH to find
  18091.      the day of the week for an arbitrary date. The current date is first
  18092.      obtained with Function 2AH and saved. The date of interest is then set
  18093.      with Function 2BH, and the day of the week for that date is obtained
  18094.      with a subsequent call to Function 2AH. Finally, the current date is
  18095.      restored with an additional call to Function 2BH, using the values
  18096.      obtained with the original Function 2AH call.
  18097.  
  18098.  Example:
  18099.  
  18100.    Obtain the current date and save its components in the variables year,
  18101.    day, and month.
  18102.  
  18103.    year    dw      0
  18104.    month   db      0
  18105.    day     db      0
  18106.            .
  18107.            .
  18108.            .
  18109.            mov     ah,2ah          ; function number
  18110.            int     21h             ; transfer to MS-DOS
  18111.            mov     year,cx         ; save year (word)
  18112.            mov     month,dh        ; save month (byte)
  18113.            mov     day,dl          ; save day (byte)
  18114.            .
  18115.            .
  18116.            .
  18117.  
  18118.  
  18119.  ────────────────────────────────────────────────────────────────────────────
  18120.  Int 21H                                                                [1.0]
  18121.  Function 2BH (43)
  18122.  Set date
  18123.  ────────────────────────────────────────────────────────────────────────────
  18124.  
  18125.    Initializes the system clock driver to a specific date. The system time is
  18126.    not affected.
  18127.  
  18128.  Call with:
  18129.  
  18130.    AH            = 2BH
  18131.    CX            = year (1980 through 2099)
  18132.    DH            = month (1 through 12)
  18133.    DL            = day (1 through 31)
  18134.  
  18135.  Returns:
  18136.  
  18137.    AL            = 00H       if date set successfully
  18138.                    FFH       if date not valid (ignored)
  18139.  
  18140.  Note:
  18141.  
  18142.    ■ This function's register format is the same as that required for Int 21H
  18143.      Function 2AH (Get Date).
  18144.  
  18145.  Example:
  18146.  
  18147.    Set the system date according to the contents of the variables year, day,
  18148.    and month.
  18149.  
  18150.    year    dw      0
  18151.    month   db      0
  18152.    day     db      0
  18153.            .
  18154.            .
  18155.            .
  18156.            mov     ah,2bh          ; function number
  18157.            mov     cx,year         ; get year (word)
  18158.            mov     dh,month        ; get month (byte)
  18159.            mov     dl,day          ; get day (byte)
  18160.            int     21h             ; transfer to MS-DOS
  18161.            or      al,al           ; check status
  18162.            jnz     error           ; jump if date invalid
  18163.            .
  18164.            .
  18165.            .
  18166.  
  18167.  
  18168.  ────────────────────────────────────────────────────────────────────────────
  18169.  Int 21H                                                                [1.0]
  18170.  Function 2CH (44)
  18171.  Get time
  18172.  ────────────────────────────────────────────────────────────────────────────
  18173.  
  18174.    Obtains the time of day from the system real-time clock driver, converted
  18175.    to hours, minutes, seconds, and hundredths of seconds.
  18176.  
  18177.  Call with:
  18178.  
  18179.    AH            = 2CH
  18180.  
  18181.  Returns:
  18182.  
  18183.    CH            = hours (0 through 23)
  18184.    CL            = minutes (0 through 59)
  18185.    DH            = seconds (0 through 59)
  18186.    DL            = hundredths of seconds (0 through 99)
  18187.  
  18188.  Notes:
  18189.  
  18190.    ■ This function's register format is the same as that required for Int 21H
  18191.      Function 2DH (Set Time).
  18192.  
  18193.    ■ On most IBM PC─compatible systems, the real-time clock does not have a
  18194.      resolution of single hundredths of seconds. On such machines, the values
  18195.      returned by this function in register DL are discontinuous.
  18196.  
  18197.  Example:
  18198.  
  18199.    Obtain the current time and save its two major components in the variables
  18200.    hours and minutes.
  18201.  
  18202.    hours   db      0
  18203.    minutes db      0
  18204.            .
  18205.            .
  18206.            .
  18207.            mov     ah,2ch          ; function number
  18208.            int     21h             ; transfer to MS-DOS
  18209.            mov     hours,ch        ; save hours (byte)
  18210.            mov     minutes,cl      ; save minutes (byte)
  18211.            .
  18212.            .
  18213.            .
  18214.  
  18215.  
  18216.  ────────────────────────────────────────────────────────────────────────────
  18217.  Int 21H                                                                [1.0]
  18218.  Function 2DH (45)
  18219.  Set time
  18220.  ────────────────────────────────────────────────────────────────────────────
  18221.  
  18222.    Initializes the system real-time clock to a specified hour, minute,
  18223.    second, and hundredth of second. The system date is not affected.
  18224.  
  18225.  Call with:
  18226.  
  18227.    AH            = 2DH
  18228.    CH            = hours (0 through 23)
  18229.    CL            = minutes (0 through 59)
  18230.    DH            = seconds (0 through 59)
  18231.    DL            = hundredths of seconds (0 through 99)
  18232.  
  18233.  Returns:
  18234.  
  18235.    AL            = 00H       if time set successfully
  18236.                    FFH       if time not valid (ignored)
  18237.  
  18238.  Note:
  18239.  
  18240.    ■ This function's register format is the same as that required for Int 21H
  18241.      Function 2CH (Get Time).
  18242.  
  18243.  Example:
  18244.  
  18245.    Set the system time according to the contents of the variables hours and
  18246.    minutes. Force the current seconds and hundredths of seconds to zero.
  18247.  
  18248.    hours   db      0
  18249.    minutes db      0
  18250.            .
  18251.            .
  18252.            .
  18253.            mov     ah,2dh          ; function number
  18254.            mov     ch,hours        ; get hours (byte)
  18255.            mov     cl,minutes      ; get minutes (byte)
  18256.            mov     dx,0            ; force seconds and
  18257.                                    ; hundredths to zero
  18258.            int     21h             ; transfer to MS-DOS
  18259.            or      al,al           ; check status
  18260.            jnz     error           ; jump if time invalid
  18261.            .
  18262.            .
  18263.            .
  18264.  
  18265.  
  18266.  ────────────────────────────────────────────────────────────────────────────
  18267.  Int 21H                                                                [1.0]
  18268.  Function 2EH (46)
  18269.  Set verify flag
  18270.  ────────────────────────────────────────────────────────────────────────────
  18271.  
  18272.    Turns off or turns on the operating-system flag for automatic
  18273.    read-after-write verification of data.
  18274.  
  18275.  Call with:
  18276.  
  18277.    AH            = 2EH
  18278.    AL            = 00H       if turning off verify flag
  18279.                    01H       if turning on verify flag
  18280.  
  18281.    DL            = 00H (MS-DOS versions 1 and 2)
  18282.  
  18283.  Returns:
  18284.  
  18285.    Nothing
  18286.  
  18287.  Notes:
  18288.  
  18289.    ■ Because read-after-write verification slows disk operations, the default
  18290.      setting of the verify flag is OFF.
  18291.  
  18292.    ■ If a particular disk unit's device driver does not support
  18293.      read-after-write verification, this function has no effect.
  18294.  
  18295.    ■ The current state of the verify flag can be determined using Int 21H
  18296.      Function 54H.
  18297.  
  18298.    ■ The state of the verify flag is also controlled by the MS-DOS commands
  18299.      VERIFY OFF and VERIFY ON.
  18300.  
  18301.  Example:
  18302.  
  18303.    Save the current state of the system verify flag in the variable vflag,
  18304.    then force all subsequent disk writes to be verified.
  18305.  
  18306.    vflag   db      0               ; previous verify flag
  18307.            .
  18308.            .
  18309.            .
  18310.                                    ; get verify flag
  18311.            mov     ah,54h          ; function number
  18312.            int     21h             ; transfer to MS-DOS
  18313.            mov     vflag,al        ; save current flag state
  18314.  
  18315.                                    ; set verify flag
  18316.            mov     ah,2eh          ; function number
  18317.            mov     al,1            ; AL = 1 for verify on
  18318.            mov     dl,0            ; DL must be zero
  18319.            int     21h             ; transfer to MS-DOS
  18320.            .
  18321.            .
  18322.            .
  18323.  
  18324.  
  18325.  ────────────────────────────────────────────────────────────────────────────
  18326.  Int 21H                                                                [2.0]
  18327.  Function 2FH (47)
  18328.  Get DTA address
  18329.  ────────────────────────────────────────────────────────────────────────────
  18330.  
  18331.    Obtains the current address of the disk transfer area (DTA) for FCB file
  18332.    read/write operations.
  18333.  
  18334.  Call with:
  18335.  
  18336.    AH            = 2FH
  18337.  
  18338.  Returns:
  18339.  
  18340.    ES:BX         = segment:offset of disk transfer area
  18341.  
  18342.  Note:
  18343.  
  18344.    ■ The disk transfer area address is set with Int 21H Function 1AH. The
  18345.      default DTA is a 128-byte buffer at offset 80H in the program segment
  18346.      prefix.
  18347.  
  18348.  Example:
  18349.  
  18350.    Obtain the current disk transfer area address and save it in the variable
  18351.    olddta.
  18352.  
  18353.    olddta  dd      ?               ; save disk transfer address
  18354.            .
  18355.            .
  18356.            .
  18357.            mov     ah,2fh          ; function number
  18358.            int     21h             ; transfer to MS-DOS
  18359.  
  18360.                                    ; save it as DWORD pointer
  18361.            mov     word ptr olddta,bx
  18362.            mov     word ptr olddta+2,es
  18363.            .
  18364.            .
  18365.            .
  18366.  
  18367.  
  18368.  ────────────────────────────────────────────────────────────────────────────
  18369.  Int 21H                                                                [2.0]
  18370.  Function 30H (48)
  18371.  Get MS-DOS version number
  18372.  ────────────────────────────────────────────────────────────────────────────
  18373.  
  18374.    Returns the version number of the host MS-DOS operating system. This
  18375.    function is used by application programs to determine the capabilities of
  18376.    their environment.
  18377.  
  18378.  Call with:
  18379.  
  18380.    AH            = 30H
  18381.    AL            = 00H
  18382.  
  18383.  Returns:
  18384.  
  18385.    If running under MS-DOS version 1
  18386.  
  18387.    AL            = 00H
  18388.  
  18389.    If running under MS-DOS versions 2.0 or later
  18390.  
  18391.    AL            = major version number (MS-DOS 3.10 = 3, etc.)
  18392.    AH            = minor version number (MS-DOS 3.10 = 0AH, etc.)
  18393.    BH            = Original Equipment Manufacturer's (OEM's) serial number
  18394.                    (OEM-dependent──usually 00H for IBM's PC-DOS, 0FFH or
  18395.                    other values for MS-DOS)
  18396.    BL:CX         = 24-bit user serial number (optional, OEM-dependent)
  18397.  
  18398.  Notes:
  18399.  
  18400.    ■ Because this function was not defined under MS-DOS version 1, it should
  18401.      always be called with AL = 00H. In an MS-DOS version 1 environment, AL
  18402.      will be returned unchanged.
  18403.  
  18404.    ■ Care must be taken not to exit in an unacceptable fashion if an MS-DOS
  18405.      version 1 environment is detected. For example, Int 21H Function 4CH
  18406.      (Terminate Process with Return Code), Int 21H Function 40H (Write to
  18407.      File or Device), and the standard error handle are not available in
  18408.      MS-DOS version 1. In such cases a program should display an error
  18409.      message using Int 21H Function 09H and then terminate with Int 20H or
  18410.      Int 21H Function 00H.
  18411.  
  18412.  Example:
  18413.  
  18414.    Get the MS-DOS version number, terminating the current process with an
  18415.    error message if not running under MS-DOS version 2.0 or later.
  18416.  
  18417.    cr      equ     0dh             ; ASCII carriage return
  18418.    lf      equ     0ah             ; ASCII line feed
  18419.  
  18420.    msg     db      cr,lf
  18421.            db      'Wrong MS-DOS version'
  18422.            db      cr,lf,'$'
  18423.            .
  18424.            .
  18425.            .
  18426.            mov     ax,3000h        ; function number
  18427.            int     21h             ; transfer to MS-DOS
  18428.            cmp     al,2            ; version 2 or later?
  18429.            jae     label1          ; yes, jump
  18430.  
  18431.                                    ; display error message
  18432.            mov     ah,09           ; function number
  18433.            mov     dx,offset msg   ; message address
  18434.            int     21h             ; transfer to MS-DOS
  18435.  
  18436.                                    ; terminate process
  18437.            mov     ah,0            ; function number
  18438.            int     21h             ; transfer to MS-DOS
  18439.  
  18440.    label1: .
  18441.            .
  18442.            .
  18443.  
  18444.  
  18445.  ────────────────────────────────────────────────────────────────────────────
  18446.  Int 21H                                                                [2.0]
  18447.  Function 31H (49)
  18448.  Terminate and stay resident
  18449.  ────────────────────────────────────────────────────────────────────────────
  18450.  
  18451.    Terminates execution of the currently executing program, passing a return
  18452.    code to the parent process, but reserves part or all of the program's
  18453.    memory so that it will not be overlaid by the next transient program to be
  18454.    loaded. MS-DOS then takes the following actions:
  18455.  
  18456.    ■ File buffers are flushed and any open handles for files or devices owned
  18457.      by the process are closed.
  18458.  
  18459.    ■ The termination handler vector (Int 22H) is restored from PSP:000AH.
  18460.  
  18461.    ■ The Ctrl-C handler vector (Int 23H) is restored from PSP:000EH.
  18462.  
  18463.    ■ [2.0+] The critical-error handler vector (Int 24H) is restored from
  18464.      PSP:0012H.
  18465.  
  18466.    ■ Control is transferred to the termination handler.
  18467.  
  18468.    If the program is returning to COMMAND.COM, control transfers to the
  18469.    resident portion, and the transient portion is reloaded if necessary. If a
  18470.    batch file is in progress, the next line of the file is fetched and
  18471.    interpreted; otherwise, a prompt is issued for the next user command.
  18472.  
  18473.  Call with:
  18474.  
  18475.    AH            = 31H
  18476.    AL            = return code
  18477.    DX            = amount of memory to reserve (in paragraphs)
  18478.  
  18479.  Returns:
  18480.  
  18481.    Nothing
  18482.  
  18483.  Notes:
  18484.  
  18485.    ■ This function call is typically used to allow user-written utilities,
  18486.      drivers, or interrupt handlers to be loaded as ordinary .COM or .EXE
  18487.      programs and then remain resident. Subsequent entrance to the code is
  18488.      via a hardware or software interrupt.
  18489.  
  18490.    ■ This function attempts to set the initial memory allocation block to the
  18491.      length in paragraphs specified in register DX. If other memory blocks
  18492.      have been requested by the application using Int 21H Function 48H, they
  18493.      will not be released by this function.
  18494.  
  18495.    ■ Other methods of performing a final exit are:
  18496.  
  18497.      ∙ Int 20H
  18498.  
  18499.      ∙ Int 21H Function 00H
  18500.  
  18501.      ∙ Int 21H Function 4CH
  18502.  
  18503.      ∙ Int 27H
  18504.  
  18505.    ■ The return code may be retrieved by a parent process with Int 21H
  18506.      Function 4DH (Get Return Code). It can also be tested in a batch file
  18507.      with an IF ERRORLEVEL statement. By convention, a return code of zero
  18508.      indicates successful execution, and a nonzero return code indicates an
  18509.      error.
  18510.  
  18511.    ■ This function should not be called by .EXE programs that are loaded at
  18512.      the high end of the transient program area (that is, linked with the
  18513.      /HIGH switch) because doing so reserves the memory that is normally used
  18514.      by the transient part of COMMAND.COM. If COMMAND.COM cannot be reloaded,
  18515.      the system will fail.
  18516.  
  18517.    ■ [2.0+] This function should be used in preference to Int 27H because it
  18518.      supports return codes, allows larger amounts of memory to be reserved,
  18519.      and does not require CS to contain the segment of the program segment
  18520.      prefix.
  18521.  
  18522.    ■ [3.0+] If the program is running on a network, it should remove all
  18523.      locks it has placed on file regions before terminating.
  18524.  
  18525.  Example:
  18526.  
  18527.    Exit with a return code of 1 but stay resident, reserving 16 KB of memory
  18528.    starting at the program segment prefix of the process.
  18529.  
  18530.            .
  18531.            .
  18532.            .
  18533.            mov     ah,31h          ; function number
  18534.            mov     al,1            ; return code for parent
  18535.            mov     dx,0400h        ; paragraphs to reserve
  18536.            int     21h             ; transfer to MS-DOS
  18537.            .
  18538.            .
  18539.            .
  18540.  
  18541.  
  18542.  ────────────────────────────────────────────────────────────────────────────
  18543.  Int 21H
  18544.  Function 32H (50)
  18545.  Reserved
  18546.  ────────────────────────────────────────────────────────────────────────────
  18547.  
  18548.  
  18549.  ────────────────────────────────────────────────────────────────────────────
  18550.  Int 21H                                                                [2.0]
  18551.  Function 33H (51)
  18552.  Get or set break flag, get boot drive
  18553.  ────────────────────────────────────────────────────────────────────────────
  18554.  
  18555.    Obtains or changes the status of the operating system's break flag, which
  18556.    influences Ctrl-C checking during function calls. Also returns the system
  18557.    boot drive in version 4.0.
  18558.  
  18559.  Call with:
  18560.  
  18561.    If getting break flag
  18562.  
  18563.    AH            = 33H
  18564.    AL            = 00H
  18565.  
  18566.    If setting break flag
  18567.  
  18568.    AH            = 33H
  18569.    AL            = 01H
  18570.    DL            = 00H       if turning break flag OFF
  18571.                    01H       if turning break flag ON
  18572.  
  18573.    [4] If getting boot drive
  18574.  
  18575.    AH            = 33H
  18576.    AL            = 05H
  18577.  
  18578.  Returns:
  18579.  
  18580.    If called with AL = 00H or 01H
  18581.  
  18582.    DL            = 00H       break flag is OFF
  18583.                    01H       break flag is ON
  18584.  
  18585.    [4] If called with AL = 05H
  18586.  
  18587.    DL            = boot drive (1 = A, 2 = B, etc.)
  18588.  
  18589.  Notes:
  18590.  
  18591.    ■ When the system break flag is on, the keyboard is examined for a Ctrl-C
  18592.      entry whenever any operating-system input or output is requested; if
  18593.      Ctrl-C is detected, control is transferred to the Ctrl-C handler (Int
  18594.      23H). When the break flag is off, MS-DOS only checks for a Ctrl-C entry
  18595.      when executing the traditional character I/O functions (Int 21H
  18596.      Functions 01H through 0CH).
  18597.  
  18598.    ■ The break flag is not part of the local environment of the currently
  18599.      executing program; it affects all programs. An application that alters
  18600.      the flag should first save the flag's original status, then restore the
  18601.      flag before terminating.
  18602.  
  18603.  Example:
  18604.  
  18605.    Save the current state of the system break flag in the variable brkflag,
  18606.    then turn the break flag off to disable Ctrl-C checking during most MS-DOS
  18607.    function calls.
  18608.  
  18609.    brkflag db      0               ; save break flag
  18610.            .
  18611.            .
  18612.            .
  18613.                                    ; get current break flag
  18614.            mov     ah,33h          ; function number
  18615.            mov     al,0            ; AL = 0 to get flag
  18616.            int     21h             ; transfer to MS-DOS
  18617.            mov     brkflag,dl      ; save current flag
  18618.  
  18619.                                    ; now set break flag
  18620.            mov     ah,33h          ; function number
  18621.            mov     al,1            ; AL = 1 to set flag
  18622.            mov     dl,0            ; set break flag OFF
  18623.            int     21h             ; transfer to MS-DOS
  18624.            .
  18625.            .
  18626.            .
  18627.  
  18628.  
  18629.  ────────────────────────────────────────────────────────────────────────────
  18630.  Int 21H
  18631.  Function 34H (52)
  18632.  Reserved
  18633.  ────────────────────────────────────────────────────────────────────────────
  18634.  
  18635.  
  18636.  ────────────────────────────────────────────────────────────────────────────
  18637.  Int 21H                                                                [2.0]
  18638.  Function 35H (53)
  18639.  Get interrupt vector
  18640.  ────────────────────────────────────────────────────────────────────────────
  18641.  
  18642.    Obtains the address of the current interrupt-handler routine for the
  18643.    specified machine interrupt.
  18644.  
  18645.  Call with:
  18646.  
  18647.    AH            = 35H
  18648.    AL            = interrupt number
  18649.  
  18650.  Returns:
  18651.  
  18652.    ES:BX         = segment:offset of interrupt handler
  18653.  
  18654.  Note:
  18655.  
  18656.    ■ Together with Int 21H Function 25H (Set Interrupt Vector), this
  18657.      function is used by well-behaved application programs to modify or
  18658.      inspect the machine interrupt vector table.
  18659.  
  18660.  Example:
  18661.  
  18662.    Obtain the address of the current interrupt handler for hardware interrupt
  18663.    level 0 (divide by zero) and save it in the variable oldint0.
  18664.  
  18665.    oldint0 dd      ?               ; previous handler address
  18666.            .
  18667.            .
  18668.            .
  18669.            mov     ah,35h          ; function number
  18670.            mov     al,0            ; interrupt level
  18671.            int     21h             ; transfer to MS-DOS
  18672.  
  18673.                                    ; save old handler address
  18674.            mov     word ptr oldint0,bx
  18675.            mov     word ptr oldint0+2,es
  18676.            .
  18677.            .
  18678.            .
  18679.  
  18680.  
  18681.  ────────────────────────────────────────────────────────────────────────────
  18682.  Int 21H                                                                [2.0]
  18683.  Function 36H (54)
  18684.  Get drive allocation information
  18685.  ────────────────────────────────────────────────────────────────────────────
  18686.  
  18687.    Obtains selected information about a disk drive, from which the drive's
  18688.    capacity and remaining free space can be calculated.
  18689.  
  18690.  Call with:
  18691.  
  18692.    AH            = 36H
  18693.    DL            = drive code (0 = default, 1 = A, etc.)
  18694.  
  18695.  Returns:
  18696.  
  18697.    If function successful
  18698.  
  18699.    AX            = sectors per cluster
  18700.    BX            = number of available clusters
  18701.    CX            = bytes per sector
  18702.    DX            = clusters per drive
  18703.  
  18704.    If function unsuccessful (drive invalid)
  18705.  
  18706.    AX            = FFFFH
  18707.  
  18708.  Notes:
  18709.  
  18710.    ■ This function regards "lost" clusters as being in use and does not
  18711.      report them as part of the number of available clusters, even though
  18712.      they are not assigned to a file.
  18713.  
  18714.    ■ Similar information is returned by Int 21H Functions 1BH and 1CH.
  18715.  
  18716.  Example:
  18717.  
  18718.    Calculate the capacity of disk drive C in bytes, leaving the result in the
  18719.    variable drvsize. (This code assumes that the product of sectors/cluster *
  18720.    bytes/sector will not overflow 16 bits.)
  18721.  
  18722.    drvsize dd      ?               ; drive C size in bytes
  18723.            .
  18724.            .
  18725.            .
  18726.            mov     ah,36h          ; function number
  18727.            mov     dl,3            ; drive C = 3
  18728.            int     21h             ; transfer to MS-DOS
  18729.  
  18730.            mul     cx              ; sectors/cluster
  18731.                                    ; * bytes/sector
  18732.            mul     dx              ; * total clusters
  18733.                                    ; result now in DX:AX
  18734.  
  18735.                                    ; store low word
  18736.            mov     word ptr drvsize,ax
  18737.                                    ; store high word
  18738.            mov     word ptr drvsize+2,dx
  18739.            .
  18740.            .
  18741.            .
  18742.  
  18743.  
  18744.  ────────────────────────────────────────────────────────────────────────────
  18745.  Int 21H
  18746.  Function 37H (55)
  18747.  Reserved
  18748.  ────────────────────────────────────────────────────────────────────────────
  18749.  
  18750.  
  18751.  ────────────────────────────────────────────────────────────────────────────
  18752.  Int 21H                                                                [2.0]
  18753.  Function 38H (56)
  18754.  Get or set country information
  18755.  ────────────────────────────────────────────────────────────────────────────
  18756.  
  18757.    [2] Obtains internationalization information for the current country.
  18758.  
  18759.    [3.0+] Obtains internationalization information for the current or
  18760.    specified country or sets the current country code.
  18761.  
  18762.  Call with:
  18763.  
  18764.    If getting country information (MS-DOS version 2)
  18765.  
  18766.    AH            = 38H
  18767.    AL            = 0         to get "current" country information
  18768.  
  18769.    DS:DX         = segment:offset of buffer for returned information
  18770.  
  18771.    If getting country information (MS-DOS versions 3.0 and later)
  18772.  
  18773.    AH            = 38H
  18774.    AL            = 0         to get "current" country information
  18775.                    1─FEH     to get information for countries with code < 255
  18776.                    FFH       to get information for countries with code >=
  18777.                              255
  18778.  
  18779.    BX            = country code, if AL = FFH
  18780.    DS:DX         = segment:offset of buffer for returned information
  18781.  
  18782.    If setting current country code (MS-DOS versions 3.0 and later)
  18783.  
  18784.    AH            = 38H
  18785.    AL            = 1─FEH     country code for countries with code < 255
  18786.                    FFH       for countries with code >= 255
  18787.  
  18788.    BX            = country code, if AL = 0FFH
  18789.    DX            = FFFFH
  18790.  
  18791.  Returns:
  18792.  
  18793.    If function successful
  18794.  
  18795.    Carry flag    = clear
  18796.  
  18797.    and, if getting internationalization information
  18798.  
  18799.    BX            = country code
  18800.    DS:DX         = segment:offset of buffer holding internationalization
  18801.                    information
  18802.  
  18803.    and buffer filled in as follows:
  18804.  
  18805.    (for PC-DOS 2.0 and 2.1)
  18806.  
  18807.    Byte(s)    Contents
  18808.    00H─01H    date format
  18809.  
  18810.               0 = USA              m d y
  18811.               1 = Europe           d m y
  18812.               2 = Japan            y m d
  18813.  
  18814.    02H─03H    ASCIIZ currency symbol
  18815.    04H─05H    ASCIIZ thousands separator
  18816.    06H─07H    ASCIIZ decimal separator
  18817.    08H─1FH    reserved
  18818.  
  18819.    (for MS-DOS versions 2.0 and later, PC-DOS versions 3.0 and later)
  18820.  
  18821.    Byte(s)    Contents
  18822.    00H─01H    date format
  18823.  
  18824.               0 = USA   m d y
  18825.               1 =       d m y
  18826.               Europe
  18827.               2 = Japan y m d
  18828.  
  18829.    02H─06H    ASCIIZ currency symbol string
  18830.    07H─08H    ASCIIZ thousands separator character
  18831.    09H─0AH    ASCIIZ decimal separator character
  18832.    0BH─0CH    ASCIIZ date separator character
  18833.    0DH─0EH    ASCIIZ time separator character
  18834.    0FH        currency format
  18835.  
  18836.               bit 0                = 0 if currency symbol precedes value
  18837.                                    = 1 if currency symbol follows value
  18838.               bit 1                = 0 if no space between value and currency
  18839.                                    symbol
  18840.                                    = 1 if one space between value and
  18841.                                    currency symbol
  18842.               bit 2                = 0 if currency symbol and decimal are
  18843.                                    separate
  18844.                                    = 1 if currency symbol replaces decimal
  18845.                                    separator
  18846.  
  18847.    10H        number of digits after decimal in currency
  18848.    11H        time format
  18849.  
  18850.               bit 0                = 0 if 12-hour clock
  18851.                                    = 1 if 24-hour clock
  18852.  
  18853.    12H─15H    case-map call address
  18854.    16H─17H    ASCIIZ data-list separator
  18855.    18H─21H    reserved
  18856.  
  18857.    If function unsuccessful
  18858.  
  18859.    Carry flag    = set
  18860.    AX            = error code
  18861.  
  18862.  Notes:
  18863.  
  18864.    ■ The default country code is determined by the COUNTRY= directive in
  18865.      CONFIG.SYS or by the KEYBxx keyboard driver file if one is loaded.
  18866.      Otherwise, the default country code is OEM-dependent.
  18867.  
  18868.    ■ The previous contents of register CX may be destroyed by the Get Country
  18869.      Information subfunction.
  18870.  
  18871.    ■ The case-map call address is the segment:offset of a FAR procedure that
  18872.      performs country-specific mapping on character values from 80H through
  18873.      0FFH. The procedure must be called with the character to be mapped in
  18874.      register AL. If an alternate value exists for that character, it is
  18875.      returned in AL; otherwise, AL is unchanged. In general, lowercase
  18876.      characters are mapped to their uppercase equivalents, and accented or
  18877.      otherwise modified vowels are mapped to their plain vowel equivalents.
  18878.  
  18879.    ■ [3.0+] The value in register DX is used by MS-DOS to select between the
  18880.      Set Country and Get Country Information subfunctions.
  18881.  
  18882.    ■ [3.3+] Int 21H Function 65H (Get Extended Country Information) returns
  18883.      a superset of the information supplied by this function.
  18884.  
  18885.  Examples:
  18886.  
  18887.    Obtain internationalization information for the current country in the
  18888.    buffer ctrybuf.
  18889.  
  18890.    ctrybuf db      34 dup (0)
  18891.            .
  18892.            .
  18893.            .
  18894.            mov     ah,38h          ; function number
  18895.            mov     al,0            ; get current country
  18896.            mov     dx,seg ctrybuf  ; address of buffer
  18897.            mov     ds,dx           ; for country information
  18898.            mov     dx,offset ctrybuf
  18899.            int     21h             ; transfer to MS-DOS
  18900.            jc      error           ; jump if function failed
  18901.            .
  18902.            .
  18903.            .
  18904.  
  18905.    If the program is running under PC-DOS 3.3 and the current country code is
  18906.    49 (West Germany), ctrybuf is filled in with the following information:
  18907.  
  18908.            dw      0001h           ; date format
  18909.            db      'DM',0,0,0      ; ASCIIZ currency symbol
  18910.            db      '.',0           ; ASCIIZ thousands separator
  18911.            db      ',',0           ; ASCIIZ decimal separator
  18912.            db      '.',0           ; ASCIIZ date separator
  18913.            db      '.',0           ; ASCIIZ time separator
  18914.            db      02h             ; currency format
  18915.            db      02h             ; digits after decimal
  18916.            db      01h             ; time format
  18917.            dd      026ah:176ch     ; case-map call address
  18918.            db      ';',0           ; ASCIIZ data-list separator
  18919.            db      10 dup (0)      ; reserved
  18920.  
  18921.  
  18922.  ────────────────────────────────────────────────────────────────────────────
  18923.  Int 21H                                                                [2.0]
  18924.  Function 39H (57)
  18925.  Create directory
  18926.  ────────────────────────────────────────────────────────────────────────────
  18927.  
  18928.    Creates a directory using the specified drive and path.
  18929.  
  18930.  Call with:
  18931.  
  18932.    AH            = 39H
  18933.    DS:DX         = segment:offset of ASCIIZ pathname
  18934.  
  18935.  Returns:
  18936.  
  18937.    If function successful
  18938.  
  18939.    Carry flag    = clear
  18940.  
  18941.    If function unsuccessful
  18942.  
  18943.    Carry flag    = set
  18944.    AX            = error code
  18945.  
  18946.  Note:
  18947.  
  18948.    ■ The function fails if:
  18949.  
  18950.      ∙ any element of the pathname does not exist.
  18951.  
  18952.      ∙ a directory with the same name at the end of the same path already
  18953.        exists.
  18954.  
  18955.      ∙ the parent directory for the new directory is the root directory and
  18956.        is full.
  18957.  
  18958.      ∙ [3.0+] the program is running on a network and the user running the
  18959.        program has insufficient access rights.
  18960.  
  18961.  Example:
  18962.  
  18963.    Create a directory named MYSUB in the root directory on drive C.
  18964.  
  18965.    dname   db      'C:\MYSUB',0
  18966.            .
  18967.            .
  18968.            .
  18969.            mov     ah,39h          ; function number
  18970.            mov     dx,seg dname    ; address of pathname
  18971.            mov     ds,dx
  18972.            mov     dx,offset dname
  18973.            int     21h             ; transfer to MS-DOS
  18974.            jc      error           ; jump if create failed
  18975.            .
  18976.            .
  18977.            .
  18978.  
  18979.  
  18980.  ────────────────────────────────────────────────────────────────────────────
  18981.  Int 21H                                                                [2.0]
  18982.  Function 3AH (58)
  18983.  Delete directory
  18984.  ────────────────────────────────────────────────────────────────────────────
  18985.  
  18986.    Removes a directory using the specified drive and path.
  18987.  
  18988.  Call with:
  18989.  
  18990.    AH            = 3AH
  18991.    DS:DX         = segment:offset of ASCIIZ pathname
  18992.  
  18993.  Returns:
  18994.  
  18995.    If function successful
  18996.  
  18997.    Carry flag    = clear
  18998.  
  18999.    If function unsuccessful
  19000.  
  19001.    Carry flag    = set
  19002.    AX            = error code
  19003.  
  19004.  Note:
  19005.  
  19006.    ■ The function fails if:
  19007.  
  19008.      ∙ any element of the pathname does not exist.
  19009.  
  19010.      ∙ the specified directory is also the current directory.
  19011.  
  19012.      ∙ the specified directory contains any files.
  19013.  
  19014.      ∙ [3.0+] the program is running on a network and the user running the
  19015.        program has insufficient access rights.
  19016.  
  19017.  Example:
  19018.  
  19019.    Remove the directory named MYSUB in the root directory on drive C.
  19020.  
  19021.    dname   db      'C:\MYSUB',0
  19022.            .
  19023.            .
  19024.            .
  19025.            mov     ah,3ah          ; function number
  19026.            mov     dx,seg dname    ; address of pathname
  19027.            mov     ds,dx
  19028.            mov     dx,offset dname
  19029.            int     21h             ; transfer to MS-DOS
  19030.            jc      error           ; jump if delete failed
  19031.            .
  19032.            .
  19033.            .
  19034.  
  19035.  
  19036.  ────────────────────────────────────────────────────────────────────────────
  19037.  Int 21H                                                                [2.0]
  19038.  Function 3BH (59)
  19039.  Set current directory
  19040.  ────────────────────────────────────────────────────────────────────────────
  19041.  
  19042.    Sets the current, or default, directory using the specified drive and
  19043.    path.
  19044.  
  19045.  Call with:
  19046.  
  19047.    AH            = 3BH
  19048.    DS:DX         = segment:offset of ASCIIZ pathname
  19049.  
  19050.  Returns:
  19051.  
  19052.    If function successful
  19053.  
  19054.    Carry flag    = clear
  19055.  
  19056.    If function unsuccessful
  19057.  
  19058.    Carry flag    = set
  19059.    AX            = error code
  19060.  
  19061.  Notes:
  19062.  
  19063.    ■ The function fails if any element of the pathname does not exist.
  19064.  
  19065.    ■ Int 21H Function 47H can be used to obtain the name of the current
  19066.      directory before using Int 21H Function 3BH to select another, so that
  19067.      the original directory can be restored later.
  19068.  
  19069.  Example:
  19070.  
  19071.    Change the current directory for drive C to the directory \MYSUB.
  19072.  
  19073.    dname   db      'C:\MYSUB',0
  19074.            .
  19075.            .
  19076.            .
  19077.            mov     ah,3bh          ; function number
  19078.            mov     dx,seg dname    ; address of pathname
  19079.            mov     ds,dx
  19080.            mov     dx,offset dname
  19081.            int     21h             ; transfer to MS-DOS
  19082.            jc      error           ; jump if bad path
  19083.            .
  19084.            .
  19085.            .
  19086.  
  19087.  
  19088.  ────────────────────────────────────────────────────────────────────────────
  19089.  Int 21H                                                                [2.0]
  19090.  Function 3CH (60)
  19091.  Create file
  19092.  ────────────────────────────────────────────────────────────────────────────
  19093.  
  19094.    Given an ASCIIZ pathname, creates a new file in the designated or default
  19095.    directory on the designated or default disk drive. If the specified file
  19096.    already exists, it is truncated to zero length. In either case, the file
  19097.    is opened and a handle is returned that can be used by the program for
  19098.    subsequent access to the file.
  19099.  
  19100.  Call with:
  19101.  
  19102.    AH            = 3CH
  19103.    CX            = file attribute (bits may be combined)
  19104.  
  19105.                    Bit(s)    Significance (if set)
  19106.                    0         read-only
  19107.                    1         hidden
  19108.                    2         system
  19109.                    3         volume label
  19110.                    4         reserved (0)
  19111.                    5         archive
  19112.                    6─15      reserved (0)
  19113.  
  19114.    DS:DX         = segment:offset of ASCIIZ pathname
  19115.  
  19116.  Returns:
  19117.  
  19118.    If function successful
  19119.  
  19120.    Carry flag    = clear
  19121.    AX            = handle
  19122.  
  19123.    If function failed
  19124.  
  19125.    Carry flag    = set
  19126.    AX            = error code
  19127.  
  19128.  Notes:
  19129.  
  19130.    ■ The function fails if:
  19131.  
  19132.      ∙ any element of the pathname does not exist.
  19133.  
  19134.      ∙ the file is being created in the root directory and the root directory
  19135.        is full.
  19136.  
  19137.      ∙ a file with the same name and the read-only attribute already exists
  19138.        in the specified directory.
  19139.  
  19140.      ∙ [3.0+] the program is running on a network and the user running the
  19141.        program has insufficient access rights.
  19142.  
  19143.    ■ A file is usually given a normal (0) attribute when it is created. The
  19144.      file's attribute can subsequently be modified with Int 21H Function
  19145.      43H.
  19146.  
  19147.    ■ [3.0+] A volume label can be created using an attribute of 0008H, if one
  19148.      does not already exist. When files are created, bit 3 of the attribute
  19149.      parameter should always be clear (0).
  19150.  
  19151.    ■ [3.0+] See the entries for Int 21H Functions 5AH and 5BH, which may
  19152.      also be used to create files.
  19153.  
  19154.    ■ [4.0+] Int 21H Function 6CH combines the services of Functions 3CH,
  19155.      3DH, and 5BH.
  19156.  
  19157.  Example:
  19158.  
  19159.    Create and open, or truncate to zero length and open, the file
  19160.    C:\MYDIR\MYFILE.DAT, and save the handle for subsequent access to the
  19161.    file.
  19162.  
  19163.    fname   db      'C:\MYDIR\MYFILE.DAT',0
  19164.  
  19165.    fhandle dw      ?
  19166.            .
  19167.            .
  19168.            .
  19169.            mov     ah,3ch          ; function number
  19170.            xor     cx,cx           ; normal attribute
  19171.            mov     dx,seg fname    ; address of pathname
  19172.            mov     ds,dx
  19173.            mov     dx,offset fname
  19174.            int     21h             ; transfer to MS-DOS
  19175.            jc      error           ; jump if create failed
  19176.            mov     fhandle,ax      ; save file handle
  19177.            .
  19178.            .
  19179.            .
  19180.  
  19181.  
  19182.  ────────────────────────────────────────────────────────────────────────────
  19183.  Int 21H                                                                [2.0]
  19184.  Function 3DH (61)
  19185.  Open file
  19186.  ────────────────────────────────────────────────────────────────────────────
  19187.  
  19188.    Given an ASCIIZ pathname, opens the specified file in the designated or
  19189.    default directory on the designated or default disk drive. A handle is
  19190.    returned which can be used by the program for subsequent access to the
  19191.    file.
  19192.  
  19193.  Call with:
  19194.  
  19195.    AH            = 3DH
  19196.    AL            = access mode
  19197.  
  19198.                    Bit(s)    Significance
  19199.                    0─2       access mode
  19200.                              000 = read access
  19201.                              001 = write access
  19202.                              010 = read/write access
  19203.                    3         reserved (0)
  19204.                    4─6       sharing mode (MS-DOS versions 3.0 and later)
  19205.                              000 = compatibility mode
  19206.                              001 = deny all
  19207.                              010 = deny write
  19208.                              011 = deny read
  19209.                              100 = deny none
  19210.                    7         inheritance flag (MS-DOS versions 3.0 and later)
  19211.                              0 = child process inherits handle
  19212.                              1 = child does not inherit handle
  19213.  
  19214.    DS:DX         = segment:offset of ASCIIZ pathname
  19215.  
  19216.  Returns:
  19217.  
  19218.    If function successful
  19219.  
  19220.    Carry flag    = clear
  19221.    AX            = handle
  19222.  
  19223.    If function unsuccessful
  19224.  
  19225.    Carry flag    = set
  19226.    AX            = error code
  19227.  
  19228.  Notes:
  19229.  
  19230.    ■ Any normal, system, or hidden file with a matching name will be opened
  19231.      by this function. If the file is read-only, the success of the operation
  19232.      also depends on the access code in bits 0─2 of register AL. After
  19233.      opening the file, the file read/write pointer is set to offset zero (the
  19234.      first byte of the file).
  19235.  
  19236.    ■ The function fails if:
  19237.  
  19238.      ∙ any element of the pathname does not exist.
  19239.  
  19240.      ∙ the file is opened with an access mode of read/write and the file has
  19241.        the read-only attribute.
  19242.  
  19243.      ∙ [3.0+] SHARE.EXE is loaded and the file has already been opened by one
  19244.        or more other processes in a sharing mode that is incompatible with
  19245.        the current program's request.
  19246.  
  19247.    ■ The file's date and time stamp can be accessed after a successful open
  19248.      call with Int 21H Function 57H.
  19249.  
  19250.    ■ The file's attributes (hidden, system, read-only, or archive) can be
  19251.      obtained with Int 21H Function 43H.
  19252.  
  19253.    ■ When a file handle is inherited by a child process or is duplicated with
  19254.      Int 21H Function 45H or 46H, all sharing and access restrictions are
  19255.      also inherited.
  19256.  
  19257.    ■ [2] Only bits 0─2 of register AL are significant; the remaining bits
  19258.      should be zero for upward compatibility.
  19259.  
  19260.    ■ [3.0+] Bits 4─7 of register AL control access to the file by other
  19261.      programs. (Bits 4─6 have no effect unless SHARE.EXE is loaded.)
  19262.  
  19263.    ■ [3.0+] A file-sharing error causes a critical-error exception (Int 24H)
  19264.      with an error code of 02H. Int 21H Function 59H can be used to obtain
  19265.      information about the sharing violation.
  19266.  
  19267.    ■ [4.0+] Int 21H Function 6CH combines the services of Functions 3CH,
  19268.      3DH, and 5BH.
  19269.  
  19270.  Example:
  19271.  
  19272.    Open the file C:\MYDIR\MYFILE.DAT for both reading and writing, and save
  19273.    the handle for subsequent access to the file.
  19274.  
  19275.    fname   db      'C:\MYDIR\MYFILE.DAT',0
  19276.  
  19277.    fhandle dw      ?
  19278.            .
  19279.            .
  19280.            .
  19281.            mov     ah,3dh          ; function number
  19282.            mov     al,2            ; mode = read/write
  19283.            mov     dx,seg fname    ; address of pathname
  19284.            mov     ds,dx
  19285.            mov     dx,offset fname
  19286.            int     21h             ; transfer to MS-DOS
  19287.            jc      error           ; jump if open failed
  19288.            mov     fhandle,ax      ; save file handle
  19289.            .
  19290.            .
  19291.            .
  19292.  
  19293.  
  19294.  ────────────────────────────────────────────────────────────────────────────
  19295.  Int 21H                                                                [2.0]
  19296.  Function 3EH (62)
  19297.  Close file
  19298.  ────────────────────────────────────────────────────────────────────────────
  19299.  
  19300.    Given a handle that was obtained by a previous successful open or create
  19301.    operation, flushes all internal buffers associated with the file to disk,
  19302.    closes the file, and releases the handle for reuse. If the file was
  19303.    modified, the time and date stamp and file size are updated in the file's
  19304.    directory entry.
  19305.  
  19306.  Call with:
  19307.  
  19308.    AH            = 3EH
  19309.    BX            = handle
  19310.  
  19311.  Returns:
  19312.  
  19313.    If function successful
  19314.  
  19315.    Carry flag    = clear
  19316.  
  19317.    If function unsuccessful
  19318.  
  19319.    Carry flag    = set
  19320.    AX            = error code
  19321.  
  19322.  Note:
  19323.  
  19324.    ■ If you accidentally call this function with a zero handle, the standard
  19325.      input device is closed, and the keyboard appears to go dead. Make sure
  19326.      you always call the close function with a valid, nonzero handle.
  19327.  
  19328.  Example:
  19329.  
  19330.    Close the file whose handle is saved in the variable fhandle.
  19331.  
  19332.    fhandle dw      0
  19333.            .
  19334.            .
  19335.            .
  19336.            mov     ah,3eh          ; function number
  19337.            mov     bx,fhandle      ; file handle
  19338.            int     21h             ; transfer to MS-DOS
  19339.            jc      error           ; jump if close failed
  19340.            .
  19341.            .
  19342.            .
  19343.  
  19344.  
  19345.  ────────────────────────────────────────────────────────────────────────────
  19346.  Int 21H                                                                [2.0]
  19347.  Function 3FH (63)
  19348.  Read file or device
  19349.  ────────────────────────────────────────────────────────────────────────────
  19350.  
  19351.    Given a valid file handle from a previous open or create operation, a
  19352.    buffer address, and a length in bytes, transfers data at the current
  19353.    file-pointer position from the file into the buffer and then updates the
  19354.    file pointer position.
  19355.  
  19356.  Call with:
  19357.  
  19358.    AH            = 3FH
  19359.    BX            = handle
  19360.    CX            = number of bytes to read
  19361.    DS:DX         = segment:offset of buffer
  19362.  
  19363.  Returns:
  19364.  
  19365.    If function successful
  19366.  
  19367.    Carry flag    = clear
  19368.    AX            = bytes transferred
  19369.  
  19370.    If function unsuccessful
  19371.  
  19372.    Carry flag    = set
  19373.    AX            = error code
  19374.  
  19375.  Notes:
  19376.  
  19377.    ■ If reading from a character device (such as the standard input) in
  19378.      cooked mode, at most one line of input will be read (i.e., up to a
  19379.      carriage return character or the specified length, whichever comes
  19380.      first).
  19381.  
  19382.    ■ If the carry flag is returned clear but AX = 0, then the file pointer
  19383.      was already at end of file when the program requested the read.
  19384.  
  19385.    ■ If the carry flag is returned clear but AX < CX, then a partial record
  19386.      was read at end of file or there is an error.
  19387.  
  19388.    ■ [3.0+] If the program is running on a network, the user must have Read
  19389.      access rights to the directory and file.
  19390.  
  19391.  Example:
  19392.  
  19393.    Using the file handle from a previous open or create operation, read 1024
  19394.    bytes at the current file pointer into the buffer named buff.
  19395.  
  19396.    buff    db      1024 dup (?)    ; buffer for read
  19397.  
  19398.    fhandle dw      ?               ; contains file handle
  19399.            .
  19400.            .
  19401.            .
  19402.            mov     ah,3fh          ; function number
  19403.            mov     dx,seg buff     ; buffer address
  19404.            mov     ds,dx
  19405.            mov     dx,offset buff
  19406.            mov     bx,fhandle      ; file handle
  19407.            mov     cx,1024         ; length to read
  19408.            int     21h             ; transfer to MS-DOS
  19409.            jc      error           ; jump, read failed
  19410.  
  19411.            cmp     ax,cx           ; check length of read
  19412.            jl      done            ; jump, end of file
  19413.            .
  19414.            .
  19415.            .
  19416.  
  19417.  
  19418.  ────────────────────────────────────────────────────────────────────────────
  19419.  Int 21H                                                                [2.0]
  19420.  Function 40H (64)
  19421.  Write file or device
  19422.  ────────────────────────────────────────────────────────────────────────────
  19423.  
  19424.    Given a valid file handle from a previous open or create operation, a
  19425.    buffer address, and a length in bytes, transfers data from the buffer into
  19426.    the file and then updates the file pointer position.
  19427.  
  19428.  Call with:
  19429.  
  19430.    AH            = 40H
  19431.    BX            = handle
  19432.    CX            = number of bytes to write
  19433.    DS:DX         = segment:offset of buffer
  19434.  
  19435.  Returns:
  19436.  
  19437.    If function successful
  19438.  
  19439.    Carry flag    = clear
  19440.    AX            = bytes transferred
  19441.  
  19442.    If function unsuccessful
  19443.  
  19444.    Carry flag    = set
  19445.    AX            = error code
  19446.  
  19447.  Notes:
  19448.  
  19449.    ■ If the carry flag is returned clear but AX < CX, then a partial record
  19450.      was written or there is an error. This can be caused by a Ctrl-Z (1AH)
  19451.      embedded in the data if the destination is a character device in cooked
  19452.      mode or by a disk full condition if the destination is a file.
  19453.  
  19454.    ■ If the function is called with CX = 0, the file is truncated or extended
  19455.      to the current file pointer position.
  19456.  
  19457.    ■ [3.0+] If the program is running on a network, the user must have Write
  19458.      access rights to the directory and file.
  19459.  
  19460.  Example:
  19461.  
  19462.    Using the handle from a previous open or create operation, write 1024
  19463.    bytes to disk at the current file pointer from the buffer named buff.
  19464.  
  19465.    buff    db      1024 dup (?)    ; buffer for write
  19466.  
  19467.    fhandle dw      ?               ; contains file handle
  19468.            .
  19469.            .
  19470.            .
  19471.            mov     ah,40h          ; function number
  19472.            mov     dx,seg buff     ; buffer address
  19473.            mov     ds,dx
  19474.            mov     dx,offset buff
  19475.            mov     bx,fhandle      ; file handle
  19476.            mov     cx,1024         ; length to write
  19477.            int     21h             ; transfer to MS-DOS
  19478.            jc      error           ; jump, write failed
  19479.            cmp     ax,1024         ; entire record written?
  19480.            jne     error           ; no, jump
  19481.            .
  19482.            .
  19483.            .
  19484.  
  19485.  
  19486.  ────────────────────────────────────────────────────────────────────────────
  19487.  Int 21H                                                                [2.0]
  19488.  Function 41H (65)
  19489.  Delete file
  19490.  ────────────────────────────────────────────────────────────────────────────
  19491.  
  19492.    Deletes a file from the specified or default disk and directory.
  19493.  
  19494.  Call with:
  19495.  
  19496.    AH            = 41H
  19497.    DS:DX         = segment:offset of ASCIIZ pathname
  19498.  
  19499.  Returns:
  19500.  
  19501.    If function successful
  19502.  
  19503.    Carry flag    = clear
  19504.  
  19505.    If function unsuccessful
  19506.  
  19507.    Carry flag    = set
  19508.    AX            = error code
  19509.  
  19510.  Notes:
  19511.  
  19512.    ■ This function deletes a file by replacing the first character of its
  19513.      filename in the directory with the character e (E5H) and marking the
  19514.      file's clusters as "free" in the disk's file allocation table. The
  19515.      actual data stored in those clusters is not overwritten.
  19516.  
  19517.    ■ Only one file at a time may be deleted with this function. Unlike the
  19518.      FCB-related Delete File function (Int 21H Function 13H), the * and ?
  19519.      wildcard characters are not allowed in the file specification.
  19520.  
  19521.    ■ The function fails if:
  19522.  
  19523.      ∙ any element of the pathname does not exist.
  19524.  
  19525.      ∙ the designated file exists but has the read-only attribute. (Int 21H
  19526.        Function 43H can be used to examine and modify a file's attribute
  19527.        before attempting to delete it.)
  19528.  
  19529.      ∙ [3.0+] the program is running on a network, and the user running the
  19530.        program has insufficient access rights.
  19531.  
  19532.  Example:
  19533.  
  19534.    Delete the file named MYFILE.DAT from the directory \MYDIR on drive C.
  19535.  
  19536.    fname   db      'C:\MYDIR\MYFILE.DAT',0
  19537.            .
  19538.            .
  19539.            .
  19540.            mov     ah,41h          ; function number
  19541.            mov     dx,seg fname    ; filename address
  19542.            mov     ds,dx
  19543.            mov     dx,offset fname
  19544.            int     21h             ; transfer to MS-DOS
  19545.            jc      error           ; jump if delete failed
  19546.            .
  19547.            .
  19548.            .
  19549.  
  19550.  
  19551.  ────────────────────────────────────────────────────────────────────────────
  19552.  Int 21H                                                                [2.0]
  19553.  Function 42H (66)
  19554.  Set file pointer
  19555.  ────────────────────────────────────────────────────────────────────────────
  19556.  
  19557.    Sets the file location pointer relative to the start of file, end of file,
  19558.    or current file position.
  19559.  
  19560.  Call with:
  19561.  
  19562.    AH            = 42H
  19563.    AL            = method code
  19564.                    00H absolute offset from start of file
  19565.                    01H signed offset from current file pointer
  19566.                    02H signed offset from end of file
  19567.    BX            = handle
  19568.    CX            = most significant half of offset
  19569.    DX            = least significant half of offset
  19570.  
  19571.  Returns:
  19572.  
  19573.    If function successful
  19574.  
  19575.    Carry flag    = clear
  19576.    DX            = most significant half of resulting file pointer
  19577.    AX            = least significant half of resulting file pointer
  19578.  
  19579.    If function unsuccessful
  19580.  
  19581.    Carry flag    = set
  19582.    AX            = error code
  19583.  
  19584.  Notes:
  19585.  
  19586.    ■ This function uses a method code and a double-precision (32-bit) value
  19587.      to set the file pointer. The next record read or written in the file
  19588.      will begin at the new file pointer location. No matter what method is
  19589.      used in the call to this function, the file pointer returned in DX:AX is
  19590.      always the resulting absolute byte offset from the start of file.
  19591.  
  19592.    ■ Method 02H may be used to find the size of the file by calling Int 21H
  19593.      Function 42H with an offset of 0 and examining the pointer location
  19594.      that is returned.
  19595.  
  19596.    ■ Using methods 01H or 02H, it is possible to set the file pointer to a
  19597.      location that is before the start of file. If this is done, no error is
  19598.      returned by this function, but an error will be encountered upon a
  19599.      subsequent attempt to read or write the file.
  19600.  
  19601.  Examples:
  19602.  
  19603.    Using the file handle from a previous open or create operation, set the
  19604.    current file pointer position to 1024 bytes after the start of file.
  19605.  
  19606.    fhandle dw      ?
  19607.            .
  19608.            .
  19609.            .
  19610.            mov     ah,42h          ; function number
  19611.            mov     al,0            ; method = absolute
  19612.            mov     bx,fhandle      ; file handle
  19613.            mov     cx,0            ; upper half of offset
  19614.            mov     dx,1024         ; lower half of offset
  19615.            int     21h             ; transfer to MS-DOS
  19616.            jc      error           ; jump, function failed
  19617.            .
  19618.            .
  19619.            .
  19620.  
  19621.    The following subroutine accepts a record number, record size, and handle
  19622.    and sets the file pointer appropriately.
  19623.  
  19624.    ; call this routine with BX = handle
  19625.    ;                        AX = record number
  19626.    ;                        CX = record size
  19627.    ; returns all registers unchanged
  19628.    ;
  19629.    setptr  proc    near
  19630.            push    ax              ; save record number
  19631.            push    cx              ; save record size
  19632.            push    dx              ; save whatever's in DX
  19633.            mul     cx              ; size * record number
  19634.            mov     cx,ax           ; upper part to CX
  19635.            xchg    cx,dx           ; lower part to DX
  19636.            mov     ax,4200h        ; function number & method
  19637.            int     21h             ; transfer to MS-DOS
  19638.            pop     dx              ; restore previous DX
  19639.            pop     cx              ; restore record size
  19640.            pop     ax              ; restore record number
  19641.            ret                     ; back to caller
  19642.    setptr  endp
  19643.  
  19644.  
  19645.  ────────────────────────────────────────────────────────────────────────────
  19646.  Int 21H                                                                [2.0]
  19647.  Function 43H (67)
  19648.  Get or set file attributes
  19649.  ────────────────────────────────────────────────────────────────────────────
  19650.  
  19651.    Obtains or alters the attributes of a file (read-only, hidden, system, or
  19652.    archive) or directory.
  19653.  
  19654.  Call with:
  19655.  
  19656.    AH            = 43H
  19657.    AL            = 00H       to get attributes
  19658.                    01H       to set attributes
  19659.  
  19660.    CX            = file attribute, if AL = 01H (bits can be combined)
  19661.  
  19662.                    Bit(s)    Significance (if set)
  19663.                    0         read-only
  19664.                    1         hidden
  19665.                    2         system
  19666.                    3─4       reserved (0)
  19667.                    5         archive
  19668.                    6─15      reserved (0)
  19669.  
  19670.    DS:DX         = segment:offset of ASCIIZ pathname
  19671.  
  19672.  Returns:
  19673.  
  19674.    If function successful
  19675.  
  19676.    Carry flag    = clear
  19677.    CX            = file attribute
  19678.  
  19679.                    Bit(s)    Significance (if set)
  19680.                    0         read-only
  19681.                    1         hidden
  19682.                    2         system
  19683.                    3         volume label
  19684.                    4         directory
  19685.                    5         archive
  19686.                    6─15      reserved (0)
  19687.  
  19688.    If function unsuccessful
  19689.  
  19690.    Carry flag    = set
  19691.    AX            = error code
  19692.  
  19693.  Notes:
  19694.  
  19695.    ■ Bits 3 and 4 of register CX must always be clear (0) when this function
  19696.      is called; in other words, you cannot change an existing file into a
  19697.      directory or volume label. However, you can assign the "hidden"
  19698.      attribute to an existing directory with this function.
  19699.  
  19700.    ■ [3.0+] If the program is running on a network, the user must have Create
  19701.      access rights to the directory containing the file whose attribute is to
  19702.      be modified.
  19703.  
  19704.  Example:
  19705.  
  19706.    Change the attribute of the file D:\MYDIR\MYFILE.DAT to read-only, so that
  19707.    it cannot be accidentally modified or deleted by other application
  19708.    programs.
  19709.  
  19710.    rdonly  equ     01h             ; file attributes
  19711.    hidden  equ     02h
  19712.    system  equ     04h
  19713.    volume  equ     08h
  19714.    subdir  equ     10h
  19715.    archive equ     20h
  19716.  
  19717.    fname   db      'D:\MYDIR\MYFILE.DAT',0
  19718.            .
  19719.            .
  19720.            .
  19721.            mov     ah,43h          ; function number
  19722.            mov     al,01h          ; subfunction = modify
  19723.            mov     cx,rdonly       ; read-only attribute
  19724.            mov     dx,seg fname    ; filename address
  19725.            mov     ds,dx
  19726.            mov     dx,offset fname
  19727.            int     21h             ; transfer to MS-DOS
  19728.            jc      error           ; jump if modify failed
  19729.            .
  19730.            .
  19731.            .
  19732.  
  19733.  
  19734.  ────────────────────────────────────────────────────────────────────────────
  19735.  Int 21H                                                                [2.0]
  19736.  Function 44H (68)
  19737.  IOCTL (I/O control)
  19738.  ────────────────────────────────────────────────────────────────────────────
  19739.  
  19740.    Provides a direct path of communication between an application program and
  19741.    a device driver. Allows a program to obtain hardware-dependent information
  19742.    and to request operations that are not supported by other MS-DOS function
  19743.    calls.
  19744.  
  19745.    The IOCTL subfunctions and the MS-DOS versions in which they first became
  19746.    available are:
  19747.  
  19748. ╓┌─┌──────────────┌──────────────────────────────────────────┌───────────────╖
  19749.    Subfunction     Name                                      MS-DOS version
  19750.    ──────────────────────────────────────────────────────────────────────────
  19751.    00H            Get Device Information                    2.0
  19752.    01H            Set Device Information                    2.0
  19753.    02H            Receive Control Data from Character       2.0
  19754.                    Device Driver
  19755.    03H            Send Control Data to Character Device     2.0
  19756.                    Driver
  19757.    04H            Receive Control Data from Block Device    2.0
  19758.                    Driver
  19759.    05H            Send Control Data to Block Device Driver  2.0
  19760.    06H            Check Input Status                        2.0
  19761.    07H            Check Output Status                       2.0
  19762.    08H            Check If Block Device Is Removable        3.0
  19763.    Subfunction     Name                                      MS-DOS version
  19764.    ──────────────────────────────────────────────────────────────────────────
  19765.   08H            Check If Block Device Is Removable        3.0
  19766.    09H            Check If Block Device Is Remote           3.1
  19767.    0AH (10)       Check If Handle Is Remote                 3.1
  19768.    0BH (11)       Change Sharing Retry Count                3.1
  19769.    0CH (12)       Generic I/O Control for Character Devices
  19770.                    CL = 45H: Set Iteration Count             3.2
  19771.                    CL = 4AH: Select Code Page                3.3
  19772.                    CL = 4CH: Start Code Page Preparation     3.3
  19773.                    CL = 4DH: End Code Page Preparation       3.3
  19774.                    CL = 5FH: Set Display Information         4.0
  19775.                    CL = 65H: Get Iteration Count             3.2
  19776.                    CL = 6AH: Query Selected Code Page        3.3
  19777.                    CL = 6BH: Query Prepare List              3.3
  19778.                    CL = 7FH: Get Display Information         4.0
  19779.    0DH (13)       Generic I/O Control for Block Devices
  19780.                    CL = 40H: Set Device Parameters           3.2
  19781.                    CL = 41H: Write Track                     3.2
  19782.                    CL = 42H: Format and Verify Track         3.2
  19783.                    CL = 47H: Set Access Flag                 4.0
  19784.    Subfunction     Name                                      MS-DOS version
  19785.    ──────────────────────────────────────────────────────────────────────────
  19786.                   CL = 47H: Set Access Flag                 4.0
  19787.                    CL = 60H: Get Device Parameters           3.2
  19788.                    CL = 61H: Read Track                      3.2
  19789.                    CL = 62H: Verify Track                    3.2
  19790.                    CL = 67H: Get Access Flag                 4.0
  19791.    0EH (14)       Get Logical Drive Map                     3.2
  19792.    0FH (15)       Set Logical Drive Map                     3.2
  19793.    ──────────────────────────────────────────────────────────────────────────
  19794.  
  19795.  
  19796.    Only IOCTL Subfunctions 00H, 06H, and 07H may be used for handles
  19797.    associated with files. Subfunctions 00H─08H are not supported on network
  19798.    devices.
  19799.  
  19800.  
  19801.  ────────────────────────────────────────────────────────────────────────────
  19802.  Int 21H                                                                [2.0]
  19803.  Function 44H (68) Subfunction 00H
  19804.  IOCTL: get device information
  19805.  ────────────────────────────────────────────────────────────────────────────
  19806.  
  19807.    Returns a device information word for the file or device associated with
  19808.    the specified handle.
  19809.  
  19810.  Call with:
  19811.  
  19812.    AH            = 44H
  19813.    AL            = 00H
  19814.    BX            = handle
  19815.  
  19816.  Returns:
  19817.  
  19818.    If function successful
  19819.  
  19820.    Carry flag    = clear
  19821.    DX            = device information word
  19822.  
  19823.                    For a file:
  19824.  
  19825.                    Bit(s)    Significance
  19826.                    0─5       drive number (0 = A, 1 = B, etc.)
  19827.                    6         0 if file has been written
  19828.                              1 if file has not been written
  19829.                    7         0, indicating a file
  19830.                    8─15      reserved
  19831.  
  19832.    For a device:
  19833.  
  19834.                    Bit(s)    Significance
  19835.                    0         1 if standard input
  19836.                    1         1 if standard output
  19837.                    2         1 if NUL device
  19838.                    3         1 if clock device
  19839.                    4         reserved
  19840.                    5         0 if handle in ASCII mode
  19841.                              1 if handle in binary mode
  19842.                    6         0 if end of file on input
  19843.                    7         1, indicating a device
  19844.                    8─13      reserved
  19845.                    14        0 if IOCTL subfunctions 02H and 03H not
  19846.                              supported
  19847.                              1 if IOCTL subfunctions 02H and 03H supported
  19848.                    15        reserved
  19849.                              If function unsuccessful
  19850.  
  19851.    Carry flag    = set
  19852.    AX            = error code
  19853.  
  19854.  Notes:
  19855.  
  19856.    ■ Bits 8─15 of DX correspond to the upper 8 bits of the device-driver
  19857.      attribute word.
  19858.  
  19859.    ■ Bit 5 of the device information word for a handle associated with a
  19860.      character device signifies whether MS-DOS considers that handle to be in
  19861.      binary ("raw") mode or ASCII ("cooked") mode. In ASCII mode, MS-DOS
  19862.      filters the character stream and may take special action when the
  19863.      characters Ctrl-C, Ctrl-S, Ctrl-P, Ctrl-Z, and carriage return are
  19864.      detected. In binary mode, all characters are treated as data, and the
  19865.      exact number of characters requested is always read or written.
  19866.  
  19867.  Example:
  19868.  
  19869.    See Int 21H Function 44H Subfunction 01H.
  19870.  
  19871.  
  19872.  ────────────────────────────────────────────────────────────────────────────
  19873.  Int 21H                                                                [2.0]
  19874.  Function 44H (68) Subfunction 01H
  19875.  IOCTL: set device information
  19876.  ────────────────────────────────────────────────────────────────────────────
  19877.  
  19878.    Sets certain flags for a handle associated with a character device. This
  19879.    subfunction may not be used for a handle that is associated with a file.
  19880.  
  19881.  Call with:
  19882.  
  19883.    AH            = 44H
  19884.    AL            = 01H
  19885.    BX            = handle
  19886.    DX            = device information word
  19887.  
  19888.                    Bit(s)    Significance
  19889.                    0         1 if standard input
  19890.                    1         1 if standard output
  19891.                    2         1 if NUL device
  19892.                    3         1 if clock device
  19893.                    4         reserved (0)
  19894.                    5         0 to select ASCII mode
  19895.                              1 to select binary mode
  19896.                    6         reserved (0)
  19897.                    7         1, indicating a device
  19898.                    8─15      reserved (0)
  19899.  
  19900.  Returns:
  19901.  
  19902.    If function successful
  19903.  
  19904.    Carry flag    = clear
  19905.  
  19906.    If function unsuccessful
  19907.  
  19908.    Carry flag    = set
  19909.    AX            = error code
  19910.  
  19911.  Notes:
  19912.  
  19913.    ■ If register DH does not contain 00H, control returns to the program with
  19914.      the carry flag set and error code 0001H (invalid function) in register
  19915.      AX.
  19916.  
  19917.    ■ Bit 5 of the information word for a handle associated with a character
  19918.      device signifies whether MS-DOS considers that handle to be in binary
  19919.      ("raw") or ASCII ("cooked") mode. See Notes for Int 21H Function 44H
  19920.      Subfunction 00H.
  19921.  
  19922.  Example:
  19923.  
  19924.    Place the standard output handle into binary ("raw") mode. This speeds up
  19925.    output by disabling checking for Ctrl-C, Ctrl-S, and Ctrl-P between each
  19926.    character.
  19927.  
  19928.            .
  19929.            .
  19930.            .
  19931.                                    ; get device information
  19932.            mov     ax,4400h        ; function & subfunction
  19933.            mov     bx,1            ; standard output handle
  19934.            int     21h             ; transfer to MS-DOS
  19935.  
  19936.            mov     dh,0            ; force DH = 0
  19937.            or      dl,20h          ; set binary mode bit
  19938.  
  19939.                                    ; set device information
  19940.            mov     ax,4401h        ; function & subfunction
  19941.            int     21h             ; transfer to MS-DOS
  19942.            .
  19943.            .
  19944.            .
  19945.  
  19946.  
  19947.  ────────────────────────────────────────────────────────────────────────────
  19948.  Int 21H                                                                [2.0]
  19949.  Function 44H (68) Subfunction 02H
  19950.  IOCTL: read control data from character device driver
  19951.  ────────────────────────────────────────────────────────────────────────────
  19952.  
  19953.    Reads control data from a character-device driver. The length and contents
  19954.    of the data are specific to each device driver and do not follow any
  19955.    standard format. This function does not necessarily result in any input
  19956.    from the physical device.
  19957.  
  19958.  Call with:
  19959.  
  19960.    AH            = 44H
  19961.    AL            = 02H
  19962.    BX            = handle
  19963.    CX            = number of bytes to read
  19964.    DS:DX         = segment:offset of buffer
  19965.  
  19966.  Returns:
  19967.  
  19968.    If function successful
  19969.  
  19970.    Carry flag    = clear
  19971.    AX            = bytes read
  19972.  
  19973.    and buffer contains control data from driver
  19974.  
  19975.    If function unsuccessful
  19976.  
  19977.    Carry flag    = set
  19978.    AX            = error code
  19979.  
  19980.  Notes:
  19981.  
  19982.    ■ If supported by the driver, this subfunction can be used to obtain
  19983.      hardware-dependent status and availability information that is not
  19984.      supported by other MS-DOS function calls.
  19985.  
  19986.    ■ Character-device drivers are not required to support IOCTL Subfunction
  19987.      02H. A program can test bit 14 of the device information word returned
  19988.      by IOCTL Subfunction 00H to determine whether the driver supports this
  19989.      subfunction. If Subfunction 02H is requested and the driver does not
  19990.      have the ability to process control data, control returns to the program
  19991.      with the carry flag set and error code 0001H (invalid function) in
  19992.      register AX.
  19993.  
  19994.  Example:
  19995.  
  19996.    Read a control string from the standard list driver into the buffer buff.
  19997.  
  19998.    stdprn  equ     4               ; standard list handle
  19999.    buflen  equ     64              ; length of buffer
  20000.  
  20001.    ctllen  dw      ?               ; length of control string
  20002.    buff    db      buflen dup (0)  ; receives control string
  20003.            .
  20004.            .
  20005.            .
  20006.            mov     ax,4402h        ; function & subfunction
  20007.            mov     bx,stdprn       ; standard list handle
  20008.            mov     cx,buflen       ; buffer length
  20009.            mov     dx,seg buff     ; buffer address
  20010.            mov     ds,dx
  20011.            mov     dx,offset buff
  20012.            int     21h             ; transfer to MS-DOS
  20013.            jc      error           ; jump if read failed
  20014.            mov     ctllen,ax       ; save control string length
  20015.            .
  20016.            .
  20017.            .
  20018.  
  20019.  
  20020.  ────────────────────────────────────────────────────────────────────────────
  20021.  Int 21H                                                                [2.0]
  20022.  Function 44H (68) Subfunction 03H
  20023.  IOCTL: write control data to character-device driver
  20024.  ────────────────────────────────────────────────────────────────────────────
  20025.  
  20026.    Transfers control data from an application to a character-device driver.
  20027.    The length and contents of the data are specific to each device driver and
  20028.    do not follow any standard format. This function does not necessarily
  20029.    result in any output to the physical device.
  20030.  
  20031.  Call with:
  20032.  
  20033.    AH            = 44H
  20034.    AL            = 03H
  20035.    BX            = handle
  20036.    CX            = number of bytes to write
  20037.    DS:DX         = segment:offset of data
  20038.  
  20039.  Returns:
  20040.  
  20041.    If function successful
  20042.  
  20043.    Carry flag    = clear
  20044.    AX            = bytes transferred
  20045.  
  20046.    If function unsuccessful
  20047.  
  20048.    Carry flag    = set
  20049.    AX            = error code
  20050.  
  20051.  Notes:
  20052.  
  20053.    ■ If supported by the driver, this subfunction can be used to request
  20054.      hardware-dependent operations (such as setting baud rate for a serial
  20055.      port) that are not supported by other MS-DOS function calls.
  20056.  
  20057.    ■ Character-device drivers are not required to support IOCTL Subfunction
  20058.      03H. A program can test bit 14 of the device information word returned
  20059.      by IOCTL Subfunction 00H to determine whether the driver supports this
  20060.      subfunction. If Subfunction 03H is requested and the driver does not
  20061.      have the ability to process control data, control returns to the program
  20062.      with the carry flag set and error code 0001H (invalid function) in
  20063.      register AX.
  20064.  
  20065.  Example:
  20066.  
  20067.    Write a control string from the buffer buff to the standard list device
  20068.    driver. The length of the string is assumed to be in the variable ctllen.
  20069.  
  20070.    stdprn  equ     4               ; standard list handle
  20071.    buflen  equ     64              ; length of buffer
  20072.  
  20073.    ctllen  dw      ?               ; length of control data
  20074.    buff    db      buflen dup (?)  ; contains control data
  20075.            .
  20076.            .
  20077.            .
  20078.            mov     ax,4403h        ; function & subfunction
  20079.            mov     bx,stdprn       ; standard list handle
  20080.            mov     dx,seg buff     ; buffer address
  20081.            mov     ds,dx
  20082.            mov     dx,offset buff
  20083.            mov     cx,ctllen       ; length of control data
  20084.            int     21h             ; transfer to MS-DOS
  20085.            jc      error           ; jump if write failed
  20086.            .
  20087.            .
  20088.            .
  20089.  
  20090.  
  20091.  
  20092.  
  20093.  ────────────────────────────────────────────────────────────────────────────
  20094.  Int 21H                                                                [2.0]
  20095.  Function 44H (68) Subfunction 04H
  20096.  IOCTL: read control data from block-device driver
  20097.  ────────────────────────────────────────────────────────────────────────────
  20098.  
  20099.    Transfers control data from a block-device driver directly into an
  20100.    application program's buffer. The length and contents of the data are
  20101.    specific to each device driver and do not follow any standard format. This
  20102.    function does not necessarily result in any input from the physical
  20103.    device.
  20104.  
  20105.  Call with:
  20106.  
  20107.    AH            = 44H
  20108.    AL            = 04H
  20109.    BL            = drive code (0 = default, 1 = A, 2 = B, etc.)
  20110.    CX            = number of bytes to read
  20111.    DS:DX         = segment:offset of buffer
  20112.  
  20113.  Returns:
  20114.  
  20115.    If function successful
  20116.  
  20117.    Carry flag    = clear
  20118.    AX            = bytes transferred
  20119.  
  20120.    and buffer contains control data from device driver
  20121.  
  20122.    If function unsuccessful
  20123.  
  20124.    Carry flag    = set
  20125.    AX            = error code
  20126.  
  20127.  Notes:
  20128.  
  20129.    ■ When supported by the driver, this subfunction can be used to obtain
  20130.      hardware-dependent status and availability information that is not
  20131.      provided by other MS-DOS function calls.
  20132.  
  20133.    ■ Block-device drivers are not required to support IOCTL Subfunction 04H.
  20134.      If this subfunction is requested and the driver does not have the
  20135.      ability to process control data, control returns to the program with the
  20136.      carry flag set and error code 0001H (invalid function) in register AX.
  20137.  
  20138.  Example:
  20139.  
  20140.    Read a control string from the block-device driver for drive C into the
  20141.    buffer buff.
  20142.  
  20143.    buflen  equ     64              ; length of buffer
  20144.  
  20145.    ctllen  dw      ?               ; length of control string
  20146.    buff    db      buflen dup (0)  ; receives control string
  20147.            .
  20148.            .
  20149.            .
  20150.            mov     ax,4404h        ; function & subfunction
  20151.            mov     bl,3            ; drive C = 3
  20152.            mov     cx,buflen       ; buffer length
  20153.            mov     dx,seg buff     ; buffer address
  20154.  
  20155.            mov     ds,dx
  20156.            mov     dx,offset buff
  20157.            int     21h             ; transfer to MS-DOS
  20158.            jc      error           ; jump if read failed
  20159.            mov     ctllen,ax       ; save control string length
  20160.            .
  20161.            .
  20162.            .
  20163.  
  20164.  
  20165.  ────────────────────────────────────────────────────────────────────────────
  20166.  Int 21H                                                                [2.0]
  20167.  Function 44H (68) Subfunction 05H
  20168.  IOCTL: write control data to block-device driver
  20169.  ────────────────────────────────────────────────────────────────────────────
  20170.  
  20171.    Transfers control data from an application program directly to a
  20172.    block-device driver. The length and contents of the control data are
  20173.    specific to each device driver and do not follow any standard format. This
  20174.    function does not necessarily result in any output to the physical device.
  20175.  
  20176.  Call with:
  20177.  
  20178.    AH            = 44H
  20179.    AL            = 05H
  20180.    BL            = drive code (0 = default, 1 = A, 2 = B, etc.)
  20181.    CX            = number of bytes to write
  20182.    DS:DX         = segment:offset of data
  20183.  
  20184.  Returns:
  20185.  
  20186.    If function successful
  20187.  
  20188.    Carry flag    = clear
  20189.    AX            = bytes transferred
  20190.  
  20191.    If function unsuccessful
  20192.  
  20193.    Carry flag    = set
  20194.    AX            = error code
  20195.  
  20196.  Notes:
  20197.  
  20198.    ■ When supported by the driver, this subfunction can be used to request
  20199.      hardware-dependent operations (such as tape rewind or disk eject) that
  20200.      are not provided by other MS-DOS function calls.
  20201.  
  20202.    ■ Block-device drivers are not required to support IOCTL Subfunction 05H.
  20203.      If this subfunction is requested and the driver does not have the
  20204.      ability to process control data, control returns to the program with the
  20205.      carry flag set and error code 0001H (invalid function) in register AX.
  20206.  
  20207.  Example:
  20208.  
  20209.    Write a control string from the buffer buff to the block-device driver for
  20210.    drive C. The length of the string is assumed to be in the variable ctllen.
  20211.  
  20212.    buflen  equ     64              ; length of buffer
  20213.  
  20214.    ctllen  dw      ?               ; length of control data
  20215.    buff    db      buflen dup (?)  ; contains control data
  20216.            .
  20217.            .
  20218.            .
  20219.            mov     ax,4405h        ; function & subfunction
  20220.            mov     bl,3            ; drive C = 3
  20221.            mov     dx,seg buff     ; buffer address
  20222.            mov     ds,dx
  20223.            mov     dx,offset buff
  20224.            mov     cx,ctllen       ; length of control data
  20225.            int     21h             ; transfer to MS-DOS
  20226.            jc      error           ; jump if write failed
  20227.            .
  20228.            .
  20229.            .
  20230.  
  20231.  
  20232.  ────────────────────────────────────────────────────────────────────────────
  20233.  Int 21H                                                                [2.0]
  20234.  Function 44H (68) Subfunction 06H
  20235.  IOCTL: check input status
  20236.  ────────────────────────────────────────────────────────────────────────────
  20237.  
  20238.    Returns a code indicating whether the device or file associated with a
  20239.    handle is ready for input.
  20240.  
  20241.  Call with:
  20242.  
  20243.    AH            = 44H
  20244.    AL            = 06H
  20245.    BX            = handle
  20246.  
  20247.  Returns:
  20248.  
  20249.    If function successful
  20250.  
  20251.    Carry flag    = clear
  20252.  
  20253.    and, for a device:
  20254.  
  20255.    AL            = 00H       if device not ready
  20256.                    FFH       if device ready
  20257.  
  20258.    or, for a file:
  20259.  
  20260.    AL            = 00H       if file pointer at EOF
  20261.                    FFH       if file pointer not at EOF
  20262.  
  20263.    If function unsuccessful
  20264.  
  20265.    Carry flag    = set
  20266.    AX            = error code
  20267.  
  20268.  Note:
  20269.  
  20270.    ■ This function can be used to check the status of character devices, such
  20271.      as the serial port, that do not have their own "traditional" MS-DOS
  20272.      status calls.
  20273.  
  20274.  Example:
  20275.  
  20276.    Check whether a character is ready from the standard auxiliary device
  20277.    (usually COM1).
  20278.  
  20279.    stdaux  equ     3               ; standard auxiliary handle
  20280.            .
  20281.            .
  20282.            .
  20283.            mov     ax,4406h        ; function & subfunction
  20284.            mov     bx,stdaux       ; standard auxiliary handle
  20285.            int     21h             ; transfer to MS-DOS
  20286.            jc      error           ; jump if function failed
  20287.            or      al,al           ; test status flag
  20288.            jnz     ready           ; jump if character ready
  20289.            .
  20290.            .
  20291.            .
  20292.  
  20293.  
  20294.  ────────────────────────────────────────────────────────────────────────────
  20295.  Int 21H                                                                [2.0]
  20296.  Function 44H (68) Subfunction 07H
  20297.  IOCTL: check output status
  20298.  ────────────────────────────────────────────────────────────────────────────
  20299.  
  20300.    Returns a code indicating whether the device associated with a handle is
  20301.    ready for output.
  20302.  
  20303.  Call with:
  20304.  
  20305.    AH            = 44H
  20306.    AL            = 07H
  20307.    BX            = handle
  20308.  
  20309.  Returns:
  20310.  
  20311.    If function successful
  20312.  
  20313.    Carry flag    = clear
  20314.  
  20315.    and, for a device:
  20316.  
  20317.    AL            = 00H       if device not ready
  20318.                    FFH       if device ready
  20319.  
  20320.    or, for a file:
  20321.  
  20322.    AL            = FFH
  20323.  
  20324.    If function unsuccessful
  20325.  
  20326.    Carry flag    = set
  20327.    AX            = error code
  20328.  
  20329.  Note:
  20330.  
  20331.    ■ When used with a handle for a file, this function always returns a ready
  20332.      status, even if the disk is full or no disk is in the drive.
  20333.  
  20334.  Example:
  20335.  
  20336.    Check whether the standard auxiliary device (usually COM1) can accept a
  20337.    character for output.
  20338.  
  20339.    stdaux  equ     3               ; standard auxiliary handle
  20340.            .
  20341.            .
  20342.            .
  20343.            mov     ax,4407h        ; function & subfunction
  20344.            mov     bx,stdaux       ; standard auxiliary handle
  20345.            int     21h             ; transfer to MS-DOS
  20346.            jc      error           ; jump if function failed
  20347.            or      al,al           ; test status flag
  20348.            jnz     ready           ; jump if not busy
  20349.            .
  20350.            .
  20351.            .
  20352.  
  20353.  
  20354.  ────────────────────────────────────────────────────────────────────────────
  20355.  Int 21H                                                                [3.0]
  20356.  Function 44H (68) Subfunction 08H
  20357.  IOCTL: check if block device is removable
  20358.  ────────────────────────────────────────────────────────────────────────────
  20359.  
  20360.    Checks whether the specified block device contains a removable storage
  20361.    medium, such as a floppy disk.
  20362.  
  20363.  Call with:
  20364.  
  20365.    AH            = 44H
  20366.    AL            = 08H
  20367.    BL            = drive number (0 = default, 1 = A, 2 = B, etc.)
  20368.  
  20369.  Returns:
  20370.  
  20371.    If function successful
  20372.  
  20373.    Carry flag    = clear
  20374.    AL            = 00H       if medium is removable
  20375.                    01H       if medium is not removable
  20376.  
  20377.    If function unsuccessful
  20378.  
  20379.    Carry flag    = set
  20380.    AX            = error code
  20381.  
  20382.  Notes:
  20383.  
  20384.    ■ If a file is not found as expected on a particular drive, a program can
  20385.      use this subfunction to determine whether the user should be prompted to
  20386.      insert another disk.
  20387.  
  20388.    ■ This subfunction may not be used for a network drive.
  20389.  
  20390.    ■ Block drivers are not required to support Subfunction 08H. If this
  20391.      subfunction is requested and the block device cannot supply the
  20392.      information, control returns to the program with the carry flag set and
  20393.      error code 0001H (invalid function) in register AX.
  20394.  
  20395.  Example:
  20396.  
  20397.    Check whether drive C is removable.
  20398.  
  20399.            .
  20400.            .
  20401.            .
  20402.            mov     ax,4408h        ; function & subfunction
  20403.            mov     bl,3            ; drive 3 = C
  20404.            int     21h             ; transfer to MS-DOS
  20405.            jc      error           ; jump if function failed
  20406.            and     al,1            ; test type of medium
  20407.            jnz     fixed           ; jump if not removable
  20408.            .
  20409.            .
  20410.            .
  20411.  
  20412.  
  20413.  ────────────────────────────────────────────────────────────────────────────
  20414.  Int 21H                                                                [3.1]
  20415.  Function 44H (68) Subfunction 09H
  20416.  IOCTL: check if block device is remote
  20417.  ────────────────────────────────────────────────────────────────────────────
  20418.  
  20419.    Checks whether the specified block device is local (attached to the
  20420.    computer running the program) or remote (redirected to a network server).
  20421.  
  20422.  Call with:
  20423.  
  20424.    AH            = 44H
  20425.    AL            = 09H
  20426.    BL            = drive number (0 = default, 1 = A, 2 = B, etc.)
  20427.  
  20428.  Returns:
  20429.  
  20430.    If function successful
  20431.  
  20432.    Carry flag    = clear
  20433.    DX            = device attribute word
  20434.                    bit 12        = 0 if drive is local
  20435.                                    1 if drive is remote
  20436.  
  20437.    If function unsuccessful
  20438.  
  20439.    Carry flag    = set
  20440.    AX            = error code
  20441.  
  20442.  Note:
  20443.  
  20444.    ■ Use of this subfunction should be avoided. Application programs should
  20445.      not distinguish between files on local and remote devices.
  20446.  
  20447.  Example:
  20448.  
  20449.    Check whether drive D is mounted on the machine running the program or is
  20450.    a network drive.
  20451.  
  20452.            .
  20453.            .
  20454.            .
  20455.            mov     ax,4409h        ; function & subfunction
  20456.            mov     bl,4            ; drive 4 = D
  20457.            int     21h             ; transfer to MS-DOS
  20458.            jc      error           ; jump if function failed
  20459.            and     dx,1000h        ; test local/remote bit
  20460.            jnz     remote          ; jump if network drive
  20461.            .
  20462.            .
  20463.            .
  20464.  
  20465.  
  20466.  ────────────────────────────────────────────────────────────────────────────
  20467.  Int 21H                                                                [3.1]
  20468.  Function 44H (68) Subfunction 0AH (10)
  20469.  IOCTL: check if handle is remote
  20470.  ────────────────────────────────────────────────────────────────────────────
  20471.  
  20472.    Checks whether the specified handle refers to a file or device that is
  20473.    local (located on the PC that is running the program) or remote (located
  20474.    on a network server).
  20475.  
  20476.  Call with:
  20477.  
  20478.    AH            = 44H
  20479.    AL            = 0AH
  20480.    BX            = handle
  20481.  
  20482.  Returns:
  20483.  
  20484.    If function successful
  20485.  
  20486.    Carry flag    = clear
  20487.    DX            = attribute word for file or device
  20488.  
  20489.                    bit 15             = 0 if local
  20490.  
  20491.                                         1 if remote
  20492.  
  20493.                                         If function unsuccessful
  20494.  
  20495.    Carry flag    = set
  20496.    AX            = error code
  20497.  
  20498.  Notes:
  20499.  
  20500.    ■ Application programs should not ordinarily attempt to distinguish
  20501.      between files on local and remote devices.
  20502.  
  20503.    ■ If the network has not been started, control returns to the calling
  20504.      program with the carry flag set and error code 0001H (invalid function)
  20505.      in register AX.
  20506.  
  20507.  Example:
  20508.  
  20509.    Check if the handle saved in the variable fhandle is associated with a
  20510.    file or device on the machine running the program or on a network server.
  20511.  
  20512.    fhandle dw      ?               ; device handle
  20513.            .
  20514.            .
  20515.            .
  20516.            mov     ax,440ah        ; function & subfunction
  20517.            mov     bx,fhandle      ; file/device handle
  20518.            int     21h             ; transfer to MS-DOS
  20519.            jc      error           ; jump if function failed
  20520.            and     dx,8000h        ; test local/remote bit
  20521.            jnz     remote          ; jump if network handle
  20522.            .
  20523.            .
  20524.            .
  20525.  
  20526.  
  20527.  ────────────────────────────────────────────────────────────────────────────
  20528.  Int 21H                                                                [3.1]
  20529.  Function 44H (68) Subfunction 0BH (11)
  20530.  IOCTL: change sharing retry count
  20531.  ────────────────────────────────────────────────────────────────────────────
  20532.  
  20533.    Sets the number of times MS-DOS retries a disk operation after a failure
  20534.    caused by a file-sharing violation before it returns an error to the
  20535.    requesting process. This subfunction is not available unless the
  20536.    file-sharing module (SHARE.EXE) is loaded.
  20537.  
  20538.  Call with:
  20539.  
  20540.    AH            = 44H
  20541.    AL            = 0BH
  20542.    CX            = delays per retry (default = 1)
  20543.    DX            = number of retries (default = 3)
  20544.  
  20545.  Returns:
  20546.  
  20547.    If function successful
  20548.  
  20549.    Carry flag    = clear
  20550.  
  20551.    If function unsuccessful
  20552.  
  20553.    Carry flag    = set
  20554.    AX            = error code
  20555.  
  20556.  Notes:
  20557.  
  20558.    ■ The length of a delay is a machine-dependent value determined by the CPU
  20559.      type and clock speed. Each delay consists of the following instruction
  20560.      sequence:
  20561.  
  20562.            xor     cx,cx
  20563.            loop    $
  20564.  
  20565.    which executes 65,536 times before falling out of the loop.
  20566.  
  20567.    ■ The sharing retry count affects the behavior of the system as a whole
  20568.      and is not a local parameter for the process. If a program changes the
  20569.      sharing retry count, it should restore the default values before
  20570.      terminating.
  20571.  
  20572.  Example:
  20573.  
  20574.    Change the number of automatic retries for a file-sharing violation to
  20575.    five.
  20576.  
  20577.            .
  20578.            .
  20579.            .
  20580.            mov     ax,440bh        ; function & subfunction
  20581.            mov     cx,1            ; delays per retry
  20582.            mov     dx,5            ; number of retries
  20583.            int     21h             ; transfer to MS-DOS
  20584.            jc      error           ; jump if function failed
  20585.            .
  20586.            .
  20587.            .
  20588.  
  20589.  
  20590.  ────────────────────────────────────────────────────────────────────────────
  20591.  Int 21H                                                                [3.2]
  20592.  Function 44H (68) Subfunction 0CH (12)
  20593.  IOCTL: generic I/O control for character devices
  20594.  ────────────────────────────────────────────────────────────────────────────
  20595.  
  20596.    Provides a general-purpose mechanism for communication between application
  20597.    programs and character-device drivers.
  20598.  
  20599.  Call with:
  20600.  
  20601.    AH            = 44H
  20602.    AL            = 0CH
  20603.    BX            = handle
  20604.    CH            = category (major) code:
  20605.                    00H = unknown
  20606.                    01H = COM1, COM2, COM3, or COM4 (3.3)
  20607.                    03H = CON (keyboard and display) (3.3)
  20608.                    05H = LPT1, LPT2, or LPT3 (3.2)
  20609.    CL            = function (minor) code:
  20610.                    45H = Set Iteration Count (3.2)
  20611.                    4AH = Select Code Page (3.3)
  20612.                    4CH = Start Code Page Preparation (3.3)
  20613.                    4DH = End Code Page Preparation (3.3)
  20614.                    5FH = Set Display Information (4.0)
  20615.                    65H = Get Iteration Count (3.2)
  20616.                    6AH = Query Selected Code Page (3.3)
  20617.                    6BH = Query Prepare List (3.3)
  20618.                    7FH = Get Display Information (4.0)
  20619.    DS:DX         = segment:offset of parameter block
  20620.  
  20621.  Returns:
  20622.  
  20623.    If function successful
  20624.  
  20625.    Carry flag    = clear
  20626.  
  20627.    and, if called with CL = 65H, 6AH, 6BH, or 7FH
  20628.  
  20629.    DS:DX         = segment:offset of parameter block
  20630.  
  20631.    If function unsuccessful
  20632.  
  20633.    Carry flag    = set
  20634.    AX            = error code
  20635.  
  20636.  Notes:
  20637.  
  20638.    ■ If the minor code is 45H (Set Iteration Count) or 65H (Get Iteration
  20639.      Count), the parameter block is simply a 2-byte buffer containing or
  20640.      receiving the iteration count for the printer. This call is valid only
  20641.      for printer drivers that support Output Until Busy, and determines the
  20642.      number of times the device driver will wait for the device to signal
  20643.      ready before returning from the output call.
  20644.  
  20645.    ■ The parameter block for minor code 4DH (End Code Page Preparation) has
  20646.      the following format:
  20647.  
  20648.            dw      2               ; length of following data
  20649.            dw      0               ; (reserved)
  20650.  
  20651.    ■ For MS-DOS version 3.3, the parameter block for minor codes 4AH (Select
  20652.      Code Page) and 6AH (Query Code Page) has the following format:
  20653.  
  20654.            dw      2               ; length of following data
  20655.            dw      ?               ; code page ID
  20656.  
  20657.    For MS-DOS version 4.0, minor codes 4AH and 6AH also set or get the
  20658.    double-byte character set (DBCS) lead byte table, and the following format
  20659.    is used:
  20660.  
  20661.            dw      (n+2)*2+1       ; length of following data
  20662.            dw      ?               ; code page ID
  20663.            db      start,end       ; DBCS lead byte range 1
  20664.            .
  20665.            .
  20666.            .
  20667.            db      start,end       ; DBCS lead byte range n
  20668.            db      0,0
  20669.  
  20670.    ■ The parameter block for minor code 4CH (Start Code Page Preparation) has
  20671.      the following format:
  20672.  
  20673.            dw      0               ; font type
  20674.                                    ; bit 0   = 0 downloaded
  20675.                                    ;         = 1 cartridge
  20676.                                    ; bits 1-15 = reserved (0)
  20677.            dw      (n+1)*2         ; length of remainder of
  20678.                                    ;   parameter block
  20679.            dw      n               ; number of code pages in
  20680.                                    ;   the following list
  20681.            dw      ?               ; code page 1
  20682.            dw      ?               ; code page 2
  20683.            .
  20684.            .
  20685.            .
  20686.            dw      ?               ; code page n
  20687.  
  20688.    ■ The parameter block for minor code 6BH (Query Prepare List) has the
  20689.      following format, assuming n hardware code pages and m prepared code
  20690.      pages (n <= 12, m <= 12):
  20691.  
  20692.            dw      (n+m+2)*2       ; length of following data
  20693.            dw      n               ; number of hardware code pages
  20694.            dw      ?               ; hardware code page 1
  20695.            dw      ?               ; hardware code page 2
  20696.            .
  20697.            .
  20698.            .
  20699.            dw      ?               ; hardware code page n
  20700.            dw      m               ; number of prepared code pages
  20701.            dw      ?               ; prepared code page 1
  20702.            dw      ?               ; prepared code page 2
  20703.            .
  20704.            .
  20705.            .
  20706.            dw      ?               ; prepared code page m
  20707.  
  20708.    ■ After a minor code 4CH (Start Code Page Preparation) call, the data
  20709.      defining the code page font is written to the driver using one or more
  20710.      calls to the IOCTL Write Control Data subfunction (Interrupt 21H,
  20711.      Function 44H, Subfunction 03H). The format of the data is device- and
  20712.      driver-specific. After the font data has been written to the driver, a
  20713.      minor code 4DH (End Code Page Preparation) call must be issued. If no
  20714.      data is written to the driver between the minor code 4CH and 4DH calls,
  20715.      the driver interprets the newly prepared code pages as hardware code
  20716.      pages.
  20717.  
  20718.    ■ A special variation of the minor code 4CH (Start Code Page Preparation)
  20719.      call, called "Refresh," is required to actually load the peripheral
  20720.      device with the prepared code pages. The refresh operation is obtained
  20721.      by requesting minor code 4CH with each code page position in the
  20722.      parameter block set to -1, followed by an immediate call for minor code
  20723.      4DH (End Code Page Preparation).
  20724.  
  20725.    ■ [4.0+] For minor codes 5FH (Set Display Information) and 7FH (Get
  20726.      Display Information), the parameter block is formatted as follows:
  20727.  
  20728.            db      0               ; level (0 in MS-DOS 4.0)
  20729.            db      0               ; reserved (must be 0)
  20730.            dw      14              ; length of following data
  20731.            dw      ?               ; control flags
  20732.                                    ; bit 0     = 0 intensity
  20733.                                    ;           = 1 blink
  20734.                                    ; bits 1-15 = reserved (0)
  20735.            db      ?               ; mode type (1 = text, 2 = APA)
  20736.            db      0               ; reserved (must be 0)
  20737.            dw      ?               ; colors
  20738.                                    ; 0 = monochrome compatible
  20739.                                    ; 1 = 2 colors
  20740.                                    ; 2 = 4 colors
  20741.                                    ; 4 = 16 colors
  20742.                                    ; 8 = 256 colors
  20743.            dw      ?               ; pixel columns
  20744.            dw      ?               ; pixel rows
  20745.            dw      ?               ; character columns
  20746.            dw      ?               ; character rows
  20747.  
  20748.  Example:
  20749.  
  20750.    Get the current code page for the standard list device.
  20751.  
  20752.    stdprn  equ     4               ; standard list handle
  20753.  
  20754.    pars    dw      2               ; length of data
  20755.            dw      ?               ; receives code page
  20756.            .
  20757.            .
  20758.            .
  20759.            mov     ax,440ch        ; function & subfunction
  20760.            mov     bx,stdprn       ; standard list handle
  20761.            mov     ch,5            ; LPTx category
  20762.            mov     cl,6ah          ; query code page
  20763.            mov     dx,seg pars     ; parameter block address
  20764.            mov     ds,dx
  20765.            mov     dx,offset pars
  20766.            int     21h             ; transfer to MS-DOS
  20767.            jc      error           ; jump if function failed
  20768.            .
  20769.            .
  20770.            .
  20771.  
  20772.  
  20773.  ────────────────────────────────────────────────────────────────────────────
  20774.  Int 21H                                                                [3.2]
  20775.  Function 44H Subfunction 0DH (13)
  20776.  IOCTL: generic I/O control for block devices
  20777.  ────────────────────────────────────────────────────────────────────────────
  20778.  
  20779.    Provides a general-purpose mechanism for communication between application
  20780.    programs and block-device drivers. Allows a program to inspect or change
  20781.    device parameters for a logical drive and to read, write, format, and
  20782.    verify disk tracks in a hardware-independent manner.
  20783.  
  20784.  Call with:
  20785.  
  20786.    AH            = 44H
  20787.    AL            = 0DH
  20788.    BL            = drive code (0 = default, 1 = A, 2 = B, etc.)
  20789.    CH            = category (major) code:
  20790.                    08H = disk drive
  20791.    CL            = function (minor) code:
  20792.                    40H = Set Device Parameters
  20793.                    41H = Write Track
  20794.                    42H = Format and Verify Track
  20795.                    47H = Set Access Flag (4.0)
  20796.                    60H = Get Device Parameters
  20797.                    61H = Read Track
  20798.                    62H = Verify Track
  20799.                    67H = Get Access Flag (4.0)
  20800.    DS:DX         = segment:offset of parameter block
  20801.  
  20802.  Returns:
  20803.  
  20804.    If function successful
  20805.  
  20806.    Carry flag    = clear
  20807.  
  20808.    and, if called with CL = 60H or 61H
  20809.  
  20810.    DS:DX         = segment:offset of parameter block
  20811.  
  20812.    If function unsuccessful
  20813.  
  20814.    Carry flag    = set
  20815.    AX            = error code
  20816.  
  20817.  Notes:
  20818.  
  20819.    ■ The minor code 40H (Set Device Parameters) function must be used before
  20820.      an attempt to write, read, format, or verify a track on a logical drive.
  20821.      In general, the following sequence applies to any of these operations:
  20822.  
  20823.      ∙ Get the current parameters (minor code 60H). Examine and save them.
  20824.  
  20825.      ∙ Set the new parameters (minor code 40H).
  20826.  
  20827.      ∙ Perform the task.
  20828.  
  20829.      ∙ Retrieve the original parameters and restore them with minor code 40H.
  20830.  
  20831.    ■ For minor codes 40H (Set Device Parameters) and 60H (Get Device
  20832.      Parameters), the parameter block is formatted as follows:
  20833.  
  20834.      Special-functions field: offset 00H, length = 1 byte
  20835.  
  20836.      Bit(s)    Value      Meaning
  20837.      0         0          device BPB field contains a new default BPB
  20838.                1          use current BPB
  20839.      1         0          use all fields in parameter block
  20840.                1          use track layout field only
  20841.      2         0          sectors in track may be different sizes (should
  20842.                           always be avoided)
  20843.                1          sectors in track are all same size; sector numbers
  20844.                           range from 1 to the total number of sectors in the
  20845.                           track (should always be used)
  20846.      3─7       0          reserved
  20847.  
  20848.      Device type field: offset 01H, length 1 byte
  20849.  
  20850.      Value             Meaning
  20851.      0                 320/360 KB, 5.25-inch disk
  20852.      1                 1.2 MB, 5.25-inch disk
  20853.      2                 720 KB, 3.5-inch disk
  20854.      3                 single-density, 8-inch disk
  20855.      4                 double-density, 8-inch disk
  20856.      5                 fixed disk
  20857.      6                 tape drive
  20858.      7                 other type of block device
  20859.  
  20860.      Device attributes field: offset 02H, length 1 word
  20861.  
  20862.      Bit(s)        Value          Meaning
  20863.      0             0              removable storage medium
  20864.                    1              nonremovable storage medium
  20865.      1             0              door lock not supported
  20866.                    1              door lock supported
  20867.      2─15          0              reserved
  20868.  
  20869.      Number of cylinders field: offset 04H, length 1 word
  20870.      Maximum number of cylinders supported on the block device
  20871.  
  20872.      Media type field: offset 06H, length 1 byte
  20873.  
  20874.      Value             Meaning
  20875.      0                 1.2 MB, 5.25-inch disk
  20876.      1                 320/360 KB, 5.25-inch disk
  20877.  
  20878.      Device BPB field: offset 07H, length 31 bytes
  20879.      For format of the device BPB, see separate Note below.
  20880.      If bit 0 = 0 in special-functions field, this field contains the new
  20881.      default BPB for the device.
  20882.      If bit 0 = 1 in special-functions field, the BPB in this field is
  20883.      returned by the device driver in response to subsequent Build BPB
  20884.      requests.
  20885.  
  20886.      Track layout field: offset 26H, variable-length table
  20887.  
  20888.      Length      Meaning
  20889.      Word        number of sectors in track
  20890.      Word        number of first sector in track
  20891.      Word        size of first sector in track
  20892.      .
  20893.      .
  20894.      .
  20895.      Word        number of last sector in track
  20896.      Word        size of last sector in track
  20897.  
  20898.    ■ The device BPB field is a 31-byte data structure that describes the
  20899.      current disk and its control areas. The field is formatted as follows:
  20900.  
  20901.      Byte(s)     Meaning
  20902.      00H─01H     bytes per sector
  20903.      02H         sectors per cluster (allocation unit)
  20904.      03─04H      reserved sectors, beginning at sector 0
  20905.      05H         number of file allocation tables (FATs)
  20906.      06H─07H     maximum number of root-directory entries
  20907.      08H─09H     number of sectors
  20908.      0AH         media descriptor
  20909.      0BH─0CH     sectors per FAT
  20910.      0DH─0EH     sectors per track
  20911.      0FH─10H     number of heads
  20912.      11H─14H     number of hidden sectors
  20913.      15H─18H     large number of sectors (if bytes 08H─09H=0)
  20914.      19H─1EH     reserved
  20915.  
  20916.    ■ When minor code 40H (Set Device Parameters) is used, the number of
  20917.      cylinders should not be altered, or some or all of the volume may become
  20918.      inaccessible.
  20919.  
  20920.    ■ For minor codes 41H (Write Track) and 61H (Read Track), the parameter
  20921.      block is formatted as follows:
  20922.  
  20923.      Byte(s)     Meaning
  20924.      00H         special-functions field (must be 0)
  20925.      01H─02H     head
  20926.      03H─04H     cylinder
  20927.      05H─06H     starting sector
  20928.      07H─08H     sectors to transfer
  20929.      09H─0CH     transfer buffer address
  20930.  
  20931.    ■ For minor codes 42H (Format and Verify Track) and 62H (Verify Track),
  20932.      the parameter block is formatted as follows:
  20933.  
  20934.      Byte(s)     Meaning
  20935.      00H         special-functions field
  20936.  
  20937.                  Bit(s)      Significance
  20938.                  0           0 = Format/Verify track
  20939.                              1 = Format status call (MS-DOS 4.0 only)
  20940.                  1─7         reserved (0)
  20941.      01H─02H     head
  20942.      03H─04H     cylinder
  20943.  
  20944.      In MS-DOS 4.0, this function may be called with bit 0 of the
  20945.      special-functions field set after a minor code 40H call (Set Device
  20946.      Parameters) to determine whether the driver supports the specified
  20947.      number of tracks and sectors per track. A status is returned in the
  20948.      special-functions field which is interpreted as follows:
  20949.  
  20950.      Value       Meaning
  20951.      0           specified number of tracks and sectors per track supported
  20952.      1           this function not supported by the ROM BIOS
  20953.      2           specified number of tracks and sectors per track not
  20954.                  supported
  20955.      3           no disk in drive
  20956.  
  20957.    ■ For minor codes 47H (Set Access Flag) and 67H (Get Access Flag), the
  20958.      parameter block is formatted as follows:
  20959.  
  20960.      Byte        Meaning
  20961.      00H         special-functions field (must be 0)
  20962.      01H         disk access flag
  20963.  
  20964.      When the disk access flag is zero, access to the medium is blocked by
  20965.      the driver. The flag is set to zero when the driver detects an
  20966.      unformatted medium or a medium with an invalid boot record. When the
  20967.      access flag is nonzero, read/write operations to the medium are allowed
  20968.      by the driver. A formatting program must clear the disk access flag with
  20969.      minor code 47H before it requests minor code 42H (Format and Verify
  20970.      Track).
  20971.  
  20972.  Example:
  20973.  
  20974.    Get the device parameter block for disk drive C.
  20975.  
  20976.    dbpb    db      128 dup (0)     ; device parameter block
  20977.            .
  20978.            .
  20979.            .
  20980.            mov     ax,440dh        ; function & subfunction
  20981.            mov     bl,3            ; drive C = 3
  20982.            mov     ch,8            ; disk category
  20983.            mov     cl,60h          ; get device parameters
  20984.            mov     dx,seg dbpb     ; buffer address
  20985.            mov     ds,dx
  20986.            mov     dx,offset dbpb
  20987.            int     21h             ; transfer to MS-DOS
  20988.            jc      error           ; jump if function failed
  20989.            .
  20990.            .
  20991.            .
  20992.  
  20993.  
  20994.  ────────────────────────────────────────────────────────────────────────────
  20995.  Int 21H                                                                [3.2]
  20996.  Function 44H (68) Subfunction 0EH (14)
  20997.  IOCTL: get logical drive map
  20998.  ────────────────────────────────────────────────────────────────────────────
  20999.  
  21000.    Returns the logical drive code that was most recently used to access the
  21001.    specified block device.
  21002.  
  21003.  Call with:
  21004.  
  21005.    AH            = 44H
  21006.    AL            = 0EH
  21007.    BL            = drive code (0 = default, 1 = A, 2 = B, etc.)
  21008.  
  21009.  Returns:
  21010.  
  21011.    If function successful
  21012.  
  21013.    Carry flag    = clear
  21014.    AL            = mapping code
  21015.  
  21016.                    00H       if only one logical drive code assigned to the
  21017.                              block device
  21018.                    01H─1AH   logical drive code (1 = A, 2 = B, etc.) mapped
  21019.                              to the block device
  21020.  
  21021.    If function unsuccessful
  21022.  
  21023.    Carry flag    = set
  21024.    AX            = error code
  21025.  
  21026.  Note:
  21027.  
  21028.    ■ If a drive has not been assigned a logical mapping with Function 44H
  21029.      Subfunction 0FH, the logical and physical drive codes are the same.
  21030.  
  21031.  Example:
  21032.  
  21033.    Check whether drive A has more than one logical drive code.
  21034.  
  21035.            .
  21036.            .
  21037.            .
  21038.            mov     ax,440eh        ; function & subfunction
  21039.            mov     bl,1            ; drive 1 = A
  21040.            int     21h             ; transfer to MS-DOS
  21041.            jc      error           ; jump if function failed
  21042.            or      al,al           ; test drive code
  21043.            jz      label1          ; jump, no drive aliases
  21044.            .
  21045.            .
  21046.            .
  21047.  
  21048.  
  21049.  ────────────────────────────────────────────────────────────────────────────
  21050.  Int 21H                                                                [3.2]
  21051.  Function 44H (68) Subfunction 0FH (15)
  21052.  IOCTL: set logical drive map
  21053.  ────────────────────────────────────────────────────────────────────────────
  21054.  
  21055.    Sets the next logical drive code that will be used to reference a block
  21056.    device.
  21057.  
  21058.  Call with:
  21059.  
  21060.    AH            = 44H
  21061.    AL            = 0FH
  21062.    BL            = drive code (0 = default, 1 = A, 2 = B, etc.)
  21063.  
  21064.  Returns:
  21065.  
  21066.    If function successful
  21067.  
  21068.    Carry flag    = clear
  21069.    AL            = mapping code
  21070.  
  21071.                    00H       if only one logical drive code assigned to the
  21072.                              block device
  21073.                    01H─1AH   logical drive code (1 = A, 2 = B, etc.) mapped
  21074.                              to the block device
  21075.  
  21076.    If function unsuccessful
  21077.  
  21078.    Carry flag    = set
  21079.    AX            = error code
  21080.  
  21081.  Note:
  21082.  
  21083.    ■ When a physical block device is aliased to more than one logical drive
  21084.      code, this function can be used to inform the driver which drive code
  21085.      will next be used to access the device.
  21086.  
  21087.  Example:
  21088.  
  21089.    Notify the floppy-disk driver that the next access will be for logical
  21090.    drive B.
  21091.  
  21092.            .
  21093.            .
  21094.            .
  21095.            mov     ax,440fh        ; function & subfunction
  21096.            mov     bl,2            ; drive 2 = B
  21097.            int     21h             ; transfer to MS-DOS
  21098.            jc      error           ; jump if function failed
  21099.            .
  21100.            .
  21101.            .
  21102.  
  21103.  
  21104.  ────────────────────────────────────────────────────────────────────────────
  21105.  Int 21H                                                                [2.0]
  21106.  Function 45H (69)
  21107.  Duplicate handle
  21108.  ────────────────────────────────────────────────────────────────────────────
  21109.  
  21110.    Given a handle for a currently open device or file, returns a new handle
  21111.    that refers to the same device or file at the same position.
  21112.  
  21113.  Call with:
  21114.  
  21115.    AH            = 45H
  21116.    BX            = handle to be duplicated
  21117.  
  21118.  Returns:
  21119.  
  21120.    If function successful
  21121.  
  21122.    Carry flag    = clear
  21123.    AX            = new handle
  21124.  
  21125.    If function unsuccessful
  21126.  
  21127.    Carry flag    = set
  21128.    AX            = error code
  21129.  
  21130.  Notes:
  21131.  
  21132.    ■ A seek, read, or write operation that moves the file pointer for one of
  21133.      the two handles also moves the file pointer associated with the other.
  21134.  
  21135.    ■ This function can be used to efficiently update the directory for a file
  21136.      that has changed in length, without incurring the overhead of closing
  21137.      and then reopening the file. The handle for the file is simply
  21138.      duplicated with this function and the duplicate is closed, leaving the
  21139.      original handle open for further read/write operations.
  21140.  
  21141.    ■ [3.3] See also Int 21H Function 68H (Commit File).
  21142.  
  21143.  Example:
  21144.  
  21145.    Duplicate the handle stored in the variable fhandle, then close the
  21146.    duplicate. This ensures that all buffered data is physically written to
  21147.    disk and that the directory entry for the corresponding file is updated,
  21148.    but leaves the original handle open for subsequent file operations.
  21149.  
  21150.    fhandle dw      0               ; file handle
  21151.            .
  21152.            .
  21153.            .
  21154.                                    ; get duplicate handle
  21155.            mov     ah,45h          ; function number
  21156.            mov     bx,fhandle      ; original file handle
  21157.            int     21h             ; transfer to MS-DOS
  21158.            jc      error           ; jump if dup failed
  21159.                                    ; now close dup'd handle
  21160.            mov     bx,ax           ; put handle into BX
  21161.            mov     ah,3eh          ; function number
  21162.            int     21h             ; transfer to MS-DOS         jc      error
  21163.     ; jump if close failed
  21164.            .
  21165.            .
  21166.            .
  21167.  
  21168.  
  21169.  ────────────────────────────────────────────────────────────────────────────
  21170.  Int 21H                                                                [2.0]
  21171.  Function 46H (70)
  21172.  Redirect handle
  21173.  ────────────────────────────────────────────────────────────────────────────
  21174.  
  21175.    Given two handles, makes the second handle refer to the same device or
  21176.    file at the same location as the first handle. The second handle is then
  21177.    said to be redirected.
  21178.  
  21179.  Call with:
  21180.  
  21181.    AH            = 46H
  21182.    BX            = handle for file or device
  21183.    CX            = handle to be redirected
  21184.  
  21185.  Returns:
  21186.  
  21187.    If function successful
  21188.  
  21189.    Carry flag    = clear
  21190.  
  21191.    If function unsuccessful
  21192.  
  21193.    Carry flag    = set
  21194.    AX            = error code
  21195.  
  21196.  Notes:
  21197.  
  21198.    ■ If the handle passed in CX already refers to an open file, that file is
  21199.      closed first.
  21200.  
  21201.    ■ A seek, read, or write operation that moves the file pointer for one of
  21202.      the two handles also moves the file pointer associated with the other.
  21203.  
  21204.    ■ This function is commonly used to redirect the standard input and output
  21205.      handles to another file or device before a child process is executed
  21206.      with Int 21H Function 4BH.
  21207.  
  21208.  Example:
  21209.  
  21210.    Redirect the standard output to the list device, so that all output
  21211.    directed to the console will appear on the printer instead. Later, restore
  21212.    the original meaning of the standard output handle.
  21213.  
  21214.    stdin   equ     0
  21215.    stdout  equ     1
  21216.    stderr  equ     2
  21217.    stdaux  equ     3
  21218.    stdprn  equ     4
  21219.  
  21220.    dhandle dw      0               ; duplicate handle
  21221.            .
  21222.            .
  21223.            .
  21224.                                    ; get dup of stdout
  21225.            mov     ah,45h          ; function number
  21226.            mov     bx,stdout       ; standard output handle
  21227.            int     21h             ; transfer to MS-DOS
  21228.            jc      error           ; jump if dup failed
  21229.            mov     dhandle,ax      ; save dup'd handle
  21230.                                    ;
  21231.                                    ; redirect standard output
  21232.                                    ; to standard list device
  21233.            mov     ah,46h          ; function number
  21234.            mov     bx,stdprn       ; standard list handle
  21235.            mov     cx,stdout       ; standard output handle
  21236.            int     21h             ; transfer to MS-DOS
  21237.            jc      error           ; jump if redirect failed
  21238.            .
  21239.            .
  21240.            .
  21241.                                    ; restore standard output
  21242.                                    ; to original meaning
  21243.            mov     ah,46h          ; function number
  21244.            mov     bx,dhandle      ; saved duplicate handle
  21245.            mov     cx,stdout       ; standard output handle
  21246.            int     21h             ; transfer to MS-DOS
  21247.            jc      error           ; jump if redirect failed
  21248.                                    ; close duplicate handle
  21249.                                    ; because no longer needed
  21250.            mov     ah,3eh          ; function number
  21251.            mov     bx,dhandle      ; saved duplicate handle
  21252.            int     21h             ; transfer to MS-DOS
  21253.            jc      error           ; jump if close failed
  21254.            .
  21255.            .
  21256.            .
  21257.  
  21258.  
  21259.  ────────────────────────────────────────────────────────────────────────────
  21260.  Int 21H                                                                [2.0]
  21261.  Function 47H (71)
  21262.  Get current directory
  21263.  ────────────────────────────────────────────────────────────────────────────
  21264.  
  21265.    Obtains an ASCIIZ string that describes the path from the root to the
  21266.    current directory, and the name of that directory.
  21267.  
  21268.  Call with:
  21269.  
  21270.    AH            = 47H
  21271.    DL            = drive code (0 = default, 1 = A, etc.)
  21272.    DS:SI         = segment:offset of 64-byte buffer
  21273.  
  21274.  Returns:
  21275.  
  21276.    If function successful
  21277.  
  21278.    Carry flag    = clear
  21279.  
  21280.    and buffer is filled in with full pathname from root of current directory.
  21281.  
  21282.    If function unsuccessful
  21283.  
  21284.    Carry flag    = set
  21285.    AX            = error code
  21286.  
  21287.  Notes:
  21288.  
  21289.    ■ The returned path name does not include the drive identifier or a
  21290.      leading backslash (\). It is terminated with a null (00H) byte.
  21291.      Consequently, if the current directory is the root directory, the first
  21292.      byte in the buffer will contain 00H.
  21293.  
  21294.    ■ The function fails if the drive code is invalid.
  21295.  
  21296.    ■ The current directory may be set with Int 21H Function 3BH.
  21297.  
  21298.  Example:
  21299.  
  21300.    Get the name of the current directory for drive C into the buffer named
  21301.    dbuff.
  21302.  
  21303.    dbuff   db      64 dup (0)      ; receives path string
  21304.            .
  21305.            .
  21306.            .
  21307.            mov     ah,47h          ; function number
  21308.            mov     dl,03           ; drive C = 3
  21309.            mov     si,seg dbuff    ; buffer address
  21310.            mov     ds,si
  21311.            mov     si,offset dbuff
  21312.            int     21h             ; transfer to MS-DOS
  21313.            jc      error           ; jump if error
  21314.            .
  21315.            .
  21316.            .
  21317.  
  21318.  
  21319.  ────────────────────────────────────────────────────────────────────────────
  21320.  Int 21H                                                                [2.0]
  21321.  Function 48H (72)
  21322.  Allocate memory block
  21323.  ────────────────────────────────────────────────────────────────────────────
  21324.  
  21325.    Allocates a block of memory and returns a pointer to the beginning of the
  21326.    allocated area.
  21327.  
  21328.  Call with:
  21329.  
  21330.    AH            = 48H
  21331.    BX            = number of paragraphs of memory needed
  21332.  
  21333.  Returns:
  21334.  
  21335.    If function successful
  21336.  
  21337.    Carry flag    = clear
  21338.    AX            = base segment address of allocated block
  21339.  
  21340.    If function unsuccessful
  21341.  
  21342.    Carry flag    = set
  21343.    AX            = error code
  21344.    BX            = size of largest available block (paragraphs)
  21345.  
  21346.  Notes:
  21347.  
  21348.    ■ If the function succeeds, the base address of the newly allocated block
  21349.      is AX:0000.
  21350.  
  21351.    ■ The default allocation strategy used by MS-DOS is "first fit"; that is,
  21352.      the memory block at the lowest address that is large enough to satisfy
  21353.      the request is allocated. The allocation strategy can be altered with
  21354.      Int 21H Function 58H.
  21355.  
  21356.    ■ When a .COM program is loaded, it ordinarily already "owns" all of the
  21357.      memory in the transient program area, leaving none for dynamic
  21358.      allocation. The amount of memory initially allocated to a .EXE program
  21359.      at load time depends on the MINALLOC and MAXALLOC fields in the .EXE
  21360.      file header. See Int 21H Function 4AH.
  21361.  
  21362.  Example:
  21363.  
  21364.    Request a 64 KB block of memory for use as a buffer.
  21365.  
  21366.    bufseg  dw      ?               ; segment base of new block
  21367.            .
  21368.            .
  21369.            .
  21370.            mov     ah,48h          ; function number
  21371.            mov     bx,1000h        ; block size (paragraphs)
  21372.            int     21h             ; transfer to MS-DOS
  21373.            jc      error           ; jump if allocation failed
  21374.            mov     bufseg,ax       ; save segment of new block
  21375.            .
  21376.            .
  21377.            .
  21378.  
  21379.  
  21380.  ────────────────────────────────────────────────────────────────────────────
  21381.  Int 21H                                                                [2.0]
  21382.  Function 49H (73)
  21383.  Release memory block
  21384.  ────────────────────────────────────────────────────────────────────────────
  21385.  
  21386.    Releases a memory block and makes it available for use by other programs.
  21387.  
  21388.  Call with:
  21389.  
  21390.    AH            = 49H
  21391.    ES            = segment of block to be released
  21392.  
  21393.  Returns:
  21394.  
  21395.    If function successful
  21396.  
  21397.    Carry flag    = clear
  21398.  
  21399.    If function unsuccessful
  21400.  
  21401.    Carry flag    = set
  21402.    AX            = error code
  21403.  
  21404.  Notes:
  21405.  
  21406.    ■ This function assumes that the memory block being released was
  21407.      previously obtained by a successful call to Int 21H Function 48H.
  21408.  
  21409.    ■ The function will fail or can cause unpredictable system errors if:
  21410.  
  21411.      ∙ the program releases a memory block that does not belong to it.
  21412.  
  21413.      ∙ the segment address passed in register ES is not a valid base address
  21414.        for an existing memory block.
  21415.  
  21416.  Example:
  21417.  
  21418.    Release the memory block that was previously allocated in the example for
  21419.    Int 21H Function 48H (page 438).
  21420.  
  21421.    bufseg  dw      ?               ; segment base of block
  21422.            .
  21423.            .
  21424.            .
  21425.            mov     ah,49h          ; function number
  21426.            mov     es,bufseg       ; base segment of block
  21427.            int     21h             ; transfer to MS-DOS
  21428.            jc      error           ; jump if release failed
  21429.            .
  21430.            .
  21431.            .
  21432.  
  21433.  
  21434.  ────────────────────────────────────────────────────────────────────────────
  21435.  Int 21H                                                                [2.0]
  21436.  Function 4AH (74)
  21437.  Resize memory block
  21438.  ────────────────────────────────────────────────────────────────────────────
  21439.  
  21440.    Dynamically shrinks or extends a memory block, according to the needs of
  21441.    an application program.
  21442.  
  21443.  Call with:
  21444.  
  21445.    AH            = 4AH
  21446.    BX            = desired new block size in paragraphs
  21447.    ES            = segment of block to be modified
  21448.  
  21449.  Returns:
  21450.  
  21451.    If function successful
  21452.  
  21453.    Carry flag    = clear
  21454.  
  21455.    If function unsuccessful
  21456.  
  21457.    Carry flag    = set
  21458.    AX            = error code
  21459.    BX            = maximum block size available (paragraphs)
  21460.  
  21461.  Notes:
  21462.  
  21463.    ■ This function modifies the size of a memory block that was previously
  21464.      allocated with a call to Int 21H Function 48H.
  21465.  
  21466.    ■ If the program is requesting an increase in the size of an allocated
  21467.      block, and this function fails, the maximum possible size for the
  21468.      specified block is returned in register BX. The program can use this
  21469.      value to determine whether it should terminate, or continue in a
  21470.      degraded fashion with less memory.
  21471.  
  21472.    ■ A program that uses EXEC (Int 21H Function 4BH) to load and execute a
  21473.      child program must call this function first to make memory available for
  21474.      the child, passing the address of its PSP in register ES and the amount
  21475.      of memory needed for its own code, data, and stacks in register BX.
  21476.  
  21477.  Example:
  21478.  
  21479.    Resize the memory block that was allocated in the example for Int 21H
  21480.    Function 48H (page 438), shrinking it to 32 KB.
  21481.  
  21482.    bufseg  dw      ?               ; segment base of block
  21483.            .
  21484.            .
  21485.            .
  21486.            mov     ah,4ah          ; function number
  21487.            mov     bx,0800h        ; new size (paragraphs)
  21488.            mov     es,bufseg       ; segment base of block
  21489.            int     21h             ; transfer to MS-DOS
  21490.            jc      error           ; jump, resize failed
  21491.            .
  21492.            .
  21493.            .
  21494.  
  21495.  
  21496.  ────────────────────────────────────────────────────────────────────────────
  21497.  Int 21H                                                                [2.0]
  21498.  Function 4BH (75)
  21499.  Execute program (EXEC)
  21500.  ────────────────────────────────────────────────────────────────────────────
  21501.  
  21502.    Allows an application program to run another program, regaining control
  21503.    when it is finished. Can also be used to load overlays, although this use
  21504.    is uncommon.
  21505.  
  21506.  Call with:
  21507.  
  21508.    AH            = 4BH
  21509.    AL            = subfunction
  21510.                    00H = Load and Execute Program
  21511.                    03H = Load Overlay
  21512.    ES:BX         = segment:offset of parameter block
  21513.    DS:DX         = segment:offset of ASCIIZ program pathname
  21514.  
  21515.  Returns:
  21516.  
  21517.    If function successful
  21518.  
  21519.    Carry flag    = clear
  21520.  
  21521.    [2]         all registers except for CS:IP may be destroyed
  21522.    [3.0+]      registers are preserved in the usual fashion
  21523.  
  21524.    If function unsuccessful
  21525.  
  21526.    Carry flag    = set
  21527.    AX            = error code
  21528.  
  21529.  Notes:
  21530.  
  21531.    ■ The parameter block format for Subfunction 00H (Load and Execute
  21532.      Program) is as follows:
  21533.  
  21534.      Bytes       Contents
  21535.      00H─01H     segment pointer to environment block
  21536.      02H─03H     offset of command line tail
  21537.      04H─05H     segment of command line tail
  21538.      06H─07H     offset of first FCB to be copied into new PSP + 5CH
  21539.      08H─09H     segment of first FCB
  21540.      0AH─0BH     offset of second FCB to be copied into new PSP + 6CH
  21541.      0CH─0DH     segment of second FCB
  21542.  
  21543.    ■ The parameter block format for Subfunction 03H (Load Overlay) is as
  21544.      follows:
  21545.  
  21546.      Bytes       Contents
  21547.      00H─01H     segment address where overlay is to be loaded
  21548.      02H─03H     relocation factor to apply to loaded image
  21549.  
  21550.    ■ The environment block must be paragraph-aligned. It consists of a
  21551.      sequence of ASCIIZ strings in the form:
  21552.  
  21553.              db      'COMSPEC=A:\COMMAND.COM',0
  21554.  
  21555.      The entire set of strings is terminated by an extra null (00H) byte.
  21556.  
  21557.    ■ The command tail format consists of a count byte, followed by an ASCII
  21558.      string, terminated by a carriage return (which is not included in the
  21559.      count). The first character in the string should be an ASCII space (20H)
  21560.      for compatibility with the command tail passed to programs by
  21561.      COMMAND.COM. For example:
  21562.  
  21563.              db      6,' *.DAT',0dh
  21564.  
  21565.    ■ Before a program uses Int 21H Function 4BH to run another program, it
  21566.      must release all memory it is not actually using with a call to Int 21H
  21567.      Function 4AH, passing the segment address of its own PSP and the number
  21568.      of paragraphs to retain.
  21569.  
  21570.    ■ Ordinarily, all active handles of the parent program are inherited by
  21571.      the child program, although the parent can prevent this in MS-DOS 3.0
  21572.      and later by setting the inheritance bit when the file or device is
  21573.      opened. Any redirection of the standard input and/or output in the
  21574.      parent process also affects the child process.
  21575.  
  21576.    ■ The environment block can be used to pass information to the child
  21577.      process. If the environment block pointer in the parameter block is
  21578.      zero, the child program inherits an exact copy of the parent's
  21579.      environment. In any case, the segment address of the child's environment
  21580.      is found at offset 002CH in the child's PSP.
  21581.  
  21582.    ■ After return from the EXEC function call, the termination type and
  21583.      return code of the child program may be obtained with Int 21H Function
  21584.      4DH.
  21585.  
  21586.  Example:
  21587.  
  21588.    See Chapter 12.
  21589.  
  21590.  
  21591.  ────────────────────────────────────────────────────────────────────────────
  21592.  Int 21H                                                                [2.0]
  21593.  Function 4CH (76)
  21594.  Terminate process with return code
  21595.  ────────────────────────────────────────────────────────────────────────────
  21596.  
  21597.    Terminates the current process, passing a return code to the parent
  21598.    process. This is one of several methods that a program can use to perform
  21599.    a final exit. MS-DOS then takes the following actions:
  21600.  
  21601.    ■ All memory belonging to the process is released.
  21602.  
  21603.    ■ File buffers are flushed and any open handles for files or devices owned
  21604.      by the process are closed.
  21605.  
  21606.    ■ The termination handler vector (Int 22H) is restored from PSP:000AH.
  21607.  
  21608.    ■ The Ctrl-C handler vector (Int 23H) is restored from PSP:000EH.
  21609.  
  21610.    ■ [2.0+] The critical-error handler vector (Int 24H) is restored from
  21611.      PSP:0012H.
  21612.  
  21613.    ■ Control is transferred to the termination handler.
  21614.  
  21615.    If the program is returning to COMMAND.COM, control transfers to the
  21616.    resident portion and the transient portion is reloaded if necessary. If a
  21617.    batch file is in progress, the next line of the file is fetched and
  21618.    interpreted; otherwise, a prompt is issued for the next user command.
  21619.  
  21620.  Call with:
  21621.  
  21622.    AH            = 4CH
  21623.    AL            = return code
  21624.  
  21625.  Returns:
  21626.  
  21627.    Nothing
  21628.  
  21629.  Notes:
  21630.  
  21631.    ■ [2.0+] This is the preferred method of termination for application
  21632.      programs because it allows a return code to be passed to the parent
  21633.      program and does not rely on the contents of any segment register. Other
  21634.      methods of performing a final exit are:
  21635.  
  21636.      ∙ Int 20H
  21637.  
  21638.      ∙ Int 21H Function 00H
  21639.  
  21640.      ∙ Int 21H Function 31H
  21641.  
  21642.      ∙ Int 27H
  21643.  
  21644.    ■ Any files that have been opened using FCBs and modified by the program
  21645.      should be closed before program termination; otherwise, data may be
  21646.      lost.
  21647.  
  21648.    ■ The return code can be retrieved by the parent process with Int 21H
  21649.      Function 4DH (Get Return Code). It can also be tested in a batch file
  21650.      with an IF ERRORLEVEL statement. By convention, a return code of zero
  21651.      indicates successful execution, and a non-zero return code indicates an
  21652.      error.
  21653.  
  21654.    ■ [3.0+] If the program is running on a network, it should remove all
  21655.      locks it has placed on file regions before terminating.
  21656.  
  21657.  Example:
  21658.  
  21659.    Terminate the current process, passing a return code of 1 to the parent
  21660.    process.
  21661.  
  21662.            .
  21663.            .
  21664.            .
  21665.            mov     ah,4ch          ; function number
  21666.            mov     al,01h          ; return code
  21667.            int     21h             ; transfer to MS-DOS
  21668.  
  21669.  
  21670.  ────────────────────────────────────────────────────────────────────────────
  21671.  Int 21H                                                                [2.0]
  21672.  Function 4DH (77)
  21673.  Get return code
  21674.  ────────────────────────────────────────────────────────────────────────────
  21675.  
  21676.    Used by a parent process, after the successful execution of an EXEC call
  21677.    (Int 21H Function 4BH), to obtain the return code and termination type of
  21678.    a child process.
  21679.  
  21680.  Call with:
  21681.  
  21682.    AH            = 4DH
  21683.  
  21684.  Returns:
  21685.  
  21686.    AH            = exit type
  21687.                    00H if normal termination by Int 20H, Int 21H Function
  21688.                    00H, or Int 21H Function 4CH
  21689.                    01H if termination by user's entry of CtrlDC
  21690.                    02H if termination by critical-error handler
  21691.                    03H if termination by Int 21H Function 31H or Int 27H
  21692.    AL            = return code passed by child process (0 if child terminated
  21693.                    by Int 20H, Int 21H Function 00H, or Int 27H)
  21694.  
  21695.  Notes:
  21696.  
  21697.    ■ This function will yield the return code of a child process only once. A
  21698.      subsequent call without an intervening EXEC (Int 21H Function 4BH) will
  21699.      not necessarily return any valid information.
  21700.  
  21701.    ■ This function does not set the carry flag to indicate an error. If no
  21702.      previous child process has been executed, the values returned in AL and
  21703.      AH are undefined.
  21704.  
  21705.  Example:
  21706.  
  21707.    Get the return code and termination kind of child process that was
  21708.    previously executed with Int 21H Function 4BH (EXEC).
  21709.  
  21710.    retcode dw      ?               ; return code, termination type
  21711.            .
  21712.            .
  21713.            .
  21714.            mov     ah,4dh          ; function number
  21715.            int     21h             ; transfer to MS-DOS
  21716.            mov     retcode,ax      ; save child process info
  21717.            .
  21718.            .
  21719.            .
  21720.  
  21721.  
  21722.  ────────────────────────────────────────────────────────────────────────────
  21723.  Int 21H                                                                [2.0]
  21724.  Function 4EH (78)
  21725.  Find first file
  21726.  ────────────────────────────────────────────────────────────────────────────
  21727.  
  21728.    Given a file specification in the form of an ASCIIZ string, searches the
  21729.    default or specified directory on the default or specified drive for the
  21730.    first matching file.
  21731.  
  21732.  Call with:
  21733.  
  21734.    AH            = 4EH
  21735.    CX            = search attribute (bits may be combined)
  21736.  
  21737.                    Bit(s)    Significance (if set)
  21738.                    0         read-only
  21739.                    1         hidden
  21740.                    2         system
  21741.                    3         volume label
  21742.                    4         directory
  21743.                    5         archive
  21744.                    6─15      reserved (0)
  21745.  
  21746.    DS:DX         = segment:offset of ASCIIZ pathname
  21747.  
  21748.  Returns:
  21749.  
  21750.    If function successful (matching file found)
  21751.  
  21752.    Carry flag    = clear
  21753.  
  21754.    and search results returned in current disk transfer area as follows:
  21755.  
  21756.    Byte(s)            Description
  21757.    00H─14H            reserved (0)
  21758.    15H                attribute of matched file or directory
  21759.    16H─17H            file time
  21760.                       bits 00H─04H = 2-second increments (0─29)
  21761.                       bits 05H─0AH = minutes (0─59)
  21762.                       bits 0BH─0FH = hours (0─23)
  21763.    18H─19H            file date
  21764.                       bits 00H─04H = day (1─31)
  21765.                       bits 05H─08H = month (1─12)
  21766.                       bits 09H─0FH = year (relative to 1980)
  21767.    1AH─1DH            file size
  21768.    1EH─2AH            ASCIIZ filename and extension
  21769.  
  21770.    If function unsuccessful (no matching files)
  21771.  
  21772.    Carry flag    = set
  21773.    AX            = error code
  21774.  
  21775.  Notes:
  21776.  
  21777.    ■ This function assumes that the DTA has been previously set by the
  21778.      program with Int 21H Function 1AH to point to a buffer of adequate
  21779.      size.
  21780.  
  21781.    ■ The * and ? wildcard characters are allowed in the filename. If wildcard
  21782.      characters are present, this function returns only the first matching
  21783.      filename.
  21784.  
  21785.    ■ If the attribute is 0, only ordinary files are found. If the volume
  21786.      label attribute bit is set, only volume labels will be returned (if any
  21787.      are present). Any other attribute or combination of attributes (hidden,
  21788.      system, and directory) results in those files and all normal files being
  21789.      matched.
  21790.  
  21791.  Example:
  21792.  
  21793.    Find the first .COM file in the directory \MYDIR on drive C.
  21794.  
  21795.    fname   db      'C:\MYDIR\*.COM',0
  21796.  
  21797.    dbuff   db      43 dup (0)      ; receives search results
  21798.            .
  21799.            .
  21800.            .
  21801.                                    ; set DTA address
  21802.            mov     ah,1ah          ; function number
  21803.            mov     dx,seg dbuff    ; result buffer address
  21804.            mov     ds,dx
  21805.            mov     dx,offset dbuff
  21806.            int     21h             ; transfer to MS-DOS
  21807.  
  21808.                                    ; search for first match
  21809.            mov     ah,4eh          ; function number
  21810.            mov     cx,0            ; normal attribute
  21811.            mov     dx,seg fname    ; address of filename
  21812.            mov     ds,dx
  21813.            mov     dx,offset fname
  21814.            int     21h             ; transfer to MS-DOS
  21815.            jc      error           ; jump if no match
  21816.            .
  21817.            .
  21818.            .
  21819.  
  21820.  
  21821.  ────────────────────────────────────────────────────────────────────────────
  21822.  Int 21H                                                                [2.0]
  21823.  Function 4FH (79)
  21824.  Find next file
  21825.  ────────────────────────────────────────────────────────────────────────────
  21826.  
  21827.    Assuming a previous successful call to Int 21H Function 4EH, finds the
  21828.    next file in the default or specified directory on the default or
  21829.    specified drive that matches the original file specification.
  21830.  
  21831.  Call with:
  21832.  
  21833.    AH            = 4FH
  21834.  
  21835.    Assumes DTA points to working buffer used by previous successful Int 21H
  21836.    Function 4EH or 4FH.
  21837.  
  21838.  Returns:
  21839.  
  21840.    If function successful (matching file found)
  21841.  
  21842.    Carry flag    = clear
  21843.  
  21844.    and search results returned in current disk transfer area as described for
  21845.    Int 21H Function 4EH
  21846.  
  21847.    If function unsuccessful (no more matching files)
  21848.  
  21849.    Carry flag    = set
  21850.    AX            = error code
  21851.  
  21852.  Notes:
  21853.  
  21854.    ■ Use of this call assumes that the original file specification passed to
  21855.      Int 21H Function 4EH contained one or more * or ? wildcard characters.
  21856.  
  21857.    ■ When this function is called, the current disk transfer area (DTA) must
  21858.      contain information from a previous successful call to Int 21H Function
  21859.      4EH or 4FH.
  21860.  
  21861.  Example:
  21862.  
  21863.    Continuing the search operation in the example for Int 21H Function 4EH,
  21864.    find the next .COM file (if any) in the directory \MYDIR on drive C.
  21865.  
  21866.    fname   db      'C:\MYDIR\*.COM',0
  21867.  
  21868.    dbuff   db      43 dup (0)      ; receives search results
  21869.            .
  21870.            .
  21871.            .
  21872.                                    ; search for next match
  21873.            mov     ah,4fh          ; function number
  21874.            int     21h             ; transfer to MS-DOS
  21875.            jc      error           ; jump if no more files
  21876.            .
  21877.            .
  21878.            .
  21879.  
  21880.  
  21881.  ────────────────────────────────────────────────────────────────────────────
  21882.  Int 21H
  21883.  Function 50H (80)
  21884.  Reserved
  21885.  ────────────────────────────────────────────────────────────────────────────
  21886.  
  21887.  
  21888.  ────────────────────────────────────────────────────────────────────────────
  21889.  Int 21H
  21890.  Function 51H (81)
  21891.  Reserved
  21892.  ────────────────────────────────────────────────────────────────────────────
  21893.  
  21894.  
  21895.  ────────────────────────────────────────────────────────────────────────────
  21896.  Int 21H
  21897.  Function 52H (82)
  21898.  Reserved
  21899.  ────────────────────────────────────────────────────────────────────────────
  21900.  
  21901.  
  21902.  ────────────────────────────────────────────────────────────────────────────
  21903.  Int 21H
  21904.  Function 53H (83)
  21905.  Reserved
  21906.  ────────────────────────────────────────────────────────────────────────────
  21907.  
  21908.  
  21909.  ────────────────────────────────────────────────────────────────────────────
  21910.  Int 21H                                                                [2.0]
  21911.  Function 54H (84)
  21912.  Get verify flag
  21913.  ────────────────────────────────────────────────────────────────────────────
  21914.  
  21915.    Obtains the current value of the system verify (read-after-write) flag.
  21916.  
  21917.  Call with:
  21918.  
  21919.    AH            = 54H
  21920.  
  21921.  Returns:
  21922.  
  21923.    AL            = current verify flag value
  21924.                    00H if verify off
  21925.                    01H if verify on
  21926.  
  21927.  Notes:
  21928.  
  21929.    ■ Because read-after-write verification slows disk operations, the default
  21930.      state of the system verify flag is OFF.
  21931.  
  21932.    ■ The state of the system verify flag can be changed through a call to Int
  21933.      21H Function 2EH or by the MS-DOS commands VERIFY ON and VERIFY OFF.
  21934.  
  21935.  Example:
  21936.  
  21937.    Obtain the state of the system verify flag.
  21938.  
  21939.            .
  21940.            .
  21941.            .
  21942.            mov     ah,54h          ; function number
  21943.            int     21h             ; transfer to MS-DOS
  21944.            cmp     al,01h          ; check verify state
  21945.            je      label1          ; jump if verify on
  21946.                                    ; else assume verify off
  21947.            .
  21948.            .
  21949.            .
  21950.  
  21951.  
  21952.  ────────────────────────────────────────────────────────────────────────────
  21953.  Int 21H
  21954.  Function 55H (85)
  21955.  Reserved
  21956.  ────────────────────────────────────────────────────────────────────────────
  21957.  
  21958.  
  21959.  ────────────────────────────────────────────────────────────────────────────
  21960.  Int 21H                                                                [2.0]
  21961.  Function 56H (86)
  21962.  Rename file
  21963.  ────────────────────────────────────────────────────────────────────────────
  21964.  
  21965.    Renames a file and/or moves its directory entry to a different directory
  21966.    on the same disk. In MS-DOS version 3.0 and later, this function can also
  21967.    be used to rename directories.
  21968.  
  21969.  Call with:
  21970.  
  21971.    AH            = 56H
  21972.    DS:DX         = segment:offset of current ASCIIZ pathname
  21973.    ES:DI         = segment:offset of new ASCIIZ pathname
  21974.  
  21975.  Returns:
  21976.  
  21977.    If function successful
  21978.  
  21979.    Carry flag    = clear
  21980.  
  21981.    If function unsuccessful
  21982.  
  21983.    Carry flag    = set
  21984.    AX            = error code
  21985.  
  21986.  Notes:
  21987.  
  21988.    ■ The function fails if:
  21989.  
  21990.      ∙ any element of the pathname does not exist.
  21991.  
  21992.      ∙ a file with the new pathname already exists.
  21993.  
  21994.      ∙ the current pathname specification contains a different disk drive
  21995.        than does the new pathname.
  21996.  
  21997.      ∙ the file is being moved to the root directory, and the root directory
  21998.        is full.
  21999.  
  22000.      ∙ [3.0+] the program is running on a network and the user has
  22001.        insufficient access rights to either the existing file or the new
  22002.        directory.
  22003.  
  22004.    ■ The * and ? wildcard characters are not allowed in either the current or
  22005.      new pathname specifications.
  22006.  
  22007.  Example:
  22008.  
  22009.    Change the name of the file MYFILE.DAT in the directory \MYDIR on drive C
  22010.    to MYTEXT.DAT. At the same time, move the file to the directory \SYSTEM on
  22011.    the same drive.
  22012.  
  22013.    oldname db      'C:\MYDIR\MYFILE.DAT',0
  22014.  
  22015.    newname db      'C:\SYSTEM\MYTEXT.DAT',0
  22016.            .
  22017.            .
  22018.            .
  22019.            mov     ah,56h          ; function number
  22020.            mov     dx,seg oldname  ; old filename address
  22021.            mov     ds,dx
  22022.            mov     dx,offset oldname
  22023.            mov     di,seg newname  ; new filename address
  22024.            mov     es,di
  22025.            mov     di,offset newname
  22026.            int     21h             ; transfer to MS-DOS
  22027.            jc      error           ; jump if rename failed
  22028.            .
  22029.            .
  22030.            .
  22031.  
  22032.  
  22033.  ────────────────────────────────────────────────────────────────────────────
  22034.  Int 21H                                                                [2.0]
  22035.  Function 57H (87)
  22036.  Get or set file date and time
  22037.  ────────────────────────────────────────────────────────────────────────────
  22038.  
  22039.    Obtains or modifies the date and time stamp in a file's directory entry.
  22040.  
  22041.  Call with:
  22042.  
  22043.    If getting date and time
  22044.  
  22045.    AH            = 57H
  22046.    AL            = 00H
  22047.    BX            = handle
  22048.  
  22049.    If setting date and time
  22050.  
  22051.    AH            = 57H
  22052.    AL            = 01H
  22053.    BX            = handle
  22054.    CX            = time
  22055.                    bits 00H─04H = 2-second increments (0─29)
  22056.                    bits 05H─0AH = minutes (0─59)
  22057.                    bits 0BH─0FH = hours (0─23)
  22058.    DX            = date
  22059.                    bits 00H─04H = day (1─31)
  22060.                    bits 05H─08H = month (1─12)
  22061.                    bits 09H─0FH = year (relative to 1980)
  22062.  
  22063.  Returns:
  22064.  
  22065.    If function successful
  22066.  
  22067.    Carry flag    = clear
  22068.  
  22069.    and, if called with AL = 00H
  22070.  
  22071.    CX            = time
  22072.    DX            = date
  22073.  
  22074.    If function unsuccessful
  22075.  
  22076.    Carry flag    = set
  22077.    AX            = error code
  22078.  
  22079.  Notes:
  22080.  
  22081.    ■ The file must have been previously opened or created via a successful
  22082.      call to Int 21H Function 3CH, 3DH, 5AH, 5BH, or 6CH.
  22083.  
  22084.    ■ If the 16-bit date for a file is set to zero, that file's date and time
  22085.      are not displayed on directory listings.
  22086.  
  22087.    ■ A date and time set with this function will prevail, even if the file is
  22088.      modified afterwards before the handle is closed.
  22089.  
  22090.  Example:
  22091.  
  22092.    Get the date that the file MYFILE.DAT was created or last modified, and
  22093.    then decompose the packed date into its constituent parts in the variables
  22094.    month, day, and year.
  22095.  
  22096.    fname   db      'MYFILE.DAT',0
  22097.  
  22098.    month   dw      0
  22099.    day     dw      0
  22100.    year    dw      0
  22101.            .
  22102.            .
  22103.            .
  22104.                                    ; first open the file
  22105.            mov     ah,3dh          ; function number
  22106.            mov     al,0            ; read-only mode
  22107.            mov     dx,seg fname    ; filename address
  22108.            mov     ds,dx
  22109.            mov     dx,offset fname
  22110.            int     21h             ; transfer to MS-DOS
  22111.            jc      error           ; jump if open failed
  22112.  
  22113.                                    ; get file date/time
  22114.            mov     bx,ax           ; copy handle to BX
  22115.            mov     ah,57h          ; function number
  22116.            mov     al,0            ; 0 = get subfunction
  22117.            int     21h             ; transfer to MS-DOS
  22118.            jc      error           ; jump if function failed
  22119.  
  22120.            mov     day,dx          ; decompose date
  22121.            and     day,01fh        ; isolate day
  22122.            mov     cl,5
  22123.            shr     dx,cl
  22124.            mov     month,dx        ; isolate month
  22125.            and     month,0fh
  22126.            mov     cl,4
  22127.            shr     dx,cl           ; isolate year
  22128.            and     dx,03fh         ; relative to 1980
  22129.            add     dx,1980         ; correct to real year
  22130.            mov     year,dx         ; save year
  22131.  
  22132.                                    ; now close file,
  22133.                                    ; handle still in BX
  22134.            mov     ah,3eh          ; function number
  22135.            int     21h             ; transfer to MS-DOS
  22136.            jc      error           ; jump if close failed
  22137.            .
  22138.            .
  22139.            .
  22140.  
  22141.  
  22142.  ────────────────────────────────────────────────────────────────────────────
  22143.  Int 21H                                                                [3.0]
  22144.  Function 58H (88)
  22145.  Get or set allocation strategy
  22146.  ────────────────────────────────────────────────────────────────────────────
  22147.  
  22148.    Obtains or changes the code indicating the current MS-DOS strategy for
  22149.    allocating memory blocks.
  22150.  
  22151.  Call with:
  22152.  
  22153.    If getting strategy code
  22154.  
  22155.    AH            = 58H
  22156.    AL            = 00H
  22157.  
  22158.    If setting strategy code
  22159.  
  22160.    AH            = 58H
  22161.    AL            = 01H
  22162.    BX            = desired strategy code
  22163.  
  22164.                    00H = first fit
  22165.                    01H = best fit
  22166.                    02H = last fit
  22167.  
  22168.  Returns:
  22169.  
  22170.    If function successful
  22171.  
  22172.    Carry flag    = clear
  22173.  
  22174.    and, if called with AL = 00H
  22175.  
  22176.    AX            = current strategy code
  22177.  
  22178.    If function unsuccessful
  22179.  
  22180.    Carry flag    = set
  22181.    AX            = error code
  22182.  
  22183.  Notes:
  22184.  
  22185.    ■ The memory allocation strategies are:
  22186.  
  22187.      ∙ First fit: MS-DOS searches the available memory blocks from low
  22188.        addresses to high addresses, assigning the first one large enough to
  22189.        satisfy the block allocation request.
  22190.  
  22191.      ∙ Best fit: MS-DOS searches all available memory blocks and assigns the
  22192.        smallest available block that will satisfy the request, regardless of
  22193.        its position.
  22194.  
  22195.      ∙ Last fit: MS-DOS searches the available memory blocks from high
  22196.        addresses to low addresses, assigning the highest one large enough to
  22197.        satisfy the block allocation request.
  22198.  
  22199.    ■ The default MS-DOS memory allocation strategy is First Fit (code 0).
  22200.  
  22201.  Example:
  22202.  
  22203.    Save the code indicating the current memory allocation strategy in the
  22204.    variable strat, then change the system's memory allocation strategy to
  22205.    "best fit."
  22206.  
  22207.    strat   dw      0               ; previous strategy code
  22208.            .
  22209.            .
  22210.            .
  22211.                                    ; get current strategy
  22212.            mov     ah,58h          ; function number
  22213.            mov     al,0            ; 0 = get strategy
  22214.            int     21h             ; transfer to MS-DOS
  22215.            jc      error           ; jump if function failed
  22216.            mov     strat,ax        ; save strategy code
  22217.  
  22218.                                    ; now set new strategy
  22219.            mov     ah,58h          ; function number
  22220.            mov     al,1            ; 1 = set strategy
  22221.            mov     bx,1            ; 1 = best fit
  22222.            int     21h             ; transfer to MS-DOS
  22223.            jc      error           ; jump if function failed
  22224.            .
  22225.            .
  22226.            .
  22227.  
  22228.  
  22229.  ────────────────────────────────────────────────────────────────────────────
  22230.  Int 21H                                                                [3.0]
  22231.  Function 59H (89)
  22232.  Get extended error information
  22233.  ────────────────────────────────────────────────────────────────────────────
  22234.  
  22235.    Obtains detailed error information after a previous unsuccessful Int 21H
  22236.    function call, including the recommended remedial action.
  22237.  
  22238.  Call with:
  22239.  
  22240.    AH            = 59H
  22241.    BX            = 00H
  22242.  
  22243.  Returns:
  22244.  
  22245.    AX            = extended error code
  22246.  
  22247.                    01H       function number invalid
  22248.                    02H       file not found
  22249.                    03H       path not found
  22250.                    04H       too many open files
  22251.                    05H       access denied
  22252.                    06H       handle invalid
  22253.                    07H       memory control blocks destroyed
  22254.                    08H       insufficient memory
  22255.                    09H       memory block address invalid
  22256.                    0AH (10)  environment invalid
  22257.                    0BH (11)  format invalid
  22258.                    0CH (12)  access code invalid
  22259.                    0DH (13)  data invalid
  22260.                    0EH (14)  unknown unit
  22261.                    0FH (15)  disk drive invalid
  22262.                    10H (16)  attempted to remove current directory
  22263.                    11H (17)  not same device
  22264.                    12H (18)  no more files
  22265.                    13H (19)  disk write-protected
  22266.                    14H (20)  unknown unit
  22267.                    15H (21)  drive not ready
  22268.                    16H (22)  unknown command
  22269.                    17H (23)  data error (CRC)
  22270.                    18H (24)  bad request structure length
  22271.                    19H (25)  seek error
  22272.                    1AH (26)  unknown media type
  22273.                    1BH (27)  sector not found
  22274.                    1CH (28)  printer out of paper
  22275.                    1DH (29)  write fault
  22276.                    1EH (30)  read fault
  22277.                    1FH (31)  general failure
  22278.                    20H (32)  sharing violation
  22279.                    21H (33)  lock violation
  22280.                    22H (34)  disk change invalid
  22281.                    23H (35)  FCB unavailable
  22282.                    24H (36)  sharing buffer exceeded
  22283.                    25H─31H   reserved
  22284.                    32H (50)  unsupported network request
  22285.                    33H (51)  remote machine not listening
  22286.                    34H (52)  duplicate name on network
  22287.                    35H (53)  network name not found
  22288.                    36H (54)  network busy
  22289.                    37H (55)  device no longer exists on network
  22290.                    38H (56)  netBIOS command limit exceeded
  22291.                    39H (57)  error in network adapter hardware
  22292.                    3AH (58)  incorrect response from network
  22293.                    3BH (59)  unexpected network error
  22294.                    3CH (60)  remote adapter incompatible
  22295.                    3DH (61)  print queue full
  22296.                    3EH (62)  not enough space for print file
  22297.                    3FH (63)  print file canceled
  22298.                    40H (64)  network name deleted
  22299.                    41H (65)  network access denied
  22300.                    42H (66)  incorrect network device type
  22301.                    43H (67)  network name not found
  22302.                    44H (68)  network name limit exceeded
  22303.                    45H (69)  netBIOS session limit exceeded
  22304.                    46H (70)  file sharing temporarily paused
  22305.                    47H (71)  network request not accepted
  22306.                    48H (72)  print or disk redirection paused
  22307.                    49H─4FH   reserved
  22308.                    50H (80)  file already exists
  22309.                    51H (81)  reserved
  22310.                    52H (82)  cannot make directory
  22311.                    53H (83)  fail on Int 24H (critical error)
  22312.                    54H (84)  too many redirections
  22313.                    55H (85)  duplicate redirection
  22314.                    56H (86)  invalid password
  22315.                    57H (87)  invalid parameter
  22316.                    58H (88)  network device fault
  22317.                    59H (89)  function not supported by network
  22318.                    5AH (90)  required system component not installed
  22319.  
  22320.    BH            = error class
  22321.  
  22322.                    01H       if out of resource (such as storage or handles)
  22323.                    02H       if not error, but temporary situation (such as
  22324.                              locked region in file) that can be expected to
  22325.                              end
  22326.                    03H       if authorization problem
  22327.                    04H       if internal error in system software
  22328.                    05H       if hardware failure
  22329.                    06H       if system software failure not the fault of the
  22330.                              active process (such as missing configuration
  22331.                              files)
  22332.                    07H       if application program error
  22333.                    08H       if file or item not found
  22334.                    09H       if file or item of invalid type or format
  22335.                    0AH (10)  if file or item locked
  22336.                    0BH (11)  if wrong disk in drive, bad spot on disk, or
  22337.                              storage medium problem
  22338.                    0CH (12)  if item already exists
  22339.                    0DH (13)  unknown error
  22340.  
  22341.    BL            = recommended action
  22342.  
  22343.                    01H       retry reasonable number of times, then prompt
  22344.                              user to select abort or ignore
  22345.                    02H       retry reasonable number of times with delay
  22346.                              between retries, then prompt user to select
  22347.                              abort or ignore
  22348.                    03H       get corrected information from user (typically
  22349.                              caused by incorrect filename or drive
  22350.                              specification)
  22351.                    04H       abort application with cleanup (i.e., terminate
  22352.                              the program in as orderly a manner as possible:
  22353.                              releasing locks, closing files, etc.)
  22354.                    05H       perform immediate exit without cleanup
  22355.                    06H       ignore error
  22356.                    07H       retry after user intervention to remove cause of
  22357.                              error
  22358.  
  22359.    CH            = error locus
  22360.  
  22361.                    01H       unknown
  22362.                    02H       block device (disk or disk emulator)
  22363.                    03H       network
  22364.                    04H       serial device
  22365.                    05H       memory
  22366.  
  22367.    and, for MS-DOS 3.0 and later,
  22368.  
  22369.    ES:DI         = ASCIIZ volume label of disk to insert, if AX = 0022H
  22370.                    (invalid disk change)
  22371.  
  22372.  Notes:
  22373.  
  22374.    ■ This function may be called after any other Int 21H function call that
  22375.      returned an error status, in order to obtain more detailed information
  22376.      about the error type and the recommended action. If the previous Int 21H
  22377.      function call had no error, 0000H is returned in register AX. This
  22378.      function may also be called during the execution of a critical-error
  22379.      (Int 24H) handler.
  22380.  
  22381.    ■ The contents of registers CL, DX, SI, DI, BP, DS, and ES are destroyed
  22382.      by this function.
  22383.  
  22384.    ■ Note that extended error codes 13H─1FH (19─31) and 34 (22H) correspond
  22385.      exactly to the error codes 0─0CH (0─12) and 0FH (15) returned by Int
  22386.      24H.
  22387.  
  22388.    ■ You should not code your programs to recognize only specific error
  22389.      numbers if you wish to ensure upward compatibility, because new error
  22390.      codes are added in each version of MS-DOS.
  22391.  
  22392.  Example:
  22393.  
  22394.    Attempt to open the file named NOSUCH.DAT using a file control block; if
  22395.    the open request fails, get the extended error code.
  22396.  
  22397.    myfcb   db      0               ; drive = default
  22398.            db      'NOSUCH  '      ; filename, 8 chars
  22399.            db      'DAT'           ; extension, 3 chars
  22400.            db      25 dup (0)      ; remainder of FCB
  22401.            .
  22402.            .
  22403.            .
  22404.    label1:                         ; open the file
  22405.            mov     ah,0fh          ; function number
  22406.            mov     dx,seg myfcb    ; address of FCB
  22407.            mov     ds,dx
  22408.            mov     dx,offset myfcb
  22409.            int     21h             ; transfer to MS-DOS
  22410.            or      al,al           ; check open status
  22411.            jz      success         ; jump if opened OK
  22412.  
  22413.                                    ; open failed, get
  22414.                                    ; extended error info
  22415.            mov     ah,59h          ; function number
  22416.            xor     bx,bx           ; BX must = 0
  22417.            int     21h             ; transfer to MS-DOS
  22418.            or      ax,ax           ; double check for error
  22419.            jz      success         ; jump if no error
  22420.  
  22421.            cmp     bl,2            ; should we retry?
  22422.            jle     label1          ; yes, jump
  22423.            jmp     error           ; no, give up
  22424.            .
  22425.            .
  22426.            .
  22427.  
  22428.  
  22429.  ────────────────────────────────────────────────────────────────────────────
  22430.  Int 21H                                                                [3.0]
  22431.  Function 5AH (90)
  22432.  Create temporary file
  22433.  ────────────────────────────────────────────────────────────────────────────
  22434.  
  22435.    Creates a file with a unique name, in the current or specified directory
  22436.    on the default or specified disk drive, and returns a handle that can be
  22437.    used by the program for subsequent access to the file. The name generated
  22438.    for the file is also returned in a buffer specified by the program.
  22439.  
  22440.  Call with:
  22441.  
  22442.    AH            = 5AH
  22443.    CX            = attribute (bits may be combined)
  22444.  
  22445.                    Bit(s)    Significance (if set)
  22446.                    0         read-only
  22447.                    1         hidden
  22448.                    2         system
  22449.                    3─4       reserved (0)
  22450.                    5         archive
  22451.                    6─15      reserved (0)
  22452.  
  22453.    DS:DX         = segment:offset of ASCIIZ path
  22454.  
  22455.  Returns:
  22456.  
  22457.    If function successful
  22458.  
  22459.    Carry flag    = clear
  22460.    AX            = handle
  22461.    DS:DX         = segment:offset of complete ASCIIZ pathname
  22462.  
  22463.    If function unsuccessful
  22464.  
  22465.    Carry flag    = set
  22466.    AX            = error code
  22467.  
  22468.  Notes:
  22469.  
  22470.    ■ The ASCIIZ path supplied to this function should be followed by at least
  22471.      13 additional bytes of buffer space. MS-DOS adds a backslash (\) to the
  22472.      supplied path, if necessary, then appends a null-terminated filename
  22473.      that is a function of the current time.
  22474.  
  22475.    ■ Files created with this function are not automatically deleted when the
  22476.      calling program terminates.
  22477.  
  22478.    ■ The function fails if
  22479.  
  22480.      ∙ any element of the pathname does not exist.
  22481.  
  22482.      ∙ the file is being created in the root directory, and the root
  22483.        directory is full.
  22484.  
  22485.    ■ See also Int 21H Functions 3CH, 5BH, and 6CH, which provide
  22486.      additional facilities for creating files.
  22487.  
  22488.    ■ [3.0+] If the program is running on a network, the file is created and
  22489.      opened for read/write access in compatibility sharing mode.
  22490.  
  22491.  Example:
  22492.  
  22493.    Create a temporary file with a unique name and normal attribute in
  22494.    directory \TEMP of drive C. Note that you must allow room for MS-DOS to
  22495.    append the generated filename to the supplied path. The complete file
  22496.    specification should be used to delete the temporary file before your
  22497.    program terminates.
  22498.  
  22499.    fname   db      'C:\TEMP\'      ; pathname for temp file
  22500.            db      13 dup (0)      ; receives filename
  22501.  
  22502.    fhandle dw      ?               ; file handle
  22503.            .
  22504.            .
  22505.            .
  22506.            mov     ah,5ah          ; function number
  22507.            mov     cx,0            ; normal attribute
  22508.            mov     dx,seg fname    ; address of pathname
  22509.            mov     ds,dx
  22510.            mov     dx,offset fname
  22511.            int     21h             ; transfer to MS-DOS
  22512.            jc      error           ; jump if create failed
  22513.            mov     fhandle,ax      ; save file handle
  22514.            .
  22515.            .
  22516.            .
  22517.  
  22518.  
  22519.  ────────────────────────────────────────────────────────────────────────────
  22520.  Int 21H                                                                [3.0]
  22521.  Function 5BH (91)
  22522.  Create new file
  22523.  ────────────────────────────────────────────────────────────────────────────
  22524.  
  22525.    Given an ASCIIZ pathname, creates a file in the designated or default
  22526.    directory on the designated or default drive, and returns a handle that
  22527.    can be used by the program for subsequent access to the file. If a file
  22528.    with the same name already exists, the function fails.
  22529.  
  22530.  Call with:
  22531.  
  22532.    AH            = 5BH
  22533.    CX            = attribute (bits may be combined)
  22534.  
  22535.                    Bit(s)    Significance (if set)
  22536.                    0         read-only
  22537.                    1         hidden
  22538.                    2         system
  22539.                    3         volume label
  22540.                    4         reserved (0)
  22541.                    5         archive
  22542.                    6─15      reserved (0)
  22543.  
  22544.    DS:DX         = segment:offset of ASCIIZ pathname
  22545.  
  22546.  Returns:
  22547.  
  22548.    If function successful
  22549.  
  22550.    Carry flag    = clear
  22551.    AX            = handle
  22552.  
  22553.    If function unsuccessful
  22554.  
  22555.    Carry flag    = set
  22556.    AX            = error code
  22557.  
  22558.  Notes:
  22559.  
  22560.    ■ The function fails if:
  22561.  
  22562.      ∙ any element of the specified path does not exist.
  22563.  
  22564.      ∙ a file with the identical pathname (i.e., the same filename and
  22565.        extension in the same location in the directory structure) already
  22566.        exists.
  22567.  
  22568.      ∙ the file is being created in the root directory, and the root
  22569.        directory is full.
  22570.  
  22571.      ∙ [3.0+] the program is running on a network, and the user has
  22572.        insufficient access rights to the directory that will contain the
  22573.        file.
  22574.  
  22575.    ■ The file is usually given a normal attribute (0) when it is created, and
  22576.      is opened for both read and write operations. The attribute can
  22577.      subsequently be modified with Int 21H Function 43H.
  22578.  
  22579.    ■ See also Int 21H Functions 3CH, 5AH, and 6CH, which provide
  22580.      alternative ways of creating files.
  22581.  
  22582.    ■ This function may be used to implement semaphores in a network or
  22583.      multitasking environment. If the function succeeds, the program has
  22584.      acquired the semaphore. To release the semaphore, the program simply
  22585.      deletes the file.
  22586.  
  22587.  Example:
  22588.  
  22589.    Create and open a file named MYFILE.DAT in the directory \MYDIR on drive
  22590.    C; MS-DOS returns an error if a file with the same name already exists in
  22591.    that location.
  22592.  
  22593.    fname   db      'C:\MYDIR\MYFILE.DAT',0
  22594.  
  22595.    fhandle dw      ?               ; file handle
  22596.            .
  22597.            .
  22598.            .
  22599.            mov     ah,5bh          ; function number
  22600.            xor     cx,cx           ; normal attribute
  22601.            mov     dx,seg fname    ; filename address
  22602.            mov     ds,dx
  22603.            mov     dx,offset fname
  22604.            int     21h             ; transfer to MS-DOS
  22605.            jc      error           ; jump if create failed
  22606.            mov     fhandle,ax      ; save file handle
  22607.            .
  22608.            .
  22609.            .
  22610.  
  22611.  
  22612.  ────────────────────────────────────────────────────────────────────────────
  22613.  Int 21H                                                                [3.0]
  22614.  Function 5CH (92)
  22615.  Lock or unlock file region
  22616.  ────────────────────────────────────────────────────────────────────────────
  22617.  
  22618.    Locks or unlocks the specified region of a file. This function is not
  22619.    available unless the file-sharing module (SHARE.EXE) is loaded.
  22620.  
  22621.  Call with:
  22622.  
  22623.    AH            = 5CH
  22624.    AL            = 00H       if locking region
  22625.                    01H       if unlocking region
  22626.  
  22627.    BX            = handle
  22628.    CX            = high part of region offset
  22629.    DX            = low part of region offset
  22630.    SI            = high part of region length
  22631.    DI            = low part of region length
  22632.  
  22633.  Returns:
  22634.  
  22635.    If function successful
  22636.  
  22637.    Carry flag    = clear
  22638.  
  22639.    If function unsuccessful
  22640.  
  22641.    Carry flag    = set
  22642.    AX            = error code
  22643.  
  22644.  Notes:
  22645.  
  22646.    ■ This function is useful for file and record synchronization in a
  22647.      multitasking environment or network. Access to the file as a whole is
  22648.      controlled by the attribute and file-sharing parameters passed in open
  22649.      or create calls and by the file's attributes, which are stored in its
  22650.      directory entry.
  22651.  
  22652.    ■ The beginning location in the file to be locked or unlocked is supplied
  22653.      as a positive double precision integer, which is a byte offset into the
  22654.      file. The length of the region to be locked or unlocked is similarly
  22655.      supplied as a positive double precision integer.
  22656.  
  22657.    ■ For every call to lock a region of a file, there must be a subsequent
  22658.      unlock call with exactly the same file offset and length.
  22659.  
  22660.    ■ Locking beyond the current end of file is not an error.
  22661.  
  22662.    ■ Duplicate handles created with Int 21H Function 45H, or handles
  22663.      redirected to the file with Int 21H Function 46H, are allowed access to
  22664.      locked regions within the same process.
  22665.  
  22666.    ■ Programs that are loaded with the EXEC call (Int 21H Function 4BH)
  22667.      inherit the handles of their parent but not any active locks.
  22668.  
  22669.    ■ If a process terminates without releasing active locks on a file, the
  22670.      result is undefined. Therefore, programs using this function should
  22671.      install their own Int 23H and Int 24H handlers so that they cannot be
  22672.      terminated unexpectedly.
  22673.  
  22674.  Example:
  22675.  
  22676.    Assume that a file was previously opened and that its handle was saved in
  22677.    the variable fhandle. Lock a 4096 byte region of the file, starting at
  22678.    32,768 bytes from the beginning of the file, so that it cannot be accessed
  22679.    by other programs.
  22680.  
  22681.    fhandle dw      ?               ; file handle
  22682.            .
  22683.            .
  22684.            .
  22685.            mov     ah,5ch          ; function number
  22686.            mov     al,0            ; subfunction 0 = lock
  22687.            mov     bx,fhandle      ; file handle
  22688.            mov     cx,0            ; upper part of offset
  22689.            mov     dx,32768        ; lower part of offset
  22690.            mov     si,0            ; upper part of length
  22691.            mov     di,4096         ; lower part of length
  22692.            int     21h             ; transfer to MS-DOS
  22693.            jc      error           ; jump if lock failed
  22694.            .
  22695.            .
  22696.            .
  22697.  
  22698.  
  22699.  ────────────────────────────────────────────────────────────────────────────
  22700.  Int 21H
  22701.  Function 5DH (93)
  22702.  Reserved
  22703.  ────────────────────────────────────────────────────────────────────────────
  22704.  
  22705.  
  22706.  ────────────────────────────────────────────────────────────────────────────
  22707.  Int 21H                                                                [3.1]
  22708.  Function 5EH (94) Subfunction 00H
  22709.  Get machine name
  22710.  ────────────────────────────────────────────────────────────────────────────
  22711.  
  22712.    Returns the address of an ASCIIZ (null-terminated) string identifying the
  22713.    local computer. This function call is only available when Microsoft
  22714.    Networks is running.
  22715.  
  22716.  Call with:
  22717.  
  22718.    AH            = 5EH
  22719.    AL            = 00H
  22720.    DS:DX         = segment:offset of buffer to receive string
  22721.  
  22722.  Returns:
  22723.  
  22724.    If function successful
  22725.  
  22726.    Carry flag    = clear
  22727.  
  22728.    CH            = 00H       if name not defined
  22729.                    <> 00H    if name defined
  22730.  
  22731.    CL            = netBIOS name number (if CH <> 0)
  22732.    DX:DX         = segment:offset of identifier (if CH <> 0 )
  22733.  
  22734.    If function unsuccessful
  22735.  
  22736.    Carry flag    = set
  22737.    AX            = error code
  22738.  
  22739.  Notes:
  22740.  
  22741.    ■ The computer identifier is a 15-byte string, padded with spaces and
  22742.      terminated with a null (00H) byte.
  22743.  
  22744.    ■ The effect of this call is unpredictable if the file-sharing support
  22745.      module is not loaded.
  22746.  
  22747.  Example:
  22748.  
  22749.    Get the machine name of the local computer into the buffer named mname.
  22750.  
  22751.    mname   db      16 dup (?)
  22752.            .
  22753.            .
  22754.            .
  22755.            mov     ax,5e00h        ; function & subfunction
  22756.            mov     dx,seg mname    ; address of buffer
  22757.            mov     ds,dx
  22758.            mov     dx,offset mname
  22759.            int     21h             ; transfer to MS-DOS
  22760.            jc      error           ; jump if function failed
  22761.  
  22762.            or      ch,ch           ; make sure name exists
  22763.            jz      error           ; jump if no name defined
  22764.            .
  22765.            .
  22766.            .
  22767.  
  22768.  
  22769.  ────────────────────────────────────────────────────────────────────────────
  22770.  Int 21H                                                                [3.1]
  22771.  Function 5EH (94) Subfunction 02H
  22772.  Set printer setup string
  22773.  ────────────────────────────────────────────────────────────────────────────
  22774.  
  22775.    Specifies a string to be sent in front of all files directed to a
  22776.    particular network printer, allowing users at different network nodes to
  22777.    specify individualized operating modes on the same printer. This function
  22778.    call is only available when Microsoft Networks is running.
  22779.  
  22780.  Call with:
  22781.  
  22782.    AH            = 5EH
  22783.    AL            = 02H
  22784.    BX            = redirection list index
  22785.    CX            = length of setup string
  22786.    DS:SI         = segment:offset of setup string
  22787.  
  22788.  Returns:
  22789.  
  22790.    If function successful
  22791.  
  22792.    Carry flag    = clear
  22793.  
  22794.    If function unsuccessful
  22795.  
  22796.    Carry flag    = set
  22797.  
  22798.    AX            = error code
  22799.  
  22800.  Notes:
  22801.  
  22802.    ■ The redirection list index passed in register BX is obtained with
  22803.      Function 5FH Subfunction 02H (Get Redirection List Entry).
  22804.  
  22805.    ■ See also Function 5EH Subfunction 03H, which may be used to obtain the
  22806.      existing setup string for a particular network printer.
  22807.  
  22808.  Example:
  22809.  
  22810.    Initialize the setup string for the printer designated by redirection list
  22811.    index 2 so that the device is put into boldface mode before printing a
  22812.    file requested by this network node.
  22813.  
  22814.    setup   db      01bh,045h       ; selects boldface mode
  22815.            .
  22816.            .
  22817.            .
  22818.            mov     ax,5e02h        ; function & subfunction
  22819.            mov     bx,2            ; redirection list index 2
  22820.            mov     cx,2            ; length of setup string
  22821.            mov     si,seg setup    ; address of setup string
  22822.            mov     ds,si
  22823.            mov     si,offset setup
  22824.            int     21h             ; transfer to MS-DOS
  22825.            jc      error           ; jump if function failed
  22826.            .
  22827.            .
  22828.            .
  22829.  
  22830.  
  22831.  ────────────────────────────────────────────────────────────────────────────
  22832.  Int 21H                                                                [3.1]
  22833.  Function 5EH (94) Subfunction 03H
  22834.  Get printer setup string
  22835.  ────────────────────────────────────────────────────────────────────────────
  22836.  
  22837.    Obtains the printer setup string for a particular network printer. This
  22838.    function call is only available when Microsoft Networks is running.
  22839.  
  22840.  Call with:
  22841.  
  22842.    AH            = 5EH
  22843.    AL            = 03H
  22844.    BX            = redirection list index
  22845.    ES:DI         = segment:offset of buffer to receive setup string
  22846.  
  22847.  Returns:
  22848.  
  22849.    If function successful
  22850.  
  22851.    Carry flag    = clear
  22852.    CX            = length of printer setup string
  22853.    ES:DI         = address of buffer holding setup string
  22854.  
  22855.    If function unsuccessful
  22856.  
  22857.    Carry flag    = set
  22858.    AX            = error code
  22859.  
  22860.  Notes:
  22861.  
  22862.    ■ The redirection list index passed in register BX is obtained with
  22863.      Function 5FH Subfunction 02H (Get Redirection List Entry).
  22864.  
  22865.    ■ See also Int 21H Function 5EH Subfunction 02H, which is used to specify
  22866.      a setup string for a network printer.
  22867.  
  22868.  Example:
  22869.  
  22870.    Get the setup string for this network node associated with the printer
  22871.    designated by redirection list index 2.
  22872.  
  22873.    setup   db      64 dup (?)      ; receives setup string
  22874.            .
  22875.            .
  22876.            .
  22877.            mov     ax,5e03h        ; function & subfunction
  22878.            mov     bx,2            ; redirection list index 2
  22879.            mov     di,seg setup    ; address of buffer
  22880.            mov     es,di
  22881.            mov     di,offset setup
  22882.            int     21h             ; transfer to MS-DOS
  22883.            jc      error           ; jump if function failed
  22884.            .
  22885.            .
  22886.            .
  22887.  
  22888.  
  22889.  ────────────────────────────────────────────────────────────────────────────
  22890.  Int 21H                                                                [3.1]
  22891.  Function 5FH (95) Subfunction 02H
  22892.  Get redirection list entry
  22893.  ────────────────────────────────────────────────────────────────────────────
  22894.  
  22895.    Allows inspection of the system redirection list, which associates local
  22896.    logical names with network files, directories, or printers. This function
  22897.    call is only available when Microsoft Networks is running and the
  22898.    file-sharing module (SHARE.EXE) has been loaded.
  22899.  
  22900.  Call with:
  22901.  
  22902.    AH            = 5FH
  22903.    AL            = 02H
  22904.    BX            = redirection list index
  22905.    DS:SI         = segment:offset of 16-byte buffer to receive local device
  22906.                    name
  22907.    ES:DI         = segment:offset of 128-byte buffer to receive network name
  22908.  
  22909.  Returns:
  22910.  
  22911.    If function successful
  22912.  
  22913.    Carry flag    = clear
  22914.    BH            = device status flag
  22915.  
  22916.                    bit 0     = 0 if device valid
  22917.                              = 1 if not valid
  22918.  
  22919.    BL            = device type
  22920.  
  22921.                    03H       if printer
  22922.                    04H       if drive
  22923.  
  22924.    CX            = stored parameter value
  22925.    DX            = destroyed
  22926.    BP            = destroyed
  22927.    DS:SI         = segment:offset of ASCIIZ local device name
  22928.    ES:DI         = segment:offset of ASCIIZ network name
  22929.  
  22930.    If function unsuccessful
  22931.  
  22932.    Carry flag    = set
  22933.    AX            = error code
  22934.  
  22935.  Note:
  22936.  
  22937.    ■ The parameter returned in CX is a value that was previously passed to
  22938.      MS-DOS in register CX with Int 21H Function 5FH Subfunction 03H
  22939.      (Redirect Device). It represents data that is private to the
  22940.      applications which store and retrieve it and has no meaning to MS-DOS.
  22941.  
  22942.  Example:
  22943.  
  22944.    Get the local and network names for the device specified by the first
  22945.    redirection list entry.
  22946.  
  22947.    local   db      16 dup (?)      ; receives local device name
  22948.  
  22949.    network db      128 dup (?)     ; receives network name
  22950.            .
  22951.            .
  22952.            .
  22953.            mov     ax,5f02h        ; function & subfunction
  22954.            mov     bx,0            ; redirection list entry 0
  22955.            mov     si,seg local    ; local name buffer addr
  22956.            mov     ds,si
  22957.            mov     si,offset local
  22958.            mov     di,seg network  ; network name buffer addr
  22959.            mov     es,di
  22960.            mov     di,offset network
  22961.            int     21h             ; transfer to MS-DOS
  22962.            jc      error           ; jump if call failed
  22963.  
  22964.            or      bh,bh           ; check device status
  22965.            jnz     error           ; jump if device not valid
  22966.            .
  22967.            .
  22968.            .
  22969.  
  22970.  
  22971.  ────────────────────────────────────────────────────────────────────────────
  22972.  Int 21H                                                                [3.1]
  22973.  Function 5FH (95) Subfunction 03H
  22974.  Redirect device
  22975.  ────────────────────────────────────────────────────────────────────────────
  22976.  
  22977.    Establishes redirection across the network by associating a local device
  22978.    name with a network name. This function call is only available when
  22979.    Microsoft Networks is running and the file-sharing module (SHARE.EXE) has
  22980.    been loaded.
  22981.  
  22982.  Call with:
  22983.  
  22984.    AH            = 5FH
  22985.    AL            = 03H
  22986.    BL            = device type
  22987.  
  22988.                    03H       if printer
  22989.                    04H       if drive
  22990.  
  22991.    CX            = parameter to save for caller
  22992.    DS:SI         = segment:offset of ASCIIZ local device name
  22993.    ES:DI         = segment:offset of ASCIIZ network name, followed by ASCIIZ
  22994.                    password
  22995.  
  22996.  Returns:
  22997.  
  22998.    If function successful
  22999.  
  23000.    Carry flag    = clear
  23001.  
  23002.    If function unsuccessful
  23003.  
  23004.    Carry flag    = set
  23005.    AX            = error code
  23006.  
  23007.  Notes:
  23008.  
  23009.    ■ The local name can be a drive designator (a letter followed by a colon,
  23010.      such as "D:"), a printer name, or a null string. Printer names must be
  23011.      one of the following: PRN, LPT1, LPT2, or LPT3. If a null string
  23012.      followed by a password is used, MS-DOS attempts to grant access to the
  23013.      network directory with the specified password.
  23014.  
  23015.    ■ The parameter passed in CX can be retrieved by later calls to Int 21H
  23016.      Function 5FH Subfunction 02H. It represents data that is private to the
  23017.      applications which store and retrieve it and has no meaning to MS-DOS.
  23018.  
  23019.  Example:
  23020.  
  23021.    Redirect the local drive E to the directory \FORTH on the server named
  23022.    LMI, using the password FRED.
  23023.  
  23024.    locname db      'E:',0          ; local drive
  23025.  
  23026.    netname db      '\\LMI\FORTH',0
  23027.            db      'FRED',0
  23028.            .
  23029.            .
  23030.            .
  23031.            mov     ax,5f03h        ; function & subfunction
  23032.            mov     bl,4            ; code 4 = disk drive
  23033.            mov     si,seg locname  ; address of local name
  23034.            mov     ds,si
  23035.            mov     si,offset locname
  23036.            mov     di,seg netname  ; address of network name
  23037.            mov     es,di
  23038.            mov     di,offset netname
  23039.            int     21h             ; transfer to MS-DOS
  23040.            jc      error           ; jump if redirect failed
  23041.            .
  23042.            .
  23043.            .
  23044.  
  23045.  
  23046.  ────────────────────────────────────────────────────────────────────────────
  23047.  Int 21H                                                                [3.1]
  23048.  Function 5FH (95) Subfunction 04H
  23049.  Cancel device redirection
  23050.  ────────────────────────────────────────────────────────────────────────────
  23051.  
  23052.    Cancels a previous redirection request by removing the association of a
  23053.    local device name with a network name. This function call is only
  23054.    available when Microsoft Networks is running and the file-sharing module
  23055.    (SHARE.EXE) has been loaded.
  23056.  
  23057.  Call with:
  23058.  
  23059.    AH            = 5FH
  23060.    AL            = 04H
  23061.    DS:SI         = segment:offset of ASCIIZ local device name
  23062.  
  23063.  Returns:
  23064.  
  23065.    If function successful
  23066.  
  23067.    Carry flag    = clear
  23068.  
  23069.    If function unsuccessful
  23070.  
  23071.    Carry flag    = set
  23072.    AX            = error code
  23073.  
  23074.  Note:
  23075.  
  23076.    ■ The supplied name can be a drive designator (a letter followed by a
  23077.      colon, such as "D:"), a printer name, or a string starting with two
  23078.      backslashes (\\). Printer names must be one of the following: PRN, LPT1,
  23079.      LPT2, or LPT3. If the string with two backslashes is used, the
  23080.      connection between the local machine and the network directory is
  23081.      terminated.
  23082.  
  23083.  Example:
  23084.  
  23085.    Cancel the redirection of local drive E to the network server.
  23086.  
  23087.    locname db      'E:',0
  23088.            .
  23089.            .
  23090.            .         mov     ax,5f04h        ; function & subfunction
  23091.            mov     si,seg locname  ; address of local name
  23092.            mov     ds,si
  23093.            mov     si,offset locname
  23094.            int     21h             ; transfer to MS-DOS
  23095.            jc      error           ; jump if cancel failed
  23096.            .
  23097.            .
  23098.            .
  23099.  
  23100.  
  23101.  ────────────────────────────────────────────────────────────────────────────
  23102.  Int 21H
  23103.  Function 60H (96)
  23104.  Reserved
  23105.  ────────────────────────────────────────────────────────────────────────────
  23106.  
  23107.  
  23108.  ────────────────────────────────────────────────────────────────────────────
  23109.  Int 21H
  23110.  Function 61H (97)
  23111.  Reserved
  23112.  ────────────────────────────────────────────────────────────────────────────
  23113.  
  23114.  
  23115.  ────────────────────────────────────────────────────────────────────────────
  23116.  Int 21H                                                                [3.0]
  23117.  Function 62H (98)
  23118.  Get PSP address
  23119.  ────────────────────────────────────────────────────────────────────────────
  23120.  
  23121.    Obtains the segment (paragraph) address of the program segment prefix
  23122.    (PSP) for the currently executing program.
  23123.  
  23124.  Call with:
  23125.  
  23126.    AH            = 62H
  23127.  
  23128.  Returns:
  23129.  
  23130.    BX            = segment address of program segment prefix
  23131.  
  23132.  Notes:
  23133.  
  23134.    ■ Before a program receives control from MS-DOS, its program segment
  23135.      prefix is set up to contain certain vital information, such as:
  23136.  
  23137.      ∙ the segment address of the program's environment block
  23138.  
  23139.      ∙ the command line originally entered by the user
  23140.  
  23141.      ∙ the original contents of the terminate, Ctrl-C, and critical-error
  23142.        handler vectors
  23143.  
  23144.      ∙ the top address of available RAM
  23145.  
  23146.    ■ The segment address of the PSP is normally passed to the program in
  23147.      registers DS and ES when it initially receives control from MS-DOS. This
  23148.      function allows a program to conveniently recover the PSP address at any
  23149.      point during its execution, without having to save it at program entry.
  23150.  
  23151.  Example:
  23152.  
  23153.    Get the segment base of the program segment prefix, then copy the command
  23154.    tail from the PSP into the local buffer named buff.
  23155.  
  23156.    ctail   equ     080H            ; PSP offset, command tail
  23157.  
  23158.    buff    db      80 dup (?)      ; copy of command tail
  23159.            .
  23160.            .
  23161.            .
  23162.                                    ; get PSP address
  23163.            mov     ah,62H          ; function number
  23164.            int     21h             ; transfer to MS-DOS
  23165.  
  23166.                                    ; copy command tail
  23167.            mov     ds,bx           ; PSP segment to DS
  23168.            mov     si,offset ctail ; offset of command tail
  23169.            mov     di,seg buff     ; local buffer address
  23170.            mov     es,di
  23171.            mov     di,offset buff
  23172.            mov     cl,[si]         ; length of command tail
  23173.            inc     cl              ; include count byte
  23174.            xor     ch,ch
  23175.            cld
  23176.            rep movsb               ; copy to local buffer
  23177.            .
  23178.            .
  23179.            .
  23180.  
  23181.  
  23182.  ────────────────────────────────────────────────────────────────────────────
  23183.  Int 21H                                                          [2.25 only]
  23184.  Function 63H (99)
  23185.  Get DBCS lead byte table
  23186.  ────────────────────────────────────────────────────────────────────────────
  23187.  
  23188.    Obtains the address of the system table of legal lead byte ranges for
  23189.    double-byte character sets (DBCS), or sets or obtains the interim console
  23190.    flag. Int 21H Function 63H is available only in MS-DOS version 2.25; it
  23191.    is not supported in MS-DOS versions 3.0 and later.
  23192.  
  23193.  Call with:
  23194.  
  23195.    AH            = 63H
  23196.    AL            = subfunction
  23197.  
  23198.                    00H       if getting address of DBCS lead byte table
  23199.                    01H       if setting or clearing interim console flag
  23200.                    02H       if obtaining value of interim console flag
  23201.  
  23202.    If AL = 01H
  23203.  
  23204.    DL            = 00H       if clearing interim console flag
  23205.                    01H       if setting interim console flag
  23206.  
  23207.  Returns:
  23208.  
  23209.    If function successful
  23210.  
  23211.    Carry flag    = clear
  23212.  
  23213.    and, if called with AL = 00H
  23214.  
  23215.    DS:SI         = segment:offset of DBCS lead byte table
  23216.  
  23217.    or, if called with AL = 02H
  23218.  
  23219.    DL            = value of interim console flag
  23220.  
  23221.    If function unsuccessful
  23222.  
  23223.    Carry flag    = set
  23224.    AX            = error code
  23225.  
  23226.  Notes:
  23227.  
  23228.    ■ The DBCS lead byte table consists of a variable number of two byte
  23229.      entries, terminated by two null (00H) bytes. Each pair defines the
  23230.      beginning and ending value for a range of lead bytes. The value of a
  23231.      legal lead byte is always in the range 80─0FFH.
  23232.  
  23233.    ■ Entries in the lead byte table must be in ascending order. If no legal
  23234.      lead bytes are defined in a given system, the table consists only of the
  23235.      two null bytes.
  23236.  
  23237.    ■ If the interim console flag is set, Int 21H Functions 07H (Unfiltered
  23238.      Character Input), 08H (Character Input without Echo), and 0BH
  23239.      (Keyboard Status) will support interim characters.
  23240.  
  23241.    ■ Unlike most other MS-DOS services, this function call does not
  23242.      necessarily preserve any registers except SS:SP.
  23243.  
  23244.    ■ [4.0] The address of the DBCS lead byte table can also be obtained with
  23245.      Int 21H Function 65H.
  23246.  
  23247.  
  23248.  ────────────────────────────────────────────────────────────────────────────
  23249.  Int 21H
  23250.  Function 64H (100)
  23251.  Reserved
  23252.  ────────────────────────────────────────────────────────────────────────────
  23253.  
  23254.  
  23255.  ────────────────────────────────────────────────────────────────────────────
  23256.  Int 21H                                                                [3.3]
  23257.  Function 65H (101)
  23258.  Get extended country information
  23259.  ────────────────────────────────────────────────────────────────────────────
  23260.  
  23261.    Obtains information about the specified country and/or code page.
  23262.  
  23263.  Call with:
  23264.  
  23265.    AH            = 65H
  23266.    AL            = subfunction
  23267.                    01H = Get General Internationalization Information
  23268.                    02H = Get Pointer to Uppercase Table
  23269.                    04H = Get Pointer to Filename Uppercase Table
  23270.                    06H = Get Pointer to Collating Table
  23271.                    07H = Get Pointer to Double-Byte Character Set (DBCS)
  23272.                    Vector (MS-DOS versions 4.0 and later)
  23273.  
  23274.    BX            = code page of interest (-1 = active CON device)
  23275.    CX            = length of buffer to receive information (must be >= 5)
  23276.    DX            = country ID (-1 = default)
  23277.    ES:DI         = address of buffer to receive information
  23278.  
  23279.  Returns:
  23280.  
  23281.    If function successful
  23282.  
  23283.    Carry flag    = clear
  23284.  
  23285.    and requested data placed in calling program's buffer
  23286.  
  23287.    If function unsuccessful
  23288.  
  23289.    Carry flag    = set
  23290.    AX            = error code
  23291.  
  23292.  Notes:
  23293.  
  23294.    ■ The information returned by this function is a superset of the
  23295.      information returned by Int 21H Function 38H.
  23296.  
  23297.    ■ This function may fail if either the country code or the code page
  23298.      number is invalid, or if the code page does not match the country code.
  23299.  
  23300.    ■ The function fails if the specified buffer length is less than five
  23301.      bytes. If the buffer to receive the information is at least five bytes
  23302.      long but is too short for the requested information, the data is
  23303.      truncated and no error is returned.
  23304.  
  23305.    ■ The format of the data returned by Subfunction 01H is:
  23306.  
  23307.      Byte(s)        Contents
  23308.      00H            information ID code (1)
  23309.      01H─02H        length of following buffer
  23310.      03H─04H        country ID
  23311.      05H─06H        code page number
  23312.      07H─08H        date format
  23313.  
  23314.                     0 = USA         m d y
  23315.                     1 = Europe      d m y
  23316.                     2 = Japan       y m d
  23317.  
  23318.      09H─0DH        ASCIIZ currency symbol
  23319.      0EH─0FH        ASCIIZ thousands separator
  23320.      10H─11H        ASCIIZ decimal separator
  23321.      12H─13H        ASCIIZ date separator
  23322.      14H─15H        ASCIIZ time separator
  23323.      16H            currency format flags
  23324.  
  23325.                     bit 0           =>0 if currency symbol precedes value
  23326.                                     =>1 if currency symbol follows value
  23327.                     bit 1           =>0 if no space between value and
  23328.                                     currency symbol
  23329.                                     =>1 if one space between value and
  23330.                                     currency symbol
  23331.                     bit 2           =>0 if currency symbol and decimal are
  23332.                                     separate
  23333.                                     =>1 if currency symbol replaces decimal
  23334.                                     separator
  23335.  
  23336.      17H            number of digits after decimal in currency
  23337.      18H            time format
  23338.  
  23339.                     bit 0           = 0 if 12-hour clock
  23340.                                     = 1 if 24-hour clock
  23341.  
  23342.      19H─1CH        case-map routine call address
  23343.      1DH─1EH        ASCIIZ data list separator
  23344.      1FH─28H        reserved
  23345.  
  23346.    ■ The format of the data returned by Subfunctions 02H, 04H, 06H, and 07H
  23347.      is:
  23348.  
  23349.      Byte(s)        Contents
  23350.      00H            information ID code (2, 4, or 6)
  23351.      01H─05H        double-word pointer to table
  23352.  
  23353.    ■ The uppercase and filename uppercase tables are a maximum of 130 bytes
  23354.      long. The first two bytes contain the size of the table; the following
  23355.      bytes contain the uppercase equivalents, if any, for character codes
  23356.      80H─FFH. The main use of these tables is to map accented or otherwise
  23357.      modified vowels to their plain vowel equivalents. Text translated with
  23358.      the help of this table can be sent to devices that do not support the
  23359.      IBM graphics character set, or used to create filenames that do not
  23360.      require a special keyboard configuration for entry.
  23361.  
  23362.    ■ The collating table is a maximum of 258 bytes long. The first two bytes
  23363.      contain the table length, and the subsequent bytes contain the values to
  23364.      be used for the corresponding character codes (0─FFH) during a sort
  23365.      operation. This table maps uppercase and lowercase ASCII characters to
  23366.      the same collating codes so that sorts will be case-insensitive, and it
  23367.      maps accented vowels to their plain vowel equivalents.
  23368.  
  23369.    ■ [4.0+] Subfunction 07H returns a pointer to a variable length table of
  23370.      that defines ranges for double-byte character set (DBCS) lead bytes. The
  23371.      table is terminated by a pair of zero bytes, unless it must be truncated
  23372.      to fit in the buffer, and has the following format:
  23373.  
  23374.              dw      length
  23375.              db      start1,end1
  23376.              db      start2,end2
  23377.              .
  23378.              .
  23379.              .
  23380.              db      0,0
  23381.  
  23382.      For example:
  23383.  
  23384.              dw      4
  23385.              db      81h,9fh
  23386.              db      0e0h,0fch
  23387.              db      0,0
  23388.  
  23389.    ■ In some cases a truncated translation table may be presented to the
  23390.      program by MS-DOS. Applications should always check the length at the
  23391.      beginning of the table, to make sure it contains a translation code for
  23392.      the particular character of interest.
  23393.  
  23394.  Examples:
  23395.  
  23396.    Obtain the extended country information associated with the default
  23397.    country and code page 437.
  23398.  
  23399.    buffer  db      41 dup (0)      ; receives country info
  23400.            .
  23401.            .
  23402.            .
  23403.            mov     ax,6501h        ; function & subfunction
  23404.            mov     bx,437          ; code page
  23405.            mov     cx,41           ; buffer length
  23406.            mov     dx,-1           ; default country
  23407.            mov     di,seg buffer   ; buffer address
  23408.            mov     es,di
  23409.            mov     di,offset buffer
  23410.            int     21h             ; transfer to MS-DOS
  23411.            jc      error           ; jump if function failed
  23412.            .
  23413.            .
  23414.            .
  23415.  
  23416.    In this case, MS-DOS filled the following extended country information
  23417.    into the buffer:
  23418.  
  23419.    buffer  db      1               ; info ID code
  23420.            dw      38              ; length of following buffer
  23421.            dw      1               ; country ID (USA)
  23422.            dw      437             ; code page number
  23423.            dw      0               ; date format
  23424.            db      '$',0,0,0,0     ; currency symbol
  23425.            db      ',',0           ; thousands separator
  23426.            db      '.',0           ; decimal separator
  23427.            db      '-',0           ; date separator
  23428.            db      ':',0           ; time separator
  23429.            db      0               ; currency format flags
  23430.            db      2               ; digits in currency
  23431.            db      0               ; time format
  23432.            dd      026ah:176ch     ; case map entry point
  23433.            db      ',',0           ; data list separator
  23434.            db      10 dup (0)      ; reserved
  23435.  
  23436.    Obtain the pointer to the uppercase table associated with the default
  23437.    country and code page 437.
  23438.  
  23439.    buffer  db      5 dup (0)       ; receives pointer info
  23440.            .
  23441.            .
  23442.            .
  23443.            mov     ax,6502h        ; function number
  23444.            mov     bx,437          ; code page
  23445.            mov     cx,5            ; length of buffer
  23446.            mov     dx,-1           ; default country
  23447.            mov     di,seg buffer   ; buffer address
  23448.            mov     es,di
  23449.            mov     di,offset buffer
  23450.            int     21h             ; transfer to MS-DOS
  23451.            jc      error           ; jump if function failed
  23452.            .
  23453.            .
  23454.            .
  23455.  
  23456.    In this case, MS-DOS filled the following values into the buffer:
  23457.  
  23458.    buffer  db      2               ; info ID code
  23459.            dw      0204h           ; offset of uppercase table
  23460.            dw      1140h           ; segment of uppercase table
  23461.  
  23462.    and the table at 1140:0204H contains the following data:
  23463.  
  23464.                0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDE
  23465.    1140:0200              80 00 80 9A 45 41 8E 41 8F 80 45 45      ....EA.A..E
  23466.    1140:0210  45 49 49 49 8E 8F 90 92 92 4F 99 4F 55 55 59 99  EIII.....O.OUUY
  23467.    1140:0220  9A 9B 9C 9D 9E 9F 41 49 4F 55 A5 A5 A6 A7 A8 A9  ......AIOU.....
  23468.    1140:0230  AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 B7 B8 B9  ...............
  23469.    1140:0240  BA BB BC BD BE BF C0 C1 C2 C3 C4 C5 C6 C7 C8 C9  ...............
  23470.    1140:0250  CA CB CC CD CE CF D0 D1 D2 D3 D4 D5 D6 D7 D8 D9  ...............
  23471.    1140:0260  DA DB DC DD DE DF E0 E1 E2 E3 E4 E5 E6 E7 E8 E9  ...............
  23472.    1140:0270  EA EB EC ED EE EF F0 F1 F2 F3 F4 F5 F6 F7 F8 F9  ...............
  23473.    1140:0280  FA FB FC FD FE FF                                ......
  23474.  
  23475.  
  23476.  ────────────────────────────────────────────────────────────────────────────
  23477.  Int 21H                                                                [3.3]
  23478.  Function 66H (102)
  23479.  Get or set code page
  23480.  ────────────────────────────────────────────────────────────────────────────
  23481.  
  23482.    Obtains or selects the current code page.
  23483.  
  23484.  Call with:
  23485.  
  23486.    AH            = 66H
  23487.    AL            = subfunction
  23488.                    01H = Get Code Page
  23489.                    02H = Select Code Page
  23490.    BX            = code page to select, if AL = 02H
  23491.  
  23492.  Returns:
  23493.  
  23494.    If function successful
  23495.  
  23496.    Carry flag    = clear
  23497.  
  23498.    and, if called with AL = 01H
  23499.  
  23500.    BX            = active code page
  23501.    DX            = default code page
  23502.  
  23503.    If function unsuccessful
  23504.  
  23505.    Carry flag    = set
  23506.    AX            = error code
  23507.  
  23508.  Note:
  23509.  
  23510.    ■ When the Select Code Page subfunction is used, MS-DOS gets the new code
  23511.      page from the COUNTRY.SYS file. The device must be previously prepared
  23512.      for code page switching with the appropriate DEVICE= directive in the
  23513.      CONFIG.SYS file and NLSFUNC and MODE CP PREPARE commands (placed in the
  23514.      AUTOEXEC.BAT file, usually).
  23515.  
  23516.  Example:
  23517.  
  23518.    Force the active code page to be the same as the system's default code
  23519.    page, that is, restore the code page that was active when the system was
  23520.    first booted.
  23521.  
  23522.            .
  23523.            .
  23524.            .
  23525.                                    ; get current and
  23526.                                    ; default code page
  23527.            mov     ax,6601h        ; function number
  23528.            int     21h             ; transfer to MS-DOS
  23529.            jc      error           ; jump if function failed
  23530.  
  23531.                                    ; set code page
  23532.            mov     bx,dx           ; active = default
  23533.            mov     ax,6602h        ; function number
  23534.            int     21h             ; transfer to MS-DOS
  23535.            jc      error           ; jump if function failed
  23536.            .
  23537.            .
  23538.            .
  23539.  
  23540.  
  23541.  ────────────────────────────────────────────────────────────────────────────
  23542.  Int 21H                                                                [3.3]
  23543.  Function 67H (103)
  23544.  Set handle count
  23545.  ────────────────────────────────────────────────────────────────────────────
  23546.  
  23547.    Sets the maximum number of files and devices that may be opened
  23548.    simultaneously using handles by the current process.
  23549.  
  23550.  Call with:
  23551.  
  23552.    AH            = 67H
  23553.    BX            = number of desired handles
  23554.  
  23555.  Returns:
  23556.  
  23557.    If function successful
  23558.  
  23559.    Carry flag    = clear
  23560.  
  23561.    If function unsuccessful
  23562.  
  23563.    Carry flag    = set
  23564.    AX            = error code
  23565.  
  23566.  Notes:
  23567.  
  23568.    ■ This function call controls the size of the table that relates handle
  23569.      numbers for the current process to MS-DOS's internal, global table for
  23570.      all of the open files and devices in the system. The default table is
  23571.      located in the reserved area of the process's PSP and is large enough
  23572.      for 20 handles.
  23573.  
  23574.    ■ The function fails if the requested number of handles is greater than 20
  23575.      and there is not sufficient free memory in the system to allocate a new
  23576.      block to hold the enlarged table.
  23577.  
  23578.    ■ If the number of handles requested is larger than the available entries
  23579.      in the system's global table for file and device handles (controlled by
  23580.      the FILES entry in CONFIG.SYS), no error is returned. However, a
  23581.      subsequent attempt to open a file or device, or create a new file, will
  23582.      fail if all the entries in the system's global file table are in use,
  23583.      even if the requesting process has not used up all its own handles.
  23584.  
  23585.  Example:
  23586.  
  23587.    Set the maximum handle count for the current process to thirty, so that
  23588.    the process can have as many as 30 files or devices opened simultaneously.
  23589.    (Five of the handles are already assigned to the standard devices when the
  23590.    process starts up.) Note that a FILES=30 (or greater value) entry in the
  23591.    CONFIG.SYS file would also be required for the process to successfully
  23592.    open 30 files or devices.
  23593.  
  23594.            .
  23595.            .
  23596.            .
  23597.            mov     ah,67h          ; function number
  23598.            mov     bx,30           ; maximum number of handles
  23599.            int     21h             ; transfer to MS-DOS
  23600.            jc      error           ; jump if function failed
  23601.            .
  23602.            .
  23603.            .
  23604.  
  23605.  
  23606.  ────────────────────────────────────────────────────────────────────────────
  23607.  Int 21H                                                                [3.3]
  23608.  Function 68H (104)
  23609.  Commit file
  23610.  ────────────────────────────────────────────────────────────────────────────
  23611.  
  23612.    Forces all data in MS-DOS's internal buffers associated with a specified
  23613.    handle to be physically written to the device. If the handle refers to a
  23614.    file, and the file has been modified, the time and date stamp and file
  23615.    size in the file's directory entry are updated.
  23616.  
  23617.  Call with:
  23618.  
  23619.    AH            = 68H
  23620.    BX            = handle
  23621.  
  23622.  Returns:
  23623.  
  23624.    If function successful
  23625.  
  23626.    Carry flag    = clear
  23627.  
  23628.    If function unsuccessful
  23629.  
  23630.    Carry flag    = set
  23631.    AX            = error code
  23632.  
  23633.  Notes:
  23634.  
  23635.    ■ The effect of this function is equivalent to closing and reopening a
  23636.      file, or to duplicating a handle for the file with Int 21H Function
  23637.      45H and then closing the duplicate. However, this function has the
  23638.      advantage that it will not fail due to lack of handles, and the
  23639.      application does not risk losing control of the file in multitasking or
  23640.      network environments.
  23641.  
  23642.    ■ If this function is requested for a handle associated with a character
  23643.      device, a success flag is returned, but there is no other effect.
  23644.  
  23645.  Example:
  23646.  
  23647.    Assume that the file MYFILE.DAT has been previously opened and that the
  23648.    handle for that file is stored in the variable fhandle. Call the Commit
  23649.    File function to ensure that any data in MS-DOS's internal buffers
  23650.    associated with the handle is written out to disk and that the directory
  23651.    and file allocation table are up to date.
  23652.  
  23653.    fname   db      'MYFILE.DAT',0  ; ASCIIZ filename
  23654.    fhandle dw      ?               ; file handle
  23655.            .
  23656.            .
  23657.            .
  23658.            mov     ah,68h          ; function number
  23659.            mov     bx,fhandle      ; file handle
  23660.            int     21h             ; transfer to MS-DOS
  23661.            jc      error           ; jump if commit failed
  23662.            .
  23663.            .
  23664.            .
  23665.  
  23666.  
  23667.  ────────────────────────────────────────────────────────────────────────────
  23668.  Int 21H
  23669.  Function 69H (105)
  23670.  Reserved
  23671.  ────────────────────────────────────────────────────────────────────────────
  23672.  
  23673.  
  23674.  ────────────────────────────────────────────────────────────────────────────
  23675.  Int 21H
  23676.  Function 6AH (106)
  23677.  Reserved
  23678.  ────────────────────────────────────────────────────────────────────────────
  23679.  
  23680.  
  23681.  ────────────────────────────────────────────────────────────────────────────
  23682.  Int 21H
  23683.  Function 6BH (107)
  23684.  Reserved
  23685.  ────────────────────────────────────────────────────────────────────────────
  23686.  
  23687.  
  23688.  ────────────────────────────────────────────────────────────────────────────
  23689.  Int 21H                                                                [4.0]
  23690.  Function 6CH (108)
  23691.  Extended open file
  23692.  ────────────────────────────────────────────────────────────────────────────
  23693.  
  23694.    Given an ASCIIZ pathname, opens, creates or replaces a file in the
  23695.    designated or default directory on the designated or default disk drive.
  23696.    Returns a handle that can be used by the program for subsequent access to
  23697.    the file.
  23698.  
  23699.  Call with:
  23700.  
  23701.    AH            = 6CH
  23702.    AL            = 00H
  23703.    BX            = open mode
  23704.  
  23705.                    Bit(s)    Significance
  23706.                    0─2       access type
  23707.                              000 = read-only
  23708.                              001 = write-only
  23709.                              010 = read/write
  23710.                    3         reserved (0)
  23711.                    4─6       sharing mode
  23712.                              000 = compatibility
  23713.                              001 = deny read/write (deny all)
  23714.                              010 = deny write
  23715.                              011 = deny read
  23716.                              100 = deny none
  23717.                    7         inheritance
  23718.                              0 = child process inherits handle
  23719.                              1 = child does not inherit handle
  23720.                    8─12      reserved (0)
  23721.                    13        critical error handling
  23722.                              0 = execute Int 24H
  23723.                              1 = return error to process
  23724.                    14        write-through
  23725.                              0 = writes may be buffered and deferred
  23726.                              1 = physical write at request time
  23727.                    15        reserved (0)
  23728.  
  23729.    CX            = file attribute (bits may be combined; ignored if open)
  23730.  
  23731.                    Bit(s)    Significance (if set)
  23732.                    0         read-only
  23733.                    1         hidden
  23734.                    2         system
  23735.                    3         volume label
  23736.                    4         reserved (0)
  23737.                    5         archive
  23738.                    6─15      reserved (0)
  23739.  
  23740.    DX            = open flag
  23741.  
  23742.                    Bits      Significance
  23743.                    0─3       action if file exists
  23744.                              0000 = fail
  23745.                              0001 = open file
  23746.                              0010 = replace file
  23747.                    4─7       action if file doesn't exist
  23748.                              0000 = fail
  23749.                              0001 = create file
  23750.                    8─15      reserved (0)
  23751.  
  23752.    DS:SI         = segment:offset of ASCIIZ pathname
  23753.  
  23754.  Returns:
  23755.  
  23756.    If function successful
  23757.  
  23758.    Carry flag    = clear
  23759.    AX            = handle
  23760.    CX            = action taken
  23761.                    1 = file existed and was opened
  23762.                    2 = file did not exist and was created
  23763.                    3 = file existed and was replaced
  23764.  
  23765.    If function failed
  23766.  
  23767.    Carry flag    = set
  23768.    AX            = error code
  23769.  
  23770.  Notes:
  23771.  
  23772.    ■ The function fails if:
  23773.  
  23774.      ∙ any element of the pathname does not exist.
  23775.  
  23776.      ∙ the file is being created in the root directory and the root directory
  23777.        is full.
  23778.  
  23779.      ∙ the file is being created and a file with the same name and the
  23780.        read-only attribute already exists in the specified directory.
  23781.  
  23782.      ∙ the program is running on a network and the user running the program
  23783.        has insufficient access rights.
  23784.  
  23785.    ■ A file is usually given a normal (0) attribute when it is created. The
  23786.      file's attribute can subsequently be modified with Int 21H Function
  23787.      43H.
  23788.  
  23789.    ■ This function combines the capabilities of Int 21H Functions 3CH, 3DH,
  23790.      and 5BH. It was added to MS-DOS for compatibility with the DosOpen
  23791.      function of OS/2.
  23792.  
  23793.  Example:
  23794.  
  23795.    Create the file MYFILE.DAT, if it does not already exist, in directory
  23796.    \MYDIR on drive C, and save the handle for subsequent access to the file.
  23797.  
  23798.    fname   db      'C:\MYDIR\MYFILE.DAT',0
  23799.  
  23800.    fhandle dw      ?
  23801.            .
  23802.            .
  23803.            .
  23804.            mov     ax,6c00h        ; function number
  23805.            mov     bx,4042h        ; read/write, deny none,
  23806.                                    ; write-through mode
  23807.            xor     cx,cx           ; normal attribute
  23808.            mov     dx,0010h        ; create if doesn't exist,
  23809.                                    ; fail if exists
  23810.            mov     si,seg fname    ; address of pathname
  23811.            mov     ds,si
  23812.            mov     si,offset fname
  23813.            int     21h             ; transfer to MS-DOS
  23814.            jc      error           ; jump if open failed
  23815.            mov     fhandle,ax      ; save file handle
  23816.            .
  23817.            .
  23818.            .
  23819.  
  23820.  
  23821.  ────────────────────────────────────────────────────────────────────────────
  23822.  Int 22H                                                                [1.0]
  23823.  Terminate handler address
  23824.  ────────────────────────────────────────────────────────────────────────────
  23825.  
  23826.    The machine interrupt vector for Int 22H (memory locations 0000:0088H
  23827.    through 0000:008BH) contains the address of the routine that receives
  23828.    control when the currently executing program terminates via Int 20H, Int
  23829.    27H, or Int 21H Functions 00H, 31H, or 4CH. The address in this vector
  23830.    is also copied into offsets 0AH through 0DH of the program segment prefix
  23831.    (PSP) when a program is loaded but before it begins executing, and is
  23832.    restored from the PSP (in case it was modified by the application) as part
  23833.    of MS-DOS's termination handling.
  23834.  
  23835.    This interrupt should never be issued directly.
  23836.  
  23837.  
  23838.  ────────────────────────────────────────────────────────────────────────────
  23839.  Int 23H                                                                [1.0]
  23840.  Ctrl-C handler address
  23841.  ────────────────────────────────────────────────────────────────────────────
  23842.  
  23843.    The machine interrupt vector for Int 23H (memory locations 0000:008CH
  23844.    though 0000:008FH) contains the address of the routine which receives
  23845.    control when a Ctrl-C is detected during any character I/O function and,
  23846.    if the Break flag is ON, during most other MS-DOS function calls. The
  23847.    address in this vector is also copied into locations 0EH through 11H of
  23848.    the program segment prefix (PSP) when a program is loaded but before it
  23849.    begins executing, and is restored from the PSP (in case it was modified by
  23850.    the application) as part of MS-DOS's termination handling.
  23851.  
  23852.    This interrupt should never be issued directly.
  23853.  
  23854.  Notes:
  23855.  
  23856.    ■ The initialization code for an application can use Int 21H Function
  23857.      25H to reset the Interrupt 23H vector to point to its own routine for
  23858.      Ctrl-C handling. In this way, the program can avoid being terminated
  23859.      unexpectedly as the result of the user's entry of a Ctrl-C or
  23860.      Ctrl-Break.
  23861.  
  23862.    ■ When a Ctrl-C is detected and the program's Int 23H handler receives
  23863.      control, all registers are set to their values at the point of the
  23864.      original function call. The handler can then do any of the following:
  23865.  
  23866.      ∙ Set a local flag for later inspection by the application, or take any
  23867.        other appropriate action, and perform an IRET. All registers must be
  23868.        preserved. The MS-DOS function in progress will be restarted from
  23869.        scratch and will proceed to completion, control finally returning to
  23870.        the application in the normal manner.
  23871.  
  23872.      ∙ Take appropriate action and then perform a RET FAR to give control
  23873.        back to MS-DOS. The state of the carry flag is used by MS-DOS to
  23874.        determine what action to take. If the carry flag is set, the
  23875.        application will be terminated; if the carry flag is clear, the
  23876.        application will continue in the normal manner.
  23877.  
  23878.      ∙ Retain control by transferring to an error-handling routine within the
  23879.        application and then resume execution or take other appropriate
  23880.        action, never performing a RET FAR or IRET to end the
  23881.        interrupt-handling sequence. This option will cause no harm to the
  23882.        system.
  23883.  
  23884.    ■ Any MS-DOS function call may be used within the body of an Int 23H
  23885.      handler.
  23886.  
  23887.  Example:
  23888.  
  23889.    See Chapter 5.
  23890.  
  23891.  
  23892.  ────────────────────────────────────────────────────────────────────────────
  23893.  Int 24H                                                                [1.0]
  23894.  Critical-error handler address
  23895.  ────────────────────────────────────────────────────────────────────────────
  23896.  
  23897.    The machine interrupt vector for Int 24H (memory locations 0000:0090H
  23898.    through 0000:0093H) contains the address of the routine that receives
  23899.    control when a critical error (usually a hardware error) is detected. This
  23900.    address is also copied into locations 12H through 15H of the program
  23901.    segment prefix (PSP) when a program is loaded but before it begins
  23902.    executing, and is restored from the PSP (in case it was modified by the
  23903.    application) as part of MS-DOS's termination handling.
  23904.  
  23905.    This interrupt should never be issued directly.
  23906.  
  23907.  Notes:
  23908.  
  23909.    ■ On entry to the critical-error interrupt handler, bit 7 of register AH
  23910.      is clear (0) if the error was a disk I/O error; otherwise, it is set
  23911.      (1). BP:SI contains the address of a device-driver header from which
  23912.      additional information can be obtained. Interrupts are disabled. The
  23913.      registers will be set up for a retry operation, and an error code will
  23914.      be in the lower half of the DI register, with the upper half undefined.
  23915.  
  23916.      The lower byte of DI contains:
  23917.  
  23918.      00H            write-protect error
  23919.      01H            unknown unit
  23920.      02H            drive not ready
  23921.      03H            unknown command
  23922.      04H            data error (CRC)
  23923.      05H            bad request structure length
  23924.      06H            seek error
  23925.      07H            unknown media type
  23926.      08H            sector not found
  23927.      09H            printer out of paper
  23928.      0AH            write fault
  23929.      0BH            read fault
  23930.      0CH            general failure
  23931.      0DH            reserved
  23932.      0EH            reserved
  23933.      0FH            invalid disk change (MS-DOS version 3 only)
  23934.  
  23935.      Note that these are the same error codes returned by the device driver
  23936.      in the request header. Also, upon entry, the stack is set up as shown in
  23937.      Figure 8-8, page 149.
  23938.  
  23939.    ■ When a disk I/O error occurs, MS-DOS automatically retries the operation
  23940.      before issuing a critical-error Int 24H. The number of retries varies
  23941.      in different versions of MS-DOS, but is typically in the range three to
  23942.      five.
  23943.  
  23944.    ■ Int 24H handlers must preserve the SS, SP, DS, ES, BX, CX, and DX
  23945.      registers. Only Int 21H Functions 01H─0CH and 59H can be used by an
  23946.      Int 24H handler; other function calls will destroy the MS-DOS stack and
  23947.      its ability to retry or ignore an error.
  23948.  
  23949.    ■ When the Int 24H handler issues an IRET, it should return an action code
  23950.      in AL that will be interpreted by DOS as follows:
  23951.  
  23952.      0              ignore the error
  23953.      1              retry the operation
  23954.      2              terminate the program
  23955.      3              [3.0+] fail the function call in progress
  23956.  
  23957.    ■ If the Int 24H handler returns control directly to the application
  23958.      program rather than to MS-DOS, it must restore the program's registers,
  23959.      removing all but the last three words from the stack, and issue an IRET.
  23960.      Control returns to the instruction immediately following the function
  23961.      call that caused the error. This option leaves MS-DOS in an unstable
  23962.      state until a call to an Int 21H function higher than Function 0CH is
  23963.      made.
  23964.  
  23965.  Example:
  23966.  
  23967.    See Chapter 8.
  23968.  
  23969.  
  23970.  ────────────────────────────────────────────────────────────────────────────
  23971.  Int 25H                                                                [1.0]
  23972.  Absolute disk read
  23973.  ────────────────────────────────────────────────────────────────────────────
  23974.  
  23975.    Provides a direct linkage to the MS-DOS BIOS module to read data from a
  23976.    logical disk sector into memory.
  23977.  
  23978.  Call with:
  23979.  
  23980.    For access to partitions <= 32 MB
  23981.  
  23982.    AL            = drive number (0 = A, 1 = B, etc)
  23983.    CX            = number of sectors to read
  23984.    DX            = starting sector number
  23985.    DS:BX         = segment:offset of buffer
  23986.  
  23987.    For access to partitions > 32 MB (MS-DOS 4.0 and later)
  23988.  
  23989.    AL            = drive number (0 = A, 1 = B, etc)
  23990.    CX            = -1
  23991.    DS:BX         = segment:offset of parameter block (see Notes)
  23992.  
  23993.  Returns:
  23994.  
  23995.    If function successful
  23996.  
  23997.    Carry flag    = clear
  23998.  
  23999.    If function unsuccessful
  24000.  
  24001.    Carry flag    = set
  24002.    AX            = error code (see Notes)
  24003.  
  24004.  Notes:
  24005.  
  24006.    ■ All registers except the segment registers may be destroyed.
  24007.  
  24008.    ■ When this function returns, the CPU flags originally pushed on the stack
  24009.      by the INT 25H instruction are still on the stack. The stack must be
  24010.      cleared by a POPF or ADD SP,2 to prevent uncontrolled stack growth and
  24011.      to make accessible any other values that were pushed on the stack before
  24012.      the call to INT 25H.
  24013.  
  24014.    ■ Logical sector numbers are obtained by numbering each disk sector
  24015.      sequentially from cylinder 0, head 0, sector 1, and continuing until the
  24016.      last sector on the disk is counted. The head number is incremented
  24017.      before the track number. Logically adjacent sectors may not be
  24018.      physically adjacent, due to interleaving that occurs at the
  24019.      device-adapter level for some disk types.
  24020.  
  24021.    ■ The error code is interpreted as follows: The lower byte (AL) is the
  24022.      same error code that is returned in the lower byte of DI when an Int 24H
  24023.      is issued. The upper byte (AH) contains:
  24024.  
  24025.      01H            if bad command
  24026.      02H            if bad address mark
  24027.      04H            if requested sector not found
  24028.      08H            if direct memory access (DMA) failure
  24029.      10H            if data error (bad CRC)
  24030.      20H            if controller failed
  24031.      40H            if seek operation failed
  24032.      80H            if attachment failed to respond
  24033.  
  24034.    ■ [4.0+] When accessing partitions larger than 32 MB under MS-DOS version
  24035.      4, this function uses a parameter block with the following format:
  24036.  
  24037.      Bytes          Description
  24038.      00H─03H        32-bit sector number
  24039.      04H─05H        number of sectors to read
  24040.      06H─07H        offset of buffer
  24041.      08H─09H        segment of buffer
  24042.  
  24043.  Example:
  24044.  
  24045.    Read logical sector 1 of drive A into the memory area named buff. (On most
  24046.    MS-DOS floppy disks, this sector contains the beginning of the file
  24047.    allocation table.)
  24048.  
  24049.    buff    db      512 dup (?)     ; receives data from disk
  24050.            .
  24051.            .
  24052.            .
  24053.            mov     al,0            ; drive A
  24054.            mov     cx,1            ; number of sectors
  24055.            mov     dx,1            ; beginning sector number
  24056.            mov     bx,seg buff     ; buffer address
  24057.            mov     ds,bx
  24058.            mov     bx,offset buff
  24059.            int     25h             ; request disk read
  24060.            jc      error           ; jump if read failed
  24061.            add     sp,2            ; clear stack
  24062.            .
  24063.            .
  24064.            .
  24065.  
  24066.  
  24067.  ────────────────────────────────────────────────────────────────────────────
  24068.  Int 26H                                                                [1.0]
  24069.  Absolute disk write
  24070.  ────────────────────────────────────────────────────────────────────────────
  24071.  
  24072.    Provides a direct linkage to the MS-DOS BIOS module to write data from
  24073.    memory to a logical disk sector.
  24074.  
  24075.  Call with:
  24076.  
  24077.    For access to partitions <= 32 MB
  24078.  
  24079.    AL            = drive number (0 = A, 1 = B, etc)
  24080.    CX            = number of sectors to write
  24081.    DX            = starting sector number
  24082.    DS:BX         = segment:offset of buffer
  24083.  
  24084.    For access to partitions > 32 MB (MS-DOS 4.0 and later)
  24085.  
  24086.    AL            = drive number (0 = A, 1 = B, etc)
  24087.    CX            = -1
  24088.    DS:BX         = segment:offset of parameter block (see Notes)
  24089.  
  24090.  Returns:
  24091.  
  24092.    If function successful
  24093.  
  24094.    Carry flag    = clear
  24095.  
  24096.    If function unsuccessful
  24097.  
  24098.    Carry flag    = set
  24099.    AX            = error code (see Notes)
  24100.  
  24101.  Notes:
  24102.  
  24103.    ■ All registers except the segment registers may be destroyed.
  24104.  
  24105.    ■ When this function returns, the CPU flags originally pushed onto the
  24106.      stack by the INT 26H instruction are still on the stack. The stack must
  24107.      be cleared by a POPF or ADD SP,2 to prevent uncontrolled stack growth
  24108.      and to make accessible any other values that were pushed on the stack
  24109.      before the call to INT 26H.
  24110.  
  24111.    ■ Logical sector numbers are obtained by numbering each disk sector
  24112.      sequentially from cylinder 0, head 0, sector 1, and continuing until the
  24113.      last sector on the disk is counted. The head number is incremented
  24114.      before the track number. Logically adjacent sectors may not be
  24115.      physically adjacent, due to interleaving that occurs at the
  24116.      device-adapter level for some disk types.
  24117.  
  24118.    ■ The error code is interpreted as follows: The lower byte (AL) is the
  24119.      same error code that is returned in the lower byte of DI when an Int
  24120.      24H is issued. The upper byte (AH) contains:
  24121.  
  24122.      01H            if bad command
  24123.      02H            if bad address mark
  24124.      03H            if write-protect fault
  24125.      04H            if requested sector not found
  24126.      08H            if direct memory access (DMA) failure
  24127.      10H            if data error (bad CRC)
  24128.      20H            if controller failed
  24129.      40H            if seek operation failed
  24130.      80H            if attachment failed to respond
  24131.  
  24132.    ■ [4.0+] When accessing partitions larger than 32 MB under MS-DOS version
  24133.      4, this function uses a parameter block with the following format:
  24134.  
  24135.      Bytes          Description
  24136.      00H─03H        32-bit sector number
  24137.      04H─05H        number of sectors to read
  24138.      06H─07H        offset of buffer
  24139.      08H─09H        segment of buffer
  24140.  
  24141.  Example:
  24142.  
  24143.    Write the contents of the memory area named buff into logical sector 3 of
  24144.    drive C.
  24145.  
  24146.    Warning: Verbatim use of the following code could damage the file system
  24147.    on your fixed disk. There is, unfortunately, no way to provide a really
  24148.    safe example of this function.
  24149.  
  24150.    buff    db      512 dup (?)     ; contains data for write
  24151.            .
  24152.            .
  24153.            .
  24154.            mov     al,2            ; drive C
  24155.            mov     cx,1            ; number of sectors
  24156.            mov     dx,3            ; beginning sector number
  24157.            mov     bx,seg buff     ; buffer address
  24158.            mov     ds,bx
  24159.            mov     bx,offset buff
  24160.            int     26h             ; request disk write
  24161.            jc      error           ; jump if write failed
  24162.            add     sp,2            ; clear stack
  24163.            .
  24164.            .
  24165.            .
  24166.  
  24167.  
  24168.  ────────────────────────────────────────────────────────────────────────────
  24169.  Int 27H                                                                [1.0]
  24170.  Terminate and stay resident
  24171.  ────────────────────────────────────────────────────────────────────────────
  24172.  
  24173.    Terminates execution of the currently executing program, but reserves part
  24174.    or all of its memory so that it will not be overlaid by the next transient
  24175.    program to be loaded. MS-DOS then takes the following actions:
  24176.  
  24177.    ■ File buffers are flushed and any open handles for files or devices owned
  24178.      by the process are closed.
  24179.  
  24180.    ■ The termination handler vector (Int 22H) is restored from PSP:000AH.
  24181.  
  24182.    ■ The Ctrl-C handler vector (Int 23H) is restored from PSP:000EH.
  24183.  
  24184.    ■ [2.0+] The critical-error handler vector (Int 24H) is restored from
  24185.      PSP:0012H.
  24186.  
  24187.    ■ Control is transferred to the termination handler.
  24188.  
  24189.    If the program is returning to COMMAND.COM, control transfers to the
  24190.    resident portion and the transient portion is reloaded if necessary. If a
  24191.    batch file is in progress, the next line of the file is fetched and
  24192.    interpreted; otherwise a prompt is issued for the next user command.
  24193.  
  24194.  Call with:
  24195.  
  24196.    DX            = offset of the last byte plus one (relative to the program
  24197.                    segment prefix)
  24198.                    of program to be protected
  24199.    CS            = segment of program segment prefix
  24200.  
  24201.  Returns:
  24202.  
  24203.    Nothing
  24204.  
  24205.  Notes:
  24206.  
  24207.    ■ This function call is typically used to allow user-written utilities,
  24208.      drivers, or interrupt handlers to be loaded as ordinary .COM or .EXE
  24209.      programs, then remain resident. Subsequent entrance to the code is via a
  24210.      hardware or software interrupt.
  24211.  
  24212.    ■ This function attempts to set the initial memory allocation block to the
  24213.      length in bytes specified in register DX. If other memory blocks have
  24214.      been requested by the application via Int 21H Function 48H, they will
  24215.      not be released by this function.
  24216.  
  24217.    ■ Other methods of performing a final exit are:
  24218.  
  24219.      ∙ Int 20H
  24220.  
  24221.      ∙ Int 21H Function 00H
  24222.  
  24223.      ∙ Int 21H Function 31H
  24224.  
  24225.      ∙ Int 21H Function 4CH
  24226.  
  24227.    ■ This function should not be called by .EXE programs that are loaded at
  24228.      the high end of the transient program area (i.e., linked with the /HIGH
  24229.      switch), because doing so reserves the memory normally used by the
  24230.      transient part of COMMAND.COM. If COMMAND.COM cannot be reloaded, the
  24231.      system will fail.
  24232.  
  24233.    ■ This function does not work correctly when DX contains values in the
  24234.      range 0FFF1H─0FFFFH. In this case, MS-DOS discards the high bit of the
  24235.      value in DX, resulting in the reservation of 32 KB less memory than was
  24236.      requested by the program.
  24237.  
  24238.    ■ [2.0+] Int 21H Function 31H should be used in preference to this
  24239.      function, because it supports return codes, allows larger amounts of
  24240.      memory to be reserved, and does not require CS to contain the segment of
  24241.      the program segment prefix.
  24242.  
  24243.    ■ [3.0+] If the program is running on a network, it should remove all
  24244.      locks it has placed on file regions before terminating.
  24245.  
  24246.  Example:
  24247.  
  24248.    Terminate and stay resident, reserving enough memory to contain the entire
  24249.    program.
  24250.  
  24251.            .
  24252.            .
  24253.            .
  24254.            mov     dx,offset pend  ; DX = bytes to reserve
  24255.            int     27h             ; terminate, stay resident
  24256.            .
  24257.            .
  24258.            .
  24259.    pend    equ     $               ; offset, end of program
  24260.  
  24261.            end
  24262.  
  24263.  
  24264.  ────────────────────────────────────────────────────────────────────────────
  24265.  Int 28H
  24266.  Reserved
  24267.  ────────────────────────────────────────────────────────────────────────────
  24268.  
  24269.  
  24270.  ────────────────────────────────────────────────────────────────────────────
  24271.  Int 29H
  24272.  Reserved
  24273.  ────────────────────────────────────────────────────────────────────────────
  24274.  
  24275.  
  24276.  ────────────────────────────────────────────────────────────────────────────
  24277.  Int 2AH
  24278.  Reserved
  24279.  ────────────────────────────────────────────────────────────────────────────
  24280.  
  24281.  
  24282.  ────────────────────────────────────────────────────────────────────────────
  24283.  Int 2BH
  24284.  Reserved
  24285.  ────────────────────────────────────────────────────────────────────────────
  24286.  
  24287.  
  24288.  ────────────────────────────────────────────────────────────────────────────
  24289.  Int 2CH
  24290.  Reserved
  24291.  ────────────────────────────────────────────────────────────────────────────
  24292.  
  24293.  
  24294.  ────────────────────────────────────────────────────────────────────────────
  24295.  Int 2DH
  24296.  Reserved
  24297.  ────────────────────────────────────────────────────────────────────────────
  24298.  
  24299.  
  24300.  ────────────────────────────────────────────────────────────────────────────
  24301.  Int 2EH
  24302.  Reserved
  24303.  ────────────────────────────────────────────────────────────