home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft_Programmers_Library.7z / MPL / intel / ps2vga.txt < prev   
Encoding:
Text File  |  2013-11-08  |  1.3 MB  |  32,151 lines

Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
  1.  PROGRAMMER'S GUIDE TO PC(R) AND PS/2(TM) VIDEO SYSTEMS
  2.  
  3.  
  4.  
  5.  ───────────────────────────────────────────────────────────────────────────
  6.  
  7.  
  8.  
  9.                 PROGRAMMER'S GUIDE TO PC(R) AND PS/2(TM)
  10.  
  11.                              VIDEO SYSTEMS
  12.  
  13.  
  14.      Maximum Video Performance from the EGA(TM), VGA, HGC, and MCGA
  15.  
  16.  
  17.  
  18.  
  19.                              RICHARD WILTON
  20.  
  21.  
  22.  
  23.  ───────────────────────────────────────────────────────────────────────────
  24.  
  25.  
  26.  
  27.  PUBLISHED BY
  28.  Microsoft Press
  29.  A Division of Microsoft Corporation
  30.  16011 NE 36th Way, Box 97017, Redmond, Washington 98073-9717
  31.  
  32.  Copyright(C) 1987 by Richard Wilton
  33.  All rights reserved. No part of the contents of this book may
  34.  be reproduced or transmitted in any form or by any means without
  35.  the written permission of the publisher.
  36.  
  37.  Library of Congress Cataloging in Publication Data
  38.  Wilton, Richard, 1953-
  39.  The programmer's guide to PC and PS/2 video systems.
  40.  Includes index.
  41.  1. IBM Personal Computer--Programming.  2. Expansion boards
  42.     (Microcomputers).
  43.  3. Computer graphics.  I. Title.
  44.  QA76.8.I2594W55    1987    005.265    87-20264
  45.  ISBN 1-55615-103-9
  46.  
  47.  Printed and bound in the United States of America.
  48.  
  49.  1 2 3 4 5 6 7 8 9  FGFG  8 9 0 9 8 7
  50.  
  51.  Distributed to the book trade in the
  52.  United States by Harper & Row.
  53.  
  54.  Distributed to the book trade in
  55.  Canada by General Publishing Company, Ltd.
  56.  
  57.  Distributed to the book trade outside the
  58.  United States and Canada by Penguin Books Ltd.
  59.  
  60.  Penguin Books Ltd., Harmondsworth, Middlesex, England
  61.  Penguin Books Australia Ltd., Ringwood, Victoria, Australia
  62.  Penguin Books N.Z. Ltd., 182-190 Wairau Road, Auckland 10, New Zealand
  63.  
  64.  British Cataloging in Publication Data available
  65.  
  66.  
  67.  Acquisitions Editor: Claudette Moore
  68.  Technical Editor: Jeff Hinsch
  69.  
  70.  
  71.  IBM(R) is a registered trademark and PC/AT(TM), PC-DOS(TM), PC/XT(TM), and
  72.  PS/2(TM) are trademarks of International Business Machines Corporation.
  73.  Microsoft(R) and MS-DOS(R) are registered trademarks of Microsoft
  74.  Corporation.
  75.  
  76.  
  77.  
  78.       Contents
  79.  
  80.  
  81.  
  82.       Acknowledgments
  83.  
  84.       Introduction
  85.  
  86.        1     IBM Video Hardware and Firmware
  87.  
  88.        2     Programming the Hardware
  89.  
  90.        3     Alphanumeric Modes
  91.  
  92.        4     Graphics Modes
  93.  
  94.        5     Pixel Programming
  95.  
  96.        6     Lines
  97.  
  98.        7     Circles and Ellipses
  99.  
  100.        8     Region Fill
  101.  
  102.        9     Graphics Text
  103.  
  104.       10     Alphanumeric Character Sets
  105.  
  106.       11     Bit Blocks and Animation
  107.  
  108.       12     Some Advanced Video Programming Techniques
  109.  
  110.       13     Graphics Subroutines in High-Level Languages
  111.  
  112.       Appendix A: Video BIOS Summary
  113.  
  114.       Appendix B: Printing the Screen
  115.  
  116.       Appendix C: Identifying Video Subsystems
  117.  
  118.       Glossary
  119.  
  120.       Index
  121.  
  122.  
  123.  
  124.  Acknowledgments
  125.  
  126.  
  127.  
  128.       The material in Chapters 6, 7, and 8 owes a great deal to the orig-
  129.       inal efforts of several respected workers in the field of computer
  130.       graphics. In each of these chapters I have included references to some
  131.       of their best-known publications. If you are intrigued by the
  132.       algorithms described in these chapters, by all means obtain the
  133.       original publications and explore them yourself.
  134.  
  135.       This book could not have been written without the encouragement of my
  136.       family, friends, and colleagues, who deserve great thanks for their
  137.       patience and support. My gratitude also to Andy Fischer and to Charles
  138.       Petzold, both of whom graciously reviewed portions of this book and
  139.       offered accurate criticism and suggestions.
  140.  
  141.       And, of course, my special thanks to the enthusiastic people at
  142.       Microsoft Press--Claudette Moore, Jeff Hinsch, and many others--who
  143.       painstakingly transformed the raw material of this book into the
  144.       finished product.
  145.  
  146.  
  147.  
  148.  Introduction
  149.  
  150.  
  151.  
  152.       I clearly remember the day I first plugged a new IBM Enhanced Graphics
  153.       Adapter (EGA) into an IBM PC. It was good to have IBM's new "enhanced"
  154.       video hardware, with its better resolution and control over colors, as
  155.       well as features not found in any of IBM's earlier PC video hardware.
  156.       Now I was ready to write some really sharp graphics applications.
  157.  
  158.       Or so I thought. The problem was, I couldn't figure out how to program
  159.       the contraption. I had no technical documentation at all. (It arrived
  160.       in the mail six months and $125 later.) I tried disassembling the
  161.       EGA's ROM BIOS, but studying 6000 uncommented machine instructions
  162.       soon raised more questions than it answered. I desperately tried the
  163.       shotgun approach--changing the contents of memory locations and
  164.       machine registers just to see what would happen--but this was like
  165.       chopping out random pieces of an automobile just to see what would
  166.       stop working.
  167.  
  168.       What I lacked was the details--conceptual descriptions of the hardware
  169.       design, tables describing the programming interface, and, above all,
  170.       source code examples for some typical programming techniques. A few
  171.       well-chosen source code examples would have saved many hours of
  172.       experimentation and frustration when I was trying to understand how to
  173.       program that video adapter.
  174.  
  175.       This book was inspired by the painful memory of that experience. It is
  176.       filled with source code examples. Its text describes the source code,
  177.       and vice versa. This book also has many tables and summary
  178.       descriptions of the hardware programming interface. In short, this
  179.       book is what I wish I'd had when I started to program PC video
  180.       hardware.
  181.  
  182.  
  183.  What This Book Is About
  184.  
  185.       The first chapter of this book is a general overview of the video
  186.       display environment. It describes the commonly used PC and PS/2 video
  187.       hardware the rest of the book deals with. It also introduces you (if
  188.       you aren't already on speaking terms) to the well-known ROM BIOS video
  189.       support routines.
  190.  
  191.       The next 10 chapters contain the nuts and bolts of IBM video
  192.       programming. The earlier chapters cover the fundamentals, including
  193.       hardware architecture, video display modes, and the nature of the
  194.       interface between your programs and the hardware. The later chapters
  195.       build upon the fundamentals to demonstrate a number of techniques for
  196.       producing text and graphics output.
  197.  
  198.       The last two chapters of this book take you to the low and high levels
  199.       of video graphics programming. Chapter 12 is the hardware tinkerer's
  200.       chapter--if you want to work with vertical interrupts or play with bit
  201.       planes, this one's for you. Finally, Chapter 13 tells how to link
  202.       your video hardware drivers to high-level programs and introduces you
  203.       to several commercial video output packages.
  204.  
  205.  
  206.  What You Need to Use This Book
  207.  
  208.       This book is not really meant for beginners. That's not to say that a
  209.       programmer who is just learning how to write working code will not
  210.       benefit from this material. On the contrary, the many working examples
  211.       of useful source code should be valuable to anyone who plans to do
  212.       serious programming for PCs or PS/2s. Nevertheless, the broader your
  213.       programming background, the more tools you will have for solving the
  214.       diverse and exacting problems involved in video programming.
  215.  
  216.  Languages
  217.  
  218.       I use assembly language and C for most of the programming examples in
  219.       this book, although I intentionally avoid some of C's more cryptic
  220.       syntactic constructs. If you are comfortable with assembly language
  221.       and with a high-level language such as C, Pascal, FORTRAN, PL/1, or
  222.       structured BASIC, you should have no problem reading the source code
  223.       examples.
  224.  
  225.       Moreover, Chapter 13 discusses interfaces for several high-level
  226.       languages using different memory models and subroutine-calling
  227.       protocols. You can follow the guidelines there to convert any of the
  228.       C-callable source code examples to the subroutine-calling protocol
  229.       used by your favorite language translator.
  230.  
  231.       You might want to use some other programming tools if you plan to
  232.       experiment with the source code examples that follow. For example, a
  233.       good assembly-language debugger can be extremely helpful. You will
  234.       probably need an object linker if you plan to call the assembly-
  235.       language routines in this book from high-level-language programs.
  236.       Also, as source files and object modules proliferate, you might find a
  237.       UNIX-like make utility quite useful in keeping things straight.
  238.  
  239.  Operating System
  240.  
  241.       Everything in this book is intended to run under MS-DOS, or PC-DOS,
  242.       version 2.0 or later. However, there is nothing in any of the source
  243.       code that verifies which operating system is in use, so be careful if
  244.       you transport the code to earlier versions of MS-DOS or to another
  245.       operating system.
  246.  
  247.  Hardware
  248.  
  249.       Having a PC or PS/2 with a video display attached is essential. Video
  250.       programming is like swimming: It's one thing to read about it, but
  251.       it's quite another experience to try it yourself. In fact, if you plan
  252.       to do a great deal of video programming, you should consider
  253.       installing two different video subsystems and displays in your PC.
  254.       With two separate sets of video hardware in the same computer, you can
  255.       run a debugger on one screen while a test program produces output on
  256.       the other screen. This dual-display hardware configuration is a real
  257.       timesaver, particularly when you're developing video graphics routines
  258.       such as those described in Chapters 5 through 9.
  259.  
  260.       Here is a list of the various computers and video adapters I used to
  261.       develop the techniques discussed in this book:
  262.  
  263.       Computers
  264.         IBM PC/XT
  265.         IBM PC/AT
  266.         IBM PS/2 Model 30
  267.         IBM PS/2 Model 60
  268.  
  269.       Adapters
  270.         IBM Monochrome Display Adapter
  271.         IBM Color Graphics Adapter
  272.         IBM Enhanced Graphics Adapter
  273.         IBM PS/2 Display Adapter
  274.         Hercules Graphics Card
  275.         Hercules Graphics Card Plus
  276.         Hercules Color Card
  277.         Hercules InColor Card
  278.  
  279.       If you are using one of these computers or adapters, or a hardware-
  280.       compatible clone, then you should be able to run the source code
  281.       examples.
  282.  
  283.  Manuals
  284.  
  285.       To program IBM PC video hardware effectively, you need to know what
  286.       the hardware is designed to do and how software and the system BIOS
  287.       are expected to interact with it. This basic information is found in
  288.       IBM's Technical Reference manuals for the PC, PC/XT, PC/AT, and PS/2s
  289.       and in its Options and Adapters Technical Reference manuals. Most
  290.       second-source manufacturers of IBM PC video equipment also provide
  291.       detailed technical information on their hardware.
  292.  
  293.       The material in this book is intended to complement the discussions in
  294.       the manufacturers' technical documentation. I tried to follow the
  295.       manufacturers' terminology and hardware descriptions wherever
  296.       possible. However, the manufacturers' documentation goes somewhat awry
  297.       at times. If you find a discrepancy between the official documentation
  298.       and this book, you can (I hope) rely on this book to contain the right
  299.       information.
  300.  
  301.       Still, in a book this size, I have certainly made some mistakes. I
  302.       welcome your comments, criticisms, and suggestions.
  303.  
  304.       I have found that writing good video software is challenging, but the
  305.       rewards are particularly satisfying. I hope to share some of the
  306.       challenges--and some of the satisfaction--with you in this book.
  307.  
  308.  
  309.  
  310.                      1  IBM Video Hardware and Firmware
  311.  
  312.  
  313.                        IBM PC and PS/2 Video Hardware
  314.           IBM Monochrome Display Adapter and Color Graphics Adapter
  315.             Hercules Graphics Card ■ Hercules Graphics Card Plus
  316.             IBM Enhanced Graphics Adapter ■ Hercules InColor Card
  317.               Multi-Color Graphics Array ■ Video Graphics Array
  318.  
  319.                    Introduction to the ROM BIOS Interface
  320.                    Interrupt 10H ■ Video Display Data Area
  321.              Accessing the Video BIOS from a High-Level Language
  322.  
  323.  
  324.  
  325.       Microcomputer video systems keep getting better. Since the
  326.       introduction of the IBM PC in 1981, engineering technology has
  327.       improved, and the market for more powerful video hardware has widened.
  328.       Both IBM and its competitors have responded by developing increasingly
  329.       sophisticated video adapters and displays, as well as the software to
  330.       accompany them.
  331.  
  332.       This chapter provides an overview of the evolution of IBM PC and PS/2
  333.       video hardware. This overview is by no means comprehensive, but it
  334.       covers the most widely used video equipment that IBM and Hercules
  335.       offer. The chapter concludes with an introduction to IBM's video BIOS,
  336.       a set of drivers built into ROM in all IBM PCs and PS/2s, which
  337.       provides a basic programming interface for video applications.
  338.  
  339.  
  340.  IBM PC and PS/2 Video Hardware
  341.  
  342.  
  343.       A "plain vanilla" IBM PC/XT or PC/AT contains no built-in video
  344.       hardware, so you must select and install the video hardware yourself.
  345.       In a typical configuration, a video display (monitor) is attached with
  346.       a 9-wire cable to a video adapter installed inside the PC. A typical
  347.       video adapter is a printed circuit board with a 9-pin connector that
  348.       attaches to the monitor's cable and a 2-by-31-connection card-edge tab
  349.       that inserts into one of the slots on the PC's motherboard. Figure
  350.       1-1 shows these connectors, as well as some of the integrated
  351.       circuits common to many IBM video adapters. The circuitry in the video
  352.       adapter generates the signals that control what is displayed on the
  353.       monitor's screen.
  354.  
  355.       When you purchase an IBM PC, you must decide which video adapter and
  356.       monitor to use. The most widely used video adapters with the most
  357.       software written for them are IBM's Monochrome Display Adapter, Color
  358.       Graphics Adapter, and Enhanced Graphics Adapter, and the monochrome
  359.       Graphics Card made by Hercules.
  360.  
  361.       In contrast, all IBM PS/2 series computers are equipped with a built-
  362.       in video subsystem, so purchasing a separate video adapter is
  363.       unnecessary. The video subsystem in the PS/2 Models 25 and 30 is
  364.       called the Multi-Color Graphics Array. In Models 50, 60, and 80, the
  365.       integrated video subsystem is commonly known as the Video Graphics
  366.       Array. The Video Graphics Array subsystem also is available as an
  367.       adapter for the PC/XT, PC/AT, and PS/2 Model 30. This adapter has
  368.       essentially the same hardware features as the integrated  PS/2
  369.       subsystem.
  370.  
  371.  
  372.  IBM Monochrome Display Adapter and Color Graphics Adapter
  373.  
  374.       When the PC was introduced in 1981, IBM offered two video adapters:
  375.       the Monochrome Display Adapter (MDA) and the Color Graphics Adapter
  376.       (CGA). The MDA is designed for use with a monochrome monitor (the IBM
  377.       Monochrome Display) that displays 80 columns and 25 rows of
  378.       alphanumeric text. The CGA supports either an RGB display (a monitor
  379.       with separate input signals for red, green, and blue) or a home
  380.       television set (which uses a composite video signal). The CGA, of
  381.       course, can display graphics information on a dot-by-dot basis as well
  382.       as alphanumeric text.
  383.  
  384.  
  385.               ╔══════════════════════════════════════════╗
  386.               ║                                          ║
  387.               ║    Figure 1-1 is found on page 3         ║
  388.               ║    in the printed version of the book.   ║
  389.               ║                                          ║
  390.               ╚══════════════════════════════════════════╝
  391.  
  392.             Figure 1-1.  A typical IBM PC video adapter.
  393.  
  394.  
  395.       Even though both the MDA and the CGA can display 25 rows of 80-column
  396.       text, most people find the MDA's green monochrome display easier on
  397.       the eyes. This is because the monochrome display used with an MDA has
  398.       significantly higher resolution than that of any monitor you can use
  399.       with the CGA. Its resolution is 720 dots wide and 350 dots high; the
  400.       maximum resolution of a CGA-driven display is 640 dots wide and 200
  401.       dots high.
  402.  
  403.       Both adapters display characters in a rectangular matrix of dots. A
  404.       simple calculation shows that each character is 9 dots wide and 14
  405.       dots high on a Monochrome Display but only 8-by-8 dots on a CGA
  406.       display. The MDA's higher resolution produces more crisply defined
  407.       characters that are easier to read. For this reason, most PC users who
  408.       need to read text prefer an MDA to a CGA.
  409.  
  410.       On the other hand, many computer users need to display charts,
  411.       diagrams, and other graphics information in addition to alphanumeric
  412.       text. Also, displaying colors on the screen is essential to many
  413.       computer applications. Because the MDA can display only monochrome
  414.       text, PC users who need graphics output can compromise by using the
  415.       CGA, with its dot-by-dot color graphics capability but less-readable
  416.       text.
  417.  
  418.       Why not just attach the higher-resolution monochrome display to a
  419.       Color Graphics Adapter and get the best of both worlds? Unfortunately,
  420.       the video signals generated by an MDA are incompatible with those
  421.       required to drive a CGA monitor, and vice versa. Mismatching the
  422.       monitor and the adapter leads to a malfunctioning monitor instead of a
  423.       higher-resolution display.
  424.  
  425.       If you need sharp, readable text as well as color graphics, and you
  426.       can afford the extra equipment, you can install both an MDA and a CGA
  427.       in the same PC. You can then use the monochrome display (attached to
  428.       the MDA) for text processing and an RGB color display (driven by the
  429.       CGA) for color graphics.
  430.  
  431.  
  432.  Hercules Graphics Card
  433.  
  434.       Hercules' solution to the problem of displaying readable text and dot-
  435.       by-dot graphics on the same monitor was to add graphics capability to
  436.       a monochrome display adapter. The monochrome Hercules Graphics Card
  437.       (HGC), introduced in 1982, can display graphics and alphanumeric text
  438.       on the same green monochrome display that is used with an IBM MDA. (In
  439.       addition to its graphics capabilities, the HGC exactly duplicates the
  440.       function of IBM's original MDA.) The ability to display a combination
  441.       of readable text and monochrome graphics is sufficient for many
  442.       applications, so many PC users find the HGC an economical option.
  443.       Because it has received support from major software vendors, the HGC
  444.       has become firmly established in the marketplace.
  445.  
  446.  
  447.  Hercules Graphics Card Plus
  448.  
  449.       The HGC+ was released in June 1986. The big difference in this upgrade
  450.       of the original HGC is that it can display customized, RAM-based
  451.       alphanumeric character sets, whereas the MDA and HGC can display only
  452.       one, predefined, ROM-based alphanumeric character set. Because
  453.       alphanumeric characters can be displayed much more rapidly than dot-
  454.       by-dot graphics characters, using the HGC+ can double or triple the
  455.       speed of some text-oriented applications.
  456.  
  457.  
  458.  IBM Enhanced Graphics Adapter
  459.  
  460.       A different response to the demand for better text and graphics
  461.       resolution is IBM's Enhanced Graphics Adapter (EGA), released in early
  462.       1985. The EGA can be configured to emulate either an MDA or a CGA;
  463.       what makes the EGA "enhanced" is that it can also do things its
  464.       predecessors cannot. Unlike the MDA, the EGA can produce dot-by-dot
  465.       graphics on a monochrome display. Furthermore, the EGA improves on the
  466.       CGA with the ability to generate 16-color alphanumeric or graphics
  467.       images with 640-by-350 resolution.
  468.  
  469.       Although the resolution and color capabilities of the EGA are not that
  470.       much greater than those of the CGA, both text and graphics appear much
  471.       sharper on the EGA than on the CGA. The availability of low-priced EGA
  472.       clones and of high-quality software applications that exploit the
  473.       adapter's capabilities have made the EGA a de facto hardware standard
  474.       in the marketplace.
  475.  
  476.  
  477.  Hercules InColor Card
  478.  
  479.       The Hercules InColor Card, introduced in April 1987, is essentially a
  480.       16-color version of the HGC+. The InColor hardware fully emulates the
  481.       HGC+, so programs that run properly on the HGC+ can run without change
  482.       on the InColor Card. The InColor Card's resolution is the same as that
  483.       of the HGC and HGC+: 720 horizontal by 348 vertical pixels. The
  484.       adapter's color capabilities equal those of the EGA. It can display 16
  485.       colors at once from a palette of 64 colors. The adapter must be used
  486.       with an EGA-compatible color display that has 350-line vertical
  487.       resolution.
  488.  
  489.         ╔═══╗     Don't confuse the InColor Card with the Hercules Color
  490.         ║ T ║     Card, an augmented CGA clone designed for use in the same
  491.         ║ I ║     computer with an HGC or HGC+.
  492.         ║ P ║
  493.         ╚═══╝
  494.  
  495.  
  496.  Multi-Color Graphics Array
  497.  
  498.       The Multi-Color Graphics Array (MCGA) is the video subsystem
  499.       integrated into the PS/2 Models 25 and 30. From a programmer's
  500.       perspective, the MCGA resembles the CGA in many ways, yet the MCGA has
  501.       much better resolution (a maximum of 640 horizontal by 480 vertical
  502.       dots) and improved color-display capabilities.
  503.  
  504.       A significant difference between the MCGA and the above video adapters
  505.       is that the MCGA generates analog RGB video signals, whereas the
  506.       others produce digital RGB signals. The difference between digital and
  507.       analog RGB is something like the difference between an on-off wall
  508.       switch and a dimmer switch. With digital RGB signals, the video
  509.       display must recognize only whether the signal for a particular color
  510.       (red, green, or blue) is on or off. On the other hand, a video display
  511.       that uses analog RGB signals translates the voltage of each signal
  512.       into a wide range of corresponding color intensities. Only an analog
  513.       video display can be used with the MCGA.
  514.  
  515.         ╔═══╗     Some video monitors can be configured for either analog or
  516.         ║ T ║     digital video signals. If you use the right cable, these
  517.         ║ I ║     monitors can be connected to an MCGA if they are
  518.         ║ P ║     configured for analog video.
  519.         ╚═══╝
  520.  
  521.       The justification for using analog video is that it can display a
  522.       wider range of colors. The MCGA has a video Digital to Analog
  523.       Converter (DAC) that enables the subsystem to display as many as 256
  524.       different colors at once from a palette of 262,144 (256 K or 2^18)
  525.       colors. In addition to an analog color display, IBM supplies an analog
  526.       monochrome display for use with the MCGA. With a monochrome monitor,
  527.       the MCGA can display as many as 64 shades of gray.
  528.  
  529.  
  530.  Video Graphics Array
  531.  
  532.       The term Video Graphics Array (VGA) refers specifically to part of the
  533.       circuitry of the video subsystem in PS/2 Models 50, 60, and 80. The
  534.       VGA is actually a single chip that integrates the same set of
  535.       functions performed by several chips on the EGA. Nevertheless, people
  536.       generally use the abbreviation VGA to describe the entire video
  537.       subsystem.
  538.  
  539.       The VGA's programming interface is similar to the EGA's, so many
  540.       programs written for the EGA will run unchanged on the VGA. The VGA is
  541.       capable of somewhat higher display resolution (as much as 720-by-400
  542.       in text modes, or 640-by-480 in graphics modes). Like the MCGA,
  543.       however, the VGA contains a video DAC that can generate 256 colors at
  544.       a time from a possible 262,144. Because the VGA generates the same
  545.       analog RGB signals as the MCGA, it must be used with the same analog
  546.       monochrome or color monitors.
  547.  
  548.  
  549.  Introduction to the ROM BIOS Interface
  550.  
  551.  
  552.       A set of BIOS (Basic Input/Output System) routines in ROM is built
  553.       into every IBM PC and PS/2. The ROM BIOS routines provide an interface
  554.       to standard hardware features, including the time-of-day clock, the
  555.       keyboard, floppy and hard disks, and of course the video subsystem.
  556.       The video BIOS routines comprise a set of simple tools for performing
  557.       basic video programming tasks such as writing strings of characters to
  558.       the screen, erasing the screen, changing colors, and so on.
  559.  
  560.       Although the ROM BIOS video routines are sometimes slow and relatively
  561.       unsophisticated, programs that use them are portable among different
  562.       video subsystems in IBM PCs and PS/2s. Furthermore, most manufacturers
  563.       of IBM PC clones have duplicated the functions of IBM's BIOS in their
  564.       machines. Thus, a program that uses BIOS routines to access the video
  565.       hardware is likely to be more portable than one that does not.
  566.  
  567.  
  568.  Interrupt 10H
  569.  
  570.       The BIOS routines are written in assembly language, so accessing them
  571.       is easiest when you program in assembly language. All BIOS video
  572.       routines are accessed by executing 80x86 software interrupt 10H. (The
  573.       term 80x86 refers to the microprocessors in the Intel 8086 family:
  574.       8086, 8088, 80286, and 80386.) For this reason, the ROM BIOS video
  575.       interface is widely known as the INT 10H interface. The ROM BIOS
  576.       supports a number of video input/output functions, each accessed by
  577.       executing interrupt 10H. The functions are numbered; before executing
  578.       interrupt 10H, you place the number of the desired function in 80x86
  579.       register AH.
  580.  
  581.       At the time the interrupt is executed, the remaining 80x86 registers
  582.       usually contain parameters to be passed to the BIOS routines. If the
  583.       INT 10H function returns data to your program, it does so by leaving
  584.       the data in one or more of the 80x86 registers. This register-based
  585.       parameter-passing protocol is intended for use in assembly-language
  586.       programs.
  587.  
  588.       To see how the INT 10H interface is typically used, examine the
  589.       assembly-language routine SetVmode() in Listing 1-1. This routine can
  590.       be linked with a program written in Microsoft C. (The underscore
  591.       preceding the procedure name, the near keyword in the PROC
  592.       declaration, and the use of the stack to pass parameters all follow
  593.       Microsoft C conventions.) The heart of the routine is its call to the
  594.       ROM BIOS to configure the video hardware for a particular video mode.
  595.       (The details of this operation are discussed in Chapter 2 and in
  596.       Appendix A.)
  597.  
  598.  
  599.  ───────────────────────────────────────────────────────────────────────────
  600.  
  601.       Listing 1-1.  SetVmode().
  602.  
  603.  ───────────────────────────────────────────────────────────────────────────
  604.  
  605.  
  606.       The actual call to the video BIOS is simple. First, the desired
  607.       function number is placed into register AH (XOR AH,AH). Then, after
  608.       preserving the contents of register BP on the stack (PUSH BP), the
  609.       routine invokes the ROM BIOS function by executing interrupt 10H
  610.       (INT 10H).
  611.  
  612.       In Listing 1-2, a complementary routine called GetVmode() interrogates
  613.       the BIOS for the number of the current video mode. The routine obtains
  614.       this number by executing interrupt 10H function 0FH. The ROM BIOS
  615.       function leaves the mode number in register AL. GetVmode() then
  616.       returns the number to the calling program.
  617.  
  618.  
  619.  ───────────────────────────────────────────────────────────────────────────
  620.  
  621.       Listing 1-2.  GetVmode().
  622.  
  623.  ───────────────────────────────────────────────────────────────────────────
  624.  
  625.  
  626.  Video Display Data Area
  627.  
  628.       The code that precedes the actual call to the ROM BIOS in Listing 1-1
  629.       modifies one of several global variables that reflect the status of
  630.       the PC's video subsystem. These variables are updated and referenced
  631.       by all ROM BIOS video routines. They are collected in a block of RAM
  632.       called, in IBM's technical documentation, the Video Display Data Area
  633.       (or Video Control Data Area). The Video Display Data Area consists of
  634.       two blocks of RAM. The first block is found between memory locations
  635.       0040:0049 and 0040:0066, the second between 0040:0084 and 0040:008A.
  636.  
  637.       Some video BIOS routines also reference a 2-bit field in a global
  638.       variable at 0040:0010 (called EQUIP_FLAG in IBM's technical
  639.       documentation). Bits 4 and 5 of this variable indicate a default video
  640.       mode to be used when the computer is first booted. The code in
  641.       SetVmode() updates this bit field to conform with the video mode being
  642.       selected. For example, if a Monochrome Display Adapter (MDA) is
  643.       required for the desired video mode, the bit field in EQUIP_FLAG is
  644.       updated accordingly. (Again, details on ROM BIOS video modes are found
  645.       in Chapter 2 and in Appendix A.)
  646.  
  647.         ╔═══╗     Throughout this book are references to the INT 10H
  648.         ║ T ║     interface, the BIOS's Video Display Data Area, and the
  649.         ║ I ║     symbolic names of specific locations in the Video Display
  650.         ║ P ║     Data Area that are of particular interest. If you aren't
  651.         ╚═══╝     already familiar with the available INT 10H functions and
  652.                   the contents of the Video Display Data Area, a perusal of
  653.                   Appendix A might be very helpful.
  654.  
  655.  
  656.  Accessing the Video BIOS from a High-Level Language
  657.  
  658.       You can make ROM BIOS routines accessible in high-level language
  659.       programs with an assembly-language routine such as SetVmode() or
  660.       GetVmode(). Listings 1-3 and 1-4 are short C programs that can be
  661.       executed as MS-DOS commands. The program in Listing 1-3 calls
  662.       SetVmode() to select a video mode. This program may be executed
  663.       interactively or from a batch file. The program in Listing 1-4 calls
  664.       GetVmode() and returns the video mode number in a way that can be used
  665.       in a batch file (that is, with IF ERRORLEVEL == commands).
  666.  
  667.  
  668.  ───────────────────────────────────────────────────────────────────────────
  669.  
  670.       Listing 1-3.  A C program based on SetVmode().
  671.  
  672.  ───────────────────────────────────────────────────────────────────────────
  673.  
  674.  
  675.  ───────────────────────────────────────────────────────────────────────────
  676.  
  677.       Listing 1-4.  A C program based on GetVmode().
  678.  ───────────────────────────────────────────────────────────────────────────
  679.  
  680.  
  681.       The overall process of generating an executable file for one of these
  682.       programs consists of compiling the C code to produce an object module,
  683.       assembling the assembly-language code to produce another object
  684.       module, and linking the object modules to create the executable file.
  685.       If the C source code in Listing 1-3 is contained in a file named SM.C
  686.       and the assembly code in Listing 1-1 is saved in SETVMODE.ASM, you
  687.       can build the executable file SM.EXE as follows:
  688.  
  689.  
  690.  msc       sm;          (compile the C code)
  691.  masm      setvmode;    (assemble the subroutine)
  692.  link      sm+setvmode; (link the object modules)
  693.  
  694.  
  695.         ╔═══╗     Some high-level language compilers can generate
  696.         ║ T ║     appropriate object code for loading the 80x86 registers,
  697.         ║ I ║     executing interrupt 10H, and copying the results from the
  698.         ║ P ║     registers to the calling program. If your compiler has
  699.         ╚═══╝     this capability, you might prefer to access the INT 10H
  700.                   interface directly, instead of linking an assembly-
  701.                   language subroutine to your high-level program. For
  702.                   example, Listing 1-5 uses Microsoft C's int86() function
  703.                   to implement GetVmode().
  704.  
  705.  
  706.  ───────────────────────────────────────────────────────────────────────────
  707.  
  708.       Listing 1-5.  Microsoft C's int86() function.
  709.  
  710.  ───────────────────────────────────────────────────────────────────────────
  711.  
  712.  
  713.       Many other INT 10H functions are available in the ROM BIOS. Your
  714.       application program accesses them by loading the appropriate registers
  715.       and executing interrupt 10H. Although the INT 10H support for video
  716.       input/output admittedly is less than perfect, it is widely used in
  717.       operating-system software (including MS-DOS) as well as in countless
  718.       applications. If you want to write effective video and graphics
  719.       programs, become familiar with the capabilities and the limitations of
  720.       the INT 10H interface.
  721.  
  722.  
  723.  
  724.                          2  Programming the Hardware
  725.  
  726.  
  727.             Functional Components of IBM PC and PS/2 Video Subsystems
  728.                              Monitor ■ Video Buffer
  729.                       Color and Character Display Hardware
  730.                                  CRT Controller
  731.  
  732.                             The Display Refresh Cycle
  733.                        Horizontal Timing ■ Vertical Timing
  734.  
  735.                          Programming the CRT Controller
  736.                 MDA ■ CGA ■ Hercules Adapters ■ EGA ■ MCGA ■ VGA
  737.  
  738.                              Basic CRTC Computations
  739.                  Dot Clock ■ Horizontal Timing ■ Vertical Timing
  740.  
  741.                              The CRT Status Register
  742.  
  743.                                    Video Modes
  744.                  Resolution ■ Colors ■ Video Buffer Organization
  745.  
  746.                            Hardware Video Mode Control
  747.                             MDA ■ CGA and MCGA ■ HGC
  748.                        HGC+ and InColor Card ■ EGA and VGA
  749.                                Video BIOS Support
  750.  
  751.                         Combinations of Video Subsystems
  752.                      MDA ■ Hercules ■ CGA ■ EGA ■ MCGA ■ VGA
  753.  
  754.  
  755.  
  756.       This chapter describes IBM PC and PS/2 video hardware from a
  757.       programmer's point of view. It covers the basics: which parts of the
  758.       computer's video subsystem can be programmed, how a program interacts
  759.       with the hardware, and how calculations for changing the video display
  760.       format are performed. Many of the programming techniques in later
  761.       chapters are based on the fundamental information discussed here.
  762.  
  763.       The purpose of this chapter is to demystify the hardware programming
  764.       interface. Because most programmers rely on the video BIOS to perform
  765.       most, if not all, hardware-level programming in their applications, an
  766.       aura of mystery surrounds the way software interacts with video
  767.       hardware. Of course, after you learn about it, you may wish it had
  768.       remained a mystery--but the more you know, the more your programs will
  769.       be able to do with the video hardware.
  770.  
  771.  
  772.  Functional Components of IBM PC and PS/2 Video Subsystems
  773.  
  774.  
  775.       As you write programs that interact with IBM video hardware, it helps
  776.       to visualize the relationships among the programmable components of
  777.       IBM video subsystems (see Figure 2-1). You do not need a circuit
  778.       designer's understanding of the hardware to write a good video
  779.       interface. You do need to know where and how your program can interact
  780.       with the hardware to produce video output efficiently.
  781.  
  782.  
  783.       ┌─────────────┐                   ┌─────────────┐
  784.       │             │  Attributes       │  Attribute  │
  785.       │    Video    ├──────────────────│   decoder   │
  786.       │    Buffer   │                   │             │
  787.       │             │                   └──────┬──────┘
  788.       │             │                          │Color, intensity, etc.
  789.       └──────┬──────┘                          
  790.              │Character codes           ┌─────────────┐
  791.                                        │             │
  792.       ┌─────────────┐                   │    Video    │Video drive signals
  793.       │ Alphanumeric│                   │   signal    ├────────────────────
  794.       │  character  ├──────────────────│  generator  │(to video display)
  795.       │  generator  │                   │             │
  796.       └─────────────┘                   └─────────────┘
  797.                                               
  798.              │Horizontal & vertical timing   │  │Internal timings,
  799.              ├───────────────────────────────┘  │buffer addressing, etc.
  800.       ┌──────┴──────┐                   ┌───────┴─────┐
  801.       │     CRT     │                   │    Mode     │
  802.       │ Controller  │                   │   control   │
  803.       └─────────────┘                   └─────────────┘
  804.  
  805.       Figure 2-1.  Programmable components (video buffer, attribute
  806.       controller, and so on) of the IBM PC and PS/2 video subsystems. Some
  807.       or all of these components are under software control in each of the
  808.       video subsystems described in this book.
  809.  
  810.  
  811.  Monitor
  812.  
  813.       The most tangible part of a computer's video hardware is the monitor,
  814.       or video display. However, there's nothing you can directly program in
  815.       the monitor's hardware. It is the computer's video subsystem that
  816.       contains programmable hardware. The signals generated by the video
  817.       subsystem control what appears on the screen.
  818.  
  819.       The monitor differs from a home television receiver in that a group of
  820.       separate timing and color signals drives it. In contrast, a home TV
  821.       decodes a single "composite" signal that contains timing, color, and
  822.       audio information. Although some IBM PC video adapters can generate
  823.       such composite video output signals, as well as the direct drive
  824.       signals that computer monitors use, most people avoid using a home
  825.       television with their computers. Both text and colors appear sharper
  826.       on a computer monitor than they do on a composite television screen.
  827.  
  828.       All the video monitors discussed in this book are raster-scan devices.
  829.       The image on the screen of a monitor is made up of a group of closely
  830.       spaced horizontal lines called the raster. An electron beam scans each
  831.       successive line from left to right, starting at the upper left corner
  832.       of the display. As the beam sweeps each line, the color and brightness
  833.       of each of several hundred points (pixels) in the line are varied, and
  834.       the entire raster appears as a coherent image.
  835.  
  836.       Conceptually, you can regard the electron beam as having "color" and
  837.       "intensity," but in color video monitors the beam actually comprises
  838.       three separate electron beams. Each beam controls the display of one
  839.       of the three primary video colors (red, green, and blue) on the
  840.       screen. Each pixel on a color display is physically represented by a
  841.       small, closely spaced triad of red, green, and blue luminescent dots
  842.       or stripes of phosphor. The three electron beams are masked in such a
  843.       way that each illuminates dots of only one primary color. Thus, the
  844.       relative intensity of the beams as they sweep over each triad
  845.       determines the color and brightness of the pixels. Of course, unless
  846.       you use a magnifying glass or look closely at the display, you do not
  847.       perceive the red, green, and blue dots individually, but rather as
  848.       blended colors.
  849.  
  850.  
  851.  Video Buffer
  852.  
  853.       The video buffer is a block of RAM in the video subsystem where
  854.       displayable data is stored. This RAM lies within the address space of
  855.       the computer's CPU, so a program may read from and write to the video
  856.       buffer in the same way it accesses any other portion of RAM.
  857.  
  858.       The video subsystem's display circuitry updates, or refreshes, the
  859.       screen by continually and repeatedly reading the data in the video
  860.       buffer. Each bit or group of bits in the video buffer specifies the
  861.       color and brightness of a particular location on the screen. The
  862.       screen is refreshed between 50 and 70 times a second, depending on
  863.       which video subsystem is in use. Obviously, when a program changes the
  864.       displayed contents of the video buffer, the screen changes almost
  865.       immediately.
  866.  
  867.       The actual amount of RAM available as a video buffer varies with the
  868.       video subsystem. Most IBM video subsystems incorporate video buffers
  869.       large enough to hold more than one screen of displayable data, so only
  870.       part of the buffer is visible on the screen at any time. (Chapter 3
  871.       discusses how to make full use of available video RAM.)
  872.  
  873.  
  874.  Color and Character Display Hardware
  875.  
  876.       All IBM video subsystems incorporate hardware that reads and decodes
  877.       the data in the video buffer. For example, an alphanumeric character
  878.       generator translates ASCII codes from the video buffer into the dot
  879.       patterns that make up characters on the screen. An attribute decoder
  880.       translates other data in the video buffer into the signals that
  881.       produce colors, underlining, and so forth. Software can control these
  882.       and other specialized components of the video subsystem; later
  883.       chapters describe such programming in detail.
  884.  
  885.  
  886.  CRT Controller
  887.  
  888.       The CRT Controller (or CRTC for short) generates horizontal and
  889.       vertical timing signals. It also increments a video buffer address
  890.       counter at a rate that is synchronized with the timing signals. The
  891.       video display circuitry reads data from the video buffer using the
  892.       CRTC's address value, decodes the data, and sends the resulting color
  893.       and brightness signals to the monitor along with the CRTC's timing
  894.       signals. In this way the CRTC synchronizes the display of data from
  895.       the video buffer with the timing signals that drive the video display.
  896.  
  897.       The CRTC performs several other miscellaneous functions. Among them
  898.       are determining the size and displayed position of the hardware
  899.       cursor, selecting the portion of the video buffer to be displayed,
  900.       locating the hardware underline, and detecting light pen signals.
  901.       (Chapter 3 contains examples of CRTC programming for some of these
  902.       functions.)
  903.  
  904.       On the MDA, CGA, and Hercules cards, the CRTC is a single chip, the
  905.       Motorola 6845. On the EGA, the CRTC is a custom LSI (large-scale
  906.       integration) chip designed by IBM. On the MCGA, the CRTC is part of
  907.       its Memory Controller Gate Array. The VGA's CRTC is one component of
  908.       the single-chip Video Graphics Array. Regardless of the hardware
  909.       implementation, the CRTC can be programmed to generate a variety of
  910.       timing parameters in all these subsystems. Before delving into the
  911.       techniques of CRTC programming, however, it is worthwhile to review
  912.       how the CRTC's timing signals control the monitor's display of a
  913.       raster-scan video image.
  914.  
  915.  
  916.  The Display Refresh Cycle
  917.  
  918.  
  919.       The video image is refreshed in a cyclic manner between 50 and 70
  920.       times a second, depending on the configuration of the video subsystem.
  921.       During each refresh cycle, the electron beam sweeps across the screen
  922.       in a zigzag fashion, starting at the left side of the topmost
  923.       horizontal line in the raster (see Figure 2-2). After scanning a line
  924.       from left to right, the beam is deflected down to the start of the
  925.       next line until the entire raster is scanned. Then the beam returns to
  926.       the upper left corner of the display, and the cycle repeats.
  927.  
  928.  
  929.               ╔══════════════════════════════════════════╗
  930.               ║                                          ║
  931.               ║    Figure 2-2 is found on page 17        ║
  932.               ║    in the printed version of the book.   ║
  933.               ║                                          ║
  934.               ╚══════════════════════════════════════════╝
  935.  
  936.       Figure 2-2.  The path followed by the electron beam in a raster scan.
  937.  
  938.  
  939.  Horizontal Timing
  940.  
  941.       A number of carefully timed events occur as the beam moves across the
  942.       display. At the beginning of each line, the electron beam is turned on
  943.       in response to a Display Enable signal that the CRTC generates. As the
  944.       beam sweeps left to right across the line, the video display circuitry
  945.       uses the CRTC's address counter to read a sequence of bytes from the
  946.       video buffer. The data is decoded and used to control the color and
  947.       brightness signals sent to the monitor. As the beam sweeps across the
  948.       screen, its color and brightness vary in response to these signals.
  949.  
  950.       Near the screen's right edge, the CRTC turns off the Display Enable
  951.       signal and no further data is displayed from the video buffer. The
  952.       CRTC then generates a horizontal sync signal, which causes the monitor
  953.       to deflect the electron beam leftward and downward to the start of the
  954.       next horizontal line in the raster. Then the CRTC turns the Display
  955.       Enable signal back on to display the next line of data.
  956.  
  957.       The short period of time between the end of one line of video data and
  958.       the beginning of the next is called the horizontal blanking interval.
  959.       Because the horizontal retrace interval (the amount of time required
  960.       to deflect the beam to the start of the next line) is shorter than the
  961.       horizontal blanking interval, a certain amount of horizontal overscan
  962.       is generated on both ends of each line (see Figure 2-3).
  963.  
  964.  
  965.                  ┌──────────────────────────────────┐
  966.                  │        │ Vertical overscan        │
  967.                  │   ┌──────────────────────────┐   │
  968.                  │   │                           │   │
  969.                  │   │                           │   │
  970.                  │   │                           │   │
  971.        Horizontal│   │                           │   │ Horizontal
  972.        overscan  │─│                           │─│ overscan
  973.                  │   │                           │   │
  974.                  │   │                           │   │
  975.                  │   │                           │   │
  976.                  │   │                           │   │
  977.                  │   │                           │   │
  978.                  │   └──────────────────────────┘   │
  979.                  │        │ Vertical overscan        │
  980.                  └──────────────────────────────────┘
  981.  
  982.       Figure 2-3.  Overscan.
  983.  
  984.  
  985.       During periods of horizontal overscan, the electron beam can be left
  986.       on, displaying an overscan, or border, color. However, the primary
  987.       reason horizontal overscan is designed into a video subsystem is to
  988.       provide a margin of error in centering the raster, so that no data is
  989.       lost at the edges of the screen.
  990.  
  991.  
  992.  Vertical Timing
  993.  
  994.       Once the electron beam has scanned all horizontal lines in the raster,
  995.       the Display Enable signal is turned off. The CRTC then generates a
  996.       vertical sync signal, which tells the monitor to deflect the electron
  997.       beam from the bottom of the screen back to the upper left corner. The
  998.       vertical retrace interval (during which the beam travels from the
  999.       bottom to the top of the screen) is shorter than the vertical blanking
  1000.       interval (during which no data from the video buffer is displayed), so
  1001.       there are areas of vertical overscan at the top and bottom of the
  1002.       raster (see Figure 2-3). Like horizontal overscan, vertical overscan
  1003.       provides a border as well as a safety margin so that the raster can be
  1004.       centered on the screen.
  1005.  
  1006.  
  1007.  Programming the CRT Controller
  1008.  
  1009.  
  1010.       The CRTC programming interface is well defined and easy to use. The
  1011.       same general programming approach applies to all IBM PC and PS/2 video
  1012.       subsystems.
  1013.  
  1014.  
  1015.  MDA
  1016.  
  1017.       The Monochrome Display Adapter's CRTC, the Motorola 6845, has nineteen
  1018.       8-bit internal data registers. The contents of each register control
  1019.       various characteristics of the timing signals generated by the 6845
  1020.       (see Figure 2-4). One of these registers is an address register; its
  1021.       contents indicate which of the other 18 can be accessed. Most of the
  1022.       registers are write-only, but registers 0EH and 0FH, which control the
  1023.       position of the hardware cursor, may be read as well as written. On
  1024.       the MDA, the 6845's Address register is mapped to an I/O port at 3B4H,
  1025.       and the remaining 18 registers are all mapped to the next I/O port
  1026.       at 3B5H.
  1027.  
  1028.       To access the 6845's data registers, you first write the register
  1029.       number to the 6845's Address register (I/O port 3B4H). Then you access
  1030.       the specified data register with an I/O write or read at port 3B5H.
  1031.       For example, Listing 2-1 shows how to determine the current cursor
  1032.       location by reading the contents of registers 0EH and 0FH on the 6845.
  1033.       These two registers (Cursor Location High and Cursor Location Low)
  1034.       contain the high-order and low-order bytes of the cursor location
  1035.       relative to the start of the video buffer.
  1036.  
  1037.  
  1038. ╓┌─────────┌─────────────────────────────┌───────────────────────────────────╖
  1039.  Register  Name                          Read/Write Access
  1040.  ──────────────────────────────────────────────────────────────────────────
  1041.  00H       Horizontal Total              Write only
  1042.  01H       Horizontal Displayed          Write only
  1043.  02H       Horizontal Sync Position      Write only
  1044.  03H       Horizontal Sync Pulse Width   Write only
  1045.  04H       Vertical Total                Write only
  1046.  05H       Vertical Total Adjust         Write only
  1047.  06H       Vertical Displayed            Write only
  1048.  07H       Vertical Sync Position        Write only
  1049.  08H       Interlace Mode                Write only
  1050.  09H       Maximum Scan Line             Write only
  1051.  0AH       Cursor Start                  Write only
  1052.  Register  Name                          Read/Write Access
  1053. 0AH       Cursor Start                  Write only
  1054.  0BH       Cursor End                    Write only
  1055.  0CH       Start Address High            Write only
  1056.  0DH       Start Address Low             Write only
  1057.  0EH       Cursor Location High          Read/Write
  1058.  0FH       Cursor Location Low           Read/Write
  1059.  10H       Light Pen High                Read only
  1060.  11H       Light Pen Low                 Read only
  1061.  
  1062.       Figure 2-4.  Motorola 6845 CRTC data registers (for the MDA, CGA, and
  1063.       Hercules video adapters).
  1064.  
  1065.  
  1066.  ───────────────────────────────────────────────────────────────────────────
  1067.  
  1068.       Listing 2-1.  Reading the 6845 Cursor Location registers.
  1069.  
  1070.  ───────────────────────────────────────────────────────────────────────────
  1071.  
  1072.  
  1073.       With the MDA, there is rarely any reason to change the values in any
  1074.       of the 6845 registers except 0AH and 0BH (Cursor Start and Cursor End)
  1075.       and 0EH and 0FH (Cursor Location High and Low). Registers 00H through
  1076.       09H control the horizontal and vertical timing signals, which should
  1077.       not be changed. Registers 0CH and 0DH (Start Address High and Start
  1078.       Address Low), which indicate what part of the MDA's video buffer is
  1079.       displayed, should always be set to 0.
  1080.  
  1081.  
  1082.  CGA
  1083.  
  1084.       The Color Graphics Adapter's CRTC is a Motorola 6845, as is the MDA's.
  1085.       The same programming technique used to access the CRTC on the MDA also
  1086.       works on the CGA. On the CGA, however, the CRTC Address register is
  1087.       mapped to I/O port 3D4H and the data registers are accessed at 3D5H.
  1088.       If you write a program that can run on either an MDA or a CGA, you can
  1089.       take advantage of the fact that the video BIOS routines in both the PC
  1090.       and PS/2 families maintain the value of the CRTC's Address register
  1091.       I/O port in a variable. Many of the programming examples in this book
  1092.       reference this variable, ADDR_6845, which is located at 0040:0063 in
  1093.       the BIOS Video Display Data Area.
  1094.  
  1095.  
  1096.  Hercules Adapters
  1097.  
  1098.       Like the MDA and CGA, the Hercules Graphics Card, Graphics Card Plus,
  1099.       and InColor Card all use a Motorola 6845 as a CRTC. The CRTC registers
  1100.       are mapped at I/O ports 3B4H and 3B5H on all Hercules adapters.
  1101.       Although it is a color adapter, the InColor Card uses the MDA's I/O
  1102.       port and video buffer addresses in order to preserve compatibility
  1103.       with the MDA and with Hercules monochrome adapters.
  1104.  
  1105.         ╔═══╗     On all Hercules video adapters (as well as the EGA, MCGA,
  1106.         ║ T ║     and VGA), you can set both the address and data registers
  1107.         ║ I ║     of the CRTC with one 16-bit port write (OUT DX,AX) instead
  1108.         ║ P ║     of two 8-bit port writes  (OUT DX,AL). For example, the
  1109.         ╚═══╝     two sequences of code that follow do the same thing to the
  1110.                   CRTC.
  1111.  
  1112.  mov     dx,3B4h         ; CRTC address register
  1113.  mov     al,0Ch          ; CRTC register number
  1114.  out     dx,al           ; select this register
  1115.  inc     dx              ; DX := 3B5h (CRTC data register)
  1116.  mov     al,8            ; data
  1117.  out     dx,al           ; store data in register
  1118.  dec     dx
  1119.  
  1120.       and
  1121.  
  1122.  mov     dx,3B4h         ; CRTC address register
  1123.  mov     ax,080Ch        ; AL := reg number, AH := data
  1124.  out     dx,ax           ; store data in register
  1125.  
  1126.  
  1127.  EGA
  1128.  
  1129.       The Enhanced Graphics Adapter's CRTC is a proprietary LSI chip with a
  1130.       set of registers different from those in the 6845 (see Figure 2-5).
  1131.       The programming interface is similar to the 6845's, but the register
  1132.       assignments and formats are different enough that programs that write
  1133.       directly to CRTC registers on the MDA or CGA will probably crash on an
  1134.       EGA.
  1135.  
  1136.       The EGA's CRTC supports a wider set of control functions than does the
  1137.       6845. For example, the CRTC can cause a hardware interrupt at the
  1138.       start of a vertical blanking interval. The CRTC also supports the
  1139.       simultaneous display of two noncontiguous portions of the video
  1140.       buffer. (Chapter 12 describes these CRTC capabilities.)
  1141.  
  1142.       A curious feature of the EGA's CRTC is its Overflow register (07H).
  1143.       Because the EGA can display a raster of more than 256 lines, the CRTC
  1144.       registers that contain a number of scan lines must be 9 bits wide
  1145.       instead of 8. The high-order bit in each of these registers is stored
  1146.       in the Overflow register.
  1147.  
  1148.  
  1149. ╓┌─────────┌─────────────────────────────┌───────────────────────────────────╖
  1150.  Register  Name                          EGA Read/Write Access
  1151.  ──────────────────────────────────────────────────────────────────────────
  1152.  00H       Horizontal Total              Write only
  1153.  01H       Horizontal Display Enable End Write only
  1154.  02H       Start Horizontal Blanking     Write only
  1155.  03H       End Horizontal Blanking       Write only
  1156.  04H       Start Horizontal Retrace      Write only
  1157.  Register  Name                          EGA Read/Write Access
  1158. 04H       Start Horizontal Retrace      Write only
  1159.  05H       End Horizontal Retrace        Write only
  1160.  06H       Vertical Total                Write only
  1161.  07H       Overflow                      Write only
  1162.  08H       Preset Row Scan               Write only
  1163.  09H       Maximum Scan Line Address     Write only
  1164.  0AH       Cursor Start                  Write only
  1165.  0BH       Cursor End                    Write only
  1166.  0CH       Start Address High            Read/Write
  1167.  0DH       Start Address Low             Read/Write
  1168.  0EH       Cursor Location High          Read/Write
  1169.  0FH       Cursor Location Low           Read/Write
  1170.  10H       Vertical Retrace Start        Write only
  1171.  10H       Light Pen High                Read only
  1172.  11H       Vertical Retrace End          Write only
  1173.  11H       Light Pen Low                 Read only
  1174.  12H       Vertical Display Enable End   Write only
  1175.  13H       Offset (Logical Line Width)   Write only
  1176.  14H       Underline Location            Write only
  1177.  15H       Start Vertical Blanking       Write only
  1178.  Register  Name                          EGA Read/Write Access
  1179. 15H       Start Vertical Blanking       Write only
  1180.  16H       End Vertical Blanking         Write only
  1181.  17H       Mode Control                  Write only
  1182.  18H       Line Compare                  Write only
  1183.  
  1184.       Figure 2-5.  EGA and VGA CRT Controller data
  1185.       registers.
  1186.  
  1187.  
  1188.  MCGA
  1189.  
  1190.       In the MCGA, the functions of a CRTC are integrated into a circuit
  1191.       component called the Memory Controller Gate Array. The first 16 Memory
  1192.       Controller registers are analogous to those in the 6845 (see Figure 2-
  1193.       6). As on the CGA, all MCGA Memory Controller registers, including
  1194.       the CRTC registers, are indexed through an address register at I/O
  1195.       port 3D4H. The data registers themselves may be accessed at port
  1196.       3D5H.
  1197.  
  1198.       Several features of the MCGA's CRTC distinguish it from the CGA's
  1199.       6845. All of the Memory Controller registers can be read as well as
  1200.       written. Moreover, registers 00H through 07H may be designated read-
  1201.       only so that horizontal and vertical timing parameters are not
  1202.       inadvertently disrupted. Setting bit 7 of the Memory Controller Mode
  1203.       Control register (10H) to 1 protects registers 00H through 07H.
  1204.  
  1205.       Another feature of the MCGA CRTC is that the hardware can compute the
  1206.       horizontal timing parameters for each of the available video modes.
  1207.       When bit 3 of the Mode Control register is set to 1, and when the
  1208.       values in registers 00H through 03H represent appropriate horizontal
  1209.       timing values for 40-by-25 alphanumeric mode (video BIOS mode 0), the
  1210.       Memory Controller generates proper horizontal timing signals in all
  1211.       available video modes.
  1212.  
  1213.       If you compare the MCGA CRTC and the Motorola 6845 register by
  1214.       register, you will note several discrepancies in the interpretation of
  1215.       the values stored in some CRTC registers. In particular, the values
  1216.       expected in registers 09H, 0AH, and 0BH are specified in units of two
  1217.       scan lines on the MCGA, instead of one scan line on the 6845. Because
  1218.       the default alphanumeric character matrix on the MCGA is 16 scan lines
  1219.       high, this feature provides a certain amount of low-level
  1220.       compatibility, letting you use the same values for these registers as
  1221.       you would on a CGA.
  1222.  
  1223.  
  1224. ╓┌─────────┌──────────────────────────────────┌──────────────────────────────╖
  1225.  Register  Name                               Read/Write Access
  1226.  ──────────────────────────────────────────────────────────────────────────
  1227.  00H       Horizontal Total                   Read/Write
  1228.  01H       Horizontal Displayed               Read/Write
  1229.  02H       Start Horizontal Sync              Read/Write
  1230.  03H       Sync Pulse Width                   Read/Write
  1231.  04H       Vertical Total                     Read/Write
  1232.  05H       Vertical Total Adjust              Read/Write
  1233.  06H       Vertical Displayed                 Read/Write
  1234.  07H       Start Vertical Sync                Read/Write
  1235.  08H       (reserved)
  1236.  09H       Scan Lines per Character           Read/Write
  1237.  0AH       Cursor Start                       Read/Write
  1238.  0BH       Cursor End                         Read/Write
  1239.  0CH       Start Address High                 Read/Write
  1240.  0DH       Start Address Low                  Read/Write
  1241.  Register  Name                               Read/Write Access
  1242. 0DH       Start Address Low                  Read/Write
  1243.  0EH       Cursor Location High               Read/Write
  1244.  0FH       Cursor Location Low                Read/Write
  1245.  10H       Mode Control                       Read/Write
  1246.  11H       Interrupt Control                  Read/Write
  1247.  12H       Character Generator, Sync Polarity Read/Write
  1248.  13H       Character Generator Pointer        Read/Write
  1249.  14H       Character Generator Count          Read/Write
  1250.  20-3FH    (reserved)
  1251.  
  1252.       Figure 2-6.  MCGA Memory Controller data registers. Registers 00H
  1253.       through 0FH are comparable to those in the CGA's CRT Controller.
  1254.  
  1255.  
  1256.  VGA
  1257.  
  1258.       Functionally, the VGA's CRTC registers (see Figure 2-5) comprise a
  1259.       superset of those in the EGA's CRTC. The VGA's CRTC register set is
  1260.       addressable at the same I/O ports as the EGA's. A few more bit fields
  1261.       have been added to the register set, primarily so that the CRTC can
  1262.       handle 400-line and 480-line rasters. However, unlike the EGA's CRTC,
  1263.       the VGA's CRTC does not support the use of a light pen.
  1264.  
  1265.       More important, however, all the EGA's CRTC register specifications
  1266.       have been carried over to the VGA. Thus, programs that write to the
  1267.       EGA's CRTC registers can be run unchanged on VGA-based hardware.
  1268.  
  1269.       As on the MCGA, the VGA's CRTC data registers can all be read as well
  1270.       as written. Also, the VGA horizontal and vertical timing registers
  1271.       (CRTC registers 00H through 07H) can be write-protected by setting bit
  1272.       7 of the Vertical Retrace End register (11H) to 1.
  1273.  
  1274.         ╔═══╗     As on Hercules adapters, you can program the CRTC on the
  1275.         ║ T ║     EGA, MCGA, and VGA using a 16-bit port write (OUT DX,AX).
  1276.         ║ I ║     Moreover, you will find by experimenting that 16-bit port
  1277.         ║ P ║     writes work on many non-IBM video adapters. But stay away
  1278.         ╚═══╝     from this technique on MDAs, CGAs, and clones if
  1279.                   portability is important.
  1280.  
  1281.  
  1282.  Basic CRTC Computations
  1283.  
  1284.  
  1285.       To use the CRTC effectively, you must be able to perform the basic
  1286.       computations necessary to specify the CRTC's timings correctly. These
  1287.       computations are based on three constraints: the bandwidth of the
  1288.       video signal sent to the monitor and the monitor's horizontal and
  1289.       vertical synchronization rates.
  1290.  
  1291.  
  1292.  Dot Clock
  1293.  
  1294.       IBM PC video subsystems display pixels at a rate determined by the
  1295.       hardware. This rate is variously known as the video bandwidth, the dot
  1296.       rate, or the pixel rate; the oscillator that generates this rate is
  1297.       called the dot clock. The MDA, CGA, and Hercules adapter use only one
  1298.       dot clock; on the EGA and VGA, more than one dot clock is available
  1299.       (see Figure 2-7). The higher the dot clock frequency, the better the
  1300.       displayed pixel resolution.
  1301.  
  1302.       Given the dot rate, the CRTC must be programmed so that the horizontal
  1303.       and vertical scan frequencies sent to the video display are limited to
  1304.       frequencies the display can handle. Older displays, such as the IBM
  1305.       Monochrome Display, are designed to handle only one horizontal and one
  1306.       vertical scan rate. Newer displays, such as the NEC MultiSync, can
  1307.       synchronize with a range of horizontal and/or vertical scan rates.
  1308.  
  1309.  
  1310. ╓┌──────────────────────┌───────────────────┌─────────────────┌──────────────╖
  1311.                         Video Bandwidth     Horizontal Scan   Vertical Scan
  1312.  IBM Subsystem          (Dot Rate) in MHz   Rate in KHz       Rate in Hz
  1313.  ───────────────────────────────────────────────────────────────────────────
  1314.  MDA, HGC
  1315.  720x350 mono           16.257              18.43             50
  1316.  
  1317.  CGA
  1318.  640x200 color          14.318              15.75             60
  1319.  
  1320.  EGA
  1321.  640x350 color          16.257              21.85             60
  1322.  640x200 color          14.318              15.75             60
  1323.  720x350 mono           16.257              18.43             50
  1324.  
  1325.                         Video Bandwidth     Horizontal Scan   Vertical Scan
  1326.  IBM Subsystem          (Dot Rate) in MHz   Rate in KHz       Rate in Hz
  1327. 
  1328.  InColor
  1329.  720x350 color          19.000              21.80             60
  1330.  
  1331.  MCGA
  1332.  640x400 mono/color     25.175              31.50             70
  1333.  640x480 mono/color     25.175              31.50             60
  1334.  
  1335.  VGA
  1336.  640x400 mono/color     25.175              31.50             70
  1337.  720x400 mono/color     28.322              31.50             70
  1338.  640x480 mono/color     25.175              31.50             60
  1339.  640x350 mono/color     25.175              31.50             70
  1340.  
  1341.       Figure 2-7.  Basic timings for IBM video subsystems.
  1342.  
  1343.  
  1344.  Horizontal Timing
  1345.  
  1346.       Consider how you would calculate the typical CRTC register values
  1347.       shown in Figure 2-8 for an MDA with an IBM Monochrome Display. The
  1348.       MDA's video bandwidth (dot rate) is 16.257 MHz; that is, 16,257,000
  1349.       dots per second. The monochrome display's horizontal scan rate is
  1350.       18.432 KHz (18,432 lines per second). Dividing the dot rate by the
  1351.       horizontal scan rate gives 882 dots per line. Each character displayed
  1352.       by the MDA is 9 dots wide, so the total number of characters in each
  1353.       line is 882 / 9, or 98.
  1354.  
  1355.       This value is used to program the CRTC's Horizontal Total register.
  1356.       For the MDA's CRTC, a Motorola 6845, the value you store in the
  1357.       Horizontal Total register must be 1 less than the computed total, or
  1358.       97 (61H).
  1359.  
  1360.  
  1361. ╓┌─────────┌─────────────────────────┌───────────────┌───────────────────────╖
  1362.  Register  Name                      Parameter       Description
  1363.  ──────────────────────────────────────────────────────────────────────────
  1364.  00H       Horizontal Total          97 (61H)        (total characters per
  1365.                                                      scan line) - 1
  1366.  
  1367.  Register  Name                      Parameter       Description
  1368. 
  1369.  01H       Horizontal Displayed      80 (50H)        Characters displayed in
  1370.                                                      each scan line
  1371.  
  1372.  02H       Horizontal Sync Position  82 (52H)        Position in scan line
  1373.                                                      where horizontal
  1374.                                                      retrace starts
  1375.  
  1376.  03H       Horizontal Sync Width     15 (0FH)        Duration of horizontal
  1377.                                                      retrace interval
  1378.                                                      (character clocks)
  1379.  
  1380.  04H       Vertical Total            25 (19H)        Total character rows in
  1381.                                                      one frame
  1382.  
  1383.  05H       Vertical Total Adjust     2               Remaining scan lines in
  1384.                                                      one frame
  1385.  
  1386.  06H       Vertical Displayed        25 (19H)        Character rows dis-
  1387.                                                      played in each frame
  1388.  Register  Name                      Parameter       Description
  1389.                                                     played in each frame
  1390.  
  1391.  07H       Vertical Sync Position    25 (19H)        Position in frame where
  1392.                                                      vertical retrace
  1393.                                                      starts
  1394.  
  1395.  08H       Interlace Mode            2               Always set to 2
  1396.  
  1397.  09H       Maximum Scan Line         13 (0DH)        (height of one charac-
  1398.                                                      ter in scan lines) - 1
  1399.  
  1400.       Figure 2-8.  Typical CRTC parameters for the Monochrome Display
  1401.       Adapter.
  1402.  
  1403.  
  1404.       In terms of CRTC timings, the Horizontal Total value describes the
  1405.       amount of time, in "character clocks," required to complete one
  1406.       horizontal scan. During this period, 80 characters are actually
  1407.       displayed. (This is the value used for the Horizontal Displayed
  1408.       register.) The other 18 character clocks are spent in horizontal
  1409.       overscan and in horizontal retrace.
  1410.  
  1411.       The duration of the horizontal retrace interval is about 10 to 15
  1412.       percent of the Horizontal Total value. The exact value depends on the
  1413.       video subsystem. On the MDA, the horizontal retrace interval is set at
  1414.       15 character clocks by storing this value in the CRTC Horizontal Sync
  1415.       Width register. This leaves 3 character clocks of horizontal overscan.
  1416.       The horizontal retrace signal is programmed to start 2 character
  1417.       clocks after the rightmost displayed character by storing the value 82
  1418.       (52H) in the CRTC Horizontal Sync Position register. Thus, there are 2
  1419.       character clocks of right horizontal overscan and 1 character clock of
  1420.       left overscan.
  1421.  
  1422.         ╔═══╗     Changing the value in the Horizontal Sync Position
  1423.         ║ T ║     register changes the size of the right and left overscan
  1424.         ║ I ║     areas and thus the horizontal position of the displayed
  1425.         ║ P ║     raster. For example, to shift the displayed raster to the
  1426.         ╚═══╝     left, increase the size of the right overscan interval by
  1427.                   increasing the value in the CRTC Horizontal Sync Position
  1428.                   register.
  1429.  
  1430.  
  1431.  Vertical Timing
  1432.  
  1433.       Similar considerations apply in programming the CRTC to generate
  1434.       appropriate vertical timings. The nominal horizontal scan rate in the
  1435.       MDA's monochrome display is 18.432 KHz (18,432 lines per second) with
  1436.       a vertical scan rate of 50 Hz (50 frames per second), so the number of
  1437.       lines in one frame is 18,432 / 50, or 368. Since each character
  1438.       displayed is 14 lines high, 25 rows of characters account for 350
  1439.       lines. The MDA's CRTC always uses 16 lines for vertical retrace; this
  1440.       leaves 368 - (350 + 16), or 2 lines of vertical overscan.
  1441.  
  1442.       The CRTC programming follows these calculations. The height of each
  1443.       displayed character is specified by the value in the CRTC Maximum Scan
  1444.       Line register. Since characters are 14 scan lines high, the maximum
  1445.       scan line value is 13 (0DH). Taken together, the values for Vertical
  1446.       Total (25 character rows) and Vertical Total Adjust (2 scan lines)
  1447.       indicate the total number of scan lines in one frame. The number of
  1448.       character rows displayed (25) is indicated in the Vertical Displayed
  1449.       register. The position in the frame where vertical retrace starts (25)
  1450.       is specified by the value in the Vertical Sync Position register.
  1451.  
  1452.       The CRTCs on the MCGA, EGA, and VGA are more complex than the Motorola
  1453.       6845 CRTC on the MDA and CGA. Nevertheless, the registers that control
  1454.       horizontal and vertical timings in the newer video subsystems are
  1455.       similar in nomenclature and functionality to the 6845's registers. The
  1456.       computations for the MCGA, EGA, and VGA CRTCs are derived from the dot
  1457.       rate, the character size, and the horizontal and vertical capabilities
  1458.       of the video display, just as they are for the MDA and CGA.
  1459.  
  1460.  
  1461.  The CRT Status Register
  1462.  
  1463.  
  1464.       All IBM video subsystems have a read-only CRT Status register. This
  1465.       register is located at I/O port 3BAH on the MDA and Hercules adapters
  1466.       and at 3DAH on the CGA and MCGA; on the EGA and VGA, this register is
  1467.       at 3BAH in monochrome configurations and at 3DAH in color
  1468.       configurations. Generally, two of the eight bits in this register
  1469.       reflect the current status of the horizontal and vertical timing
  1470.       signals generated by the CRTC. These status bits can be used to
  1471.       synchronize video buffer updates with the screen refresh cycle to
  1472.       minimize interference with the displayed image. (Chapter 3 contains
  1473.       examples of this type of programming.)
  1474.  
  1475.       Unfortunately, the exact interpretation of the status bits in the CRT
  1476.       Status register varies among the different IBM video subsystems (see
  1477.       Figure 2-9). Therefore, programs should be designed to determine
  1478.       which hardware they are running on (Appendix C) before they attempt
  1479.       to use the status information in this register.
  1480.  
  1481.       Listing 2-2 shows how the status bits in the CRTC Status register are
  1482.       used to synchronize program operation with the video refresh cycle.
  1483.       This subroutine can be used on the CGA to time the horizontal blanking
  1484.       interval. The subroutine uses bit 3 of the CRT Status register, which
  1485.       indicates when the CRTC's vertical sync signal is active, to
  1486.       synchronize with the start of a refresh cycle. The loops at L01 and
  1487.       L02 show how this is done.
  1488.  
  1489.       The loops at L03 and L04 then synchronize with the Display Enable
  1490.       signal, using bit 0 of the CRT Status value. When the Display Enable
  1491.       signal goes off, the loop at L05 decrements the value in CX during the
  1492.       horizontal blanking interval, that is, while the Display Enable signal
  1493.       is off. The number of iterations counted in CX can then be used as a
  1494.       timeout value to determine when the last horizontal line in the frame
  1495.       has been scanned. (See Chapter 3.)
  1496.  
  1497.  
  1498.  ───────────────────────────────────────────────────────────────────────────
  1499.  
  1500.       Listing 2-2.  Timing the horizontal blanking interval on the CGA
  1501.  
  1502.  ───────────────────────────────────────────────────────────────────────────
  1503.  
  1504.  
  1505.  Video Modes
  1506.  
  1507.  
  1508.       Despite the timing constraints imposed by the dot clock and the rated
  1509.       horizontal and vertical scan rates of available monitors, all IBM
  1510.       video subsystems except the MDA can be programmed with a variety of
  1511.       different CRTC parameters. This makes a number of video modes
  1512.       available. Each video mode is characterized by its resolution (the
  1513.       number of characters or pixels displayed horizontally and vertically),
  1514.       by the number of different colors that can be displayed
  1515.       simultaneously, and by the format of the displayable data in the video
  1516.       buffer.
  1517.  
  1518.  
  1519.  Resolution
  1520.  
  1521.       The horizontal and vertical resolution in a video mode is a function
  1522.       of the dot rate as well as the monitor's horizontal and vertical scan
  1523.       rates. The number of pixels displayed in each frame corresponds to the
  1524.       dot rate divided by the vertical scan rate. The actual horizontal and
  1525.       vertical resolution then depends on the horizontal scan rate.
  1526.  
  1527.  
  1528. ╓┌───────────────────┌───────────┌───────────────────┌───────────────────┌───
  1529.                      Register    Bit 7               Bit 3               Bit 2
  1530.  ─────────────────────────────────────────────────────────────────────────────
  1531.  MDA                 3BA                             Video drive
  1532.  
  1533.  HGC, HGC+, InColor  3BA         0 = vertical sync   Video drive
  1534.  
  1535.                      Register    Bit 7               Bit 3               Bit 2
  1536. 
  1537.  CGA                 3DA                             1 = vertical sync   1 = l
  1538.                                                                              s
  1539.  EGA                 3BA or 3DA                      1 = vertical sync   1 = l
  1540.                                                                              s
  1541.  VGA                 3BA or 3DA                      1 = vertical sync
  1542.  
  1543.  MCGA                3DA                             1 = vertical sync1
  1544.  
  1545.       Figure 2-9.  CRTC Status register bit assignments
  1546.  
  1547.  
  1548.  Colors
  1549.  
  1550.       The number and variety of colors that can be displayed in a video mode
  1551.       depend on the design of the video subsystem's attribute decoding and
  1552.       video signal generator components. The attribute decoder uses data
  1553.       stored in the video buffer to control the color and brightness signals
  1554.       produced by the video signal generator. Establishing a particular
  1555.       video mode always involves programming a video subsystem's attribute
  1556.       decoder in addition to updating its CRTC parameters.
  1557.  
  1558.  
  1559.  Video Buffer Organization
  1560.  
  1561.       The format of the data in video RAM also characterizes a video mode.
  1562.       In all PC and PS/2 subsystems, video modes can be classified as
  1563.       alphanumeric or graphics modes, depending on the video buffer data
  1564.       format. In alphanumeric modes, the data in the video buffer is
  1565.       formatted as a sequence of ASCII code and attribute byte pairs; the
  1566.       alphanumeric character generator translates the ASCII codes into
  1567.       displayed characters while the attribute bytes specify the colors used
  1568.       to display them (see Chapter 3). In graphics modes, the video buffer
  1569.       is organized as a sequence of bit fields; the bits in each field
  1570.       designate the color of a particular pixel on the screen.
  1571.  
  1572.  
  1573.  Hardware Video Mode Control
  1574.  
  1575.  
  1576.       Establishing a video mode on an IBM PC or PS/2 video subsystem
  1577.       generally requires specific mode control programming apart from
  1578.       specifying CRTC parameters. For example, the alphanumeric character
  1579.       generator must be enabled in alphanumeric modes and disabled in
  1580.       graphics modes. Also, the subsystem's internal character clock, which
  1581.       determines the number of pixels generated for each alphanumeric
  1582.       character code read from the video buffer, may run at different rates
  1583.       in different video modes. These and other internal functions are
  1584.       controlled by loading one or more specialized mode control registers
  1585.       with values appropriate for each video mode.
  1586.  
  1587.  
  1588.  MDA
  1589.  
  1590.       The MDA's Mode Control register is a write-only register mapped to
  1591.       port 3B8H (see Figure 2-10). Only three of the eight bits in this
  1592.       register have meaning. Bit 0 is set to 1 at powerup and must always
  1593.       remain set to 1. Bit 3, when set to 1, enables video refresh; clearing
  1594.       this bit blanks the screen. Bit 5 is the Enable Blink bit; it controls
  1595.       whether characters can blink. On the MDA, most programs leave bit 3
  1596.       set at all times. Chapter 3 explains how to use bit 5 (the Enable
  1597.       Blink bit).
  1598.  
  1599.  
  1600. ╓┌───────────┌───────────────────────────────────────────────────────────────╖
  1601.  Bit         Settings
  1602.  ──────────────────────────────────────────────────────────────────────────
  1603.  0           1 = adapter enabled (should always = 1)
  1604.  1           (unused, should always = 0)
  1605.  2           (unused, should always = 0)
  1606.  3           1 = video enabled
  1607.              0 = video disabled (screen blank)
  1608.  4           (unused, should always = 0)
  1609.  5           1 = blinking attribute enabled
  1610.              0 = blinking attribute disabled
  1611.  6           (unused, should always = 0)
  1612.  7           (unused, should always = 0)
  1613.  
  1614.       Figure 2-10.  Bit settings for the MDA Mode Control register (3B8H).
  1615.  
  1616.  
  1617.  CGA and MCGA
  1618.  
  1619.       The Mode Control register on the CGA and MCGA is found at 3D8H (see
  1620.       Figure 2-11a). The five low-order bits control internal timings
  1621.       appropriate for the video modes they select, while bit 5 is an Enable
  1622.       Blink bit just as it is on the MDA. The useful bit patterns for the
  1623.       CGA's Mode Control register are listed in Figure 2-11b. These values
  1624.       correspond to the available BIOS video modes on the CGA.
  1625.  
  1626.       The Mode Control registers on the CGA and the MCGA have two
  1627.       differences. One is that the MCGA Mode Control register may be read as
  1628.       well as written; the CGA register is write-only. The other difference
  1629.       relates to the function of bit 2. On the CGA, setting bit 2 to 1
  1630.       disables the color burst component of the composite video output
  1631.       signal. This can improve the quality of the display if you are using a
  1632.       composite green or amber monitor with a CGA. On the MCGA, which does
  1633.       not support a composite monitor, the function of bit 2 of the Mode
  1634.       Control register is to select between two sources for the foreground
  1635.       color in 2-color graphics modes.
  1636.  
  1637.  
  1638. ╓┌───────────┌───────────────────────────────────────────────────────────────╖
  1639.  Bit         Settings
  1640.  Bit         Settings
  1641.  ──────────────────────────────────────────────────────────────────────────
  1642.  0           1 = 80-character alphanumeric modes
  1643.              0 = 40-character alphanumeric modes
  1644.  1           1 = 320-wide graphics mode
  1645.              0 = (all other modes)
  1646.  2           1 = color burst disabled (CGA only)
  1647.              1 = foreground color from video DAC register 7 (MCGA only)
  1648.              0 = color burst enabled (CGA only)
  1649.              0 = foreground color from the video DAC register specified
  1650.                  in bits 0-3 of the Palette register (3D9H) (MCGA only)
  1651.  3           1 = video enabled
  1652.              0 = video disabled (screen blank)
  1653.  4           1 = 640-wide graphics modes
  1654.              0 = (all other modes)
  1655.  5           1 = blinking attribute enabled
  1656.              0 = blinking attribute disabled
  1657.  6           (unused, should always = 0)
  1658.  7           (unused, should always = 0)
  1659.  
  1660.       Figure 2-11a.  Bit settings for the CGA and MCGA Mode Control register
  1661.       (3D8H).
  1662.  
  1663.  
  1664. ╓┌───────────┌────────────────────────────┌──────────────────────────────────╖
  1665.  BIOS Mode                                Value for Mode
  1666.  Number      Description                  Control Register
  1667.  ──────────────────────────────────────────────────────────────────────────
  1668.  0           40x25 alpha                  00101100b (2CH)
  1669.                (color burst disabled)
  1670.  1           40x25 alpha                  00101000b (28H)
  1671.  2           80x25 alpha                  00101101b (2DH)
  1672.                (color burst disabled)
  1673.  3           80x25 alpha                  00101001b (29H)
  1674.  4           320x200 graphics             00101010b (2AH)
  1675.  5           320x200 graphics             00101110b (2EH)
  1676.                (color burst disabled)
  1677.  6           640x200 graphics             00011100b (1CH)
  1678.  7           80x25 alpha                  00101001b (29H)
  1679.                (MDA only)
  1680.  11H         640x480 graphics             00011000b (18H)
  1681.                (MCGA only)
  1682.  BIOS Mode                                Value for Mode
  1683.  Number      Description                  Control Register
  1684.               (MCGA only)
  1685.  
  1686.       Figure 2-11b.  MDA, CGA, and MCGA Mode Control register options.
  1687.  
  1688.  
  1689.       The MCGA has two additional mode control registers, which are not
  1690.       implemented on the CGA. The MCGA Memory Controller Mode Control
  1691.       register (10H) at port 3D4H/3D5H selects 640-by-480 2-color and 320-
  1692.       by-200 256-color graphics modes (see Figure 2-12). An Extended Mode
  1693.       Control register is mapped to I/O port 3DDH. This register is used
  1694.       only during machine coldstart; it has no practical use in applications
  1695.       programs.
  1696.  
  1697.  
  1698. ╓┌───────────┌───────────────────────────────────────────────────────────────╖
  1699.  Bit         Settings
  1700.  ──────────────────────────────────────────────────────────────────────────
  1701.  0           1 = select 320x200 256-color mode
  1702.              0 = (all other modes)
  1703.  Bit         Settings
  1704.             0 = (all other modes)
  1705.  1           1 = select 640x480 2-color mode
  1706.              0 = (all other modes)
  1707.  2           (reserved)
  1708.  3           1 = horizontal timing parameters computed for video mode
  1709.              0 = horizontal timing parameters as specified in registers
  1710.                  00-03H
  1711.  4           1 = enable dot clock (should always be 1)
  1712.  5           (reserved)
  1713.  6           Inverse of bit 8 of Vertical Displayed register (06H)
  1714.  7           1 = write-protect registers 00-07H
  1715.              0 = allow updating of registers 00-07H
  1716.  
  1717.       Figure 2-12.  Bit settings for the MCGA Memory Controller Mode Control
  1718.       register.
  1719.  
  1720.  
  1721.  HGC
  1722.  
  1723.       The Hercules Graphics Card has two control registers whose contents
  1724.       affect the video mode configuration. The Mode Control register at 3B8H
  1725.       is functionally compatible with the MDA's Mode Control register, but
  1726.       it maps additional mode configuration functions to bits 1 and 7 (see
  1727.       Figure 2-13). Bit 1, when set to 1, establishes internal timings for
  1728.       a 720-by-348 graphics mode. Setting bit 7 to 1 while the adapter is in
  1729.       graphics mode displays the second half of the adapter's 64 KB video
  1730.       buffer at B800:0000. These bits have no function, however, unless the
  1731.       appropriate bits in the adapter's Configuration Switch register are
  1732.       set properly.
  1733.  
  1734.       The Configuration Switch register (3BFH) determines the function of
  1735.       the Mode Control register at 3B8H (see Figure 2-14). When bit 0 of
  1736.       the Configuration Switch register is 0, the HGC cannot be placed in
  1737.       its graphics mode, so bit 1 of the Mode Control register must also be
  1738.       0. Bit 1 of the Configuration Switch register controls video buffer
  1739.       addressing when the adapter is used in combination with a CGA or
  1740.       compatible (see below).
  1741.  
  1742.  
  1743. ╓┌───────────┌───────────────────────────────────────────────────────────────╖
  1744.  Bit         Settings
  1745.  Bit         Settings
  1746.  ──────────────────────────────────────────────────────────────────────────
  1747.  0           (unused)
  1748.  1           1 = 720x348 graphics mode
  1749.              0 = 80x25 alphanumeric mode
  1750.  2           (unused, should always = 0)
  1751.  3           1 = video enabled
  1752.              0 = video disabled (screen blank)
  1753.  4           (unused, should always = 0)
  1754.  5           1 = blinking attribute enabled
  1755.              0 = blinking attribute disabled
  1756.  6           (unused, should always = 0)
  1757.  7           1 = graphics mode buffer displayed from B800:0000 (video
  1758.                  page 1)
  1759.              0 = graphics mode buffer displayed from B000:0000 (video
  1760.                  page 0)
  1761.  
  1762.       Figure 2-13.  Bit settings for the Hercules Mode Control register
  1763.       (3B8H). This register is the same on the HGC, HGC+, and InColor Card.
  1764.  
  1765.  
  1766. ╓┌───────────┌───────────────────────────────────────────────────────────────╖
  1767.  Bit         Settings
  1768.  ──────────────────────────────────────────────────────────────────────────
  1769.  0           1 = allows graphics mode
  1770.              0 = prevents graphics mode
  1771.  1           1 = enables upper 32 KB of graphics mode video buffer at
  1772.                  B800:0000
  1773.              0 = disables upper 32 KB of graphics mode buffer
  1774.  2-7         (unused)
  1775.  
  1776.       Figure 2-14.  Bit settings for the Hercules Configuration Switch
  1777.       register (3BFH). This register is the same on the HGC, HGC+, and
  1778.       InColor Card.
  1779.  
  1780.  
  1781.  HGC+ and InColor Card
  1782.  
  1783.       The HGC+ and InColor Card implement an extended mode control register
  1784.       (called the xMode register) in addition to the Mode Control and
  1785.       Configuration Switch registers found on the HGC. The xMode register is
  1786.       a write-only register addressable as register 14H at port 3B4H/3B5H.
  1787.       (The register is addressed exactly as if it were a CRTC register.) The
  1788.       xMode register controls the alphanumeric character generator; Chapter
  1789.       10 explains this in detail.
  1790.  
  1791.  
  1792.  EGA and VGA
  1793.  
  1794.       When you establish a video mode on the EGA and the VGA, you can
  1795.       control the internal timing and addressing of several different
  1796.       components of the video subsystem. These include the Sequencer, the
  1797.       Graphics Controller, and the Attribute Controller, each of which has
  1798.       several control registers. There is also a Miscellaneous Output
  1799.       register, which controls I/O port and video buffer addressing and
  1800.       selects the dot clock frequency.
  1801.  
  1802.         ╔═══╗     All Sequencer, Graphics Controller, and Attribute
  1803.         ║ T ║     Controller registers on the EGA are write-only registers,
  1804.         ║ I ║     but on the VGA they can be read as well as written.
  1805.         ║ P ║
  1806.         ╚═══╝
  1807.  
  1808.  
  1809.       Sequencer
  1810.       The Sequencer generates internal timings for video RAM addressing. It
  1811.       has five programmable data registers (see Figure 2-15) mapped to
  1812.       ports 3C4H and 3C5H in a manner analogous to CRTC register mapping.
  1813.       The Sequencer's Address register is located at 3C4H; its five data
  1814.       registers are selected by storing an index value between 0 and 4 in
  1815.       the Address register and then accessing the corresponding data
  1816.       register at 3C5H.
  1817.  
  1818.  
  1819. ╓┌───────────┌───────────────────────────────────────────────────────────────╖
  1820.  Register    Name
  1821.  ──────────────────────────────────────────────────────────────────────────
  1822.  0           Reset
  1823.  1           Clocking Mode
  1824.  2           Map Mask
  1825.  3           Character Map Select
  1826.  4           Memory Mode
  1827.  
  1828.       Figure 2-15.  EGA and VGA Sequencer registers.
  1829.  
  1830.  
  1831.       Graphics Controller
  1832.       The Graphics Controller mediates data flow between the video buffer
  1833.       and the CPU, as well as from the video buffer to the Attribute
  1834.       Controller. The Graphics Controller has nine data registers, plus an
  1835.       Address register (see Figure 2-16). The Address register maps to
  1836.       port 3CEH, and the data registers map to port 3CFH.
  1837.  
  1838.  
  1839. ╓┌───────────┌───────────────────────────────────────────────────────────────╖
  1840.  Register    Name
  1841.  ──────────────────────────────────────────────────────────────────────────
  1842.  0           Set/Reset
  1843.  1           Enable Set/Reset
  1844.  2           Color Compare
  1845.  3           Data Rotate/Function Select
  1846.  4           Read Map Select
  1847.  5           Graphics Mode
  1848.  6           Miscellaneous
  1849.  7           Color Don't Care
  1850.  Register    Name
  1851. 7           Color Don't Care
  1852.  8           Bit Mask
  1853.  
  1854.       Figure 2-16.  EGA and VGA Graphics Controller registers.
  1855.  
  1856.  
  1857.       Attribute Controller
  1858.       The Attribute Controller supports a 16-color palette on the EGA and
  1859.       VGA. It also controls the color displayed during overscan intervals.
  1860.       The Attribute Controller's Address register and 21 data registers all
  1861.       map to I/O port 3C0H (see Figure 2-17). A value written to port 3C0H
  1862.       will be stored in either the Address register or a data register,
  1863.       depending on the state of a flip-flop internal to the Attribute
  1864.       Controller.
  1865.  
  1866.  
  1867. ╓┌───────────┌───────────────────────────────────────────────────────────────╖
  1868.  Register(s) Function
  1869.  ───────────────────────────────────────────────────────────────────────────
  1870.  0-0FH       Palette
  1871.  Register(s) Function
  1872. 0-0FH       Palette
  1873.  10H         Attribute Mode Control
  1874.  11H         Overscan Color
  1875.  12H         Color Plane Enable
  1876.  13H         Horizontal Pixel Panning
  1877.  14H         Color Select (VGA only)
  1878.  
  1879.       Figure 2-17.  EGA and VGA Attribute Controller registers.
  1880.  
  1881.  
  1882.       To set the flip-flop, perform an I/O read (IN AL,DX) of the CRT Status
  1883.       register (port 3BAH in monochrome modes, 3DAH in color modes). Listing
  1884.       2-3 illustrates how this is done in updating an Attribute Controller
  1885.       register. On the VGA, Attribute Controller data registers may be read
  1886.       as well as written. Do this by writing the register number to port
  1887.       3C0H and then reading the value from port 3C1H.
  1888.  
  1889.  
  1890.  ───────────────────────────────────────────────────────────────────────────
  1891.  
  1892.       Listing 2-3.  Updating the EGA or VGA Attribute Controller
  1893.  
  1894.  ───────────────────────────────────────────────────────────────────────────
  1895.  
  1896.  
  1897.         ╔═══╗     You can use 16-bit port writes (OUT DX,AX) to store data
  1898.         ║ T ║     in EGA and VGA Sequencer and Graphics Controller
  1899.         ║ I ║     registers. On the EGA, you can use the same technique to
  1900.         ║ P ║     program the Attribute Controller, which recognizes I/O
  1901.         ╚═══╝     port writes at 3C1H as well as 3C0H. However, the VGA
  1902.                   Attribute Controller does not emulate the EGA in this
  1903.                   regard, so this technique should be used carefully when
  1904.                   VGA compatibility is important.
  1905.  
  1906.  
  1907.  Video BIOS Support
  1908.  
  1909.       The video BIOS supports a number of different video modes on IBM PC
  1910.       and PS/2 video subsystems (see Figure 2-18). The video BIOS routines,
  1911.       which can be called with INT 10H, let you establish a video mode
  1912.       simply by specifying its number.
  1913.  
  1914.       Not all of the BIOS video modes are available on all IBM PC video
  1915.       subsystems. Furthermore, the video BIOS does not support video mode
  1916.       configurations on non-IBM hardware unless it exactly emulates the
  1917.       corresponding IBM hardware.
  1918.  
  1919.       For example, all Hercules video adapters emulate IBM's MDA exactly.
  1920.       Thus, the video BIOS can be used to select the monochrome alphanumeric
  1921.       mode (BIOS mode 7) on all Hercules products. However, the Hercules
  1922.       hardware also supports a 720-by-348 graphics mode which is not
  1923.       recognized by IBM's video BIOS. Consequently, to set up the Hercules
  1924.       graphics mode, a program must configure the hardware directly (see
  1925.       Listing 2-4.)
  1926.  
  1927.  
  1928. ╓┌──────┌─────────────────────────────────┌────────┌─────────┌─────────┌────┌
  1929.  Mode
  1930.  Number                                            Mode      Buffer
  1931.  (hex)  Resolution                        Colors   Type      Segment   MDA  CG
  1932.  ─────────────────────────────────────────────────────────────────────────────
  1933.  0      40x25 chars (320x200 pixels)1,2  16       Alpha     B800           x
  1934.  Mode
  1935.  Number                                            Mode      Buffer
  1936.  (hex)  Resolution                        Colors   Type      Segment   MDA  CG
  1937. 0      40x25 chars (320x200 pixels)1,2  16       Alpha     B800           x
  1938.  0      40x25 chars (320x350 pixels)2    16       Alpha     B800
  1939.  0      40x25 chars (320x400 pixels)      16       Alpha     B800
  1940.  0      40x25 chars (360x400 pixels)2    16       Alpha     B800
  1941.  1      40x25 chars (320x200 pixels)2    16       Alpha     B800           x
  1942.  1      40x25 chars (320x350 pixels)2    16       Alpha     B800
  1943.  1      40x25 chars (320x400 pixels)      16       Alpha     B800
  1944.  1      40x25 chars (360x400 pixels)2    16       Alpha     B800
  1945.  2      80x25 chars (640x200 pixels)1,2  16       Alpha     B800           x
  1946.  2      80x25 chars (640x350 pixels)2    16       Alpha     B800
  1947.  2      80x25 chars (640x400 pixels)      16       Alpha     B800
  1948.  2      80x25 chars (720x400 pixels)2    16       Alpha     B800
  1949.  3      80x25 chars (640x200 pixels)2    16       Alpha     B800           x
  1950.  3      80x25 chars (640x350 pixels)2    16       Alpha     B800
  1951.  3      80x25 chars (640x400 pixels)      16       Alpha     B800
  1952.  3      80x25 chars (720x400 pixels)2    16       Alpha     B800
  1953.  4      320x200 pixels                     4       Graphics  B800           x
  1954.  5      320x200 pixels3                   4       Graphics  B800           x
  1955.  Mode
  1956.  Number                                            Mode      Buffer
  1957.  (hex)  Resolution                        Colors   Type      Segment   MDA  CG
  1958. 5      320x200 pixels3                   4       Graphics  B800           x
  1959.  6      640x200 pixels                     2       Graphics  B800           x
  1960.  7      80x25 chars (720x350 pixels)2     2       Alpha     B000      x
  1961.  7      80x25 chars (720x400 pixels)2     2       Alpha     B000
  1962.  8      (PCjr only)
  1963.  9      (PCjr only)
  1964.  0A     (PCjr only)
  1965.  0B     (used by EGA video BIOS)
  1966.  0C     (used by EGA video BIOS)
  1967.  0D     320x200 pixels                    16       Graphics  A000           x
  1968.  0E     640x200 pixels                    16       Graphics  A000           x
  1969.  0F     640x350 pixels                     2       Graphics  A000           x
  1970.  10     640x350 pixels4                   4       Graphics  A000           x
  1971.  10     640x350 pixels                    16       Graphics  A000           x
  1972.  11     640x480 pixels                     2       Graphics  A000
  1973.  12     640x480 pixels                    16       Graphics  A000
  1974.  13     320x200 pixels                   256       Graphics  A000
  1975.  
  1976.       Figure 2-18.  ROM BIOS video modes
  1977.  
  1978.  
  1979.  ───────────────────────────────────────────────────────────────────────────
  1980.  
  1981.       Listing 2-4.  Configuring a Hercules adapter for 720-by-348 graphics
  1982.       mode
  1983.  
  1984.  ───────────────────────────────────────────────────────────────────────────
  1985.  
  1986.  
  1987.  Combinations of Video Subsystems
  1988.  
  1989.  
  1990.       IBM designed the original MDA and CGA such that both adapters can be
  1991.       used in the same PC. This is possible because the CRTC registers and
  1992.       other control and status registers are assigned to a different range
  1993.       of I/O ports on the MDA than on the CGA. The MDA's port addresses
  1994.       range from 3B0H through 3BFH, while the CGA's range from 3D0H through
  1995.       3DFH. Also, the video buffers on the MDA and the CGA occupy different
  1996.       portions of the 80x86 address space: The MDA's 4 KB video buffer is at
  1997.       B000:0000, while the CGA's 16 KB buffer starts at B800:0000.
  1998.  
  1999.       This separation was carried forward in the design of the EGA. The
  2000.       EGA's I/O port and video buffer addressing are programmable. When the
  2001.       EGA is attached to a monochrome monitor, the MDA-compatible addresses
  2002.       are used. When the EGA is used with a color monitor, the CGA-
  2003.       compatible addresses are used. Thus, an EGA can coexist with either an
  2004.       MDA or a CGA.
  2005.  
  2006.       Figure 2-19 shows which PC and PS/2 video subsystems can coexist in
  2007.       the same computer. The table reflects the dichotomy between MDA-
  2008.       compatible and CGA-compatible I/O port and video buffer addressing. As
  2009.       a rule of thumb, you can usually combine one MDA-compatible adapter
  2010.       and one CGA-compatible adapter in the same system.
  2011.  
  2012.       NOTE: The Hercules InColor Card should be regarded as an MDA-
  2013.       compatible adapter, even though it is ostensibly a color card. In
  2014.       fact, if you use the InColor Card in a PS/2 Model 30 with a monochrome
  2015.       monitor attached to the Model 30's MCGA, you end up with the strange
  2016.       combination of an MDA-compatible color subsystem and a CGA-compatible
  2017.       monochrome subsystem in the same computer.
  2018.  
  2019.       The BIOS video mode routines generally support dual-display
  2020.       configurations. The video BIOS routines use bits 4 and 5 of the
  2021.       variable EQUIP_FLAG at 0040:0010 in the BIOS video data area to choose
  2022.       between two video subsystems. If there are addressing conflicts
  2023.       between two subsystems, the BIOS in the MCGA and VGA provides a
  2024.       "display switch" interface that lets you independently disable and
  2025.       enable each subsystem (see Appendix A).
  2026.  
  2027.  
  2028. ╓┌─────────────┌─────┌─────┌─────┌──────┌─────────────┌─────┌──────┌─────────╖
  2029.                MDA   CGA   EGA   MCGA   VGA Adapter   HGC   HGC+   InColor
  2030.  ──────────────────────────────────────────────────────────────────────────
  2031.  MDA                 x     x     x      x
  2032.  CGA           x           x                          x     x      x
  2033.  EGA           x     x                                x     x      x
  2034.  MCGA          x                        x             x     x      x
  2035.  VGA Adapter   x                 x                    x     x      x
  2036.  HGC                 x     x     x      x
  2037.  HGC+                x     x     x      x
  2038.  InColor             x     x     x      x
  2039.                MDA   CGA   EGA   MCGA   VGA Adapter   HGC   HGC+   InColor
  2040. InColor             x     x     x      x
  2041.  
  2042.       Figure 2-19.  Allowable combinations of IBM PC and PS/2 video
  2043.       subsystems.
  2044.  
  2045.  
  2046.       With some combinations of video adapters, the address space the two
  2047.       subsystems' video buffers occupy may overlap even if their I/O port
  2048.       address assignments do not. In this situation you must selectively
  2049.       exclude part or all of one subsystem's video buffer from the CPU
  2050.       memory map so that the CPU can access the other subsystem's buffer
  2051.       without addressing conflicts. The technique for doing this varies with
  2052.       the hardware.
  2053.  
  2054.  
  2055.  MDA
  2056.  
  2057.       The MDA's video buffer is mapped to the addresses between B000:0000
  2058.       and B000:FFFF. The same buffer is also mapped to the 4 KB blocks of
  2059.       RAM starting at segments B100H, B200H, and so on through B700H,
  2060.       although there is no real reason for software to use these alternate
  2061.       address maps. The MDA's video buffer address mapping cannot be
  2062.       disabled.
  2063.  
  2064.  
  2065.  Hercules
  2066.  
  2067.       On the HGC, the HGC+, and the InColor Card, the video buffer occupies
  2068.       the 64 KB of RAM starting at B000:0000. The second 32 KB of the video
  2069.       buffer overlaps the address space of a CGA's video buffer (starting at
  2070.       B800:0000). For this reason these Hercules adapters are designed so
  2071.       that the second 32 KB can be selectively excluded from the CPU memory
  2072.       map. The extent of the video buffer address space depends upon the
  2073.       value you store in the Configuration Switch register (3BFH). When bit
  2074.       1 of this register is 0 (the power-on default), video RAM occupies
  2075.       addresses from B000:0000 through B000:7FFF, which excludes the second
  2076.       32 KB portion from the CPU memory map and allows the card to be used
  2077.       with a CGA. To make the second half of the video buffer addressable,
  2078.       set bit 1 to 1.
  2079.  
  2080.  
  2081.  CGA
  2082.  
  2083.       The CGA's video buffer maps to the addresses between B800:0000 and
  2084.       B800:3FFF. The same buffer is also mapped between BC00:0000 and
  2085.       BC00:3FFF, although few programs use this alternate address map. As
  2086.       with the MDA, the CGA's video buffer mapping cannot be altered.
  2087.  
  2088.       This is not the case, however, for all CGA clones. The Hercules Color
  2089.       Card (not to be confused with the InColor Card) is a CGA work-alike
  2090.       whose video buffer can be excluded from the CPU's address space. This
  2091.       is achieved by setting bit 1 of the card's Configuration Switch
  2092.       register (3BFH) to 1. This register maps to the same I/O port as the
  2093.       equivalent register on an HGC, HGC+, or InColor Card, but the polarity
  2094.       of the control bit is opposite that on the other Hercules cards. Thus,
  2095.       by toggling this bit, software can address the video buffers on both a
  2096.       Hercules Color Card and another Hercules adapter without addressing
  2097.       conflicts.
  2098.  
  2099.  
  2100.  EGA
  2101.  
  2102.       The EGA's video buffer can be mapped to any of four locations,
  2103.       depending on the values of bits 2 and 3 in the Graphics Controller
  2104.       Miscellaneous register (see Figure 2-20). The default values for
  2105.       these bits depend on the video mode. When the video BIOS sets up a
  2106.       video mode, it sets these bits to appropriate values.
  2107.  
  2108.  
  2109. ╓┌───────────┌───────────┌───────────────────────────────────────────────────╖
  2110.  Bit 3       Bit 2       Video Buffer Address Range
  2111.  ──────────────────────────────────────────────────────────────────────────
  2112.  0           0           A000:0000-B000:FFFF
  2113.  0           1           A000:0000-A000:FFFF
  2114.  1           0           B000:0000-B000:7FFF
  2115.  1           1           B800:0000-B800:7FFF
  2116.  
  2117.       Figure 2-20.  Control of EGA and VGA video buffer addressing with the
  2118.       Graphics Controller Miscellaneous register.
  2119.  
  2120.  
  2121.       The EGA also provides another level of control over the video buffer
  2122.       address map. When set to 0, bit 1 of the EGA's Miscellaneous Output
  2123.       register (3C2H) excludes the entire video buffer from the CPU memory
  2124.       address space.
  2125.  
  2126.  
  2127.  MCGA
  2128.  
  2129.       The MCGA's 64 KB video buffer occupies the addresses between A000:0000
  2130.       and A000:FFFF, but the second 32 KB of the buffer, starting at
  2131.       A000:8000 (A800:0000), also maps to the CGA video buffer address range
  2132.       (B800:0000 through B800:7FFF). CPU addressing of the MCGA's video
  2133.       buffer and I/O ports can be disabled by setting bit 2 of the system
  2134.       board control port at 65H to 0. Listing 2-5 shows how INT 10H function
  2135.       12H can be called to set or reset this bit.
  2136.  
  2137.  
  2138.  ───────────────────────────────────────────────────────────────────────────
  2139.  
  2140.       Listing 2-5.  Enable or disable video I/O port and buffer addressing
  2141.       on an MCGA or VGA.
  2142.  
  2143.  ───────────────────────────────────────────────────────────────────────────
  2144.  
  2145.  
  2146.  VGA
  2147.  
  2148.       Control over the VGA's video buffer address map is the same as on the
  2149.       EGA. However, there are two different methods of disabling CPU
  2150.       addressing of the video subsystem, depending on whether you are using
  2151.       an integrated VGA (in a PS/2 Model 50, 60, or 80) or the VGA Adapter.
  2152.       In the integrated subsystem, the Video Subsystem Enable Register
  2153.       (3C3H) controls both video buffer addressing and I/O port addressing;
  2154.       setting bit 0 of this register to 0 disables addressing, and setting
  2155.       bit 0 to 1 enables addressing.
  2156.  
  2157.       On the VGA Adapter, the Video Subsystem Enable register does not
  2158.       exist. Instead, bit 3 of the control register at I/O port 46E8H
  2159.       enables and disables addressing: Writing a default value of 0EH to
  2160.       this port enables addressing; writing a value of 6 disables
  2161.       addressing.
  2162.  
  2163.       In all VGA subsystems, however, INT 10H function 12H provides the same
  2164.       interface as it does on the MCGA (see Listing 2-5). Because of the
  2165.       hardware differences between the MCGA, the integrated VGA and the VGA
  2166.       Adapter, it is easier to use INT 10H function 12H to enable or disable
  2167.       addressing in the PS/2 video subsystems (see Listing 2-5).
  2168.  
  2169.  
  2170.  
  2171.                           3  Alphanumeric Modes
  2172.  
  2173.  
  2174.                          Using Alphanumeric Modes
  2175.                      BIOS and Operating-System Support
  2176.                            Speed ■ Compatibility
  2177.  
  2178.                     Representation of Alphanumeric Data
  2179.  
  2180.                                 Attributes
  2181.                            MDA ■ HGC ■ CGA ■ EGA
  2182.                          InColor Card ■ MCGA ■ VGA
  2183.  
  2184.                             Gray-Scale Summing
  2185.  
  2186.                                Border Color
  2187.                              CGA ■ EGA and VGA
  2188.  
  2189.                              Avoiding CGA Snow
  2190.                            Blanking the Display
  2191.                    Using the Vertical Blanking Interval
  2192.                   Using the Horizontal Blanking Interval
  2193.  
  2194.                         Using All the Video Buffer
  2195.                               CGA Video Pages
  2196.                       EGA, MCGA, and VGA Video Pages
  2197.  
  2198.                               Cursor Control
  2199.                       Cursor Size on the MDA and CGA
  2200.                     Cursor Location on the MDA and CGA
  2201.              MCGA Cursor Control ■ EGA and VGA Cursor Control
  2202.                          ROM BIOS Cursor Emulation
  2203.                             An Invisible Cursor
  2204.  
  2205.  
  2206.  
  2207.       All IBM PC and PS/2 video subsystems except the MDA can be programmed
  2208.       to display characters in either alphanumeric or graphics modes. This
  2209.       chapter discusses what you need to know to use alphanumeric modes--the
  2210.       advantages and disadvantages of programming in alphanumeric modes; the
  2211.       basics of colors, blinking, and other character display attributes;
  2212.       and special techniques that exploit the capabilities of the hardware
  2213.       to improve the on-screen appearance and performance of your programs.
  2214.  
  2215.  
  2216.  Using Alphanumeric Modes
  2217.  
  2218.  
  2219.       The video BIOS on all IBM PCs and PS/2s always selects an alphanumeric
  2220.       video display mode when you boot the computer. In the IBM PC family,
  2221.       switches on the motherboard, the video adapter, or both determine
  2222.       whether a 40-column or 80-column mode is selected and whether a color
  2223.       or monochrome display is used. In the PS/2 series, the initial video
  2224.       mode is always an 80-column alphanumeric mode. Furthermore, the video
  2225.       mode set by the ROM BIOS is the one the operating system initially
  2226.       uses. Until you run a program that changes the video mode, all video
  2227.       output appears in the default mode--which is alphanumeric.
  2228.  
  2229.       For this reason, the simplest way to write a program is to assume that
  2230.       it runs in an alphanumeric mode and to program the video interface
  2231.       accordingly. This assumption minimizes the coding required to send
  2232.       output to the screen. Not only are alphanumeric video output routines
  2233.       simpler than equivalent routines for graphics modes, but in most cases
  2234.       the ROM BIOS or the operating system provides character output
  2235.       routines that can be used in any alphanumeric mode.
  2236.  
  2237.  
  2238.  BIOS and Operating-System Support
  2239.  
  2240.       In the IBM PC, operating-system output routines are usually based on
  2241.       the set of primitive routines in the ROM BIOS that are called with
  2242.       software interrupt 10H. You can send characters to the video display
  2243.       either by using operating-system calls or by calling the INT 10H
  2244.       routines directly. In either case, use of these routines obviates the
  2245.       need for writing your own character output routines.
  2246.  
  2247.       An additional advantage to using BIOS or operating-system character
  2248.       output functions is that programs using only such functions are more
  2249.       likely to run on different video hardware. For example, a program
  2250.       using only MS-DOS function calls for video output will run in almost
  2251.       any MS-DOS environment, regardless of the video hardware, including
  2252.       (but not limited to) the entire IBM PC and PS/2 family.
  2253.  
  2254.       Of course, routing video output through an operating system is
  2255.       relatively slow compared with writing directly to the hardware. The
  2256.       use of operating-system character output routines introduces a certain
  2257.       amount of unavoidable overhead,particularly when such features as
  2258.       input/output redirection and multiprocessing are supported. Never-
  2259.       theless, this overhead may be acceptable in many applications. You
  2260.       should always consider whether the extra programming and decreased
  2261.       portability required to improve video output performance are
  2262.       worthwhile in your application.
  2263.  
  2264.  
  2265.  Speed
  2266.  
  2267.       This is not to say that alphanumeric video output is inherently slow.
  2268.       When compared with character output in graphics modes, alphanumeric
  2269.       output is significantly faster, simply because much less data must be
  2270.       stored in the video buffer to display characters. In alphanumeric
  2271.       modes, each character is represented by a single 16-bit word; the
  2272.       video hardware takes care of displaying the pixels that make up the
  2273.       character. In graphics modes, every pixel in every character is
  2274.       represented explicitly in a bit field in the video buffer. For this
  2275.       reason, graphics-mode output is much more costly than equivalent
  2276.       character output in alphanumeric modes, both in terms of display
  2277.       memory used and processing required.
  2278.  
  2279.       For example, in a 16-color graphics mode, each character drawn on the
  2280.       screen in an 8-by-8 dot matrix is represented by 32 bytes of data in
  2281.       the video buffer (8 * 8 * 4 bits per pixel). The memory overhead
  2282.       increases rapidly, in direct relationship to increasing resolution and
  2283.       the addition of more colors, as does the amount of time the CPU spends
  2284.       in manipulating data in the video buffer. On newer video adapters,
  2285.       dedicated graphics coprocessors such as the Intel 82786 or the TI
  2286.       34010 may assume much of the computational burden of graphics-mode
  2287.       text display, thereby improving the speed of graphics-mode text
  2288.       output. Without a coprocessor, however, output in graphics modes is
  2289.       much slower than in alphanumeric modes.
  2290.  
  2291.  
  2292.  Compatibility
  2293.  
  2294.       Writing a program that is compatible with different IBM video
  2295.       subsystems is easier if you use only alphanumeric video display modes.
  2296.       The reason is simple: All commonly used IBM video subsystems support
  2297.       an 80-column by 25-row alphanumeric mode with the same video buffer
  2298.       format. If you design your video interface with an 80-by-25
  2299.       alphanumeric display in mind, your program will run on a majority of
  2300.       PCs and compatibles with little or no modification.
  2301.  
  2302.       Unfortunately, high compatibility is generally achieved only by
  2303.       sacrificing speed. Fast video output routines usually take advantage
  2304.       of hardware idiosyncrasies, so they are less likely to be portable to
  2305.       different video hardware than routines that rely on slower but more
  2306.       universal BIOS or operating-system calls. This trade-off will be
  2307.       implicit in almost every video output routine you write.
  2308.  
  2309.  
  2310.  Representation of Alphanumeric Data
  2311.  
  2312.  
  2313.       All IBM PC and PS/2 video subsystems use the same format for storing
  2314.       alphanumeric data in the video buffer. Each character is represented
  2315.       by a simple 2-byte data structure (see Figure 3-1). Characters are
  2316.       stored in the buffer in a linear sequence that maps across and down
  2317.       the screen (see Figure 3-2).
  2318.  
  2319.  
  2320.          Low-order byte      High-order byte
  2321.       ┌───────────────────┬───────────────────┐
  2322.       │  ASCII character  │     Attribute     │
  2323.       │        code       │                   │
  2324.       └───────────────────┴───────────────────┘
  2325.  
  2326.       Figure 3-1.  Alphanumeric character and attribute mapping in a 16-bit
  2327.       word.
  2328.  
  2329.  
  2330.                                                        Display
  2331.                 Video buffer                          ┌─────────────
  2332.       0000H ┌───────────────────┐                     │░░░░░░░░░
  2333.             │                   ├─── Character row O │█████████
  2334.       00A0H ├───────────────────┤                     │░░░░░░░░░
  2335.             │                   ├─── Character row 1 │█████████
  2336.       0140H ├───────────────────┤                     │░░░░░░░░░
  2337.             │                   ├─── Character row 2 │█████████
  2338.       01E0H └───────────────────┘                     │░░░░░░░░░
  2339.             │                   │                     │░░░░░░░░░
  2340.              ─ ─ ─ ─ ─ ─ ─ ─ ─ ─                      │░░░░░░░░░
  2341.             │                   │                     │░░░░░░░░░
  2342.              ─ ─ ─ ─ ─ ─ ─ ─ ─ ─                      │░░░░░░░░░
  2343.             │                   │                     │░░░░░░░░░
  2344.             └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘                     │
  2345.  
  2346.       Figure 3-2.  Video buffer map in 80-by-25 alphanumeric modes.
  2347.  
  2348.  
  2349.       A hardware character generator converts each character code into the
  2350.       proper dot pattern on the display. At the same time, attribute decoder
  2351.       circuitry generates the appropriate attribute--color, intensity
  2352.       (brightness), blinking, and so on--for each character. Since each
  2353.       character code in the video buffer is accompanied by an attribute
  2354.       byte, you can independently control the displayed attributes of each
  2355.       character on the screen.
  2356.  
  2357.       The hardware character generator displays each alphanumeric character
  2358.       within a rectangular matrix of pixels. Within this character matrix,
  2359.       the character itself is composed of a set of foreground pixels. The
  2360.       colors of the character's foreground and background pixels are
  2361.       specified by the low and high nibbles of the corresponding attribute
  2362.       byte.
  2363.  
  2364.       To display a character, you store its ASCII code and attribute in the
  2365.       proper location in the video buffer. Because of the linear mapping
  2366.       scheme, you can easily calculate the buffer address of a particular
  2367.       screen location. The general formula is
  2368.  
  2369.       offset = ((row * width) + column) * 2
  2370.  
  2371.       In this formula, width is the number of characters in each row. The
  2372.       factor of 2 is included because each character requires 2 bytes (one
  2373.       16-bit word) of storage in the video buffer. The values for row and
  2374.       column are zero-based, starting in the upper left corner of the
  2375.       screen. (The character in the upper left corner is located at row 0,
  2376.       column 0.)
  2377.  
  2378.       If you examine the contents of the video buffer, you can see how this
  2379.       data corresponds to characters on the screen (see Figure 3-3). Note
  2380.       how each character code is followed by its attribute byte. (All of the
  2381.       attribute bytes in the portion of the video buffer shown in Figure 3-3
  2382.       have the value 07H.)
  2383.  
  2384.  
  2385.             0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
  2386.  B000:0000  43 07 68 07 61 07 72 07 61 07 63 07 74 07 65 07 C.h.a.r.a.c.t.e.
  2387.  B000:0010  72 07 20 07 72 07 6F 07 77 07 20 07 30 07 30 07 r. .r.o.w. .0.0.
  2388.  B000:0020  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2389.  B000:0030  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2390.  B000:0040  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2391.  B000:0050  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2392.  B000:0060  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2393.  B000:0070  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2394.  B000:0080  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2395.  B000:0090  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2396.  B000:00A0  43 07 68 07 61 07 72 07 61 07 63 07 74 07 65 07 C.h.a.r.a.c.t.e.
  2397.  B000:00B0  72 07 20 07 72 07 6F 07 77 07 20 07 30 07 31 07 r. .r.o.w. .0.1.
  2398.  B000:00C0  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2399.  B000:00D0  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2400.  B000:00E0  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2401.  B000:00F0  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2402.  B000:0100  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2403.  B000:0110  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2404.  B000:0120  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2405.  B000:0130  00 07 00 07 00 07 00 07 00 07 00 07 00 07 00 07 ................
  2406.  
  2407.       Figure 3-3.  Hexadecimal dump of an alphanumeric video buffer.
  2408.  
  2409.  
  2410.  Attributes
  2411.  
  2412.  
  2413.       Although all IBM PC and PS/2 video subsystems use the same pattern
  2414.       of alternating character codes and attribute bytes to represent
  2415.       alphanumeric data, the way the attribute byte is interpreted varies.
  2416.       In general, the attribute byte is formatted as two 4-bit nibbles.
  2417.       The low-order nibble (bits 0 through 3) determines the character's
  2418.       foreground attribute; that is, the color and intensity of the
  2419.       character itself. The high-order nibble (bits 4 through 7) indicates
  2420.       the character's background attribute, although bit 7 may also control
  2421.       blinking in some situations.
  2422.  
  2423.       The 4-bit foreground and background attributes are ultimately decoded
  2424.       into a set of signals that drive the video monitor. In the simplest
  2425.       case, on the CGA, the four bits correspond directly to the three color
  2426.       signals and the intensity signal. The decoding scheme on other video
  2427.       subsystems can be complex, as on the EGA, MCGA, VGA, and InColor Card,
  2428.       or comparatively simple, as on the MDA.
  2429.  
  2430.  
  2431.  MDA
  2432.  
  2433.       Although you may specify any of 16 (2^4) attributes for both
  2434.       foreground and background attributes, the MDA only recognizes certain
  2435.       combinations (see Figure 3-4). Nevertheless, you can generate a
  2436.       useful variety of character attributes by creatively combining
  2437.       intensity, blinking, and underlining. You can also exchange the usual
  2438.       foreground and background attributes to obtain "reverse video"--black
  2439.       characters on a normal-intensity background.
  2440.  
  2441.  
  2442.                             Not Underlined
  2443.  ───────────────────────────────────────────────────────────────────────────
  2444.                                Foreground
  2445.                 Black     Dim1    Normal Intensity    High Intensity
  2446.  ──────────────────────────────────────────────────────────────────────────
  2447.  Background
  2448.  Black          00        2        07                  0F
  2449.  Dim1          2        88        87                  8F
  2450.  Normal         70        78        2                  2
  2451.  High           F0        F8        2                  2
  2452.  
  2453.                               Underlined
  2454.  ───────────────────────────────────────────────────────────────────────────
  2455.                               Foreground
  2456.                 Normal Intensity       High Intensity
  2457.  ───────────────────────────────────────────────────────────────────────────
  2458.  Background
  2459.  Black          01                     09
  2460.  Dim1          81                     89
  2461.  
  2462.       Figure 3-4.  MDA foreground-background attribute combinations (values
  2463.       in hex). Attribute values not in this table always map to one of the
  2464.       combinations shown.
  2465.  
  2466.  
  2467.       On the MDA, as well as on all other IBM video hardware, bit 7 of each
  2468.       character's attribute byte can serve two purposes. By default, this
  2469.       bit controls whether a character blinks when displayed; setting the
  2470.       bit to 1 causes the associated character to blink. Bit 7 controls
  2471.       blinking because bit 5 (the Enable Blink bit) of the MDA's CRT Mode
  2472.       Control register (3B8H) is set to 1 by the video BIOS when the
  2473.       computer is powered up.
  2474.  
  2475.       If the Enable Blink bit is 0, however, bit 7 of the attribute byte no
  2476.       longer controls blinking (see Listing 3-1). Instead, bit 7 is
  2477.       interpreted as an intensity bit for the background attribute. When bit
  2478.       7 is set in a character's attribute byte, the character's background
  2479.       attribute is intensified; that is, normal green becomes intense green
  2480.       and black becomes dim green. Thus, to obtain all possible combinations
  2481.       of monochrome attributes listed in Figure 3-4, you must zero the
  2482.       Enable Blink bit.
  2483.  
  2484.  
  2485.  ───────────────────────────────────────────────────────────────────────────
  2486.  
  2487.       Listing 3-1.  Resetting the Enable Blink bit on the MDA or CGA.
  2488.  
  2489.  ───────────────────────────────────────────────────────────────────────────
  2490.  
  2491.  
  2492.       The value of the Mode Control register's Enable Blink bit affects the
  2493.       interpretation of bit 7 of all attribute bytes, so you can't display
  2494.       both blinking characters and characters with intensified background at
  2495.       the same time. You must decide which attribute is more useful in your
  2496.       program and set the Enable Blink bit accordingly.
  2497.  
  2498.       All IBM PC and PS/2 video subsystems, including the MDA, blink
  2499.       alphanumeric characters by substituting the background attribute for
  2500.       the foreground attribute about twice a second. The effect is that each
  2501.       blinking character alternates with a blank character.
  2502.  
  2503.       If you fill the display with blinking characters, the overall effect
  2504.       can be disconcerting, because the screen is blanked and restored twice
  2505.       each second. But if your purpose is to attract attention to the
  2506.       display, using the blink attribute can be very effective.
  2507.  
  2508.         ╔═══╗     If you use the underline attribute (foreground attribute 1
  2509.         ║ T ║     or 9) on a Compaq portable, you won't see underlined
  2510.         ║ I ║     characters. This is because the Compaq portable decodes
  2511.         ║ P ║     attribute values into 16 progressively brighter shades
  2512.         ╚═══╝     of green; the underline attribute values of 1 and 9
  2513.                   therefore appear as shades of green.
  2514.  
  2515.         ╔═══╗     Surprisingly, a few IBM MDAs generate color as well as
  2516.         ║ T ║     monochrome output. Of course, the MDA's green monochrome
  2517.         ║ I ║     display uses only two signals to control attributes (video
  2518.         ║ P ║     on/off and intensity on/off); it ignores any color video
  2519.         ╚═══╝     signals. However, a color display that can use the MDA's
  2520.                   16.257 MHz horizontal sync and 50 Hz vertical sync signals
  2521.                   will display eight colors (with and without intensity)
  2522.                   when attached to some (but not all) MDAs. Unfortunately,
  2523.                   you can never be certain which MDA will turn out to be a
  2524.                   color adapter in disguise.
  2525.  
  2526.  
  2527.  HGC
  2528.  
  2529.       The HGC and HGC+ exactly emulate the MDA's monochrome alphanumeric
  2530.       mode. Programs written for the MDA run unchanged on either of these
  2531.       adapters.
  2532.  
  2533.  
  2534.  CGA
  2535.  
  2536.       The CGA uses the same foreground-background attribute scheme as does
  2537.       the MDA. However, the CGA's attribute decoder circuitry recognizes all
  2538.       16 possible combinations of the four bits in each nibble of the
  2539.       attribute byte. For each character on the screen, you can
  2540.       independently specify any of 16 colors for foreground and background.
  2541.  
  2542.       The available colors are simple combinations of the primary colors
  2543.       red, green, and blue. Each bit in each nibble of the attribute byte
  2544.       corresponds to a signal that the CGA supplies to the video monitor
  2545.       (see Figure 3-5). The low-order three bits of each nibble correspond
  2546.       to the red (R), green (G), and blue (B) signals. The eight possible
  2547.       combinations produce a gamut of red, green, blue, and their
  2548.       intermediate colors (see Figure 3-6).
  2549.  
  2550.  
  2551.       Bit   3     2     1     0
  2552.          ┌─────┬─────┬─────┬─────┐
  2553.          │  I  │  R  │  G  │  B  │
  2554.          └──┬──┴──┬──┴──┬──┴──┬──┘
  2555.             │     │     │     │
  2556.             │     │     │     │
  2557.             │     │     │     └─────────────Pin 5
  2558.             │     │     └───────────────────Pin 4
  2559.             │     └─────────────────────────Pin 3
  2560.             └───────────────────────────────Pin 6
  2561.  
  2562.       Figure 3-5.  CGA attributes and monitor color drive signals. Pin
  2563.       numbers refer to the CGA's 9-pin connector.
  2564.  
  2565.  
  2566. ╓┌───────────────────┌──────────────┌────────────────────────────────────────╖
  2567.  Color               Binary (IRGB)  Hexadecimal
  2568.  ──────────────────────────────────────────────────────────────────────────
  2569.  Black               0000           00
  2570.  Blue                0001           01
  2571.  Green               0010           02
  2572.  Cyan                0011           03
  2573.  Red                 0100           04
  2574.  Violet              0101           05
  2575.  Yellow (brown)      0110           06
  2576.  White               0111           07
  2577.  Black (gray)        1000           08
  2578.  Intense blue        1001           09
  2579.  Intense green       1010           0A
  2580.  Intense cyan        1011           0B
  2581.  Intense red         1100           0C
  2582.  Intense violet      1101           0D
  2583.  Intense yellow      1110           0E
  2584.  Intense white       1111           0F
  2585.  Color               Binary (IRGB)  Hexadecimal
  2586. Intense white       1111           0F
  2587.  
  2588.       Figure 3-6.  CGA display attributes.
  2589.  
  2590.  
  2591.       Setting bit 3 of the attribute byte (the intensity bit in the
  2592.       foreground nibble) displays the color designated in the R, G, and B
  2593.       bits (bits 0 through 2) with higher intensity. However, as on the MDA,
  2594.       the high-order bit (bit 7) of each attribute byte controls either
  2595.       background intensity or blinking. Again, the attribute displayed
  2596.       depends upon the state of a bit in a control register.
  2597.  
  2598.       Bit 5 of the CGA's Mode Control register (I/O port 3D8H) is an Enable
  2599.       Blink bit analogous to bit 5 of the MDA's CRT Control register. When
  2600.       you set the Enable Blink bit to 0, bit 7 of a character's attribute
  2601.       byte signifies that the background color specified in bits 4 through
  2602.       6 should be intensified. When you set the Enable Blink bit to 1, only
  2603.       nonintensified background colors are displayed, but characters whose
  2604.       attribute bytes have bit 7 set to 1 will blink.
  2605.  
  2606.       The Enable Blink bit is set to 1 whenever you call the ROM BIOS to
  2607.       select an alphanumeric video mode. By default, therefore, bit 7 of
  2608.       each character's attribute byte controls blinking rather than
  2609.       background intensity. You must reset the Enable Blink bit to display
  2610.       characters with intensified background colors.
  2611.  
  2612.       Many CGA-compatible displays squeeze a bit more out of the 16
  2613.       available colors (8 nonintensified, 8 intensified) by displaying low-
  2614.       intensity yellow as brown and high-intensity black as gray.
  2615.       Unfortunately, a program cannot determine whether a particular display
  2616.       can do this. Be careful about displaying, for example, gray characters
  2617.       on a black background with a CGA, because such color combinations are
  2618.       invisible on some color displays.
  2619.  
  2620.  
  2621.  EGA
  2622.  
  2623.       In 16-color alphanumeric modes, the EGA uses the same attribute byte
  2624.       format as the CGA. However, the 4-bit foreground and background values
  2625.       do not correspond directly to the colors displayed. Instead, each 4-
  2626.       bit value is masked with the four low-order bits of the Attribute
  2627.       Controller's Color Plane Enable register (12H); the resulting 4-bit
  2628.       value designates one of the EGA's 16 palette registers (see Figure 3-
  2629.       7). Each bit of the 6-bit color value contained in the designated
  2630.       palette register corresponds to one of the six RGB signals that drive
  2631.       the monitor (see Figure 3-8).
  2632.  
  2633.       An EGA-compatible color monitor is driven by six color signals--three
  2634.       primary (higher intensity) and three secondary (lower intensity). All
  2635.       64 combinations of these six signals appear as different colors and/or
  2636.       intensities. With a 200-line color monitor--or in 200-line modes on an
  2637.       EGA-compatible monitor--bits 0, 1, and 2 control the color signals,
  2638.       while bit 4 controls the intensity signal.
  2639.  
  2640.  
  2641.       ┌─────────────────┐                   ┌──────────────────┐
  2642.       │                 │    Logical AND    │   Color Plane    │
  2643.       │ 4-bit attribute ├─────────┬─────────┤ Enable register  │
  2644.       │                 │         │         │                  │
  2645.       └─────────────────┘         │         └──────────────────┘
  2646.                                   │
  2647.                       ┌──────────────────────┐
  2648.                       │Palette register 0-0FH │
  2649.                       └───────────┬───────────┘
  2650.                                   │
  2651.                                   
  2652.                 6-bit digital output to video display
  2653.                  (2-bits each for red, green, blue)
  2654.  
  2655.       Figure 3-7.  Attributes and colors on the EGA.
  2656.  
  2657.  
  2658.       200-line monitors (CGA-compatible):
  2659.  
  2660.       Bit   7     6     5     4     3     2     1     0
  2661.          ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
  2662.          │     │     │     │  I  │     │  R  │  G  │  B  │ Palette register
  2663.          └─────┴─────┴─────┴──┬──┴─────┴──┬──┴──┬──┴──┬──┘
  2664.                               │           │     │     └────────────Pin 5
  2665.                               │           │     └──────────────────Pin 4
  2666.                               │           └────────────────────────Pin 3
  2667.                               └────────────────────────────────────Pin 6
  2668.  
  2669.       350-line color monitors (EGA-compatible):
  2670.  
  2671.       Bit   7     6     5     4     3     2     1     0
  2672.          ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
  2673.          │     │     │  r  │  g  │  b  │  R  │  G  │  B  │ Palette register
  2674.          └─────┴─────┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┘
  2675.                         │     │     │     │     │     └────────────Pin 5
  2676.                         │     │     │     │     └──────────────────Pin 4
  2677.                         │     │     │     └────────────────────────Pin 3
  2678.                         │     │     └──────────────────────────────Pin 7
  2679.                         │     └────────────────────────────────────Pin 6
  2680.                         └──────────────────────────────────────────Pin 2
  2681.  
  2682.       350-line monochrome monitors (MDA-compatible)
  2683.  
  2684.       Bit   7     6     5     4     3    2     1     0
  2685.          ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
  2686.          │     │     │     │  I  │  V  │     │     │     │ Palette register
  2687.          └─────┴─────┴─────┴──┬──┴──┬──┴─────┴─────┴─────┘
  2688.                               │     └─────────────────────────────Pin 7
  2689.                               └───────────────────────────────────Pin 6
  2690.  
  2691.       R,G,B = primary red, green, blue (higher intensity)
  2692.       r,g,b = secondary red, green, blue (lower intensity)
  2693.       I = intensity
  2694.       V = monochrome video
  2695.  
  2696.       Figure 3-8.  EGA palette register values and corresponding monitor
  2697.       color drive signals. Pin numbers refer to the EGA's 9-pin connector.
  2698.  
  2699.  
  2700.       The EGA's method of generating colors indirectly through palette
  2701.       registers is more complex than the CGA's direct scheme, but the EGA is
  2702.       more flexible. You can select the foreground and background colors for
  2703.       each character individually, yet you can produce global color changes
  2704.       by updating the value in a particular palette register.
  2705.  
  2706.       The high-order bit of each character's attribute byte can control
  2707.       either blinking or background intensity, just as on the MDA and the
  2708.       CGA. Bit 3 of the EGA's Attribute Controller Mode Control register
  2709.       (register 10H at I/O port 3C0H) is the Enable Blink bit. Setting it to
  2710.       1 enables blinking, so only the low-order 3 bits of the background
  2711.       nibble (bits 4 through 6 of the attribute byte) designate palette
  2712.       registers. Thus, when blinking is enabled, you can reference only
  2713.       the first eight palette registers to select the background color for
  2714.       a character. Setting the Enable Blink bit to 0 disables blinking,
  2715.       making all 16 palette registers available for background colors
  2716.       (see Listing 3-2).
  2717.  
  2718.  
  2719.  ───────────────────────────────────────────────────────────────────────────
  2720.  
  2721.       Listing 3-2.  Setting and resetting the Enable Blink bit on the MCGA,
  2722.       EGA, or VGA.
  2723.  
  2724.  ───────────────────────────────────────────────────────────────────────────
  2725.  
  2726.  
  2727.       When you select an alphanumeric video mode using the EGA BIOS, the
  2728.       palette registers are loaded with default values that correspond to
  2729.       the colors available on the CGA. The color values in the second eight
  2730.       palette registers are intensified versions of those in the first
  2731.       eight. Thus, if you simply treat bit 7 of the attribute byte as a
  2732.       "background intensity or blink" bit, your program will run on both an
  2733.       EGA and a CGA.
  2734.  
  2735.       You can update the contents of any palette register either directly or
  2736.       with INT 10H function 10H (see Listing 3-3). Using the BIOS routine
  2737.       is more convenient and avoids the need to write hardware-dependent
  2738.       code. Moreover, the BIOS routine can also load all 16 palette
  2739.       registers at once, given a table of color values (see Appendix A).
  2740.       Nevertheless, you may still need to program the palette registers
  2741.       directly to produce very rapid color changes such as might be required
  2742.       in some types of animation.
  2743.  
  2744.  
  2745.  ───────────────────────────────────────────────────────────────────────────
  2746.  
  2747.       Listing 3-3.  Palette register programming on the EGA or VGA.
  2748.  
  2749.  ───────────────────────────────────────────────────────────────────────────
  2750.  
  2751.  
  2752.       In monochrome alphanumeric mode, the EGA emulates the MDA monochrome
  2753.       display attributes. The video BIOS initializes the palette registers
  2754.       with values that correspond to MDA attributes (see Figure 3-9). Bit 3
  2755.       determines whether pixels are on or off, and bit 4 (if set in addition
  2756.       to bit 3) causes a higher-intensity display. The underline attribute
  2757.       is generated whenever a character's foreground attribute is 1 or 9,
  2758.       regardless of the value in the corresponding palette register.
  2759.  
  2760.  
  2761. ╓┌───────────┌───────────────────────────────────────────────────────────────╖
  2762.  Value       Attribute
  2763.  ──────────────────────────────────────────────────────────────────────────
  2764.  0           Black
  2765.  8           Normal intensity
  2766.  10H         Dim
  2767.  18H         High intensity
  2768.  
  2769.       Figure 3-9.  Monochrome alphanumeric attribute values for the EGA
  2770.       palette registers.
  2771.  
  2772.  
  2773.         ╔═══╗     The EGA also generates an underline attribute in 16-color
  2774.         ║ T ║     alphanumeric modes when the foreground attribute is 1 or 9
  2775.         ║ I ║     and the background attribute is 0 or 8. However, you do
  2776.         ║ P ║     not normally see an underline in 16-color modes because
  2777.         ╚═══╝     the video BIOS default value for the CRTC Underline
  2778.                   Location register (14H) is 1FH. This value is greater than
  2779.                   the number of scan lines normally displayed for
  2780.                   alphanumeric characters, so the underline does not appear.
  2781.  
  2782.                   You can generate underlined characters in 16-color modes
  2783.                   by storing a displayable value in the Underline Location
  2784.                   register. Of course, only characters with attributes of 1,
  2785.                   9, 81H, or 89H will appear underlined, but you can change
  2786.                   the values in the corresponding palette registers to
  2787.                   produce underlined characters of any desired color.
  2788.  
  2789.  
  2790.  InColor Card
  2791.  
  2792.       The InColor Card can decode alphanumeric attributes in several
  2793.       different ways. The card has a set of 16 palette registers whose
  2794.       function is analogous to the EGA's Attribute Controller palette
  2795.       registers, but the InColor Card can be configured by your program to
  2796.       bypass the palette registers and decode each character's 4-bit
  2797.       foreground and background attributes in an MDA- or CGA-compatible
  2798.       manner. Bits 4 and 5 of the Exception register (17H) control how the
  2799.       InColor Card interprets alphanumeric attributes (see Figure 3-10).
  2800.       Bit 5 determines whether the InColor Card displays monochrome
  2801.       attributes (as on the MDA) or color attributes (as on the CGA or EGA).
  2802.       Bit 4 enables attribute mapping through the palette registers.
  2803.  
  2804.       When the InColor Card is powered up, Exception register bit 5 has the
  2805.       value 1 and bit 4 has the value 0. Thus, by default, the card
  2806.       interprets attributes as an MDA would. However, if you set both bits 5
  2807.       and 4 to 0 (see Listing 3-4), alphanumeric attributes specify the
  2808.       same set of 16 colors as on a CGA (refer to Figure 3-6).
  2809.  
  2810.  
  2811.  ───────────────────────────────────────────────────────────────────────────
  2812.  
  2813.       Listing 3-4.  InColor Exception register programming.
  2814.  
  2815.  ───────────────────────────────────────────────────────────────────────────
  2816.  
  2817.  
  2818. ╓┌─────────┌───────────────────┌─────────────────────────────────────────────╖
  2819.  Bit 5     Bit 4     Attribute Emulation
  2820.  ──────────────────────────────────────────────────────────────────────────
  2821.  0         0         CGA
  2822.  0         1         EGA
  2823.  1         0         MDA
  2824.  1         1         MDA mapped through palette registers.
  2825.  
  2826.       Figure 3-10.  Exception register control of attributes on the Hercules
  2827.       InColor Card.
  2828.  
  2829.  
  2830.       Setting bit 4 to 1 causes attributes to map to the card's palette
  2831.       registers, regardless of the value of bit 5. Thus, if bit 4 is 1 and
  2832.       bit 5 is 0, the InColor Card interprets attributes as does the EGA. If
  2833.       bit 4 is 1 and bit 5 is 1, however, the card maps each character's
  2834.       foreground and background attributes only to the palette registers
  2835.       that correspond to valid monochrome attribute values. In this case,
  2836.       the "black," "dim," "normal intensity," and "high intensity"
  2837.       attributes select palette registers 00H, 08H, 07H, and 0FH
  2838.       respectively.
  2839.  
  2840.       Bit 5 of the CRT Mode Control register at 3B8H is the Enable Blink
  2841.       bit. This bit controls background intensity regardless of the values
  2842.       of Exception register bits 4 and 5. However, characters are blinked
  2843.       only when Exception register bit 5 is 1 (MDA-compatible attributes);
  2844.       characters do not blink when bit 5 of the Exception register is 0
  2845.       (CGA-compatible attributes), regardless of the Enable Blink bit's
  2846.       setting.
  2847.  
  2848.       No video BIOS support is provided for the InColor Card's palette
  2849.       registers. Your program must therefore update the palette by directly
  2850.       storing values in the palette registers. Listing 3-5 is an example of
  2851.       how you might do this. The initial I/O read (IN AL,DX) of the palette
  2852.       register (1CH) resets an internal index which points to the first of
  2853.       the 16 internal palette registers. Each subsequent I/O write
  2854.       (OUT DX,AL) updates one internal palette register and increments the
  2855.       internal index to point to the next palette register, so all 16
  2856.       registers can be loaded by executing a simple loop.
  2857.  
  2858.         ╔═══╗     Because monochrome attributes can be mapped through
  2859.         ║ T ║     palette registers, you can assign as many as four
  2860.         ║ I ║     different colors to monochrome programs that run on the
  2861.         ║ P ║     InColor Card. Do this by setting Exception register bits 4
  2862.         ╚═══╝     and 5 to 1 and updating palette registers 00H, 08H, 07H,
  2863.                   and 0FH with the desired colors.
  2864.  
  2865.  
  2866.  ───────────────────────────────────────────────────────────────────────────
  2867.  
  2868.       Listing 3-5.  InColorpalette register programming.
  2869.  
  2870.  ───────────────────────────────────────────────────────────────────────────
  2871.  
  2872.  
  2873.       On the InColor Card, the colors of both the cursor and the underscore
  2874.       are independent of the foreground colors of the characters in the
  2875.       video buffer. The cursor color is specified in bits 0 through 3 of the
  2876.       Exception register, and the underscore color value is specified in
  2877.       bits 4 through 7 of the Underscore register (CRTC register 15H). When
  2878.       the InColor Card is displaying MDA attributes (that is, when bit 5 of
  2879.       the Exception register is set to 1), you can specify only the three
  2880.       low-order bits of the cursor and underscore colors; the high-order bit
  2881.       of these color values is derived from the foreground attribute of the
  2882.       character where the cursor or underscore is displayed.
  2883.  
  2884.       When palette mapping is enabled (Exception register bit 4 is set to
  2885.       1), both the cursor and underscore color values select palette
  2886.       registers. When palette mapping is disabled, the cursor and underscore
  2887.       color values are displayed using the usual CGA colors. Also, if you
  2888.       specify a value of 0 for either the underscore color or the cursor
  2889.       color, the InColor Card uses the value 7 instead.
  2890.  
  2891.  
  2892.  MCGA
  2893.  
  2894.       The components of the PS/2 Model 30's video subsystem that transform
  2895.       attribute data into color video signals are the Video Formatter and
  2896.       the video Digital-to- Analog Converter (DAC). The Video Formatter gate
  2897.       array decodes attributes and generates an 8-bit digital output which
  2898.       is passed to the video DAC; from this, the DAC generates analog red,
  2899.       green, and blue signals for the video display. The DAC converts the 8-
  2900.       bit output from the Video Formatter to the three analog color signals
  2901.       by using the 8 bits to select one of the DAC's 256 color registers.
  2902.       Each DAC color register is 18 bits wide, comprising three 6-bit values
  2903.       for red, green, and blue (see Figure 3-12). The DAC converts each
  2904.       6-bit value into an analog signal with the highest value (3FH)
  2905.       corresponding to the highest-intensity signal.
  2906.  
  2907.       In alphanumeric modes, the four low-order bits of the Video
  2908.       Formatter's 8-bit digital output are derived from attribute bytes,
  2909.       while the four high-order bits are always 0 (see Figure 3-11). Thus,
  2910.       only the first 16 of the video DAC's color registers are used in MCGA
  2911.       alphanumeric modes. The remaining 240 registers can be accessed only
  2912.       in 320-by-200 256-color graphics mode (see Chapter 4). When an MCGA
  2913.       is attached to a color display, the video BIOS initializes the first
  2914.       16 video DAC color registers with the same colors found on the CGA.
  2915.  
  2916.         ╔═══╗     The value in the video DAC Mask register (I/O port 3C6H)
  2917.         ║ T ║     masks the 8-bit value passed to the video DAC. The Mask
  2918.         ║ I ║     register value is set to 0FFH by the video BIOS
  2919.         ║ P ║     initialization routines so that all 256 video DAC color
  2920.         ╚═══╝     registers can be accessed. IBM technical documentation
  2921.                   recommends that this value not be modified.
  2922.  
  2923.  
  2924.       ┌──────────────────────┐                  ┌──────────────────────────┐
  2925.       │   4-bit attribute    │   Logical AND    │  Video DAC Mask register │
  2926.       │                      ├─────────┬────────┤                          │
  2927.       └──────────────────────┘         │        └──────────────────────────┘
  2928.                                        
  2929.                          ┌──────────────────────────┐
  2930.                          │        Video DAC         │
  2931.                          │   color register 0-0FH   │
  2932.                          └─────────────┬────────────┘
  2933.                                        │
  2934.                                        
  2935.                      18-bit analog output to video display
  2936.                       (6 bits each for red, green, blue)
  2937.  
  2938.       Figure 3-11.  Attributes and colors on the MCGA. (The value in the
  2939.       video DAC Mask register should normally be 0FFH.)
  2940.  
  2941.  
  2942.             6 bits           6 bits          6 bits
  2943.       ┌────────────────┬────────────────┬────────────────┐
  2944.       │      Red       │      Green     │      Blue      │
  2945.       └────────┬───────┴────────┬───────┴────────┬───────┘
  2946.                │                │                └──────────Pin 3
  2947.                │                └───────────────────────────Pin 2
  2948.                └────────────────────────────────────────────Pin 1
  2949.  
  2950.       Figure 3-12.  Video DAC color register values and monitor color drive
  2951.       signals. Pin numbers refer to the MCGA's 15-pin connector.
  2952.  
  2953.  
  2954.       Unlike the EGA, an MCGA with a monochrome display does not emulate the
  2955.       MDA's attributes. Instead, the 16 default video DAC color register
  2956.       values consist of four groups of four shades of gray. Each group is
  2957.       displayed with higher intensity than the preceding group. Within each
  2958.       group, the intensity increases from lower to higher attribute values.
  2959.       Thus, attribute values 0 through 3 make up a range of four shades of
  2960.       gray, values 4 through 7 a second range of somewhat higher intensity,
  2961.       and values 8 through 0BH and 0CH through 0FH a third and fourth range
  2962.       of still higher intensity.
  2963.  
  2964.         ╔═══╗     Instead of this default MCGA monochrome gray-scale
  2965.         ║ T ║     configuration, you might prefer to use gray-scale values
  2966.         ║ I ║     that increase uniformly with increasing attribute values.
  2967.         ║ P ║     The code in Listing 3-6 loads the video DAC registers
  2968.         ╚═══╝     with appropriate values for this gray-scale gamut.
  2969.  
  2970.  
  2971.  ───────────────────────────────────────────────────────────────────────────
  2972.  
  2973.       Listing 3-6.  Loading an alternative MCGA monochrome gray-scale
  2974.       palette.
  2975.  
  2976.  ───────────────────────────────────────────────────────────────────────────
  2977.  
  2978.  
  2979.  VGA
  2980.  
  2981.       In general, the VGA exactly emulates EGA alphanumeric attribute
  2982.       decoding. However, the VGA has both a video DAC and a set of 16
  2983.       Attribute Controller palette registers. Each palette register value
  2984.       selects one of 256 video DAC color registers. The value in the
  2985.       selected video DAC color register determines the color displayed.
  2986.  
  2987.       Depending on the value of bit 7 in the Attribute Controller's Mode
  2988.       Control register, you can use the palette register value to select a
  2989.       video DAC color register in one of two ways. When bit 7 is set to 0,
  2990.       the Attribute Controller combines the 6-bit palette-register value
  2991.       with bits 2 and 3 of its Color Select register (14H) to produce an 8-
  2992.       bit value that selects a video DAC color register (see Figure 3-13).
  2993.       Alternatively, when bit 7 is set to 1, only the four low-order bits of
  2994.       each palette register are meaningful. The Attribute Controller derives
  2995.       the other four bits of the 8-bit value from bits 0 through 3 of the
  2996.       Color Select register (see Figure 3-14).
  2997.  
  2998.       In the first case (when bit 7 of the Mode Control register is set to
  2999.       0), the 6-bit palette registers are used to select one of four groups
  3000.       of 64 video DAC color registers, and bits 2 and 3 of the Color Select
  3001.       register determine which group of color registers is used. In the
  3002.       second case (when bit 7 of the Mode Control register is set to 1),
  3003.       each palette register value selects one of 16 groups of 16 video DAC
  3004.       color registers, and bits 0 through 3 of the Color Select register
  3005.       specify one of the 16 groups of DAC color registers.
  3006.  
  3007.  
  3008.       ┌─────────────────┐              ┌─────────────────┐
  3009.       │ 4-bit attribute │ Logical AND  │   Color Plane   │
  3010.       │                 ├───────┬──────┤ Enable register │
  3011.       └─────────────────┘       │      └─────────────────┘
  3012.                                 
  3013.                     ┌──────────────────────┐  ┌──────────────────────┐
  3014.                     │Palette register 0-0FH│  │Color Select register │
  3015.                     │                      │  │     (bits 2-3)       │
  3016.                     └───────────┬──────────┘  └───────────┬──────────┘
  3017.                                 │Bits 0-5 of              │ Bits 6-7 of
  3018.                                 │color register           │ color register
  3019.                                 │number                   │ number
  3020.                                 └────────────┬────────────┘
  3021.                                              │
  3022.                                              │     ┌───────────────────────┐
  3023.                                              │     │    Video DAC Mask     │
  3024.                                              │     │       register        │
  3025.                                              │     └────────────┬──────────┘
  3026.                                              │   Logical AND    │
  3027.                                              └──────┬───────────┘
  3028.                                                     │
  3029.                                                     
  3030.                                          ┌──────────────────────┐
  3031.                                          │      Video DAC       │
  3032.                                          │color register 0-0FFH │
  3033.                                          └──────────┬───────────┘
  3034.                                                     │
  3035.                                                     
  3036.                                   18-bit analog output to video display
  3037.                                     (6 bits each for red, green, blue)
  3038.  
  3039.       Figure 3-13.  Attributes and colors on the VGA (when bit 7 of the
  3040.       Attribute Controller's Mode Control register is set to 0).
  3041.  
  3042.  
  3043.       This added level of indirection, afforded by the combined use of
  3044.       palette registers and video DAC color registers, makes switching
  3045.       between palettes easy, since you can select any of 16 different 16-
  3046.       color palettes just by changing the value in the Attribute
  3047.       Controller's Color Select register. If you store 16 palettes of
  3048.       gradually increasing intensity in the DAC color registers, you can
  3049.       accentuate characters on the screen by cyclically increasing and
  3050.       decreasing their intensity. This effect is more subtle than simply
  3051.       blinking the characters on and off, particularly when applied to a
  3052.       large area of the display.
  3053.  
  3054.  
  3055.       ┌─────────────────┐              ┌─────────────────┐
  3056.       │ 4-bit attribute │ Logical AND  │   Color Plane   │
  3057.       │                 ├───────┬──────┤ Enable register │
  3058.       └─────────────────┘       │      └─────────────────┘
  3059.                                 
  3060.                     ┌──────────────────────┐  ┌──────────────────────┐
  3061.                     │Palette register 0-0FH│  │Color Select register │
  3062.                     │                      │  │     (bits 0-3)       │
  3063.                     └───────────┬──────────┘  └───────────┬──────────┘
  3064.                                 │Bits 0-3 of              │ Bits 4-7 of
  3065.                                 │color register           │ color register
  3066.                                 │number                   │ number
  3067.                                 └────────────┬────────────┘
  3068.                                              │
  3069.                                              │     ┌───────────────────────┐
  3070.                                              │     │    Video DAC Mask     │
  3071.                                              │     │       register        │
  3072.                                              │     └────────────┬──────────┘
  3073.                                              │   Logical AND    │
  3074.                                              └──────┬───────────┘
  3075.                                                     │
  3076.                                                     
  3077.                                          ┌──────────────────────┐
  3078.                                          │      Video DAC       │
  3079.                                          │color register 0-0FFH │
  3080.                                          └──────────┬───────────┘
  3081.                                                     │
  3082.                                                     
  3083.                                   18-bit analog output to video display
  3084.                                     (6 bits each for red, green, blue)
  3085.  
  3086.       Figure 3-14.  Attributes and colors on the VGA (when bit 7 of the
  3087.       Attribute Controller's Mode Control register is set to 1).
  3088.  
  3089.  
  3090.         ╔═══╗     When the VGA emulates 80-by-25 16-color alphanumeric mode
  3091.         ║ T ║     on a monochrome display, the palette consists of the same
  3092.         ║ I ║     four groups of four gray-scaled values as does the
  3093.         ║ P ║     corresponding palette on the MCGA. As on the MCGA, you can
  3094.         ╚═══╝     create a gray-scale palette with gradually increasing
  3095.                   intensities. Listing 3-7 illustrates how you might do
  3096.                   this. Note how the appropriate video DAC registers are
  3097.                   selected by examining the values in the Attribute
  3098.                   Controller's palette registers.
  3099.  
  3100.  
  3101.  ───────────────────────────────────────────────────────────────────────────
  3102.  
  3103.       Listing 3-7.  Loading an alternative VGA monochrome gray-scale
  3104.       palette.
  3105.  
  3106.  ───────────────────────────────────────────────────────────────────────────
  3107.  
  3108.  
  3109.       The VGA emulates the MDA's monochrome alphanumeric mode (video BIOS
  3110.       mode 7) on either a color or a monochrome display. The Attribute
  3111.       Controller palette register values and the control of blinking and
  3112.       underlining are the same as on the EGA. In this mode, the video DAC
  3113.       registers corresponding to the palette values 00H, 07H, 08H, and 18H
  3114.       are initialized with the appropriate gray-scale values. The palette
  3115.       and video DAC register values are the same in this mode regardless of
  3116.       whether a color or monochrome display is attached.
  3117.  
  3118.  
  3119.  Gray-Scale Summing
  3120.  
  3121.  
  3122.       Both the MCGA and the VGA BIOS contain logic which can transform the
  3123.       red-green-blue values in the video DAC registers into corresponding
  3124.       gray-scale values. This transformation is performed by taking a
  3125.       weighted average of the red, green, and blue components. To compute
  3126.       the gray-scaled equivalent value, the BIOS sums 30 percent of the red
  3127.       value, 59 percent of the green, and 11 percent of the blue. (These
  3128.       percentages approximate the displayed intensities of pure red, green,
  3129.       and blue.) For example, the default color for video DAC Color Register
  3130.       02H (cyan) is made up of three 6-bit components. The value of the red
  3131.       component is 0, the green component 2AH, and the blue component 2AH.
  3132.       The gray-scale value is therefore 1DH, the sum of
  3133.  
  3134.       (.30 x 0) + (.59 x 2AH) + (.11 x 2AH)
  3135.  
  3136.       INT 10H function 10H includes a subfunction (AL = 1BH) that reads a
  3137.       set of video DAC color registers and updates them with equivalent
  3138.       gray-scale values. Appendix A contains an example of the use of this
  3139.       video BIOS function.
  3140.  
  3141.       On both the MCGA and the VGA, INT 10H function 0 uses gray-scale
  3142.       summing by default when a monochrome display is attached. With a color
  3143.       display, gray-scale summing is disabled by default. You can
  3144.       selectively enable or disable default gray-scale summing by executing
  3145.       INT 10H function 12H with BL = 33H.
  3146.  
  3147.  
  3148.  Border Color
  3149.  
  3150.  
  3151.       On the CGA, EGA, MCGA, and VGA, you can specify a color to be
  3152.       displayed during the vertical and horizontal overscan intervals. This
  3153.       overscan or border color is not represented by any data in the video
  3154.       buffer. Instead, a special control register contains the value of the
  3155.       color displayed.
  3156.  
  3157.  
  3158.  CGA
  3159.  
  3160.       On the CGA, you select the border color with the four low-order bits
  3161.       of the Color Select register at I/O port 3D9H (see Listing 3-8). The
  3162.       color values parallel those available for character attributes: bits
  3163.       0, 1, and 2 select the blue, green, and red primaries, and bit 3 is
  3164.       interpreted as an intensity bit.
  3165.  
  3166.  
  3167.  ───────────────────────────────────────────────────────────────────────────
  3168.  
  3169.       Listing 3-8.  Setting a border color.
  3170.  
  3171.  ───────────────────────────────────────────────────────────────────────────
  3172.  
  3173.  
  3174.       Using INT 10H function 0BH to update the border color is probably more
  3175.       convenient than programming the Color Select register directly. The
  3176.       code is more portable, and the BIOS routine saves the most recently
  3177.       written value of the Color Select register in its Video Display Data
  3178.       Area in the byte at 0040:0066 (CRT_PALETTE). If you do write directly
  3179.       to the Color Select register, you should update CRT_PALETTE as in the
  3180.       example in Listing 3-8.
  3181.  
  3182.         ╔═══╗     The MCGA does not generate a colored border, regardless of
  3183.         ║ T ║     the value in its Color Select register.
  3184.         ║ I ║
  3185.         ║ P ║
  3186.         ╚═══╝
  3187.  
  3188.  
  3189.  EGA and VGA
  3190.  
  3191.       On the EGA and VGA, the overscan color is specified by the contents of
  3192.       register 11H of the Attribute Controller (I/O port 3C0H). You could
  3193.       write directly to the  I/O port, but doing the job with an INT 10H
  3194.       call is usually easier. You can use the EGA BIOS to update the
  3195.       overscan color in two ways. You can use function 0BH of INT 10H or you
  3196.       can include the border color as the 17th and last entry in the table
  3197.       of palette register colors you pass to INT 10H function 10H (see
  3198.       Appendix A).
  3199.  
  3200.         ╔═══╗     On the VGA with a monochrome display, the only useful
  3201.         ║ T ║     border attributes are 0 (black), 8 (normal), and 18H
  3202.         ║ I ║     (intense).
  3203.         ║ P ║
  3204.         ╚═══╝
  3205.  
  3206.       The 350-line video modes on the EGA have relatively short vertical and
  3207.       horizontal overscan intervals. The displayed border may be only 1 or 2
  3208.       mm wide, or it may bleed across the screen during the horizontal
  3209.       retrace interval. For this reason you should avoid setting the border
  3210.       color in any 350-line mode on the EGA.
  3211.  
  3212.         ╔═══╗     You can increase the EGA's horizontal and vertical
  3213.         ║ T ║     overscan intervals in 350-line modes by modifying the CRTC
  3214.         ║ I ║     horizontal and vertical timing parameters. A reasonable
  3215.         ║ P ║     border, about as wide as that displayed with the VGA, can
  3216.         ╚═══╝     be achieved by adding one or two characters to the
  3217.                   Horizontal Total value and eight or ten scan lines to the
  3218.                   Vertical Total value. The corresponding timing values for
  3219.                   the Horizontal and Vertical Retrace and Blanking registers
  3220.                   must be adjusted accordingly (see  Figure 3-15).
  3221.  
  3222.                   The problem with reprogramming the CRTC in this way is
  3223.                   that the horizontal and vertical frequencies that drive
  3224.                   the video display are somewhat lower than nominal. For
  3225.                   example, with the CRTC values shown in Figure 3-15,
  3226.                   the horizontal scan rate becomes 16.257 MHz / (94
  3227.                   chars/line x 8/char), or 21.62 KHz, which is about 1
  3228.                   percent lower than the nominal horizontal scan frequency
  3229.                   of 21.85 KHz. Similarly, the vertical scan rate becomes
  3230.                   21.62 KHz / 374 lines, or 58 Hz, almost 4 percent lower
  3231.                   than the usual 60 Hz frame rate. Still, these scan rates
  3232.                   are usually within the tolerances of an EGA-compatible
  3233.                   video display.
  3234.  
  3235.  
  3236.                 80-by-25 16-Color Alphanumeric Mode:
  3237.  ───────────────────────────────────────────────────────────────────────────
  3238.  CRTC register  Function                      Setting (default)
  3239.  ───────────────────────────────────────────────────────────────────────────
  3240.  0              Horizontal Total              5CH (5BH)
  3241.  2              Horizontal Blanking Start     54H (53H)
  3242.  3              Horizontal Blanking End       3CH (37H)
  3243.  4              Horizontal Retrace Start      52H (51H)
  3244.  5              Horizontal Retrace End        5CH (5BH)
  3245.  6              Vertical Total                76H (6CH)
  3246.  10H            Vertical Retrace Start        64H (5EH)
  3247.  11H            Vertical Retrace End          25H (2BH)
  3248.  15H            Vertical Blank Start          64H (5EH)
  3249.  16H            Vertical Blank End            11H (0AH)
  3250.  
  3251.  
  3252.                 640-by-350 16-Color Graphics Mode:
  3253.  ───────────────────────────────────────────────────────────────────────────
  3254.  CRTC register  Function                      Setting (default)
  3255.  ───────────────────────────────────────────────────────────────────────────
  3256.  0              Horizontal Total              5CH (5BH)
  3257.  2              Horizontal Blanking Start     53H (53H)
  3258.  3              Horizontal Blanking End       3CH (37H)
  3259.  4              Horizontal Retrace Start      53H (52H)
  3260.  5              Horizontal Retrace End        00H (00H)
  3261.  6              Vertical Total                76H (6CH)
  3262.  10H            Vertical Retrace Start        64H (5EH)
  3263.  11H            Vertical Retrace End          25H (2BH)
  3264.  15H            Vertical Blank Start          64H (5EH)
  3265.  16H            Vertical Blank End            11H (0AH)
  3266.  
  3267.       Figure 3-15.  CRTC parameters for increased border width in 350-line
  3268.       EGA video modes. (Default register values are listed in parentheses.)
  3269.  
  3270.  
  3271.  Avoiding CGA Snow
  3272.  
  3273.  
  3274.       On the CGA, alphanumeric video display modes present a particular
  3275.       programming challenge whenever you are concerned about the speed of
  3276.       video display output. You must program carefully in alphanumeric modes
  3277.       to prevent interference with the display when you read or write data
  3278.       in the CGA's video buffer.
  3279.  
  3280.       Directly accessing the contents of the CGA's video buffer from
  3281.       your program has its pros and cons. On the positive side, it enables
  3282.       your program to completely control the buffer's contents, and thus
  3283.       what is displayed. The negative side is that when both the CPU and
  3284.       the display-refresh circuitry access the buffer at the same time,
  3285.       interference, or "snow," can appear on the display. The snow can be
  3286.       barely noticeable or greatly distracting, depending on the amount of
  3287.       data transferred to or from the video buffer.
  3288.  
  3289.       In general, to avoid snow you must limit CPU accesses to the video
  3290.       buffer to intervals when data is not being fetched from the buffer to
  3291.       refresh the screen. In practice, this means that your program must
  3292.       transfer data to and from the video buffer only when the electron
  3293.       beam in the video display is moving through an overscan or retrace
  3294.       interval.
  3295.  
  3296.       This synchronization can be achieved in several ways, but,
  3297.       unfortunately, all of them introduce a certain amount of hardware
  3298.       dependency into your program. As a general rule, the more hardware-
  3299.       dependent tricks you play, the faster your program runs on a CGA but
  3300.       the less likely it is to run on another video adapter.
  3301.  
  3302.  
  3303.  Blanking the Display
  3304.  
  3305.       One technique for preventing display interference on the CGA is to
  3306.       turn off the electron beam whenever you access the display buffer. You
  3307.       then leave the beam off while data is transferred to or from the video
  3308.       buffer. This method is used in the ROM BIOS routines which scroll the
  3309.       display.
  3310.  
  3311.       The best time to blank the display is when it's blank anyway, at the
  3312.       start of a vertical blanking interval. If you do not take care to turn
  3313.       the electron beam off during the vertical blanking interval, you will
  3314.       instead blank the screen while it is being refreshed. This can produce
  3315.       an annoying flicker or interference stripes.
  3316.  
  3317.       The technique is straightforward (see Listing 3-9). The trick is to
  3318.       synchronize buffer access with the start of a vertical blanking
  3319.       interval. Do this by detecting an interval when vertical blanking is
  3320.       not occurring. Then wait for the next subsequent vertical blanking
  3321.       interval to begin.
  3322.  
  3323.  
  3324.  ───────────────────────────────────────────────────────────────────────────
  3325.  
  3326.       Listing 3-9.  Display alphanumeric text on the CGA by blanking the
  3327.       display.
  3328.  
  3329.  ───────────────────────────────────────────────────────────────────────────
  3330.  
  3331.  
  3332.       The procedure for detecting the start of a vertical blanking interval
  3333.       requires you to first determine a timeout value for the horizontal
  3334.       retrace interval (see Listing 2-2). This value is then used to wait
  3335.       for the last horizontal scan in the current frame. When the last
  3336.       horizontal blanking interval times out, the vertical blanking interval
  3337.       has begun.
  3338.  
  3339.       At this point, your program should explicitly disable the electron
  3340.       beam by resetting bit 3 of the CGA's Mode Control register (port
  3341.       3D8H). When this bit is zeroed, the electron beam is disabled and the
  3342.       display remains dark. While the display is dark, you can move data to
  3343.       or from the video buffer without causing snow. When the data transfer
  3344.       is complete, restore the display by setting bit 3 of the Mode Control
  3345.       register to 1.
  3346.  
  3347.       It is not necessarily desirable to wait for another vertical blanking
  3348.       interval before reenabling the electron beam. If the period during
  3349.       which you transferred data left the screen dark long enough to cause
  3350.       noticeable flicker, waiting until the next vertical retrace will only
  3351.       prolong the duration of the flicker. If you reenable the display
  3352.       somewhere in the middle of a refresh cycle, the flicker will be worse
  3353.       in the top part of the screen but better in the bottom part. Neither
  3354.       situation is ideal; it's up to you to decide which alternative is
  3355.       preferable.
  3356.  
  3357.       The amount of time it takes to access the video buffer determines how
  3358.       long your program must keep the screen dark. Obviously, the longer the
  3359.       screen is dark, the more flicker you perceive. If your program is
  3360.       executed on one of the slower members of the IBM PC family (PC or
  3361.       PC/XT), the flicker effect can become annoying.
  3362.  
  3363.       Consider what might happen whenever you scroll an entire 80-by-25
  3364.       screen up one line. Within the video buffer, 4000 bytes of data must
  3365.       be moved. On a vintage IBM PC, with its 4.77 MHz 8088, this data
  3366.       transfer takes about 21 milliseconds. Since each video frame lasts
  3367.       about 16.7 milliseconds (1/60 second), the screen remains dark for
  3368.       about 1-1/3 frames. The resulting flicker is very noticeable,
  3369.       particularly if the background color is not black. On the other hand,
  3370.       on a PC with a faster CPU, the data transfer takes less time, so the
  3371.       flicker is less apparent.
  3372.  
  3373.  
  3374.  Using the Vertical Blanking Interval
  3375.  
  3376.       A technique that avoids the flicker problem is to access the video
  3377.       buffer only for the duration of the vertical blanking interval.
  3378.       However, this slows data transfer, because you can move only a limited
  3379.       number of bytes of data during a single vertical blanking interval.
  3380.  
  3381.       The limitations here are the duration of the vertical blanking
  3382.       interval (about 4 milliseconds) and the rate at which the CPU can move
  3383.       data in the video buffer. A 4.77 MHz 8088 in a PC or PC/XT can move
  3384.       about 450 words (900 bytes) of data before the vertical blanking
  3385.       interval ends and snow becomes visible. Obviously, a PC with a higher
  3386.       clock speed or with an 80286 or 80386 can move more data during a
  3387.       single vertical blanking interval.
  3388.  
  3389.  
  3390.  Using the Horizontal Blanking Interval
  3391.  
  3392.       If your video output routine synchronizes with the start of horizontal
  3393.       blanking intervals, you have about 7 microseconds in which to access
  3394.       the video buffer at the end of each raster scan line without causing
  3395.       snow (see Listing 3-10). Although 7 microseconds may not seem like
  3396.       much time, it is long enough to move 2 bytes into or out of the video
  3397.       buffer without causing display interference. Since each frame contains
  3398.       200 horizontal blanking intervals, you can significantly increase
  3399.       performance by taking advantage of them.
  3400.  
  3401.  
  3402.  ───────────────────────────────────────────────────────────────────────────
  3403.  
  3404.       Listing 3-10.  Display alphanumeric text on the CGA during horizontal
  3405.       and vertical blanking intervals.
  3406.  
  3407.  ───────────────────────────────────────────────────────────────────────────
  3408.  
  3409.  
  3410.       Because the horizontal blanking interval is so short, synchronization
  3411.       is critical. The technique is parallel to that used for synchronizing
  3412.       with the vertical retrace interval. In this case, you determine the
  3413.       status of the Display Enable signal by testing bit 0 of the CRT Status
  3414.       register (3DAH). When this bit has a value of 1, the Display Enable
  3415.       signal is off and a horizontal blanking interval is in progress.
  3416.  
  3417.       Keep in mind two considerations if you take the trouble to use the
  3418.       horizontal blanking intervals. First, you might as well use the
  3419.       vertical blanking intervals as well, since they're there. Second, you
  3420.       should use MOVS or STOS instructions to do the actual data transfers.
  3421.       The slower MOV mem/reg instruction can take longer than the horizontal
  3422.       blanking interval lasts, so snow isn't eliminated.
  3423.  
  3424.       The IBM ROM BIOS routines that write to the video buffer during
  3425.       horizontal retrace use the sequence
  3426.  
  3427.          mov    ax,bx
  3428.          stosw
  3429.  
  3430.       to move a character and attribute into the buffer without snow.
  3431.       Nevertheless, if you use the same two instructions in a RAM-based
  3432.       program, you see snow on a CGA running on a 4.77 MHz PC. The reason is
  3433.       that, at the point where these instructions are executed, the 4-byte
  3434.       instruction prefetch queue in the 8088 has room for only two more
  3435.       bytes. This means that the STOSW opcode cannot be prefetched. Instead,
  3436.       the 8088 must fetch the opcode from memory before it can  be executed.
  3437.  
  3438.       That last memory access to fetch the STOSW instruction makes the
  3439.       difference. Because accesses to ROM are faster than accesses to RAM,
  3440.       the instruction fetch is slightly faster out of ROM, so no snow is
  3441.       visible because the STOSW can run before the horizontal blanking
  3442.       interval ends. The routine in Listing 3-10 sidesteps the problem by
  3443.       using XCHG AX,BX (a 1-byte opcode) instead of MOV AX,BX (a 2-byte
  3444.       opcode). This avoids the extra instruction fetch, so the code executes
  3445.       fast enough to prevent display interference.
  3446.  
  3447.       Note how the interrupts are disabled in the loop that waits for the
  3448.       start of the horizontal blanking interval. Had an interrupt occurred
  3449.       between the JNZ L06 and the following XCHG AX,BX instructions, the
  3450.       horizontal blanking interval would have ended long before control
  3451.       returned from the interrupt handler. Disabling interrupts while each
  3452.       word is transferred into the video buffer avoids this possible loss of
  3453.       synchronization.
  3454.  
  3455.         ╔═══╗     The routine in Listing 3-10 never explicitly detects the
  3456.         ║ T ║     end of the vertical blanking interval, nor does it count
  3457.         ║ I ║     the 200 horizontal scans in each display refresh cycle.
  3458.         ║ P ║     Instead, the number of bytes that can be transferred
  3459.         ╚═══╝     during each vertical blanking interval (VBcount) is
  3460.                   determined empirically for a "worst case" situation (for
  3461.                   example, for a 4.77 MHz IBM PC).
  3462.  
  3463.                   The most important reason for this imprecision about the
  3464.                   number of bytes to transfer during vertical blanking
  3465.                   intervals is that interrupts can occur anywhere in a video
  3466.                   output routine except where they are explicitly disabled.
  3467.                   For example, clock-tick interrupts and keyboard interrupts
  3468.                   can occur at any time. Because you can't simply disable
  3469.                   all interrupts for the duration, you must design video
  3470.                   output routines to accommodate the unpredictable time
  3471.                   spent in interrupt handlers.
  3472.  
  3473.       The problem of snow is avoided in the hardware design of every other
  3474.       IBM PC and PS/2 video subsystem, including the MDA, EGA, MCGA, and VGA
  3475.       (and even the PCjr). Also, many second-source manufacturers of CGA-
  3476.       compatible adapters design their hardware to eliminate the problem.
  3477.       This means that retrace synchronization loops may not be needed in
  3478.       many applications.
  3479.  
  3480.       If you run a program either on a CGA (with snow) or on a CGA-
  3481.       compatible (without snow), the program should try to determine what
  3482.       type of hardware it is running on (see Appendix C). If the program is
  3483.       running on a machine without snow, it can skip over any vertical and
  3484.       horizontal synchronization loops. The slight extra overhead of
  3485.       detecting the presence of a CGA is repaid in greatly improved
  3486.       performance on video subsystems that have no snow problem.
  3487.  
  3488.  
  3489.  Using All the Video Buffer
  3490.  
  3491.  
  3492.       In alphanumeric video modes, the CGA, EGA, MCGA, and VGA have much
  3493.       more RAM available in their video buffers than is required to display
  3494.       one screen of text. In other words, you can display only a portion of
  3495.       the data in the video buffer at a time. In effect, what you see on the
  3496.       screen is a "window" on the video buffer.
  3497.  
  3498.       For example, in 80-by-25-character alphanumeric modes, only 4000 bytes
  3499.       (80 x 25 x 2 bytes per character) are displayed at any one time.
  3500.       However, the CGA has 16 KB of video RAM, so you can actually store
  3501.       four 80-by-25 screens of data in the buffer. You can then program the
  3502.       CGA's CRT Controller to display any 2000 consecutive characters (4000
  3503.       bytes) in the buffer.
  3504.  
  3505.  
  3506.  CGA Video Pages
  3507.  
  3508.       To program the CGA to display different portions of the buffer, you
  3509.       update two CRT Controller registers. When you call the ROM BIOS to
  3510.       select a video display mode, the BIOS initializes the CRTC to display
  3511.       the first 4000 bytes of the video buffer. It does this by storing 0,
  3512.       the offset of the first character to be displayed, in the CRTC Start
  3513.       Address registers (0CH and 0DH).
  3514.  
  3515.       You can display any arbitrary portion of the CGA's video buffer by
  3516.       storing a video buffer offset in words (not bytes) in the CRTC Start
  3517.       Address registers. The high-order byte of the offset belongs in
  3518.       register 0CH, the low-order byte in register 0DH. For example, loading
  3519.       the Start Address registers with the word offset of the second row
  3520.       (50H) causes the display to begin there (see Listing 3-11).
  3521.  
  3522.       Loading the Start Address registers is a much faster operation than
  3523.       transferring characters into the video buffer. Thus, you might regard
  3524.       the 16 KB video buffer as a 102-line "virtual" screen of which only 25
  3525.       lines can be displayed at a time. When the video buffer is filled with
  3526.       text, you can rapidly display any 25 consecutive lines simply by
  3527.       changing the value in the CRTC Start Address registers.
  3528.  
  3529.  
  3530.  ───────────────────────────────────────────────────────────────────────────
  3531.  
  3532.       Listing 3-11.  Setting the CRTC Start Address registers.
  3533.  
  3534.  ───────────────────────────────────────────────────────────────────────────
  3535.  
  3536.  
  3537.       Whenever you update the Start Address registers, also update the BIOS
  3538.       Video Display Data Area word at 0040:004E (CRT_START). This helps to
  3539.       maintain functionality across video BIOS calls and with MS-DOS.
  3540.  
  3541.       Instead of deciding for yourself which portions of the video buffer to
  3542.       display, you might find it more convenient to adopt the conceptual
  3543.       model of the ROM BIOS, which supports four 80-by-25 (or eight 40-by-
  3544.       25) virtual "pages" in the CGA's video buffer. To simplify addressing,
  3545.       each page starts on a 1 KB (1024-byte) boundary. The four 80-by-25
  3546.       pages thus start at B800:0000, B800:1000, B800:2000, and B800:3000.
  3547.       You can selectively display any video page by calling INT 10H function
  3548.       05H (see Listing 3-12).
  3549.  
  3550.  
  3551.  ───────────────────────────────────────────────────────────────────────────
  3552.  
  3553.       Listing 3-12.  Video page selection using the ROM BIOS.
  3554.  
  3555.  ───────────────────────────────────────────────────────────────────────────
  3556.  
  3557.  
  3558.         ╔═══╗     A technique that can improve CGA performance is to display
  3559.         ║ T ║     one video page while you fill another (nondisplayed) video
  3560.         ║ I ║     page with data. Then you display the newly filled video
  3561.         ║ P ║     page and make the previous page available for more data
  3562.         ╚═══╝     transfers. Design your user interface so that while the
  3563.                   user reads the display, a nondisplayed video page is
  3564.                   filled with the next screen of information. Careful use of
  3565.                   the video pages can make screen updates appear
  3566.                   "instantaneous."
  3567.  
  3568.                   You must still avoid display interference by using one of
  3569.                   the techniques for synchronizing the update with vertical
  3570.                   or horizontal blanking intervals, even if you write to a
  3571.                   nondisplayed portion of the buffer.
  3572.  
  3573.  
  3574.  EGA, MCGA, and VGA Video Pages
  3575.  
  3576.       With the EGA, MCGA, and VGA, the techniques for using video RAM are
  3577.       similar to those used on the CGA. The Start Address registers in the
  3578.       CRT Controller are mapped to the same I/O port addresses as they are
  3579.       on the CGA's CRTC. Furthermore, the video BIOS supports video pages
  3580.       with the same interface used for the CGA. This simplifies writing a
  3581.       program to run on all of these video subsystems.
  3582.  
  3583.         ╔═══╗     One handy feature of the CRTC on the EGA, the MCGA, the
  3584.         ║ T ║     VGA, and some but not all CGA look-alikes is that the
  3585.         ║ I ║     Start Address registers can be read as well as written.
  3586.         ║ P ║     This feature can be useful in programming these registers
  3587.         ╚═══╝     directly, because you can determine their contents at any
  3588.                   time simply by inspecting them.
  3589.  
  3590.  
  3591.  Cursor Control
  3592.  
  3593.  
  3594.       The CRT Controller also controls the size and screen location of the
  3595.       hardware cursor in alphanumeric modes. You specify the cursor's size
  3596.       by loading a CRTC register with values that indicate its top and
  3597.       bottom lines. The top line is 0; the value for the bottom line depends
  3598.       on the size of the displayed character matrix--7 for an 8-by-8 matrix
  3599.       and 0DH for a 9-by-14 matrix. The cursor's location is specified with
  3600.       a word offset into the video buffer, exactly as you specify the CRT
  3601.       Controller's start address.
  3602.  
  3603.  
  3604.  Cursor Size on the MDA and CGA
  3605.  
  3606.       CRTC registers 0AH and 0BH control the cursor size on all IBM PC and
  3607.       PS/2 video subsystems. On the MDA and the CGA, the low-order five bits
  3608.       of register 0AH (Cursor Start) indicate the top line of the displayed
  3609.       cursor. The low-order five bits of register 0BH (Cursor End) specify
  3610.       the bottom line.
  3611.  
  3612.       Changing the size of the hardware cursor is a matter of programming
  3613.       these two registers. For example, to display a "block" cursor, which
  3614.       is a rectangle filling an entire character space, set the Cursor Start
  3615.       register to 0 and the Cursor End register to one less than the height
  3616.       of the character matrix. To display the ROM BIOS's default cursor, set
  3617.       the Cursor Start and Cursor End registers to the values for the last
  3618.       two lines of the character matrix, as is done in Listing 3-13.
  3619.  
  3620.       In most applications, however, you can use INT 10H function 1 (Set
  3621.       Cursor Type) to change the cursor's size. Using this function ensures
  3622.       compatibility with the video BIOS on all IBM PC and PS/2 video
  3623.       subsystems. Although performing the software interrupt and executing
  3624.       the BIOS routine is slower than programming the CRTC directly, in
  3625.       general you modify the cursor size so infrequently that you'll never
  3626.       notice the slight slowing of your program.
  3627.  
  3628.       Also, the BIOS routine maintains the current cursor size in two bytes
  3629.       in the Video Display Data Area at 0040:0060 (CURSOR_MODE). On the MDA
  3630.       and CGA, the CRTC's Cursor Start and Cursor End registers are read-
  3631.       only registers, so you might as well use the BIOS to keep track of the
  3632.       current state of the cursor. The byte at 0040:0060 represents the
  3633.       value in 6845 register 0AH (Cursor Start), and the following byte, at
  3634.       0040:0061, represents register 0BH (Cursor End). If you do bypass the
  3635.       BIOS routine and program the 6845 directly, keep the values in
  3636.       CURSOR_MODE up to date.
  3637.  
  3638.  
  3639.  ───────────────────────────────────────────────────────────────────────────
  3640.  
  3641.       Listing 3-13.  Setting the cursor size.
  3642.  
  3643.  ───────────────────────────────────────────────────────────────────────────
  3644.  
  3645.  
  3646.  Cursor Location on the MDA and CGA
  3647.  
  3648.       To control the cursor's location, load a buffer offset into the CRTC's
  3649.       Cursor Location High (0EH) and Cursor Location Low (0FH) registers
  3650.       (see Listing 3-14). The Cursor Location offset is relative to the
  3651.       start of the video buffer. If you have changed the CRTC Start Address
  3652.       registers, you must adjust for the new Start Address offset in
  3653.       calculating the Cursor Location offset.
  3654.  
  3655.  
  3656.  ───────────────────────────────────────────────────────────────────────────
  3657.  
  3658.       Listing 3-14.  Setting the cursor location.
  3659.  
  3660.  ───────────────────────────────────────────────────────────────────────────
  3661.  
  3662.  
  3663.  MCGA Cursor Control
  3664.  
  3665.       The MCGA's CRTC doubles the values you store in the Cursor Start and
  3666.       Cursor End registers and doubles the number of scan lines in the
  3667.       displayed cursor. Thus, the size of the MCGA's alphanumeric cursor is
  3668.       a multiple of two scan lines.
  3669.  
  3670.       This doubling of the Cursor Start and Cursor End values allows you to
  3671.       specify default alphanumeric cursor sizes with the same values you
  3672.       would use on a CGA. For example, in the MCGA's default alphanumeric
  3673.       modes, the character matrix is 16 lines high. If you set Cursor Start
  3674.       to 6 and Cursor End to 7, as you would in a CGA alphanumeric mode, you
  3675.       see the MCGA's cursor at the bottom of the character matrix in lines
  3676.       0CH through 0FH. In this way the MCGA's Cursor Start and End registers
  3677.       emulate the CGA's despite the MCGA's taller character matrix.
  3678.  
  3679.       However, there are several differences in the way the MCGA interprets
  3680.       the Cursor Start and Cursor End values (see Figure 3-16). On the
  3681.       MCGA, only the four low-order bits of the Cursor Start and Cursor End
  3682.       values are significant. Furthermore, since the character matrix can be
  3683.       at most 16 scan lines high, Cursor Start and Cursor End values are
  3684.       usually limited to the range 0 through 7. Values greater than 7 can
  3685.       produce a cursor that wraps around to the top of the character matrix
  3686.       (see Figure 3-16e).
  3687.  
  3688.  
  3689.  EGA and VGA Cursor Control
  3690.  
  3691.       On the EGA and the VGA, the Cursor Start, Cursor End, Cursor Location
  3692.       High, and Cursor Location Low registers are mapped to the same CRTC
  3693.       register numbers as on the MDA and CGA. This can lead to trouble if
  3694.       you're concerned about portability and need to write to the CRTC
  3695.       registers directly. This is because the EGA and VGA Cursor Start and
  3696.       Cursor End registers do not function exactly as do those on the MDA,
  3697.       CGA, or MCGA.
  3698.  
  3699.       On the EGA, the value you specify for the Cursor End register must be
  3700.       1 greater than the bottom line of the cursor (see Figure 3-17). The
  3701.       EGA's CRT Controller displays the alphanumeric cursor from the
  3702.       character scan line specified in the Cursor Start register to the line
  3703.       specified by the Cursor End register minus 1.
  3704.  
  3705.       If the Cursor End value is less than the Cursor Start value, the
  3706.       cursor wraps around the character matrix. If the low-order four bits
  3707.       of the Cursor Start and Cursor End values are equal, the cursor
  3708.       appears only on the single line specified in the Cursor Start
  3709.       register. Finally, the Cursor End value must be less than the number
  3710.       of scan lines in the character matrix. Otherwise, the CRT Controller
  3711.       displays a full-height cursor regardless of the Cursor Start
  3712.       register's value.
  3713.  
  3714.  
  3715.       Cursor Start = 2                   Cursor Start = 2
  3716.       Cursor End = 2                     Cursor End = 4
  3717.                        ┌─╥─╥─╥─╥─╥─╥─╥─┐                  ┌─╥─╥─╥─╥─╥─╥─╥─┐
  3718.        Scan line 0-1   │ ║ ║ ║ ║ ║ ║ ║ │  Scan line 0-1   │ ║ ║ ║ ║ ║ ║ ║ │
  3719.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3720.        Scan line 2-3   │ ║ ║ ║ ║ ║ ║ ║ │  Scan line 2-3   │ ║ ║ ║ ║ ║ ║ ║ │
  3721.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3722.        Scan line 4-5   │█║█║█║█║█║█║█║█│  Scan line 4-5   │█║█║█║█║█║█║█║█│
  3723.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3724.        Scan line 6-7   │ ║ ║ ║ ║ ║ ║ ║ │  Scan line 6-7   │█║█║█║█║█║█║█║█│
  3725.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3726.        Scan line 8-9   │ ║ ║ ║ ║ ║ ║ ║ │  Scan line 8-9   │█║█║█║█║█║█║█║█│
  3727.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3728.        Scan line 10-11 │ ║ ║ ║ ║ ║ ║ ║ │  Scan line 10-11 │ ║ ║ ║ ║ ║ ║ ║ │
  3729.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3730.        Scan line 12-13 │ ║ ║ ║ ║ ║ ║ ║ │  Scan line 12-13 │ ║ ║ ║ ║ ║ ║ ║ │
  3731.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3732.        Scan line 14-15 │ ║ ║ ║ ║ ║ ║ ║ │  Scan line 14-15 │ ║ ║ ║ ║ ║ ║ ║ │
  3733.                        └─╨─╨─╨─╨─╨─╨─╨─┘                  └─╨─╨─╨─╨─╨─╨─╨─┘
  3734.       a.                                 b.
  3735.  
  3736.  
  3737.       Cursor Start = 3                   Cursor Start = 4
  3738.       Cursor End = 7                     Cursor End = 2
  3739.                        ┌─╥─╥─╥─╥─╥─╥─╥─┐                  ┌─╥─╥─╥─╥─╥─╥─╥─┐
  3740.        Scan line 0-1   │ ║ ║ ║ ║ ║ ║ ║ │  Scan line 0-1   │ ║ ║ ║ ║ ║ ║ ║ │
  3741.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3742.        Scan line 2-3   │ ║ ║ ║ ║ ║ ║ ║ │  Scan line 2-3   │ ║ ║ ║ ║ ║ ║ ║ │
  3743.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3744.        Scan line 4-5   │ ║ ║ ║ ║ ║ ║ ║ │  Scan line 4-5   │ ║ ║ ║ ║ ║ ║ ║ │
  3745.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3746.        Scan line 6-7   │█║█║█║█║█║█║█║█│  Scan line 6-7   │ ║ ║ ║ ║ ║ ║ ║ │
  3747.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3748.        Scan line 8-9   │█║█║█║█║█║█║█║█│  Scan line 8-9   │ ║ ║ ║ ║ ║ ║ ║ │
  3749.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3750.        Scan line 10-11 │█║█║█║█║█║█║█║█│  Scan line 10-11 │ ║ ║ ║ ║ ║ ║ ║ │
  3751.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3752.        Scan line 12-13 │█║█║█║█║█║█║█║█│  Scan line 12-13 │ ║ ║ ║ ║ ║ ║ ║ │
  3753.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡                  ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3754.        Scan line 14-15 │█║█║█║█║█║█║█║█│  Scan line 14-15 │ ║ ║ ║ ║ ║ ║ ║ │
  3755.                        └─╨─╨─╨─╨─╨─╨─╨─┘                  └─╨─╨─╨─╨─╨─╨─╨─┘
  3756.       c.                                 d.
  3757.  
  3758.  
  3759.       Cursor Start = 3
  3760.       Cursor End = 8
  3761.                        ┌─╥─╥─╥─╥─╥─╥─╥─┐
  3762.        Scan line 0-1   │█║█║█║█║█║█║█║█│
  3763.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3764.        Scan line 2-3   │ ║ ║ ║ ║ ║ ║ ║ │
  3765.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3766.        Scan line 4-5   │ ║ ║ ║ ║ ║ ║ ║ │
  3767.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3768.        Scan line 6-7   │█║█║█║█║█║█║█║█│
  3769.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3770.        Scan line 8-9   │█║█║█║█║█║█║█║█│
  3771.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3772.        Scan line 10-11 │█║█║█║█║█║█║█║█│
  3773.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3774.        Scan line 12-13 │█║█║█║█║█║█║█║█│
  3775.                        ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3776.        Scan line 14-15 │█║█║█║█║█║█║█║█│
  3777.                        └─╨─╨─╨─╨─╨─╨─╨─┘
  3778.       e.
  3779.  
  3780.       Figure 3-16.  Sample MCGA alphanumeric cursor settings for an 8-by-16
  3781.       character matrix.
  3782.  
  3783.  
  3784.       The VGA's Cursor Start and Cursor End values (see Figure 3-18) work
  3785.       slightly differently than do the EGA's. The VGA's Cursor End value
  3786.       indicates the last line of the displayed cursor (not the last line
  3787.       plus 1), and the displayed cursor does not wrap around to the top of
  3788.       the character matrix if the Cursor End value is less than the Cursor
  3789.       Start value. (Compare Figures 3-17 and 3-18.)
  3790.  
  3791.  
  3792.       Cursor Start = 4                     Cursor Start = 4
  3793.       Cursor End = 4                       Cursor End = 8
  3794.                    ┌─╥─╥─╥─╥─╥─╥─╥─┐                    ┌─╥─╥─╥─╥─╥─╥─╥─┐
  3795.        Scan line 0 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 0 │█║█║█║█║█║█║█║█│
  3796.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3797.        Scan line 1 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 1 │█║█║█║█║█║█║█║█│
  3798.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3799.        Scan line 2 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 2 │█║█║█║█║█║█║█║█│
  3800.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3801.        Scan line 3 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 3 │█║█║█║█║█║█║█║█│
  3802.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3803.        Scan line 4 │█║█║█║█║█║█║█║█│        Scan line 4 │█║█║█║█║█║█║█║█│
  3804.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3805.        Scan line 5 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 5 │█║█║█║█║█║█║█║█│
  3806.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3807.        Scan line 6 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 6 │█║█║█║█║█║█║█║█│
  3808.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3809.        Scan line 7 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 7 │█║█║█║█║█║█║█║█│
  3810.                    └─╨─╨─╨─╨─╨─╨─╨─┘                    └─╨─╨─╨─╨─╨─╨─╨─┘
  3811.       a.                                   b.
  3812.  
  3813.  
  3814.       Cursor Start = 4                     Cursor Start = 4
  3815.       Cursor End = 7                       Cursor End = 2
  3816.                    ┌─╥─╥─╥─╥─╥─╥─╥─┐                    ┌─╥─╥─╥─╥─╥─╥─╥─┐
  3817.        Scan line 0 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 0 │█║█║█║█║█║█║█║█│
  3818.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3819.        Scan line 1 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 1 │█║█║█║█║█║█║█║█│
  3820.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3821.        Scan line 2 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 2 │ ║ ║ ║ ║ ║ ║ ║ │
  3822.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3823.        Scan line 3 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 3 │ ║ ║ ║ ║ ║ ║ ║ │
  3824.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3825.        Scan line 4 │█║█║█║█║█║█║█║█│        Scan line 4 │█║█║█║█║█║█║█║█│
  3826.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3827.        Scan line 5 │█║█║█║█║█║█║█║█│        Scan line 5 │█║█║█║█║█║█║█║█│
  3828.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3829.        Scan line 6 │█║█║█║█║█║█║█║█│        Scan line 6 │█║█║█║█║█║█║█║█│
  3830.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3831.        Scan line 7 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 7 │█║█║█║█║█║█║█║█│
  3832.                    └─╨─╨─╨─╨─╨─╨─╨─┘                    └─╨─╨─╨─╨─╨─╨─╨─┘
  3833.       c.                                   d.
  3834.  
  3835.  
  3836.       Cursor Start = 4
  3837.       Cursor End = 0
  3838.                    ┌─╥─╥─╥─╥─╥─╥─╥─┐
  3839.        Scan line 0 │ ║ ║ ║ ║ ║ ║ ║ │
  3840.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3841.        Scan line 1 │ ║ ║ ║ ║ ║ ║ ║ │
  3842.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3843.        Scan line 2 │ ║ ║ ║ ║ ║ ║ ║ │
  3844.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3845.        Scan line 3 │ ║ ║ ║ ║ ║ ║ ║ │
  3846.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3847.        Scan line 4 │█║█║█║█║█║█║█║█│
  3848.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3849.        Scan line 5 │█║█║█║█║█║█║█║█│
  3850.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3851.        Scan line 6 │█║█║█║█║█║█║█║█│
  3852.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3853.        Scan line 7 │█║█║█║█║█║█║█║█│
  3854.                    └─╨─╨─╨─╨─╨─╨─╨─┘
  3855.       e.
  3856.  
  3857.       Figure 3-17.  Sample EGA alphanumeric cursor settings for an 8-by-8
  3858.       character matrix.
  3859.  
  3860.  
  3861.         ╔═══╗     Bits 5 and 6 of the Cursor End register (0BH) on the EGA
  3862.         ║ T ║     and VGA control the rightward skew of the cursor. If bits
  3863.         ║ I ║     5 and 6 are not 0, the cursor appears one, two, or three
  3864.         ║ P ║     characters to the right of the location that the Cursor
  3865.         ╚═══╝     Location registers specify. For most applications, the
  3866.                   cursor skew should be 0.
  3867.  
  3868.  
  3869.       Cursor Start = 4                     Cursor Start = 4
  3870.       Cursor End = 4                       Cursor End = 8
  3871.                    ┌─╥─╥─╥─╥─╥─╥─╥─┐                    ┌─╥─╥─╥─╥─╥─╥─╥─┐
  3872.        Scan line 0 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 0 │ ║ ║ ║ ║ ║ ║ ║ │
  3873.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3874.        Scan line 1 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 1 │ ║ ║ ║ ║ ║ ║ ║ │
  3875.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3876.        Scan line 2 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 2 │ ║ ║ ║ ║ ║ ║ ║ │
  3877.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3878.        Scan line 3 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 3 │ ║ ║ ║ ║ ║ ║ ║ │
  3879.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3880.        Scan line 4 │█║█║█║█║█║█║█║█│        Scan line 4 │█║█║█║█║█║█║█║█│
  3881.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3882.        Scan line 5 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 5 │█║█║█║█║█║█║█║█│
  3883.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3884.        Scan line 6 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 6 │█║█║█║█║█║█║█║█│
  3885.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3886.        Scan line 7 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 7 │█║█║█║█║█║█║█║█│
  3887.                    └─╨─╨─╨─╨─╨─╨─╨─┘                    └─╨─╨─╨─╨─╨─╨─╨─┘
  3888.       a.                                   b.
  3889.  
  3890.  
  3891.       Cursor Start = 4                     Cursor Start = 4
  3892.       Cursor End = 7                       Cursor End = 2
  3893.                    ┌─╥─╥─╥─╥─╥─╥─╥─┐                    ┌─╥─╥─╥─╥─╥─╥─╥─┐
  3894.        Scan line 0 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 0 │ ║ ║ ║ ║ ║ ║ ║ │
  3895.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3896.        Scan line 1 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 1 │ ║ ║ ║ ║ ║ ║ ║ │
  3897.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3898.        Scan line 2 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 2 │ ║ ║ ║ ║ ║ ║ ║ │
  3899.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3900.        Scan line 3 │ ║ ║ ║ ║ ║ ║ ║ │        Scan line 3 │ ║ ║ ║ ║ ║ ║ ║ │
  3901.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3902.        Scan line 4 │█║█║█║█║█║█║█║█│        Scan line 4 │ ║ ║ ║ ║ ║ ║ ║ │
  3903.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3904.        Scan line 5 │█║█║█║█║█║█║█║█│        Scan line 5 │ ║ ║ ║ ║ ║ ║ ║ │
  3905.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3906.        Scan line 6 │█║█║█║█║█║█║█║█│        Scan line 6 │ ║ ║ ║ ║ ║ ║ ║ │
  3907.                    ╞═╬═╬═╬═╬═╬═╬═╬═╡                    ╞═╬═╬═╬═╬═╬═╬═╬═╡
  3908.        Scan line 7 │█║█║█║█║█║█║█║█│        Scan line 7 │ ║ ║ ║ ║ ║ ║ ║ │
  3909.                    └─╨─╨─╨─╨─╨─╨─╨─┘                    └─╨─╨─╨─╨─╨─╨─╨─┘
  3910.        c.                                  d.
  3911.  
  3912.       Figure 3-18.  Sample VGA alphanumeric cursor settings for an 8-by-8
  3913.       character matrix.
  3914.  
  3915.  
  3916.  ROM BIOS Cursor Emulation
  3917.  
  3918.       The ROM BIOS routine for INT 10H function 01H uses the values in 80x86
  3919.       registers CH and CL to program the CRTC Cursor Start and Cursor End
  3920.       registers (see Listing 3-13). On an MDA or CGA, these values are
  3921.       simply copied into the CRTC registers. On an EGA or VGA, however, the
  3922.       BIOS can scale these values relative to an 8-line character matrix and
  3923.       program the CRTC with the scaled results. This scaling is called
  3924.       "cursor emulation" in IBM's technical manuals.
  3925.  
  3926.       When ROM BIOS cursor emulation is in effect, the values you specify to
  3927.       INT 10H function 01H represent the position of the start and end of
  3928.       the displayed cursor relative to an 8-line character matrix. When the
  3929.       actual character matrix is larger than 8 lines, the BIOS routine
  3930.       adjusts the Cursor Start and Cursor End values to maintain the
  3931.       cursor's relative location in the matrix.
  3932.  
  3933.       Consider what happens, for example, when you call INT 10H function 01H
  3934.       with CH = 6 and CL = 7. If the character matrix is 8 lines high, the
  3935.       cursor appears on the bottom two lines. (This is the usual cursor in
  3936.       200-line video modes.) If the character matrix is 14 lines high,
  3937.       however, the BIOS routine adjusts the Cursor Start and Cursor End
  3938.       values so that the cursor appears near the bottom of the matrix; that
  3939.       is, on lines 0BH and 0CH. Thus, cursor emulation allows programs that
  3940.       change the cursor size with INT 10H function 01H to run unchanged
  3941.       regardless of the size of the character matrix.
  3942.  
  3943.       The BIOS carries out cursor emulation in INT 10H functions whenever
  3944.       bit 0 of the Video Display Data Area INFO byte (0040:0087) is set to
  3945.       0. (This is the power-on default for both the EGA and the VGA.) You
  3946.       can disable cursor emulation by setting this bit to 1 before calling
  3947.       INT 10H function 01H. On the EGA, you must set and reset the bit
  3948.       directly, but on the VGA, you should use INT 10H function 12H to set
  3949.       the bit's value.
  3950.  
  3951.         ╔═══╗     On the EGA, cursor emulation is implemented by adding 5 to
  3952.         ║ T ║     any Cursor Start or Cursor End value greater than 4. This
  3953.         ║ I ║     works well when the character matrix is the default 14
  3954.         ║ P ║     lines high. For character matrices of other heights,
  3955.         ╚═══╝     however, this simple algorithm breaks down and computes
  3956.                   the Cursor Start and Cursor End values incorrectly. You
  3957.                   should therefore disable cursor emulation when you program
  3958.                   the EGA's character generator to change the size of its
  3959.                   character matrix (see Chapter 10).
  3960.  
  3961.                   On the VGA, the cursor-emulation computation takes into
  3962.                   account the height of the character matrix, so the
  3963.                   emulated cursor is displayed correctly regardless of
  3964.                   character matrix dimensions.
  3965.  
  3966.  
  3967.  An Invisible Cursor
  3968.  
  3969.       You can make the cursor "invisible" by programming the CRT Controller
  3970.       to display it at an offscreen location. Do this by setting the Cursor
  3971.       High and Cursor Low registers to a non-displayed buffer offset.
  3972.       Another way to make the cursor vanish is to load the Cursor Start and
  3973.       Cursor End registers with values below the displayed character matrix.
  3974.       On the MDA, CGA, and VGA, load the Cursor Start register with the
  3975.       value 20H to make the cursor disappear. On the EGA, set Cursor Start
  3976.       to a value greater than or equal to the number of lines in the
  3977.       character matrix and set Cursor End to 0 (see Listing 3-15).
  3978.  
  3979.  
  3980.  ───────────────────────────────────────────────────────────────────────────
  3981.  
  3982.       Listing 3-15.  An invisible alphanumeric cursor for IBM video
  3983.       subsystems.
  3984.  
  3985.  ───────────────────────────────────────────────────────────────────────────
  3986.  
  3987.  
  3988.  
  3989.                           4  Graphics Modes
  3990.  
  3991.  
  3992.                          Using Graphics Modes
  3993.  
  3994.                      Mapping Pixels to the Screen
  3995.                             CGA ■ HGC ■ EGA
  3996.                  Hercules InColor Card ■ MCGA and VGA
  3997.  
  3998.                            Pixel Coordinates
  3999.                        Pixel Coordinate Scaling
  4000.                              Aspect Ratio
  4001.  
  4002.                        Pixel Display Attributes
  4003.                             CGA ■ HGC ■ EGA
  4004.                   Hercules InColor Card ■ MCGA ■ VGA
  4005.  
  4006.  
  4007.  
  4008.       This chapter covers the basics of graphics-mode programming on the
  4009.       CGA, EGA, MCGA, VGA, and Hercules cards. First the chapter describes
  4010.       how pixels are represented in the video buffer and how they are mapped
  4011.       to the screen. Then it focuses on pixel display attributes; that is,
  4012.       on how to determine a pixel's color, intensity, and blinking.
  4013.  
  4014.  
  4015.  Using Graphics Modes
  4016.  
  4017.  
  4018.       In graphics modes, your program can manipulate the color of every
  4019.       pixel on the display. For this reason, graphics modes are sometimes
  4020.       called All Points Addressable (APA) modes. Because you have control
  4021.       over each pixel in the displayed image, you can construct complex
  4022.       geometric images, fill arbitrary areas of the screen with solid colors
  4023.       or blends of colors, and display animated images that move smoothly
  4024.       across the screen.
  4025.  
  4026.       Most programmers, however, use graphics modes only when pixel-by-pixel
  4027.       control over the screen is essential to an application. The reason:
  4028.       The price you pay for total control over the screen is increased
  4029.       source code complexity and decreased performance. A simple comparison
  4030.       of the amount of data required to display a full screen of information
  4031.       in alphanumeric and in graphics modes shows why.
  4032.  
  4033.       For example, to display 25 rows of 16-color, 80-column text in
  4034.       alphanumeric mode on an EGA, you need to store 4000 bytes (80 x 25 x
  4035.       2) in the video buffer. With a 350-line monitor, the text is displayed
  4036.       with 640-by-350-pixel resolution. Obtaining the same resolution in a
  4037.       16-color graphics mode requires 112,000 bytes (640 x 350 x 4 bits per
  4038.       pixel / 8 bits per byte). Obviously, a program that must manipulate
  4039.       112,000 bytes of data is more complex and slower than a program that
  4040.       manipulates only 4000 bytes.
  4041.  
  4042.       Of course, the performance penalty for using graphics-mode video
  4043.       output is less apparent when you use a faster computer, such as an
  4044.       80286-based or 80386-based machine whose CPU runs at a high clock
  4045.       speed. Still, before you leap into graphics-mode programming, you
  4046.       should carefully consider the alternatives. Alphanumeric modes are
  4047.       sufficient for displaying text and simple block graphics and, hence,
  4048.       for the majority of real-world applications.
  4049.  
  4050.         ╔═══╗     An alternative in some applications is to use a video
  4051.         ║ T ║     subsystem that has an alphanumeric character generator
  4052.         ║ I ║     capable of displaying RAM-based character sets. (The EGA,
  4053.         ║ P ║     MCGA, VGA, HGC+, and InColor Card all have this
  4054.         ╚═══╝     capability.) With these subsystems, you can design
  4055.                   "characters" that are actually subunits of a larger
  4056.                   graphics image and then assemble the subunits into a
  4057.                   complete image in an alphanumeric mode. (Chapter 10
  4058.                   explains the technique in detail.)
  4059.  
  4060.  
  4061.  Mapping Pixels to the Screen
  4062.  
  4063.  
  4064.       PC and PS/2 video subsystems store pixel data as groups of bits that
  4065.       represent pixel values. The color of each pixel on the display is
  4066.       determined, directly or indirectly, by its pixel value. Furthermore,
  4067.       no pixel value is ever represented by more than eight bits, so one or
  4068.       more pixels are mapped into every byte in the video buffer.
  4069.  
  4070.       The format of the pixel map or bit map in the video buffer depends on
  4071.       the number of bits required to represent each pixel, as well as on the
  4072.       architecture of the video RAM. Obviously, the number of colors that a
  4073.       given graphics mode can display at one time is determined by the
  4074.       number of bits used to represent each pixel.
  4075.  
  4076.       When pixel values are smaller than eight bits, pixels are mapped in
  4077.       bit fields from left to right across each byte. The leftmost pixel
  4078.       represented in a given byte is always found in that byte's high-order
  4079.       bit(s). This is true on all PC and PS/2 video subsystems.
  4080.  
  4081.  
  4082.  Color Graphics Adapter
  4083.  
  4084.       On the CGA, each pixel is represented either by two bits, as in 320-
  4085.       by-200 4-color mode (see Figure 4-1a) or by one bit, as in 640-by-200
  4086.       2-color mode (see Figure 4-1b). Because two bits are used to
  4087.       represent pixels in 320-by-200 mode, a pixel can have any of four
  4088.       different pixel values, so this mode can display four different colors
  4089.       at a time. Only one bit is used to represent pixel values in 640-by-
  4090.       200 mode, so that mode can display only two colors at a time.
  4091.  
  4092.  
  4093.                    Bit fields in one byte
  4094.       ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
  4095.       │           │           │           │           │
  4096.       │  0     0  │  0     1  │  1     0  │  1     1  │
  4097.       │           │           │           │           │
  4098.       └─────┼─────┴─────┼─────┴─────┼─────┴─────┼─────┘
  4099.             │           │           │           │
  4100.           ┌──┐       ┌──┐       ┌──┐       ┌──┐
  4101.           │   │       │▒▒▒│       │▓▓▓│       │███│ Pixels on screen
  4102.           └───┘       └───┘       └───┘       └───┘
  4103.       a.
  4104.  
  4105.                    Bit fields in one byte
  4106.       ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
  4107.       │           │           │           │           │
  4108.       │  0     0  │  0     1  │  1     0  │  1     1  │
  4109.       │           │           │           │           │
  4110.       └──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┘
  4111.          │     │     │     │     │     │     │     │
  4112.        ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐
  4113.        │   │ │   │ │   │ │▒▒▒│ │▒▒▒│ │   │ │▒▒▒│ │▒▒▒│ Pixels on screen
  4114.        └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘
  4115.       b.
  4116.  
  4117.       Figure 4-1.  Pixel mapping in CGA graphics modes.
  4118.  
  4119.  
  4120.       The pixel data is mapped in two interleaved halves of the CGA's 16 KB
  4121.       video buffer. Data for the 100 even-numbered scan lines starts at
  4122.       B800:0000, and data for the odd-numbered scan lines starts at
  4123.       B800:2000 (see Figure 4-2). If the scan lines are numbered
  4124.       consecutively from 0, the half of the video buffer in which the nth
  4125.       scan line is represented can be determined by calculating n MOD 2.
  4126.  
  4127.         ╔═══╗     This two-way buffer interleave lets the CGA's CRT
  4128.         ║ T ║     Controller display 200 lines of graphics data without
  4129.         ║ I ║     overflowing the 7-bit CRTC vertical timing registers. In
  4130.         ║ P ║     CGA graphics modes, the CRTC is set up to display 100 rows
  4131.         ╚═══╝     of "characters," each two scan lines high. The top (even)
  4132.                   line of each character is derived from the first half of
  4133.                   the video buffer, and the bottom (odd) line is read from
  4134.                   the second half of the buffer.
  4135.  
  4136.  
  4137.       B800:0000 ┌───────────────────┐                             ┌─────────
  4138.                 │                   ├─────────────── Scan line 0 │█████████
  4139.            0050 ├──────V────────────┤                             │░░░░D░░░░
  4140.                 │      i            ├──────┐  ┌───── Scan line 1 │████i████
  4141.            00A0 ├──────d────────────┤      │  │                   │░░░░s░░░░
  4142.                 │      e            ├────┐ └──┼───── Scan line 2 │████p████
  4143.            00F0 └──────o────────────┘    │    │                   │░░░░l░░░░
  4144.                 │                   │    │    │  ┌── Scan line 3 │████a████
  4145.                  ─ ─ ─ B ─ ─ ─ ─ ─ ─     │    │  │                │░░░░y░░░░
  4146.                 │      u            │    └────┼──┼── Scan line 4 │█████████
  4147.                  ─ ─ ─ f ─ ─ ─ ─ ─ ─          │  │                │░░░░░░░░░
  4148.                 │      f            │         │  │ ┌ Scan line 5 │█████████
  4149.       B800:2000 ┌──────e────────────┐         │  │ │
  4150.                 │      r            ├─────────┘  │ │
  4151.            2050 ├───────────────────┤            │ │
  4152.                 │                   ├────────────┘ │
  4153.            20A0 ├───────────────────┤              │
  4154.                 │                   ├──────────────┘
  4155.            20F0 └───────────────────┘
  4156.                 │                   │
  4157.                  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
  4158.                 │                   │
  4159.                  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
  4160.                 │                   │
  4161.                  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
  4162.  
  4163.       Figure 4-2.  Video buffer interleave in CGA graphics modes.
  4164.  
  4165.  
  4166.  Hercules Graphics Card
  4167.  
  4168.       In 720-by-348 graphics mode on the HGC and HGC+, pixel representation
  4169.       is similar to that in the CGA's 640-by-200 2-color graphics mode. One
  4170.       bit represents each pixel, so only two "colors" (pixel on or pixel
  4171.       off) are available.
  4172.  
  4173.       However, the HGC's 348 90-byte lines of pixel data are interleaved
  4174.       using four separate areas of the video buffer (see Figure 4-3), each
  4175.       containing 87 (348 / 4) lines of data. With this buffer organization,
  4176.       the area in the buffer in which the nth scan line is represented can
  4177.       be determined by n MOD 4.
  4178.  
  4179.       On Hercules video adapters, the four-way interleave allows the CRTC to
  4180.       be programmed to display 87 rows of characters which are four scan
  4181.       lines high. (See Listing 2-4 in Chapter 2.) Each of the four scan
  4182.       lines in a "character" is read from the corresponding location in one
  4183.       of the four interleaved portions of the video buffer.
  4184.  
  4185.  
  4186.       B000:0000 ┌───────────────────┐                            ┌─────────
  4187.                 │                   ├────────────── Scan line 0 │█████████
  4188.            005A ├────────V──────────┤                            │░░░░D░░░░
  4189.                 │        i          ├─┐  ┌───────── Scan line 1 │████i████
  4190.            00B4 └────────d──────────┘ │  │                       │░░░░s░░░░
  4191.                 │        e          │ │  │    ┌──── Scan line 2 │████p████
  4192.                  ─ ─ ─ ─ o ─ ─ ─ ─ ─  │  │    │                  │░░░░l░░░░
  4193.                 │                   │ │  │ ┌──┼──── Scan line 3 │████a████
  4194.                  ─ ─ ─ ─ B ─ ─ ─ ─ ─  │  │ │  │                  │░░░░y░░░░
  4195.                 │        u          │ └──┼─┼──┼──── Scan line 4 │█████████
  4196.       B000:2000 ┌────────f──────────┐    │ │  │                  │░░░░░░░░░
  4197.                 │        f          ├────┘ │┌─┼──── Scan line 5 │█████████
  4198.            205A ├────────e──────────┤      ││ │                  │░░░░░░░░░
  4199.                 │        r          ├──────┼┘ │  ┌─ Scan line 6 │█████████
  4200.            20B4 └───────────────────┘      │  │  │               │░░░░░░░░░
  4201.                 │                   │   ┌──┼──┼──┼─ Scan line 7 │█████████
  4202.                  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─    │  │  │  │
  4203.                 │                   │   │  │  │  │
  4204.                  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─    │  │  │  │
  4205.                 │                   │   │  │  │  │
  4206.       B000:4000 ┌───────────────────┐   │  │  │  │
  4207.                 │                   ├───┼──┼──┘  │
  4208.            405A ├───────────────────┤   │  │     │
  4209.                 │                   ├───┼──┼─────┘
  4210.            40B4 └───────────────────┘   │  │
  4211.                 │                   │   │  │
  4212.                  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─    │  │
  4213.                 │                   │   │  │
  4214.                  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─    │  │
  4215.                 │                   │   │  │
  4216.       B000:6000 ┌───────────────────┐   │  │
  4217.                 │                   ├───┼──┘
  4218.            605A ├───────────────────┤   │
  4219.                 │                   ├───┘
  4220.            60B4 └───────────────────┘
  4221.                 │                   │
  4222.                  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
  4223.                 │                   │
  4224.                  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
  4225.  
  4226.       Figure 4-3.  Video buffer interleave in Hercules graphics mode.
  4227.  
  4228.  
  4229.  Enhanced Graphics Adapter
  4230.  
  4231.       When the EGA is configured to emulate a CGA graphics mode, pixels are
  4232.       mapped in the video buffer just as they would be on the CGA. However,
  4233.       in the EGA's native graphics modes (200-line 16-color modes and all
  4234.       350-line modes), pixels are always mapped eight to a byte.
  4235.  
  4236.       This mapping is dictated by the architecture of the EGA's video
  4237.       buffer. The 256 KB video buffer consists of four 64 KB maps, or
  4238.       parallel banks of RAM. The maps are parallel in the sense that they
  4239.       occupy the same range of addresses in the CPU's address space; the
  4240.       EGA's Sequencer and Graphics Controller allow the maps to be accessed
  4241.       either individually or in parallel (more about this in Chapter 5).
  4242.  
  4243.       A pixel's value is determined by the values of the corresponding bits
  4244.       at the same byte offset and bit offset in each map (see Figure 4-4).
  4245.       For this reason, in graphics modes, the four maps are called bit
  4246.       planes. You might imagine each pixel's value as the result of
  4247.       concatenating one bit from the same location in each bit plane.
  4248.  
  4249.         ╔═══╗     The relationship of memory maps to bit planes is altered
  4250.         ║ T ║     in 350-line graphics modes on an IBM EGA equipped with
  4251.         ║ I ║     only 64 KB of video RAM. (To bring IBM's original EGA up
  4252.         ║ P ║     to 256 KB, you must install a piggyback board, called the
  4253.         ╚═══╝     Graphics Memory Expansion Card.) When you use INT 10H
  4254.                   function 00H to select 640-by-350 graphics modes (mode
  4255.                   0FH or 10H) on an EGA with a 64 KB video buffer, video
  4256.                   buffer address decoding is altered so that even-numbered
  4257.                   addresses in the buffer reference the even-numbered maps
  4258.                   and odd-numbered addresses refer to odd-numbered maps
  4259.                   (see Figure 4-5).
  4260.  
  4261.                   In this way the four video buffer maps are chained
  4262.                   together, with maps 0 and 1 forming bit plane 0 and maps
  4263.                   2 and 3 forming bit plane 2. Routines that access pixels
  4264.                   in the video buffer must accommodate this relationship
  4265.                   between the bit planes and buffer addresses.
  4266.  
  4267.  
  4268.                     ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
  4269.  Bit          Map 3 │  1  │  1  │  0  │  0  │  0  │  0  │  1  │  1  │
  4270.  fields in          └──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┘
  4271.  correspond-         ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘
  4272.  ing byte         ┌────┌────┬────┬────┬────┬────┬────┬────┐
  4273.  in each    Map 2 │  0  │  1  │  0  │  1  │  0  │  0  │  1  │  0  │
  4274.  map              └──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┘
  4275.                    ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘
  4276.                 ┌────┬────┬────┬────┬────┬────┬────┬────┐
  4277.           Map 1 │  1  │  0  │  1  │  0  │  1  │  0  │  0  │  1  │
  4278.                 └──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┘     Color
  4279.                  ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘        Plane
  4280.               ┌────┬────┬────┬────┬────┬────┬────┬────┐      Enable
  4281.         Map 0 │  1  │  0  │  1  │  1  │  0  │  1  │  1  │  0  │    register
  4282.               └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘┌──────────┐
  4283.                                                        │   0111   │
  4284.  Pixel          1011  1100  0011  0101  0010  0001  1101  1010 └─────┬────┘
  4285.  values          │     │     │     │     │     │     │     │   ─────┘
  4286.                                                     
  4287.  AND with       0011  0100  0011  0101  0010  0001  0101  0010
  4288.  Color Plane     │     │     │     │     │     │     │     │
  4289.  Enable                                             
  4290.                 ┌─┐   ┌─┐   ┌─┐   ┌─┐   ┌─┐   ┌─┐   ┌─┐   ┌─┐
  4291.  Pixels on      │▓│   │█│   │▓│   │█│   │▒│   │░│   │█│   │▒│
  4292.  screen         └─┘   └─┘   └─┘   └─┘   └─┘   └─┘   └─┘   └─┘
  4293.  
  4294.       Figure 4-4.  Pixel mapping in native EGA graphics modes.
  4295.  
  4296.  
  4297.  Bit fields
  4298.  in corres-     ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
  4299.  ponding   Map 2│  0  │  1  │  0  │  1  │  0  │  0  │  1  │  0  │
  4300.  byte in        └──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┘      Color
  4301.  each map        ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘         Plane
  4302.               ┌────┬────┬────┬────┬────┬────┬────┬────┐       Enable
  4303.          Map 0│  1  │  0  │  1  │  1  │  0  │  1  │  1  │  0  │     register
  4304.               └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘┌───────────┐
  4305.                                                        │    0101   │
  4306.  Pixel          01    10    01    11    00    01    11    00   └─────┬─────┘
  4307.  values          │     │     │     │     │     │     │     │   ─────┘
  4308.                                                     
  4309.  AND with bits  01    10    01    11    00    01    11    00   Note: Bits
  4310.  2 and 0 of      │     │     │     │     │     │     │     │   2 and 0 mask
  4311.  Color Plane     │     │     │     │     │     │     │     │   pixel values.
  4312.  Enable          │     │     │     │     │     │     │     │   Bits 3 and 1
  4313.                                                        should be 0.
  4314.  Pixels on      ┌─┐   ┌─┐   ┌─┐   ┌─┐   ┌─┐   ┌─┐   ┌─┐   ┌─┐
  4315.  screen         │▒│   │▓│   │▒│   │█│   │ │   │▒│   │█│   │ │
  4316.                 └─┘   └─┘   └─┘   └─┘   └─┘   └─┘   └─┘   └─┘
  4317.  a.
  4318.  
  4319.  
  4320.  Bit fields
  4321.  in corres-     ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
  4322.  ponding   Map 3│  1  │  1  │  0  │  0  │  0  │  0  │  1  │  1  │
  4323.  byte in        └──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┘Color
  4324.  each map        ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   ┌─┘   Plane
  4325.               ┌────┬────┬────┬────┬────┬────┬────┬────┐  Enable
  4326.          Map 1│  1  │  0  │  1  │  0  │  1  │  1  │  0  │  1  │  register
  4327.               └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘┌───────────┐
  4328.                                                        │   0101    │
  4329.  Pixel          11    10    01    00    01    01    10    11   └─────┬─────┘
  4330.  values          │     │     │     │     │     │     │     │   ─────┘
  4331.                                                     
  4332.  AND with bits  11    10    01    00    01    01    10    11   Note: Bits
  4333.  2 and 0 of      │     │     │     │     │     │     │     │   2 and 0 mask
  4334.  Color Plane     │     │     │     │     │     │     │     │   pixel values.
  4335.  Enable          │     │     │     │     │     │     │     │   Bits 3 and 1
  4336.                                                        should be 0.
  4337.  Pixels on      ┌─┐   ┌─┐   ┌─┐   ┌─┐   ┌─┐   ┌─┐   ┌─┐   ┌─┐
  4338.  screen         │█│   │▓│   │▓│   │ │   │▒│   │▒│   │▓│   │█│
  4339.                 └─┘   └─┘   └─┘   └─┘   └─┘   └─┘   └─┘   └─┘
  4340.  b.
  4341.  
  4342.       Figure 4-5.  Video buffer maps in 350-line graphics modes (EGA with 64
  4343.       KB video RAM). Pixel values at even addresses are stored in maps 0 and
  4344.       2 (Figure 4-5a); pixels at odd addresses are stored in maps 1 and 3
  4345.       (Figure 4-5b).
  4346.  
  4347.  
  4348.       In native EGA graphics modes, there is no line-by-line interleaving of
  4349.       the pixel data in the video buffer, as in CGA and HGC graphics modes.
  4350.       Instead, rows of pixels are mapped linearly, just as rows of
  4351.       characters are mapped linearly in alphanumeric video modes.
  4352.  
  4353.  
  4354.  Hercules InColor Card
  4355.  
  4356.       In its 720-by-348 graphics mode, the InColor Card's video buffer has
  4357.       four parallel maps organized as four parallel bit planes. As on the
  4358.       EGA, a pixel's value is determined by concatenating the corresponding
  4359.       bits in each of the bit planes. However, video buffer addressing is
  4360.       not linear, as it is on the EGA.
  4361.  
  4362.       Pixels are stored in the InColor Card's video buffer using the same
  4363.       four-way interleave that the HGC and HGC+ use. In the buffer, 348
  4364.       lines of 90 bytes (720 pixels) are mapped in a four-way interleave
  4365.       starting at B000:0000. The buffer also contains two video pages (as on
  4366.       the monochrome HGC), at B000:0000 and B000:8000. This aspect of the
  4367.       InColor Card's design preserves its symmetry with Hercules monochrome
  4368.       graphics cards but differentiates it from the EGA.
  4369.  
  4370.  
  4371.  MCGA and VGA
  4372.  
  4373.       The PS/2 video subsystems support three graphics modes not found on
  4374.       earlier PC video adapters. The 640-by-480 2-color mode (MCGA and VGA)
  4375.       and 640-by-480 16-color mode (VGA only) resemble the native EGA
  4376.       graphics modes: Both use a linear bit map starting at A000:0000. A
  4377.       similar linear pixel map also is used in 320-by-200 (MCGA and VGA)
  4378.       256-color mode, with one important difference: Each byte in the video
  4379.       buffer represents one pixel (see Figure 4-6). Since there are eight
  4380.       bits to a byte, each pixel can have any of 256 (2^8) different colors.
  4381.  
  4382.  
  4383.       ┌──────────────────────┐                  ┌──────────────────────────┐
  4384.       │   8-bit pixel value  │   Logical AND    │      Video DAC Mask      │
  4385.       │                      ├─────────┬────────┤                          │
  4386.       └──────────────────────┘         │        └──────────────────────────┘
  4387.                                        
  4388.                          ┌──────────────────────────┐
  4389.                          │        Video DAC         │
  4390.                          │   color register 0-0FFH  │
  4391.                          └─────────────┬────────────┘
  4392.                                        │
  4393.                                        
  4394.                      18-bit analog output to video display
  4395.                       (6 bits each for red, green, blue)
  4396.  
  4397.       Figure 4-6.  Color selection in MCGA and VGA 320-by-200 256-color
  4398.       mode.
  4399.  
  4400.  
  4401.         ╔═══╗     On the VGA, 640-by-480 2-color mode is nearly identical to
  4402.         ║ T ║     640-by-480 16-color mode. All four bit planes remain
  4403.         ║ I ║     active in the 2-color mode even though one bit plane is
  4404.         ║ P ║     sufficient to store a full screen of pixels. The only
  4405.         ╚═══╝     difference between the two modes is that the video BIOS
  4406.                   makes only two palette colors available in the 2-color
  4407.                   mode, whereas it sets up 16 palette colors in the 16-color
  4408.                   mode.
  4409.  
  4410.  
  4411.  Pixel Coordinates
  4412.  
  4413.  
  4414.       In graphics modes, the video buffer can be thought of as a flat, two-
  4415.       dimensional array of pixels with its origin at the upper left corner.
  4416.       What is visible on the screen is a subset of the pixels represented in
  4417.       the buffer. On the CGA, the video buffer can contain only one
  4418.       screenful of pixels, so the first byte in the buffer represents the
  4419.       pixels in the screen's upper left corner. On the EGA, MCGA, and VGA,
  4420.       however, the video buffer can store several screenfuls of pixels. You
  4421.       can thus select which portion of the video buffer appears on the
  4422.       screen.
  4423.  
  4424.       Every pixel on the screen can be identified by a unique pair of (x,y)
  4425.       coordinates relative to the screen's upper left corner. Each (x,y)
  4426.       pair also corresponds to a particular byte offset in the video buffer
  4427.       and a bit offset in that byte. Thus, given a pixel's (x,y) coordinates
  4428.       on the screen, you can compute where in the video buffer the pixel is
  4429.       represented.
  4430.  
  4431.       Converting from pixel coordinates to the corresponding byte and bit
  4432.       offsets is one of the most frequent operations in IBM video graphics
  4433.       programming. The program examples in Listings 4-1 through 4-5
  4434.       demonstrate how to do this efficiently and in a uniform manner.
  4435.  
  4436.  
  4437.  ───────────────────────────────────────────────────────────────────────────
  4438.  
  4439.       Listing 4-1.  Computing a pixel's address in 320-by-200 4-color
  4440.  
  4441.  ───────────────────────────────────────────────────────────────────────────
  4442.  
  4443.  
  4444.  ───────────────────────────────────────────────────────────────────────────
  4445.  
  4446.       Listing 4-2.  Computing a pixel's address in 640-by-200 2-color
  4447.  
  4448.  ───────────────────────────────────────────────────────────────────────────
  4449.  
  4450.  
  4451.       Transforming pixel coordinates to a buffer offset involves simple
  4452.       logic. Begin by calculating the offset of the start of pixel row y.
  4453.       (For CGA and Hercules graphics modes, this calculation accounts for
  4454.       the interleaving of the video buffer.) To this value, add the byte
  4455.       offset of the xth pixel in the row. Finally, add the byte offset of
  4456.       the start of the displayed portion of the video buffer to obtain the
  4457.       final byte offset of the pixel.
  4458.  
  4459.       PixelByteOffset = RowOffset(y) + ByteOffset(x) + OriginOffset
  4460.  
  4461.       The bit offset of the pixel within the byte that contains its value
  4462.       depends only on the number of pixels represented in each byte of the
  4463.       video buffer. You could express the relationship this way:
  4464.  
  4465.       PixelBitOffset = PixelsPerByte - (x MOD PixelsPerByte) - 1
  4466.  
  4467.       However, it is more practical to represent a pixel's bit offset as a
  4468.       bit mask rather than as an ordinal bit number. This can be done easily
  4469.       with a table lookup (for example, an assembler XLAT instruction) or
  4470.       with a logical shift instruction. (This is why Listings 4-1 through
  4471.       4-4 return the bit offset as a number of bits to shift.)
  4472.  
  4473.  
  4474.  ───────────────────────────────────────────────────────────────────────────
  4475.  
  4476.       Listing 4-3.  Computing a pixel's address in Hercules graphics mode.
  4477.  
  4478.  ───────────────────────────────────────────────────────────────────────────
  4479.  
  4480.  
  4481.  ───────────────────────────────────────────────────────────────────────────
  4482.  
  4483.       Listing 4-4.  Computing a pixel's address in CGA and VGA graphics
  4484.       modes.
  4485.  
  4486.  ───────────────────────────────────────────────────────────────────────────
  4487.  
  4488.  
  4489.       Here is a high-level example of a pixel coordinate transformation for
  4490.       the CGA's 320-by-200 4-color graphics mode. As Figure 4-1a shows,
  4491.       each byte in the video buffer contains four pixels. At four pixels per
  4492.       byte, 80 bytes of data represent one row of 320 pixels. The origin of
  4493.       the screen--that is, the byte offset of the displayed portion of the
  4494.       buffer--is 0, since the CGA video buffer contains only one screenful
  4495.       of pixels.
  4496.  
  4497.       int PixelsPerByte = 4;
  4498.       int BytesPerRow = 80;
  4499.       int OriginOffset = 0;
  4500.       static int Masks[] = { 0xC0, 0x30, 0x0C, 0x03 };
  4501.  
  4502.       unsigned int x,y;
  4503.       unsigned int ByteOffset,BitMask;
  4504.  
  4505.       /* buffer interleave (0 or 0x2000) */
  4506.       ByteOffset = (y & 1) << 13;
  4507.  
  4508.       /* offset of start of row */
  4509.       ByteOffset += BytesPerRow * (y/2);
  4510.  
  4511.       /* byte offset in screen */
  4512.       ByteOffset += (x / PixelsPerByte) % BytesPerRow;
  4513.  
  4514.       /* byte offset in video buffer */
  4515.       ByteOffset += OriginOffset;
  4516.  
  4517.       BitMask = Masks[x % PixelsPerByte];
  4518.  
  4519.       The same routine in assembly language is much more efficient, because
  4520.       all arithmetic can be done in registers and register halves (refer to
  4521.       Listing 4-1). Also, if you know that the number of bytes per row of
  4522.       pixels is a constant, you  can further increase performance by
  4523.       performing multiplication and division as a sequence of bit shifts.
  4524.  
  4525.       For example, in Listing 4-5, the y-coordinate is multiplied by 320
  4526.       through a series of logical shift operations instead of a single MUL
  4527.       instruction. The resulting routine runs about 40 percent faster on the
  4528.       8086-based PS/2 Model 30 and about 10 percent faster on the 80286-
  4529.       based PS/2 Model 60. This optimization complicates the assembly code
  4530.       somewhat, but the speed gained is worth the effort--low-level routines
  4531.       such as those in Listings 4-1 through 4-5 may execute many thousands
  4532.       of times in a graphics-oriented application.
  4533.  
  4534.  
  4535.  ───────────────────────────────────────────────────────────────────────────
  4536.  
  4537.       Listing 4-5.  Computing a pixel's address in 320-by-200 256-color
  4538.       mode.
  4539.  
  4540.  ───────────────────────────────────────────────────────────────────────────
  4541.  
  4542.  
  4543.  Pixel Coordinate Scaling
  4544.  
  4545.       One characteristic of most IBM graphics modes is that horizontal pixel
  4546.       resolution differs from vertical pixel resolution. For example, in a
  4547.       640-by-200 mode, a typical 200-line color monitor displays about 70
  4548.       pixels per horizontal inch, but only about 30 pixels per vertical
  4549.       inch.
  4550.  
  4551.       This discrepancy complicates the mapping of pixels in the display
  4552.       buffer to screen locations, as is shown in Figure 4-7. For example,
  4553.       in a 640-by-200 mode, a line drawn between the pixel at (0,0) in the
  4554.       screen's upper left corner and the pixel at (100,100) has a
  4555.       mathematical slope of 1, so you would expect it to be displayed at a
  4556.       45-degree angle from the display's top and left edges. However, the
  4557.       displayed line (line a, Figure 4-7) is "compressed" in the horizontal
  4558.       direction.
  4559.  
  4560.       Displaying a line at a 45-degree angle requires scaling the pixel
  4561.       coordinates to account for the discrepancy in vertical and horizontal
  4562.       resolution. In a 640-by-200 mode, the horizontal scaling factor is
  4563.       about 2.4 (horizontal resolution / vertical resolution). In the
  4564.       example, you would scale the x-coordinates of the endpoints to 0 (0 *
  4565.       2.4) and 240 (100 * 2.4). The scaled line (line b, Figure 4-7), with
  4566.       endpoints at (0,0) and (240,100), appears at a 45-degree angle on the
  4567.       screen.
  4568.  
  4569.       You must scale the (x,y) coordinates of all pixels in all geometric
  4570.       figures in all graphics modes--unless, of course, the scaling factor
  4571.       happens to be 1. Otherwise, squares appear as rectangles and circles
  4572.       as ellipses. Furthermore, you must adjust the scaling factor for the
  4573.       horizontal and vertical resolutions of each graphics mode. Figure 4-8
  4574.       is a table of the horizontal-to-vertical scaling ratios for graphics
  4575.       modes on IBM video subsystems with typical monitors.
  4576.  
  4577.  
  4578.       ┌──────────────────────────────────┐
  4579.       │ (O,O)                            │
  4580.       │   ┌──────────────────────────┐   │
  4581.       │   │││                        │   │
  4582.       │   ││└─────────┐              │   │
  4583.       │   │└──┐       │b.            │   │
  4584.       │   │ a.│       │(240,100)     │   │
  4585.       │   │(100,100)                 │   │
  4586.       │   │                          │   │
  4587.       │   │                          │   │
  4588.       │   └──────────────────────────┘   │
  4589.       │                                  │
  4590.       └──────────────────────────────────┘
  4591.  
  4592.       Figure 4-7.  Pixel coordinate scaling in 640-by-200 graphics.
  4593.  
  4594.  
  4595. ╓┌───────────┌────────────────────────┌──────────────────────────────────────╖
  4596.  BIOS Mode   Mode                     Scaling Factor
  4597.  Number      Description              (horizontal/vertical)
  4598.  ──────────────────────────────────────────────────────────────────────────
  4599.  4,5         320-by-200 4-color       1.20
  4600.  6           640-by-200 2-color       2.40
  4601.  BIOS Mode   Mode                     Scaling Factor
  4602.  Number      Description              (horizontal/vertical)
  4603. 6           640-by-200 2-color       2.40
  4604.  0DH         320-by-200 16-color      1.20
  4605.  0EH         640-by-200 16-color      2.40
  4606.  0FH         640-by-350 monochrome    1.26 (monochrome monitor)
  4607.  10H         640-by-350 16-color      1.37
  4608.  11H         640-by-480 2-color       1.00
  4609.  12H         640-by-480 16-color      1.00
  4610.  13H         320-by-200 256-color     2.40
  4611.              720-by-348 (Hercules)    1.43 (monochrome monitor)
  4612.  
  4613.       Figure 4-8.  Pixel scaling values for PC and PS/2 graphics modes. An
  4614.       aspect ratio of 1.33 (4:3) for color monitors, 1.45 for monochrome
  4615.       monitors, is assumed.
  4616.  
  4617.  
  4618.  Aspect Ratio
  4619.  
  4620.       A related programming concern is the screen's aspect ratio--the ratio
  4621.       of a screen's width to its height. The color monitors commonly used
  4622.       with IBM video subsystems have aspect ratios of about 1.33 (4:3); for
  4623.       the typical green monochrome monitor, the aspect ratio is about 1.45.
  4624.       Because the screen is rectangular instead of square, the maximum
  4625.       potential width of a screen image exceeds its maximum potential
  4626.       height. This limitation must always be considered in scaling pixel
  4627.       coordinates.
  4628.  
  4629.         ╔═══╗     One attractive feature of the MCGA, the VGA, and other
  4630.         ║ T ║     video subsystems that offer 640-by-480 resolution is that
  4631.         ║ I ║     horizontal resolution and vertical resolution are the same
  4632.         ║ P ║     on a display with an aspect ratio of 4:3. You can think of
  4633.         ╚═══╝     the pixels in this situation as being "square." With
  4634.                   "square" pixels, mapping the video buffer to the screen is
  4635.                   simpler because the pixel coordinate scaling factor is 1.
  4636.  
  4637.  
  4638.  Pixel Display Attributes
  4639.  
  4640.  
  4641.       In general, pixel values determine video attributes--in other words,
  4642.       the bits that represent a pixel in the video buffer determine how the
  4643.       pixel looks on the screen. The way that pixel values are decoded in
  4644.       graphics modes is similar to the way that alphanumeric attributes are
  4645.       decoded. But in graphics modes, pixel values may range from one
  4646.       through eight bits, while alphanumeric attributes are four bits wide.
  4647.  
  4648.  
  4649.  Color Graphics Adapter
  4650.  
  4651.       In 640-by-200 2-color mode, one bit represents each pixel. If the bit
  4652.       is 0, the pixel is displayed as black. If the bit is 1, the pixel is
  4653.       displayed with the color specified in bits 0 through 3 of the CGA's
  4654.       Color Select register (port 3D9H). This is the same register that
  4655.       specifies the overscan color in alphanumeric modes. If you change
  4656.       video modes by directly programming the CGA's CRTC and Mode Control
  4657.       registers, you should avoid spurious border colors or pixel colors by
  4658.       programming the Color Select register as well.
  4659.  
  4660.       You can use INT 10H function 0BH to select the displayed color of
  4661.       nonzero pixels in 640-by-200 2-color mode (see Listing 4-6). This BIOS
  4662.       function stores a color value in the Color Select register and updates
  4663.       the variable CRT_PALETTE in the Video Display Data Area at 0040:0066.
  4664.       If you bypass the video BIOS and program the Color Select register
  4665.       directly, you should also update CRT_PALETTE.
  4666.  
  4667.  
  4668.  ───────────────────────────────────────────────────────────────────────────
  4669.  
  4670.       Listing 4-6.  Foreground color in CGA 640-by-200 2-color graphics.
  4671.  
  4672.  ───────────────────────────────────────────────────────────────────────────
  4673.  
  4674.  
  4675.       In 320-by-200 4-color modes, two bits represent each pixel, so pixel
  4676.       values can range from 0 through 3. Pixels with the value 0 are
  4677.       displayed with the color value stored in the Color Select register at
  4678.       port 3D9H. A quirk of the CGA is that the Color Select register value
  4679.       determines both the overscan (border) color and the color for pixel
  4680.       value 0. This means you cannot specify a border color independently of
  4681.       the background color on the CGA in this video mode.
  4682.  
  4683.       The colors displayed for pixels with nonzero values are taken from one
  4684.       of three hardware palettes (see Figure 4-9). The palette is selected
  4685.       by the values of bit 5 of the Color Select register (port 3D9H) and of
  4686.       bit 2 of the Mode Control register at port 3D8H (Listing 4-7). If bit
  4687.       2 of the Mode Control register is 1, the palette comprises cyan, red,
  4688.       and white. If this bit is 0, bit 5 of the Color Select register
  4689.       selects either green, red, and yellow (if bit 2 in the Color Select
  4690.       register is 0), or cyan, violet, and white (if bit 2 in the Color
  4691.       Select register is 1). In effect, setting bit 2 in the Color Select
  4692.       register adds blue to the palette; that is, green plus blue produces
  4693.       cyan, red plus blue produces violet, and yellow plus blue produces
  4694.       white.
  4695.  
  4696.         ╔═══╗     Setting bit 2 of the CGA's Mode Control register to 1
  4697.         ║ T ║     disables the color burst component of the adapter's
  4698.         ║ I ║     composite video output signal. If you use a black-and-
  4699.         ║ P ║     white display, appropriate shades of gray are generated
  4700.         ╚═══╝     for the four possible pixel values when bit 2 is set to 1.
  4701.  
  4702.  
  4703.  ───────────────────────────────────────────────────────────────────────────
  4704.  Bit 2 of Mode Control register = 0
  4705.  Pixel Value                    Color Displayed
  4706.  ───────────────────────────────────────────────────────────────────────────
  4707.  Bit 5 of Color Select register = 0
  4708.  1                              Green
  4709.  2                              Red
  4710.  3                              Yellow
  4711.  
  4712.  Bit 5 of Color Select register = 1
  4713.  1                              Cyan
  4714.  2                              Violet
  4715.  3                              White
  4716.  ───────────────────────────────────────────────────────────────────────────
  4717.  Bit 2 of Mode Control register = 1
  4718.  Pixel Value                    Color Displayed
  4719.  ───────────────────────────────────────────────────────────────────────────
  4720.  1                              Cyan
  4721.  2                              Red
  4722.  3                              White
  4723.  
  4724.       Figure 4-9.  Palettes available in CGA 320-by-200 4-color mode.
  4725.  
  4726.  
  4727.  ───────────────────────────────────────────────────────────────────────────
  4728.  
  4729.       Listing 4-7.  Four-color palettes in CGA 320-by-200 4-color mode.
  4730.  
  4731.  ───────────────────────────────────────────────────────────────────────────
  4732.  
  4733.  
  4734.       You can use INT 10H functions to select among the three 4-color
  4735.       palettes. The video BIOS assigns two video mode numbers to 320-by-200
  4736.       4-color graphics mode: In BIOS mode 4, bit 2 of the Mode Control
  4737.       register is 0, and in mode 5, bit 2 is set to 1. Thus, to select the
  4738.       cyan-red-white palette, use INT 10H function 0 to set mode 5. To
  4739.       select the other two palettes, use INT 10H function 0 to set mode 4,
  4740.       and then call INT 10H function 0BH to choose either green-red-yellow
  4741.       or cyan-violet-white, as shown in Listing 4-8.
  4742.  
  4743.  
  4744.  ───────────────────────────────────────────────────────────────────────────
  4745.  
  4746.       Listing 4-8.  Four-color palettes in CGA 320-by-200 4-color mode using
  4747.       video BIOS.
  4748.  
  4749.  ───────────────────────────────────────────────────────────────────────────
  4750.  
  4751.  
  4752.       You can select high-intensity colors in the 320-by-200 4-color palette
  4753.       by setting bit 4 of the Color Select register to 1. When this bit is
  4754.       0, the same four colors are displayed with normal intensity.
  4755.  
  4756.  
  4757.  Hercules Graphics Card
  4758.  
  4759.       Life is easy with an HGC as far as graphics attributes are concerned.
  4760.       In the 720-by-348 monochrome graphics mode on the HGC and HGC+, one
  4761.       bit represents each pixel. If the bit is set to 1, the pixel is
  4762.       displayed. If the bit is set to 0, the pixel is not displayed.
  4763.  
  4764.  
  4765.  Enhanced Graphics Adapter
  4766.  
  4767.       Although the EGA supports a number of graphics modes with pixel values
  4768.       ranging from 1 to 4 bits, it decodes pixel values in a straightforward
  4769.       manner. As in alphanumeric modes, each pixel's value is masked by the
  4770.       value in the Attribute Controller's Color Plane Enable register; the
  4771.       resulting 4-bit value selects one of the Attribute Controller's 16
  4772.       palette registers. Thus, a pixel's displayed attribute is derived from
  4773.       the palette register that corresponds to the pixel value.
  4774.  
  4775.       When you use INT 10H function 0 to select an EGA video mode, the BIOS
  4776.       routine loads a default set of color values into the palette registers
  4777.       (see Figure 4-10). The actual values depend on the video mode, but
  4778.       each set maps the palette registers so that the color displayed for a
  4779.       given pixel value is the same as a CGA would display. Using this
  4780.       function improves the portability of programs between the CGA and the
  4781.       EGA, since a program that never touches the palette registers can run
  4782.       with the same set of colors on both adapters.
  4783.  
  4784.         ╔═══╗     The BIOS default palette register values for 320-by-200
  4785.         ║ T ║     and 640-by-200 16-color modes are correct for 200-line
  4786.         ║ I ║     monitors but incorrect for some EGA-compatible monitors.
  4787.         ║ P ║     IBM's Enhanced Color Display converts the 4-bit default
  4788.         ╚═══╝     color values in 200-line graphics modes (see Figure 4-10)
  4789.                   to 6-bit color values that emulate the 16 CGA colors.
  4790.                   Unfortunately, not all EGA-compatible monitors do this.
  4791.                   Thus, if you use INT 10H function 0 to invoke these modes
  4792.                   (mode numbers 0DH and 0EH), you generally should program
  4793.                   the palette registers with an appropriate set of values,
  4794.                   such as the default set used in 640-by-350 16-color mode.
  4795.  
  4796.  
  4797.       CGA Emulation Modes
  4798.       In 640-by-200 2-color mode, when bit 3 of the Attribute Controller
  4799.       Mode Control register (10H) is 0, a pixel value of 0 designates
  4800.       palette register 0, and a pixel value of 1 designates palette register
  4801.       1. When Mode Control bit 3 is 1, palette registers 8 and 9 are used.
  4802.       With a CGA-compatible display, these four palette registers can
  4803.       contain any of the 16 displayable color values. With an EGA-compatible
  4804.       350-line monitor, these registers can contain any four of the 64
  4805.       displayable color values.
  4806.  
  4807.  
  4808.  350-Line 16-Color Modes
  4809. ╓┌─────────────────┌────────────────────┌────────────────────────────────────╖
  4810.  Palette Register  Color Value          Attribute
  4811.  Palette Register  Color Value          Attribute
  4812.  ──────────────────────────────────────────────────────────────────────────
  4813.  00H               00H                  Black
  4814.  01H               01H                  Mid-intensity blue
  4815.  02H               02H                  Mid-intensity green
  4816.  03H               03H                  Mid-intensity cyan
  4817.  04H               04H                  Mid-intensity red
  4818.  05H               05H                  Mid-intensity violet
  4819.  06H               14H                  Brown
  4820.  07H               07H                  Mid-intensity white
  4821.  08H               38H                  Low-intensity white (gray)
  4822.  09H               39H                  High-intensity blue
  4823.  0AH               3AH                  High-intensity green
  4824.  0BH               3BH                  High-intensity cyan
  4825.  0CH               3CH                  High-intensity red
  4826.  0DH               3DH                  High-intensity violet
  4827.  0EH               3EH                  High-intensity yellow
  4828.  0FH               3FH                  High-intensity white
  4829.  
  4830.  
  4831.  200-Line 16-Color Modes
  4832. ╓┌─────────────────┌────────────────────┌────────────────────────────────────╖
  4833.  Palette Register  Color Value          Attribute
  4834.  ──────────────────────────────────────────────────────────────────────────
  4835.  00H               00H                  Black
  4836.  01H               01H                  Blue
  4837.  02H               02H                  Green
  4838.  03H               03H                  Cyan
  4839.  04H               04H                  Red
  4840.  05H               05H                  Violet
  4841.  06H               06H                  Yellow (brown)
  4842.  07H               07H                  White
  4843.  08H               10H                  Black (gray)
  4844.  09H               11H                  High-intensity blue
  4845.  0AH               12H                  High-intensity green
  4846.  0BH               13H                  High-intensity cyan
  4847.  0CH               14H                  High-intensity red
  4848.  0DH               15H                  High-intensity violet
  4849.  0EH               16H                  High-intensity yellow
  4850.  0FH               17H                  High-intensity white
  4851.  
  4852.  
  4853.  640-by-350 Monochrome Graphics
  4854. ╓┌─────────────────┌────────────────────┌────────────────────────────────────╖
  4855.  Palette Register  Color Value          Attribute
  4856.  ──────────────────────────────────────────────────────────────────────────
  4857.  00H               00H                  Not displayed
  4858.  01H               08H                  Normal intensity
  4859.  04H               18H                  High intensity
  4860.  05H               18H                  High intensity
  4861.  08H               00H                  Not displayed
  4862.  09H               08H                  Normal
  4863.  0CH               00H                  Not displayed
  4864.  0DH               18H                  High intensity
  4865.  
  4866.       Figure 4-10.  Default EGA and VGA palette register values.
  4867.  
  4868.  
  4869.       In 320-by-200 4-color mode, each of the four possible pixel values (0
  4870.       through 3) designates a corresponding palette register. When bit 3 in
  4871.       the Attribute Controller Mode Control register is 0, palette registers
  4872.       0-3 are used; when bit 3 is 1, palette registers 8-0BH are used. With
  4873.       a CGA-compatible monitor, you can store any eight of the 16
  4874.       displayable color values in these palette registers. With an EGA-
  4875.       compatible monitor, you can use any eight of the 64 displayable color
  4876.       values in these registers.
  4877.  
  4878.       In both CGA emulation modes, the video BIOS initializes the palette
  4879.       registers with default color values that match the colors in the CGA
  4880.       hardware palettes. In 640-by-200 2-color mode, the default colors are
  4881.       black, white, and intense white. In 320-by-200 4-color modes, the BIOS
  4882.       supports the green-red-yellow and cyan-violet-white palettes in normal
  4883.       and high intensities.
  4884.  
  4885.  
  4886.       16-Color Modes
  4887.       In 320-by-200, 640-by-200, and 640-by-350 16-color modes, each 4-bit
  4888.       pixel value designates one of the 16 palette registers. For a CGA-
  4889.       compatible monitor, the palette registers can contain the usual 16
  4890.       colors, but with an EGA-compatible monitor, you can specify any of the
  4891.       64 displayable colors in each palette register.
  4892.  
  4893.  
  4894.       Monochrome Graphics
  4895.       There are two bits per pixel in the EGA's 640-by-350 monochrome
  4896.       graphics mode, so pixel values can range from 0 through 3. However,
  4897.       this graphics mode uses only even-numbered bit planes, so the EGA's
  4898.       Attribute Controller interprets only the even-numbered bits of the
  4899.       usual 4-bit pixel value. Thus, bits 0 and 1 of a 2-bit monochrome
  4900.       pixel value designate bits 0 and 2 of the corresponding 4-bit palette
  4901.       register number. (Bits 1 and 3 of the palette register number are
  4902.       always 0.) Thus, the four possible pixel values--0, 1, 2, and 3--
  4903.       actually reference palette registers 0, 1, 4, and 5 respectively (see
  4904.       Figure 4-11).
  4905.  
  4906.  
  4907. ╓┌──────────────────┌────────────────────────────────────────────────────────╖
  4908.  Pixel Value        Corresponding Palette Register
  4909.  ──────────────────────────────────────────────────────────────────────────
  4910.  0 (00B)            0 (0000B)
  4911.  1 (01B)            1 (0001B)
  4912.  2 (10B)            4 (0100B)
  4913.  3 (11B)            5 (0101B)
  4914.  
  4915.       Figure 4-11.  Pixel values and palette registers in 640-by-350
  4916.       monochrome graphics.
  4917.  
  4918.  
  4919.         ╔═══╗     On EGAs with only 64 KB of video RAM, the odd bit planes
  4920.         ║ T ║     represent pixels at odd buffer addresses, and the even bit
  4921.         ║ I ║     planes represent pixels at even buffer addresses (see
  4922.         ║ P ║     Figure 4-5). In this situation, pixel values in 640-by-
  4923.         ╚═══╝     350 monochrome and 640-by-350 4-color graphics modes are
  4924.                   two bits in size, but bits 0 and 2 are used for pixels at
  4925.                   even byte addresses, while bits 1 and 3 are used for
  4926.                   pixels at odd byte addresses.
  4927.  
  4928.       Monochrome pixels can be undisplayed (palette register value 0), can
  4929.       be displayed with normal intensity (08H), or can be displayed with
  4930.       high intensity (18H). INT 10H function 00H loads the palette registers
  4931.       with a default set of monochrome values whenever you select video mode
  4932.       0FH (see Figure 4-11).
  4933.  
  4934.  
  4935.       Blinking
  4936.       In native graphics modes on the EGA (as well as on the VGA), pixels
  4937.       can have a blinking attribute. As in alphanumeric modes, you select
  4938.       blinking by setting the Enable Blink bit of the Attribute Controller's
  4939.       Mode Control register (bit 3 of register 10H at port 3C0H) to 1. In
  4940.       16-color modes, this causes the adapter to interpret the high-order
  4941.       bit (bit 3) of each 4-bit pixel value as a blink attribute, in the
  4942.       same way the high-order bit of a character's attribute byte is used in
  4943.       alphanumeric modes. Thus, when the Enable Blink bit is set, pixels
  4944.       with values 8 through 0FH blink, and pixels with values 0 through 7 do
  4945.       not. In monochrome graphics mode, all pixels blink regardless of their
  4946.       value.
  4947.  
  4948.       However, the EGA blinks pixels differently in graphics modes than it
  4949.       blinks characters in alphanumeric modes. In graphics modes, pixels are
  4950.       blinked by alternately selecting two different palette registers for
  4951.       each pixel's value. The two registers are designated by turning bit 3
  4952.       of the pixel value on and off at the blink rate (about twice per
  4953.       second). Thus, pixels are blinked by alternating the values in the
  4954.       first eight palette registers (registers 00H through 07H) with the
  4955.       values in the second eight (08H through 0FH).
  4956.  
  4957.       For example, a pixel with a value of 0AH is blinked by repeatedly
  4958.       changing the value of bit 3 whenever the Enable Blink bit is set.
  4959.       Thus, the pixel's color alternates between that designated by palette
  4960.       register 0AH (1010B) and that in palette register 02H (0010B). If you
  4961.       use the set of BIOS default palette registers, this pixel blinks
  4962.       between green and high-intensity green.
  4963.  
  4964.       A peculiarity of the EGA's blinking attribute in color graphics modes
  4965.       is what happens to pixels with values from 0 through 7; that is, where
  4966.       bit 3 of the pixel value is 0. These pixels do not blink, but they are
  4967.       displayed as if bit 3 were 1. For example, if you use the BIOS default
  4968.       palette values, pixels displayed at lower intensity (pixel values 0
  4969.       through 7) become nonblinking pixels displayed at high intensity using
  4970.       palette registers 08H through 0FH.
  4971.  
  4972.       Thus, in using the blinking attribute in graphics modes, you should
  4973.       reprogram the palette registers each time you change the Enable Blink
  4974.       bit, to maintain a consistent set of colors. For example, the palette
  4975.       register values shown in Figure 4-12 might be useful in this context.
  4976.       This palette is designed for use as an alternative to the default BIOS
  4977.       palette (see Figure 4-10) when blinking is enabled. If this palette
  4978.       is used with the Enable Blink bit set to 1, all high-intensity pixels
  4979.       (pixel values 08H through 0FH) blink, but all normal-intensity pixels
  4980.       do not.
  4981.  
  4982.  
  4983.       Border Color
  4984.       As in alphanumeric modes, you can set the overscan (border) color by
  4985.       storing a color value in the Attribute Controller's Overscan Color
  4986.       register (register 11H, port 3C0H). Techniques for setting the border
  4987.       color are covered in Chapter 3.
  4988.  
  4989.  
  4990. ╓┌───────────────────┌──────────────┌────────────────────────────────────────╖
  4991.  Palette Register    Color Value    Attribute
  4992.  ──────────────────────────────────────────────────────────────────────────
  4993.  00H                 00H            Black (background)
  4994.  
  4995.  01H                 39H▒
  4996.  02H                 3AH▒
  4997.  03H                 3BH▒
  4998.  04H                 3CH▒──────────(high-intensity colors)
  4999.  05H                 3DH▒
  5000.  Palette Register    Color Value    Attribute
  5001. 05H                 3DH▒
  5002.  06H                 3EH▒
  5003.  07H                 3FH▒
  5004.  
  5005.  08H                 00H            Black (background)
  5006.  
  5007.  09H                 01H▒
  5008.  0AH                 02H▒
  5009.  0BH                 03H▒
  5010.  0CH                 04H▒──────────(mid-intensity colors)
  5011.  0DH                 05H▒
  5012.  0EH                 14H▒
  5013.  0FH                 07H▒
  5014.  
  5015.       Figure 4-12.  Palette register values for blinking in 640-by-350
  5016.       16-color mode.
  5017.  
  5018.  
  5019.  Hercules InColor Card
  5020.  
  5021.       On the InColor Card, the value of bit 4 of the Exception register
  5022.       (17H) determines whether the palette registers are used to decode
  5023.       pixel values, just as it does in alphanumeric modes. When this bit is
  5024.       set to 1, each 4-bit pixel value specifies a palette register, and the
  5025.       6-bit color value in the palette register determines the displayed
  5026.       color of the pixel.
  5027.  
  5028.       Setting Exception register bit 4 to 0 bypasses the palette registers.
  5029.       Each 4-bit pixel value is extended to 6 bits by replicating the high-
  5030.       order bit, and the new value determines the color. This procedure,
  5031.       called sign extension, in effect causes the high-order bit of a pixel
  5032.       value to act as an "intensity" bit, similar to the way alphanumeric
  5033.       attributes are decoded.
  5034.  
  5035.  
  5036.  MCGA
  5037.  
  5038.       The MCGA emulates both of the CGA's graphics modes and adds two of its
  5039.       own, a 640-by-480 2-color mode and a 320-by-200 256-color mode. The
  5040.       256-color mode is the only MCGA video mode that uses the video Digital
  5041.       to Analog Converter (DAC) to full advantage.
  5042.  
  5043.  
  5044.       2-Color Graphics Modes
  5045.       Pixel attributes in 640-by-200 and 640-by-480 2-color modes are
  5046.       directed through the video DAC registers. Pixels with the value 0 are
  5047.       always mapped through video DAC color register 0. Nonzero pixels also
  5048.       select a predesignated video DAC color register, but this is done in
  5049.       one of two ways, depending on the value of bit 2 of the Mode Control
  5050.       register at 3D8H. If bit 2 is 1, video DAC color register 7 is
  5051.       selected. If bit 2 is 0, bits 0 through 3 of the Color Select register
  5052.       (port 3D9H) designate a video DAC register.
  5053.  
  5054.       On the MCGA, the background color in 2-color graphics modes is not
  5055.       necessarily black as it is on the CGA. Instead, both background and
  5056.       foreground can be any of the 256 K colors or the 64 gray-scale values
  5057.       that the MCGA can display. Use INT 10H function 10H to set the
  5058.       appropriate video DAC color registers.
  5059.  
  5060.         ╔═══╗     When the video BIOS sets up 2-color graphics modes, it
  5061.         ║ T ║     sets bit 2 of the Mode Control register to 0 and bits 0
  5062.         ║ I ║     through 3 of the Color Select register to 1111B (0FH).
  5063.         ║ P ║     Since the first 16 video DAC color registers contain the
  5064.         ╚═══╝     16 colors available on a CGA, this configuration emulates
  5065.                   the default color configuration on a CGA in 640-by-200 2-
  5066.                   color mode: Background pixels are displayed as black (the
  5067.                   value in video DAC color register 0) and foreground pixels
  5068.                   appear intense white (the value in video DAC color
  5069.                   register 0FH).
  5070.  
  5071.  
  5072.       4-Color Graphics Mode
  5073.       The MCGA faithfully emulates this CGA graphics mode. The major
  5074.       difference is that the MCGA maps the four available colors through the
  5075.       video DAC color registers just as it does in 2-color graphics modes.
  5076.       Thus, all four colors can be selected from the 256 K possibilities
  5077.       that the video DAC offers.
  5078.  
  5079.       The MCGA combines bits 4 and 5 of the Color Select register (port
  5080.       3D9H) with each pixel's 2-bit value to create a 4-bit value that
  5081.       designates one of the first 16 video DAC color registers (see Figure
  5082.       4-13). The video BIOS initializes the video DAC color registers with
  5083.       CGA-compatible palettes. The colors are chosen so that bit 5 of the
  5084.       Color Select register selects the green-red-yellow and cyan-violet-
  5085.       white palettes, and bit 4 toggles between normal- and high-intensity
  5086.       palettes, as they do on the CGA. Of course, you can establish
  5087.       completely arbitrary 4-color palettes by loading different color
  5088.       values into the video DAC color registers.
  5089.  
  5090.  
  5091. ╓┌────────────┌──────────────┌──────────┌───────────────┌────────────────────╖
  5092.  3D9H         Pixel Value    3D9H       Video DAC
  5093.  Bit 4                       Bit 5      Color Register
  5094.  (intensity)  Bit 1 Bit 0    (palette)  Number          Default Color
  5095.  ──────────────────────────────────────────────────────────────────────────
  5096.  1           0     0        1         00H             Black
  5097.  
  5098.  0            0     1        0          02H             Green
  5099.  0            1     0        0          04H             Red
  5100.  0            1     1        0          06H             Brown
  5101.  
  5102.  1            0     1        0          0AH             High-intensity green
  5103.  1            1     0        0          0CH             High-intensity red
  5104.  1            1     1        0          0EH             High-intensity
  5105.  3D9H         Pixel Value    3D9H       Video DAC
  5106.  Bit 4                       Bit 5      Color Register
  5107.  (intensity)  Bit 1 Bit 0    (palette)  Number          Default Color
  5108. 1            1     1        0          0EH             High-intensity
  5109.                                                         yellow
  5110.  
  5111.  0            0     1        1          03H             Cyan
  5112.  0            1     0        1          05H             Violet
  5113.  0            1     1        1          07H             White
  5114.  
  5115.  1            0     1        1          0BH             High-intensity cyan
  5116.  1            1     0        1          0DH             High-intensity
  5117.                                                         violet
  5118.  1            1     1        1          0FH             High-intensity white
  5119.  
  5120.       Figure 4-13.  Pixel values and palettes in MCGA 320-by-200 4-color
  5121.       mode.
  5122.  
  5123.  
  5124.       256-Color Graphics Mode
  5125.       In 256-color mode, each pixel's value designates one of the 256 video
  5126.       DAC color registers. To select a video DAC color register, a pixel's
  5127.       value is combined (using a logical AND) with the value in the video
  5128.       DAC Mask register (3C6H). The resulting value selects a DAC color
  5129.       register (see Figure 4-6). Since you can store any of 256 K color
  5130.       values in each video DAC color register, you can display a wide range
  5131.       of tones and intensities and create quite realistic video images.
  5132.  
  5133.  
  5134.              ┌─────────────────┐
  5135.        0-0FH │ CGA-compatible  │
  5136.              │ default colors  │
  5137.              ├─────────────────┤
  5138.       10-1FH │                 │
  5139.              │   gray scale    │
  5140.              ├─────────────────┤
  5141.              │   blue, red,    │ High        ▒
  5142.              │     green       │ saturation  ▒
  5143.              │-----------------│             ▒
  5144.       20-67H │   blue, red,    │ Moderate    ▒ High intensity
  5145.              │     green       │ saturation  ▒
  5146.              │-----------------│             ▒
  5147.              │   blue, red,    │ Low         ▒
  5148.              │     green       │ saturation  ▒
  5149.              ├─────────────────┤
  5150.              │   blue, red,    │ High        ▒
  5151.              │     green       │ saturation  ▒
  5152.              │-----------------│             ▒
  5153.       68-AFH │   blue, red,    │ Moderate    ▒ Moderate intensity
  5154.              │     green       │ saturation  ▒
  5155.              │-----------------│             ▒
  5156.              │   blue, red,    │ Low         ▒
  5157.              │     green       │ saturation  ▒
  5158.              ├─────────────────│
  5159.              │   blue, red,    │ High        ▒
  5160.              │     green       │ saturation  ▒
  5161.              │-----------------│             ▒
  5162.       B0-F7H │   blue, red,    │ Moderate    ▒ Low intensity
  5163.              │     green       │ saturation  ▒
  5164.              │-----------------│             ▒
  5165.              │   blue, red,    │ Low         ▒
  5166.              │     green       │ saturation  ▒
  5167.              ├─────────────────┤
  5168.       F8-FFH │                 │
  5169.              │     black       │
  5170.              └─────────────────┘
  5171.  
  5172.       Figure 4-14.  Default video DAC colors in 320-by-200 256-color mode
  5173.       (MCGA and VGA).
  5174.  
  5175.  
  5176.       Normally, the video BIOS programs the video DAC registers with a
  5177.       default spectrum of color values (see Figure 4-14) when 320-by-200
  5178.       256-color mode is selected. Registers 0 through 0FH contain the
  5179.       default gamut of CGA-compatible colors. Registers 10H through 1FH
  5180.       contain a gray scale of gradually increasing intensity. The next 216
  5181.       registers (20H through F7H) contain three groups of 72 colors, with
  5182.       the first group (registers 20H through 67H) at high intensity, the
  5183.       second (registers 68H through AFH) at an intermediate intensity, and
  5184.       the third (registers B0H through F7H) at low intensity. Each 72-color
  5185.       group is made up of three ranges of colors of decreasing saturation
  5186.       (increasing whiteness); each range varies smoothly in hue from blue to
  5187.       red to green.
  5188.  
  5189.         ╔═══╗     To disable or enable default video BIOS programming of the
  5190.         ║ T ║     video DAC color registers, use INT 10H function 12H (see
  5191.         ║ I ║     Appendix A).
  5192.         ║ P ║
  5193.         ╚═══╝
  5194.  
  5195.  
  5196.  VGA
  5197.  
  5198.       As on the EGA, VGA pixel values are decoded by the Attribute
  5199.       Controller, using the palette registers, and then passed to the video
  5200.       DAC, following the same logic as in alphanumeric modes (see Chapter
  5201.       3). Thus, a pixel value selects the corresponding palette register;
  5202.       the value in the palette register, along with the bit fields in the
  5203.       Attribute Controller's Color Select register, selects one of the 256
  5204.       video DAC color registers. The video DAC converts the 18-bit RGB value
  5205.       in its color registers to the corresponding analog RGB signals, which
  5206.       drive the monitor.
  5207.  
  5208.       The only exception to this scheme of attribute decoding occurs in 320-
  5209.       by-200 256-color mode. In this mode, as on the MCGA, each 8-bit pixel
  5210.       value specifies one of the video DAC's 256 color registers directly,
  5211.       without the Attribute Controller's mediation.
  5212.  
  5213.  
  5214.  
  5215.                          5  Pixel Programming
  5216.  
  5217.  
  5218.                          Bit-Plane Programming
  5219.                       EGA and VGA ■ InColor Card
  5220.  
  5221.                         Reading a Pixel's Value
  5222.                        CGA ■ HGC and HGC+ ■ EGA
  5223.                        InColor Card ■ MCGA ■ VGA
  5224.  
  5225.                         Setting a Pixel's Value
  5226.                        CGA ■ HGC and HGC+ ■ EGA
  5227.                        InColor Card ■ MCGA ■ VGA
  5228.  
  5229.                        Filling the Video Buffer
  5230.                    CGA ■ HGC and HGC+ ■ EGA and VGA
  5231.                           InColor Card ■ MCGA
  5232.  
  5233.  
  5234.  
  5235.       Many graphics programming techniques are based on routines that
  5236.       manipulate individual pixels in the video buffer. This chapter
  5237.       presents the fundamentals of pixel programming: reading a pixel's
  5238.       value, setting the value of a pixel in the video buffer, and
  5239.       initializing an area of the video buffer with a pattern of pixels.
  5240.  
  5241.  
  5242.  Bit-Plane Programming
  5243.  
  5244.  
  5245.       There is a fundamental difference between graphics-mode programming
  5246.       using video subsystems whose video RAM is organized as parallel bit
  5247.       planes (the EGA, the VGA, and the InColor Card) and graphics-mode
  5248.       programming for the other IBM video subsystems. On the CGA, the MCGA,
  5249.       or the Hercules monochrome adapter, your program accesses pixels by
  5250.       directly reading and writing bytes in video RAM. In contrast, in
  5251.       native graphics modes on the EGA, VGA, or InColor Card, your program
  5252.       cannot access video RAM directly. Instead, special hardware logic in
  5253.       the video subsystem mediates accesses to pixels in the bit planes.
  5254.  
  5255.       The graphics-mode bit planes on the EGA, VGA, and InColor Cards are
  5256.       addressed in parallel; that is, when you execute a CPU read or write
  5257.       at a particular address in the video buffer, the address refers not to
  5258.       one byte, but to four bytes, one in each of the bit planes.
  5259.  
  5260.       When you execute an 80x86 instruction that attempts to read data from
  5261.       an address in the video buffer, four bytes of data are actually moved
  5262.       out of the buffer. The data does not go directly to the CPU, however.
  5263.       Instead, it is copied into a set of four 8-bit latches. Each latch is
  5264.       assigned to one of the four bit planes. Executing an 8-bit CPU read
  5265.       from an address in the video buffer thus has the effect of
  5266.       transferring four bytes (32 bits) of data from the video buffer into
  5267.       the latches (see Figure 5-1a). Instructions such as MOV reg,mem,
  5268.       LODS, and CMP reg,mem require a CPU read, and thus cause the latches
  5269.       to be updated.
  5270.  
  5271.       Similarly, instructions such as MOV mem,reg, STOS, and XOR mem,reg
  5272.       cause a CPU write; in this case, all four bit planes can be updated in
  5273.       parallel using a combination of the data in the latches, the data byte
  5274.       that the CPU writes, and a predefined pixel value stored in a graphics
  5275.       control register (see Figure 5-1b).
  5276.  
  5277.       Some CPU instructions require both a CPU read and a CPU write. (The
  5278.       CPU reads a value from memory, performs an operation on it, and then
  5279.       writes the result back to memory.) MOVS is an obvious example, but
  5280.       OR mem,reg, AND mem,reg, and XOR mem,reg also generate a CPU read and
  5281.       write. When such an instruction refers to an address in video RAM, the
  5282.       latches are updated during the CPU read, and then the bit planes are
  5283.       updated during the CPU write.
  5284.  
  5285.       The use of latches to process bit-plane data in parallel lets you
  5286.       write deceptively simple code. For example, consider the following
  5287.       fragment, which copies the second byte of pixels in the video buffer
  5288.       to the first byte.
  5289.  
  5290.  
  5291.         ┌──┐
  5292.         │  │
  5293.         │  ├──┐
  5294.         └──┘  │
  5295.               │
  5296.         ┌──┐  │    ┌───────────────────────┐ ──┐
  5297.         │  │  └── │1  1  0  0  0  0  1  1 │   │
  5298.         │  ├──┐    ├───────────────────────┤   │
  5299.         └──┘  └── │0  1  0  1  0  0  1  0 │   │ ┌───────────────────────┐
  5300.  bit               ├───────────────────────┤   ├│x  x  x  x  x  x  x  x │
  5301.  planes ┌──┐  ┌── │1  0  1  0  1  1  0  1 │   │ └───────────────────────┘
  5302.         │  ├──┘    ├───────────────────────┤   │    8-bit CPU register
  5303.         │  │  ┌── │1  0  1  1  0  1  1  0 │   │
  5304.         └──┘  │    └───────────────────────┘ ──┘
  5305.               │       four 8-bit latches
  5306.         ┌──┐  │
  5307.         │  ├──┘
  5308.         │  │
  5309.         └──┘
  5310.       a.
  5311.  
  5312.      ┌───────────────────────┐                                  ┌──┐
  5313.      │x  x  x  x  x  x  x  x ├────┐                             │  │
  5314.      └───────────────────────┘    │        ┌───────────────────│  │
  5315.         8-bit CPU register        │        │                    └──┘
  5316.                                   │        │
  5317.                                   │        │                    ┌──┐
  5318.                                   │        │                    │  │
  5319.                                   │        │   ┌───────────────│  │
  5320.      ┌───────────────────────┐    │        │   │                └──┘
  5321.      │1  1  0  0  0  0  1  1 ├───────────┘   │                      bit
  5322.      ├───────────────────────┤    │  │         │                ┌──┐  planes
  5323.      │0  1  0  1  0  0  1  0 ├──────┼────────┘   ┌───────────│  │
  5324.      ├───────────────────────┤    │  │  │          │            │  │
  5325.      │1  0  1  0  1  1  0  1 ├──────┼──┼─────────┘            └──┘
  5326.      ├───────────────────────┤    │  │  │  │
  5327.      │1  0  1  1  0  1  1  0 ├──────┼──┼──┼──────┐            ┌──┐
  5328.      └───────────────────────┘       │  │  │  │    │            │  │
  5329.         four 8-bit latches           │  │  │  │    └───────────│  │
  5330.                         ┌────────────┴──┴──┴──┴─┐               └──┘
  5331.                         │            x  x  x  x │
  5332.                         └───────────────────────┘
  5333.                              4-bit pixel data
  5334.                       (EGA, VGA: Set/Reset register)
  5335.       b.           (InColor: Read/Write Color register)
  5336.  
  5337.       Figure 5-1.  Graphics mode data flow on the EGA, the VGA, and the
  5338.       InColor Card during CPU (a.) read and (b.) write.
  5339.  
  5340.  
  5341.          mov     ax,VideoBufferSegment
  5342.          mov     ds,ax
  5343.          mov     es,ax
  5344.          mov     si,1     ; DS:SI -> second byte
  5345.          mov     di,0     ; ES:DI -> first byte
  5346.          movsb
  5347.  
  5348.  
  5349.       This code looks straightforward. The MOVSB instruction apparently
  5350.       copies one byte from the memory location at DS:SI to the location at
  5351.       ES:DI--but this is not really what takes place in graphics modes that
  5352.       use bit planes in the EGA, VGA, or InColor video buffer.
  5353.  
  5354.       What actually happens is this: The MOVSB instruction causes a CPU
  5355.       read, followed by a CPU write. Because the CPU read references an
  5356.       address in the video buffer, a byte from each bit plane at that
  5357.       address is loaded into the latches. Then, because the CPU write
  5358.       references an address in the video buffer, the contents of the latches
  5359.       are copied into the bit planes at the specified address. Thus, the
  5360.       MOVSB actually causes four bytes of data to be moved instead of one.
  5361.  
  5362.         ╔═══╗     There is more to this example than meets the eye. Consider
  5363.         ║ T ║     what would happen if you substituted a MOVSW instruction
  5364.         ║ I ║     for the MOVSB. Without bit planes and latches, this would
  5365.         ║ P ║     result in two bytes of data being copied instead of one
  5366.         ╚═══╝     byte. However, half of the pixel data would be lost on the
  5367.                   EGA, the VGA, or the InColor Card. The reason is that the
  5368.                   MOVSW executes as a sequence of two 8-bit CPU reads,
  5369.                   followed by two 8-bit CPU writes, so the second CPU read
  5370.                   updates the latches before the bytes latched by the first
  5371.                   CPU read can be written.
  5372.  
  5373.                   For this reason, you should use 16-bit 80x86 instructions
  5374.                   cautiously when accessing the video buffer on the EGA, the
  5375.                   VGA, and the InColor Card. Instructions such as
  5376.                   OR mem,reg, AND mem,reg, and XOR mem,reg do not work
  5377.                   properly with 16-bit data.
  5378.  
  5379.       The latches clearly improve efficiency in moving data to and from the
  5380.       video buffer, but the real fun begins in transferring data between the
  5381.       latches and the CPU. Since the latches contain 32 bits of data and a
  5382.       CPU byte register contains only eight bits, some sort of data
  5383.       compression must take place during CPU reads. Conversely, in
  5384.       transferring data from the CPU to the bit planes, you can combine the
  5385.       8-bit CPU data byte with the contents of all four latches in a number
  5386.       of ways. The key to graphics-mode programming on the EGA, the VGA, and
  5387.       the InColor Card is to exploit the data transformations involving the
  5388.       CPU and the latches.
  5389.  
  5390.  
  5391.  EGA and VGA
  5392.  
  5393.       On the EGA and VGA, the Graphics Controller manages all transfers of
  5394.       data among the CPU, the latches, and the video buffer. The EGA's
  5395.       Graphics Controller consists of two LSI chips; the VGA's is part of
  5396.       the Video Graphics Array chip. The Graphics Controller has nine
  5397.       registers addressable at port 3CFH via an address register at port
  5398.       3CEH. The values you store in the registers control the way the
  5399.       Graphics Controller processes latched data during CPU reads and
  5400.       writes.
  5401.  
  5402.       In a sense, the Graphics Controller lets you manipulate the latched
  5403.       pixel data two-dimensionally. Some of the operations you can perform
  5404.       on the latched data are byte-oriented; they affect each latch
  5405.       separately. Other operations are pixel-oriented in that they regard
  5406.       the latched data as a set of eight pixel values; these operations
  5407.       affect each pixel value separately.
  5408.  
  5409.       The Graphics Controller can perform three different byte-oriented
  5410.       operations on latched data. It can copy the contents of the latches to
  5411.       and from the video buffer; this action occurs implicitly when a CPU
  5412.       write or read is executed. It can return the contents of one of the
  5413.       latches to a CPU register during a CPU read. It can also combine a
  5414.       data byte from a CPU register with the bytes in any or all of the
  5415.       latches during a single CPU write.
  5416.  
  5417.       The Graphics Controller also processes latched data pixel by pixel.
  5418.       During a CPU read, the Graphics Controller can compare each latched
  5419.       pixel value with a predefined value and return the result of the
  5420.       comparison to the CPU. During CPU writes, it can combine a 4-bit CPU
  5421.       value with any or all pixel values in the latches; it can use an 8-bit
  5422.       CPU value as a mask that indicates which of the eight latched pixels
  5423.       are copied back to the bit planes; and it can combine the latched
  5424.       pixel values with a predefined 4-bit value.
  5425.  
  5426.       Both byte-oriented and pixel-oriented operations are programmed by
  5427.       selecting a write mode and a read mode. Each write mode sets up a
  5428.       predefined sequence of byte-oriented and pixel-oriented operations
  5429.       which occur when a CPU write is executed. Similarly, each read mode
  5430.       defines a set of actions performed during CPU reads. The EGA has three
  5431.       write modes and two read modes; the VGA has these five modes and one
  5432.       additional write mode.
  5433.  
  5434.       Until you become familiar with each of the Graphics Controller's read
  5435.       and write modes, their raison d'etre may seem a bit obscure. However,
  5436.       each mode has practical advantages in certain programming situations,
  5437.       as the examples in this and subsequent chapters demonstrate.
  5438.  
  5439.       The Graphics Controller's Mode register (05H) contains two bit fields
  5440.       whose values specify the graphics read and write mode. For example, to
  5441.       establish read mode 1 you would set bit 3 of the Mode register to 1;
  5442.       to set up write mode 2, you would store the value 2 (10B) in bits 0
  5443.       and 1 of the Mode register (Listing 5-1).
  5444.  
  5445.  
  5446.  ───────────────────────────────────────────────────────────────────────────
  5447.  
  5448.       Listing 5-1.  How to set Graphics Controller read and write modes.
  5449.       This example sets read mode 0 and write mode 1 in in 640-by-350 16-
  5450.       color mode.
  5451.  
  5452.  ───────────────────────────────────────────────────────────────────────────
  5453.  
  5454.  
  5455.         ╔═══╗     The video BIOS default values for the Graphics
  5456.         ║ T ║     Controller's Mode register and its other registers are
  5457.         ║ I ║     listed in Figure 5-2. It is good practice to restore the
  5458.         ║ P ║     Graphics Controller registers to their default values
  5459.         ╚═══╝     after you modify them in your program.
  5460.  
  5461.  
  5462. ╓┌───────────┌────────────────────┌──────────────────────────────────────────╖
  5463.  Register    Function             Value
  5464.  ──────────────────────────────────────────────────────────────────────────
  5465.  0           Set/Reset            0
  5466.  1           Enable Set/Reset     0
  5467.  2           Color Compare        0
  5468.  3           Data Rotate          0
  5469.  4           Read Map Select      0
  5470.  5           Mode                 Bits 0-3 always 0
  5471.  6           Miscellaneous        (depends on video mode)
  5472.  7           Color Don't Care     0FH (16-color modes)
  5473.                                   01H (640-by-480 2-color mode)
  5474.  8           Bit Mask             FFH
  5475.  
  5476.       Figure 5-2.  Default ROM BIOS values for EGA and VGA Graphics
  5477.       Controller registers.
  5478.  
  5479.  
  5480.       Read mode 0
  5481.       In graphics read mode 0, the Graphics Controller returns the contents
  5482.       of one of the four latches to the CPU each time a CPU read loads the
  5483.       latches (see Figure 5-3). The value in the Read Map Select register
  5484.       (04H) indicates which latch to read. Read mode 0 thus lets you read
  5485.       bytes from each individual bit plane; this is useful in transferring
  5486.       data between the bit planes and system RAM or a disk file.
  5487.  
  5488.  
  5489.                                                 Read Map Select register
  5490.                                                ┌───────────────────────┐
  5491.                                                │x  x  x  x  x  x  0  0 │
  5492.                                                └───────────────────────┘
  5493.                                                                   ▒▒▒▒
  5494.                                             ┌───────────────────────┘
  5495.                ┌───────────────────────┐    │
  5496.         ──────│1  1  0  0  0  0  1  1 │3──┘
  5497.                ├───────────────────────┤
  5498.         ──────│0  1  0  1  0  0  1  0 │2
  5499.  bit           ├───────────────────────┤
  5500.  planes ──────│1  0  1  0  1  1  0  1 │1               CPU data
  5501.                ├───────────────────────┤       ┌───────────────────────┐
  5502.         ──────│1  0  1  1  0  1  1  0 │0─────│1  0  1  1  0  1  1  0 │
  5503.                └───────────────────────┘       └───────────────────────┘
  5504.  
  5505.       Figure 5-3.  EGA and VGA graphics read mode 0.
  5506.  
  5507.  
  5508.       Read Mode 1
  5509.       In graphics read mode 1, each of the eight pixel values latched during
  5510.       a CPU read is compared with the value in the Color Compare register
  5511.       (02H). The result of the comparison is returned to the CPU as a single
  5512.       byte (see Figure 5-4). Where a pixel value matches the Color Compare
  5513.       value, a bit in the CPU data byte is set to 1; where the values are
  5514.       different, the corresponding bit in the data byte is 0.
  5515.  
  5516.       Note how the value in the Color Don't Care register (07H) interacts
  5517.       with the pixel value and Color Compare value. In effect, setting a bit
  5518.       to 0 in the Color Don't  Care value excludes a latch from the
  5519.       comparison. For example, a Color Don't Care value of 0111B causes only
  5520.       the three low-order bits of each pixel value to participate in the
  5521.       comparison. Another example: If you store a 0 in the Color Don't Care
  5522.       register, all four bits in the comparison become "don't care" bits, so
  5523.       all pixel values match the Color Compare value, and the CPU always
  5524.       reads the value 11111111B in read mode 1.
  5525.  
  5526.  
  5527.                                                   Color Don't Care register
  5528.                                                   ┌───────────────────────┐
  5529.  ┌─bit planes                                     │x  x  x  x  1  1  1  1 │
  5530.  │                            latches             └───────────────────────┘
  5531.  │        ┌───────────────────────────────────────────────┐    ▒▒▒▒▒▒▒▒▒▒
  5532.  ├─────  │  1     1    ▒0▒▒   0     0    ▒0▒▒   1     1  │         │
  5533.  │        ├───────────────────────────────────────────────┤         │
  5534.  ├─────  │  0     1    ▒0▒▒   1     0    ▒0▒▒   1     0  │         │
  5535.  │        ├───────────────────────────────────────────────┤         │
  5536.  ├─────  │  1     0    ▒1▒▒   0     1    ▒1▒▒   0     1  │         │
  5537.  │        ├───────────────────────────────────────────────┤         │
  5538.  └─────  │  1     0    ▒1▒▒   1     0    ▒1▒▒   1     0  │         │
  5539.           └───────────────────────────────────────────────┘         │
  5540.                                                             │
  5541.  pixel      1011  1100  0011  0101  0010  0011  1101  1010          │
  5542.  values      │────│────│────│────│────│────│────│───────────│
  5543.                                                             │
  5544.  AND with   1011  1100  0011  0101  0010  0011  1101  1010          
  5545.  Color       │────│────│────│────│────│────│────│──────────────┐
  5546.  Don't                                                         │
  5547.  Care    ┌────────────────────────────────────────────────┐            │
  5548.          │   0     0    ▒1▒▒   0     0    ▒1▒▒   0     0  │            │
  5549.          └────────────────────────────────────────────────┘            │
  5550.                             CPU data byte                       ▒▒▒▒▒▒▒▒▒▒
  5551.                                                    ┌───────────────────────┐
  5552.                                                    │x  x  x  x ▒0▒▒0▒▒1▒▒1▒│
  5553.                                                    └───────────────────────┘
  5554.                                                     Color Compare register
  5555.  
  5556.       Figure 5-4.  EGA and VGA graphics read mode 1.
  5557.  
  5558.  
  5559.       Write mode 0
  5560.       Graphics write mode 0 sets up a combination of byte-oriented and
  5561.       pixel-oriented operations that occur when a CPU write is executed. The
  5562.       data byte written by the CPU can be used to update any or all of the
  5563.       bit planes; at the same time, a predefined pixel value can be used to
  5564.       update any or all of the eight pixels involved. This two-dimensional
  5565.       update of the latches is controlled in several different ways using
  5566.       the values in the Enable Set/Reset, Data Rotate/Function Select, and
  5567.       Bit Mask registers (see Figure 5-5).
  5568.  
  5569.       The Bit Mask register (08H) specifies how the new value of each of the
  5570.       eight pixels in the video buffer is derived. Where a bit in the Bit
  5571.       Mask register equals 0, the corresponding pixel value is copied
  5572.       directly from the latches into the video buffer. For each 1 bit in the
  5573.       Bit Mask value, the corresponding pixel is updated with the latched
  5574.       pixel value combined with either the CPU data or the pixel value in
  5575.       the Set/Reset register. Thus, if a CPU write immediately follows a CPU
  5576.       read at the same address, the only pixels updated are those for which
  5577.       the corresponding bit in the Bit Mask register is set to 1.
  5578.  
  5579.  
  5580.               Data Rotate/Function Select register
  5581.                    ┌───────────────────────┐
  5582.                    │x  x  x  0  0  x  x  x │
  5583.                    └───────────────────────┘
  5584.        replace, AND. OR, XOR ▒▒▒▒           ┌───────────────────────┐
  5585.                               │          ┌─│1  1  0  0 ▒0▒▒0▒▒0▒▒0▒│▒
  5586.          latches              │       ┌──┘  └───────────────────────┘▒
  5587.  ┌───────────────────────┐▒▒▒▒▒▒▒▒▒▒▒▒│  ┌───────────────────────┐   ▒
  5588.  │1  1  0  0  0  0  1  1 ├────────────┘┌│0  1  0  1 ▒1▒▒1▒▒1▒▒1▒│   ▒
  5589.  ├───────────────────────┤         ┌──┘ └───────────────────────┘   ▒bit
  5590.  │0  1  0  1  0  0  1  0 ├┼─────────┘  ┌───────────────────────┐     ▒planes
  5591.  ├───────────────────────┤│        ┌─│1  0  1  0 ▒1▒▒1▒▒1▒▒1▒│     ▒
  5592.  │1  0  1  0  1  1  0  1 ├┼──┼──────┘  └───────────────────────┘     ▒
  5593.  ├───────────────────────┤│  │      ┌───────────────────────┐       ▒
  5594.  │1  0  1  1  0  1  1  0 ├┼──┼──┼───│1  0  1  1 ▒1▒▒1▒▒1▒▒1▒│       ▒
  5595.  └───────────────────────┘│  │  │   └┬──┬──┬──┬──┬──┬──┬──┬─┘
  5596.                         ┌─┘┌─┘┌─┘┌─┘  |  |  |  |  |  |  |  |
  5597.  Set/Reset ┌────────────┴──┴──┴──┴─┐ ┌┴──┴──┴──┴──┴──┴──┴──┴─┐
  5598.  register  │x  x  x  x ▒0▒▒1▒▒1▒▒1▒│ │0  0  0  0  1  1  1  1 │
  5599.            └───────────────────────┘ └───────────────────────┘
  5600.             |  |  |  |  |  |  |  |      Bit Mask register
  5601.  Enable     |  |  |  |  |  |  |  |      (Pixels 0-3 are derived from
  5602.  Set/Reset ┌┴──┴──┴──┴──┴──┴──┴──┴─┐    Set/Reset; pixels 4-7 are
  5603.  register  │x  x  x  x  1  1  1  1 │    copied from latches.)
  5604.            └───────────────────────┘
  5605.  a.
  5606.  
  5607.                                             ┌───────────────────────┐
  5608.                                          ┌─│1  1  0  0 ▒0▒▒1▒▒1▒▒1▒│▒
  5609.          latches                      ┌──┘  └───────────────────────┘▒
  5610.  ┌───────────────────────┐            │  ┌───────────────────────┐   ▒
  5611.  │1  1  0  0  0  0  1  1 ├─────┬──────┘┌│0  1  0  1 ▒0▒▒1▒▒1▒▒1▒│   ▒
  5612.  ├───────────────────────┤         ┌──┘ └───────────────────────┘   ▒bit
  5613.  │0  1  0  1  0  0  1  0 ├─────┼────┘┌───────────────────────┐       ▒planes
  5614.  ├───────────────────────┤       ┌─│1  0  1  0 ▒0▒▒1▒▒1▒▒1▒│       ▒
  5615.  │1  0  1  0  1  1  0  1 ├─────┼──┘  └───────────────────────┘       ▒
  5616.  ├───────────────────────┤         ┌───────────────────────┐        ▒
  5617.  │1  0  1  1  0  1  1  0 ├─────┼───│1  0  1  1 ▒0▒▒1▒▒1▒▒1▒│        ▒
  5618.  └───────────────────────┘         └┬──┬──┬──┬──┬──┬──┬──┬─┘
  5619.          replace, AND, OR, XOR │     |  |  |  |  |  |  |  |
  5620.             ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─│    ┌┴──┴──┴──┴──┴──┴──┴──┴─┐
  5621.             |                  │    │0  0  0  0  1  1  1  1 │
  5622.             │              00010111 └───────────────────────┘
  5623.             |         Rotate        Bit Mask register (Pixels 0-3
  5624.             │       ┌ ─ ─ ─ ─ ─│     are derived from CPU data; pixels
  5625.             |       |          │     4-7 are copied from latches.)
  5626.            ▒▒▒▒  ▒▒▒▒▒▒▒       │
  5627.  ┌───────────────────────┐ ┌───┴───────────────────┐
  5628.  │x  x  x  0  0  0  0  0 │ │0  0  0  1 ▒0▒▒1▒▒1▒▒1▒│
  5629.  └───────────────────────┘ └───────────────────────┘
  5630.    Data Rotate/Function          CPU data byte
  5631.       Select register
  5632.  b.
  5633.  
  5634.       Figure 5-5.  EGA and VGA graphics write mode 0: (a.) Enable Set/Reset
  5635.       Value = 1111B, (b.) Enable Set/Reset value = 0000B.
  5636.  
  5637.  
  5638.       The Data Rotate/Function Select register (03H) contains two bit fields
  5639.       whose contents affect the way the latched pixels are updated. Bits 3
  5640.       through 4 are important because their value specifies which bitwise
  5641.       logical operation (AND, OR, XOR, or replace) is used to update the
  5642.       pixels (see Figure 5-6). Bits 0 through 2 specify the number of bits
  5643.       by which to right-rotate the CPU data byte before combining it with
  5644.       latched data.
  5645.  
  5646.  
  5647. ╓┌────────────────────────┌──────────────────────────────────────────────────╖
  5648.      Bit Value            Function
  5649.  Bit 4       Bit 3
  5650.  ──────────────────────────────────────────────────────────────────────────
  5651.      Bit Value            Function
  5652.  Bit 4       Bit 3
  5653.  ──────────────────────────────────────────────────────────────────────────
  5654.  0           0            Replace
  5655.  0           1            AND
  5656.  1           0            OR
  5657.  1           1            XOR
  5658.  
  5659.       Figure 5-6.  Functions available for updating pixels in EGA and VGA
  5660.       write modes 0, 2, and 3. Bits 3 and 4 of the Data Rotate/Function
  5661.       Select register specify which is used.
  5662.  
  5663.  
  5664.         ╔═══╗     This data-rotate capability is not particularly useful. In
  5665.         ║ T ║     practice, it is generally easier to let the CPU rotate and
  5666.         ║ I ║     shift data before writing it to the bit planes than it is
  5667.         ║ P ║     to program the Graphics Controller to do this.
  5668.         ╚═══╝
  5669.  
  5670.       The value in the Enable Set/Reset register (register 01H) determines
  5671.       whether the bit planes are updated byte by byte or pixel by pixel.
  5672.       When the Enable Set/Reset value is 0FH (1111B), each pixel is updated
  5673.       by combining the latched pixel value with the value in the Set/Reset
  5674.       register (register 00H) using the logical operation that the Data
  5675.       Rotate/Function Select register specifies (refer to Figure 5-5a).
  5676.       When the Enable Set/Reset value is 0, the rotated CPU data byte is
  5677.       combined with the bytes in each of the latches, again using the
  5678.       function that the Data Rotate/Function Select register specifies (see
  5679.       Figure 5-5b). In either case, only the pixels masked by the Bit
  5680.       Mask register are updated.
  5681.  
  5682.         ╔═══╗     Of course, you can set the Enable Set/Reset register to
  5683.         ║ T ║     any value from 0 through 0FH. Each bit in each pixel is
  5684.         ║ I ║     then updated by combining it either with the corresponding
  5685.         ║ P ║     bit in the Set/Reset register or with the corresponding
  5686.         ╚═══╝     bit in the CPU data byte--depending on the value of the
  5687.                   corresponding bit in the Enable Set/Reset register.
  5688.                   Needless to say, this kind of programming is tricky and
  5689.                   infrequently used.
  5690.  
  5691.  
  5692.       Write mode 1
  5693.       In write mode 1, the latches are copied directly to the bit planes
  5694.       when a CPU write occurs (see Figure 5-7). Neither the value of the
  5695.       CPU data byte nor those of the Data Rotate/Function Select, the Bit
  5696.       Mask, the Set/Reset, and the Enable Set/Reset registers affect this
  5697.       process. Clearly, for a write mode 1 operation to make sense, you must
  5698.       first perform a CPU read to initialize the latches.
  5699.  
  5700.  
  5701.               latches
  5702.       ┌───────────────────────┐
  5703.       │x  x  x  x  x  x  x  x ├─────────────
  5704.       ├───────────────────────┤
  5705.       │x  x  x  x  x  x  x  x ├─────────────
  5706.       ├───────────────────────┤               bit planes
  5707.       │x  x  x  x  x  x  x  x ├─────────────
  5708.       ├───────────────────────┤
  5709.       │x  x  x  x  x  x  x  x ├─────────────
  5710.       └───────────────────────┘
  5711.  
  5712.       Figure 5-7.  EGA and VGA graphics write mode 1.
  5713.  
  5714.  
  5715.       Write mode 2
  5716.       In write mode 2, the low-order bits of the byte written by the CPU
  5717.       play the same role as the Set/Reset register value in write mode 0.
  5718.       That is, the bit planes are updated by combining the pixel values in
  5719.       the latches with the CPU data, using the logical operation specified
  5720.       in the Data Rotate/Function Select register (see Figure 5-8). As in
  5721.       write mode 0, the Bit Mask register specifies which pixels are updated
  5722.       using the combined pixel values and which pixels are updated directly
  5723.       from the latches.
  5724.  
  5725.  
  5726.               Data Rotate/Function Select register
  5727.                     ┌───────────────────────┐
  5728.                     │x  x  x  0  0  x  x  x │
  5729.                     └───────────────────────┘
  5730.                               ▒▒▒▒
  5731.          replace, AND, OR, XOR │            ┌───────────────────────┐
  5732.                                │   ┌───────│1  1  0  0 ▒0▒▒0▒▒0▒▒0▒│▒
  5733.           latches                 │        └───────────────────────┘▒
  5734.  ┌───────────────────────┐▒▒▒▒▒▒▒▒▒│      ┌───────────────────────┐  ▒
  5735.  │1  1  0  0  0  0  1  1 ├─────────┘ ┌───│0  1  0  1 ▒1▒▒1▒▒1▒▒1▒│  ▒
  5736.  ├───────────────────────┤          │    └───────────────────────┘  ▒
  5737.  │0  1  0  1  0  0  1  0 ├─┼─────────┘  ┌───────────────────────┐    ▒bit
  5738.  ├───────────────────────┤ │        ┌─│1  0  1  0 ▒1▒▒1▒▒1▒▒1▒│    ▒planes
  5739.  │1  0  1  0  1  1  0  1 ├─┼──┼──────┘  └───────────────────────┘    ▒
  5740.  ├───────────────────────┤ │  │        ┌───────────────────────┐    ▒
  5741.  │1  0  1  1  0  1  1  0 ├─┼──┼──┼─────│1  0  1  1 ▒1▒▒1▒▒1▒▒1▒│    ▒
  5742.  └───────────────────────┘ │  │  │     └┬──┬──┬──┬──┬──┬──┬──┬─┘
  5743.                            │  │  │  │    |  |  |  |  |  |  |  |
  5744.               ┌────────────┴──┴──┴──┴─┐ ┌┴──┴──┴──┴──┴──┴──┴──┴─┐
  5745.               │x  x  x  x ▒0▒▒1▒▒1▒▒1▒│ │0  0  0  0  1  1  1  1 │
  5746.               └───────────────────────┘ └───────────────────────┘
  5747.                       CPU data              Bit Mask register
  5748.  
  5749.       Figure 5-8.  EGA and VGA graphics write mode 2.
  5750.  
  5751.  
  5752.       Write mode 3
  5753.       In write mode 3 (supported on the VGA only), the pixels are updated by
  5754.       combining the pixel values in the latches with the value in the
  5755.       Set/Reset register. Again, the Data Rotate/Function Select register
  5756.       specifies the logical operation used to combine the values. The CPU
  5757.       data byte is rotated by the number of bits indicated in the Data
  5758.       Rotate/Function Select register and combined with the value in the Bit
  5759.       Mask register using a logical AND. The resulting bit mask then plays
  5760.       the same role as the Bit Mask register value in write modes 0 and 2;
  5761.       that is, it determines which pixels in the bit planes are updated by
  5762.       combining the latched pixel values with the Set/Reset value, and which
  5763.       are updated directly from the latches (see Figure 5-9).
  5764.  
  5765.  
  5766.         Enable ┌───────────────────────┐
  5767.      Set/Reset │x  x  x  x  1  1  1  1 │
  5768.       register └────────────┬──┬──┬──┬─┘
  5769.                             |  |  |  |
  5770.                ┌────────────┴──┴──┴──┴─┐
  5771.      Set/Reset │x  x  x  x ▒0▒▒1▒▒1▒▒1▒│
  5772.       register └────────────┬──┬──┬──┬─┘       ┌───────────────────────┐
  5773.                             │  │  │  │ ┌──────│1  1  0  0 ▒0▒▒0▒▒0▒▒0▒│  ▒
  5774.           latches           │  │  │  │ │       └───────────────────────┘  ▒
  5775.  ┌───────────────────────┐    │  │  │ │    ┌───────────────────────┐     ▒
  5776.  │1  1  0  0  0  0  1  1 ├─────┼──┼──┼─┘┌──│0  1  0  1 ▒1▒▒1▒▒1▒▒1▒│     ▒
  5777.  ├───────────────────────┤       │  │  │   └───────────────────────┘     ▒
  5778.  │0  1  0  1  0  0  1  0 ├────────┼──┼──┘  ┌───────────────────────┐   bit▒
  5779.  ├───────────────────────┤          │  ┌─│1  0  1  0 ▒1▒▒1▒▒1▒▒1▒│planes▒
  5780.  │1  0  1  0  1  1  0  1 ├───────────┼──┘  └───────────────────────┘      ▒
  5781.  ├───────────────────────┤               ┌───────────────────────┐       ▒
  5782.  │1  0  1  1  0  1  1  0 ├───────────────│1  0  1  1 ▒1▒▒1▒▒1▒▒1▒│       ▒
  5783.  └───────────────────────┘▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ └┬──┬──┬──┬──┬──┬──┬──┬─┘
  5784.                                 │          |  |  |  |  |  |  |  |
  5785.                                 │          0  0  0  0  1  1  1  1
  5786.                                 │                     
  5787.                                 │             ┌───────┴─────┐
  5788.                      ┌──────────┘             │ Logical AND │
  5789.                      │                        │   ┌─────────┴─────────────┐
  5790.         replace, AND,│                   00001111 │1  1  1  1  1  1  1  1 │
  5791.         OR, XOR      │           Rotate       │   └───────────────────────┘
  5792.                      │       ┌ ─ ─ ─ ─ ─ ─ ─ ─|       Bit Mask register
  5793.                      │       |                │
  5794.                    ▒▒▒▒  ▒▒▒▒▒▒▒              |
  5795.          ┌───────────────────────┐  ┌─────────┴─────────────┐
  5796.          │x  x  x  0  0  0  1  0 │  │0  0  1  1  1  1  0  0 │
  5797.          └───────────────────────┘  └───────────────────────┘
  5798.         Data Rotate/Function Select        CPU data
  5799.                   register
  5800.  
  5801.       Figure 5-9.  VGA graphics write mode 3.
  5802.  
  5803.  
  5804.       Sequencer Map Mask
  5805.       One additional level of control is available in all of the EGA's and
  5806.       the VGA's Graphics Controller write modes. You can use the Sequencer
  5807.       Map Mask register (Sequencer register 02H) to selectively enable or
  5808.       disable data transfers to the bit planes. In 16-color graphics modes,
  5809.       bits 0 through 3 of this register are normally set to 1 to allow
  5810.       graphics writes to access all four maps. However, by zeroing one or
  5811.       more of these bits, you can write-protect the corresponding memory
  5812.       maps.
  5813.  
  5814.       The Sequencer Map Mask register is not often used, because the
  5815.       Graphics Controller provides better control for pixel-oriented
  5816.       operations. Use of this register is better suited to techniques such
  5817.       as bit-plane layering (see Chapter 12).
  5818.  
  5819.  
  5820.  InColor Card
  5821.  
  5822.       The InColor Card has two gate arrays, the Encoder and the Decoder,
  5823.       which mediate CPU accesses to video RAM. The Encoder gate array
  5824.       participates in CPU writes to video RAM. The Decoder gate array
  5825.       manages the transfer of data from video RAM to the CPU, as well as to
  5826.       the card's attribute-decoding circuitry.
  5827.  
  5828.       The programming interface to the InColor Card's graphics-mode
  5829.       hardware, including the Encoder and Decoder chips, is unified through
  5830.       the card's control register set at I/O ports 3B4H and 3B5H (see Figure
  5831.       5-10). There is no distinction between the Encoder, the Decoder, and
  5832.       their associated circuitry from a software point of view. The InColor
  5833.       Card's graphics-mode control registers are similar to control
  5834.       registers on the EGA and the VGA (see Figure 5-11).
  5835.  
  5836.  
  5837. ╓┌──────────────────┌──────────────────────────────────┌─────────────────────╖
  5838.  Register Number    Register Function                  Read/Write Status
  5839.  ──────────────────────────────────────────────────────────────────────────
  5840.  Register Number    Register Function                  Read/Write Status
  5841.  ──────────────────────────────────────────────────────────────────────────
  5842.  18H                Plane Mask register                Write only
  5843.  19H                Read/Write Control register        Write only
  5844.  1AH                Read/Write Color register          Write only
  5845.  1BH                Latch Protect register             Write only
  5846.  
  5847.       Figure 5-10.  Graphics control registers on the Hercules InColor Card.
  5848.  
  5849.  
  5850. ╓┌─────────────────────────────┌─────────────────────────────────────────────╖
  5851.  InColor                       EGA and VGA
  5852.  ──────────────────────────────────────────────────────────────────────────
  5853.  Plane Mask register           Sequencer Map Mask register
  5854.                                Attribute Controller Color Plane
  5855.                                Enable register
  5856.  Read/Write Control register   Graphics Controller Mode register
  5857.                                Graphics Controller Color Don't Care
  5858.                                register
  5859.  Read/Write Color register     Graphics Controller Set/Reset register
  5860.  Palette register              Attribute Controller Palette registers
  5861.  InColor                       EGA and VGA
  5862. Palette register              Attribute Controller Palette registers
  5863.  
  5864.       Figure 5-11.  Functionally similar control registers on the EGA, VGA,
  5865.       and InColor Card.
  5866.  
  5867.  
  5868.       As on the EGA and VGA, video RAM accesses in graphics mode are
  5869.       performed using a set of four 8-bit latches. CPU reads and writes
  5870.       cause bytes to be transferred in parallel between the latches and the
  5871.       corresponding bit planes. When a CPU read is executed, the Decoder
  5872.       latches a byte from each bit plane and returns a single byte of data
  5873.       to the CPU. When a CPU write is executed, the Encoder combines the
  5874.       latched data with the pixel values stored in the Read/Write Color
  5875.       register and updates the bit planes with the result.
  5876.  
  5877.       Like the EGA and VGA, the InColor Card can process CPU data and
  5878.       latched data in several ways. The card supports four graphics write
  5879.       modes (see Figure 5-12), selected by bits 4 and 5 of the Read/Write
  5880.       Control register (19H). There is only one graphics read mode, which
  5881.       is similar to read mode 1 on the EGA and VGA.
  5882.  
  5883.  
  5884. ╓┌────────────┌───────────────────┌──────────────────────────────────────────╖
  5885.  Write Mode   CPU Data Bit = 0    CPU Data Bit = 1
  5886.  ──────────────────────────────────────────────────────────────────────────
  5887.  0            Background value    Foreground value
  5888.  1            Latch               Foreground value
  5889.  2            Background value    Latch
  5890.  3            NOT latch           Latch
  5891.  
  5892.       Figure 5-12.  Source of pixel data in InColor graphics write modes.
  5893.  
  5894.  
  5895.       Write modes 0-3
  5896.       In all four InColor graphics write modes, the CPU data functions as an
  5897.       8-bit mask. The Encoder uses the value of each bit in the mask to
  5898.       determine how to update the corresponding pixel value in the latches.
  5899.       That is, the source of the pixel value at a particular bit position is
  5900.       determined by the value of the corresponding bit in the CPU data byte.
  5901.  
  5902.       For example, in graphics write mode 1, when a bit in the CPU data byte
  5903.       is 1, the corresponding pixel in the video buffer is replaced with the
  5904.       foreground value in the Read/Write Control register; when a bit in the
  5905.       CPU data byte is 0, the corresponding pixel value is copied from the
  5906.       latches. For example, in Figure 5-13, the pixels corresponding to
  5907.       bits 0 through 3 are replaced with the Read/Write Control register
  5908.       foreground value, while the remaining pixels are updated from the
  5909.       pixel values in the latches.
  5910.  
  5911.       Similarly, in the other three graphics write modes, the value of each
  5912.       bit in the CPU data byte controls how the corresponding pixel is
  5913.       updated. The write modes differ only in how the pixel values are
  5914.       derived (see Figure 5-12). In write mode 0, either the foreground or
  5915.       the background value in the Read/Write Control register replaces the
  5916.       pixels in the bit planes. In write mode 2, for each 0 bit in the CPU
  5917.       data byte, the Read/Write Control register background value is used to
  5918.       update the corresponding pixel in the bit planes. In write mode 3,
  5919.       each 0 bit in the CPU data byte causes the corresponding pixel in the
  5920.       video buffer to be replaced with the bitwise NOT of the pixel value in
  5921.       the latches.
  5922.  
  5923.  
  5924.                               Plane Mask register
  5925.                            ┌───────────────────────┐
  5926.                            │0  0  0  0  x  x  x  x │
  5927.                            └┬──┬──┬──┬─────────────┘
  5928.                             |  |  |  |        ┌───────────────────────┐
  5929.                             |  |  |  | ╔═════│1  1  0  0 ▒0▒▒0▒▒0▒▒0▒│  ▒
  5930.           latches           |  |  |  | ║      └───────────────────────┘  ▒
  5931.  ┌───────────────────────┐  |  |  |  | ║    ┌───────────────────────┐    ▒
  5932.  │1  1  0  0  0  0  1  1 ├─╒╧══|══|══|═╝╔══│0  1  0  1 ▒1▒▒1▒▒1▒▒1▒│    ▒
  5933.  ├───────────────────────┤ │   |  |  |  ║   └───────────────────────┘    ▒
  5934.  │0  1  0  1  0  0  1  0 ├─┼──╒╧══|══|══╝ ┌───────────────────────┐   bit▒
  5935.  ├───────────────────────┤ │  │   |  | ╔═│1  0  1  0 ▒1▒▒1▒▒1▒▒1▒│planes▒
  5936.  │1  0  1  0  1  1  0  1 ├─┼──┼──╒╧══|═╝  └───────────────────────┘      ▒
  5937.  ├───────────────────────┤ │  │  │   |  ┌───────────────────────┐        ▒
  5938.  │1  0  1  1  0  1  1  0 ├─┼──┼──┼──╒╧═│1  0  1  1 ▒1▒▒1▒▒1▒▒1▒│        ▒
  5939.  └───────────────────────┘ │  │  │  │   └┬──┬──┬──┬──┬──┬──┬──┬─┘
  5940.                            │  │  │  │    |  |  |  |  |  |  |  |
  5941.               ┌────────────┴──┴──┴──┴─┐ ┌┴──┴──┴──┴──┴──┴──┴──┴─┐
  5942.               │x  x  x  x ▒0▒▒1▒▒1▒▒1▒│ │0  0  0  0  1  1  1  1 │
  5943.               └───────────────────────┘ └───────────────────────┘
  5944.                   Read/Write Color              CPU data
  5945.                       register       (Pixels 0-3 copied from foreground
  5946.                                      value in R/W Color register; pixels
  5947.                                          4-7 copied from latches.)
  5948.  
  5949.       Figure 5-13.  InColor graphics write mode 1.
  5950.  
  5951.  
  5952.       CPU writes affect only those bit planes specified in the Plane Mask
  5953.       register (18H). This register's function is thus analogous to that of
  5954.       the EGA's Sequencer Map Mask register. Bits 4 through 7 of this
  5955.       register control which of the four bit planes are writable; setting
  5956.       any of these bits to 1 prevents updating of the corresponding bit
  5957.       planes during CPU writes.
  5958.  
  5959.  
  5960.       Read mode
  5961.       The InColor Card has only one graphics read mode (see Figure 5-14). It
  5962.       resembles read mode 1 on the EGA and the VGA. When a CPU read is
  5963.       executed, the latches are loaded with data from the bit planes. Unlike
  5964.       the EGA and the VGA, however, the InColor Card lets you control which
  5965.       individual pixel values are latched during a CPU read. The bit mask
  5966.       value in the Latch Protect register (1BH) indicates which pixel values
  5967.       are latched. Where a bit in the Latch Protect register is 0, the
  5968.       corresponding pixel value is latched; where a bit is 1, the
  5969.       corresponding pixel value in the latch remains unchanged.
  5970.  
  5971.  
  5972. ╓┌─────────────────────────────────────────────────────────────┌─────────────
  5973.             ┌────────────────────────────────────────────────┐ Latch
  5974.             │  0     0     0     0     0     0     0     0   │ Protect
  5975.             └──┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬───┘ register
  5976.                |     |     |     |     |     |     |     |
  5977.             ┌──┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴──┐
  5978.        ────│  1     1    ▒0▒▒   0     0    ▒0▒▒   1     1  │
  5979.             ├───────────────────────────────────────────────┤
  5980.  bit   ────│  0     1    ▒0▒▒   1     0    ▒0▒▒   1     0  │
  5981.  planes     ├───────────────────────────────────────────────┤
  5982.        ────│  1     0    ▒1▒▒   0     1    ▒1▒▒   0     1  │
  5983.             ├───────────────────────────────────────────────┤
  5984.        ────│  1     0    ▒1▒▒   1     0    ▒1▒▒   1     0  │ ┌───────────────
  5985.             └───────────────────────────────────────────────┘ │ x  0  x  x  0
  5986.  pixel                                                └───────────────
  5987. pixel                                                └───────────────
  5988.  values       1011  1100  0011  0101  0010  0011  1101  1010    ▒▒▒▒        ▒▒
  5989.                │     │     │     │     │     │     │     │       │  ┌─────────
  5990.  OR with Don't │────│────│────│────│────│────│────│─────────┤ ┌───────
  5991.  Care value                                              │  │ │▒0▒▒0▒▒
  5992.               1011  1100  0011  0101  0010  0011  1101  1010     │  │ └───────
  5993.  COMPARE with  │     │     │     │     │     │     │     │       │     ▒▒▒▒▒▒
  5994.  (background   │────│────│────│────│────│────│────│─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
  5995.  value OR      │     │     │     │     │     │     │     │       │
  5996.  Don't Care                                              │
  5997.  value.)       0     0     1     0     0     1     0     0       │
  5998.                │     │     │     │     │     │     │     │       │
  5999.  XOR with Mask │     │     │     │     │     │     │     │       │
  6000.  Polarity bit  │────┤────┤────┤────┤────┤────┤────┤──────┘
  6001.                │     │     │     │     │     │     │     │
  6002.                                                   
  6003.  CPU        ┌───────────────────────────────────────────────┐
  6004.  data       │  1     1    ▒0▒▒    1     1    ▒0▒▒  1     1  │
  6005.             └───────────────────────────────────────────────┘
  6006.  
  6007.       Figure 5-14.  InColor graphics read.
  6008.  
  6009.  
  6010.       After the specified pixel values in the latches have been updated from
  6011.       the bit planes, the Decoder compares each pixel value in the latches
  6012.       with the background value in the Read/Write Color register. The 8-bit
  6013.       result of the comparison is returned to the CPU. This is similar to
  6014.       read mode 1 on the EGA and the VGA.
  6015.  
  6016.       Bits 0 through 3 of the Read/Write Control register are "don't care"
  6017.       bits analogous to the Color Don't Care value on the EGA and the VGA.
  6018.       Setting a Read/Write Control "don't care" bit to 1 has the effect of
  6019.       excluding a latch from the background value compare operation. If you
  6020.       set all four "don't care" bits to 1, all pixel values match the
  6021.       background value no matter what it is.
  6022.  
  6023.       The polarity of the bits in the result returned to the CPU depends
  6024.       upon the value of the Mask Polarity bit (bit 6 of the Read/Write
  6025.       Control register). When this bit  is 0, bits in the result are 1 where
  6026.       a pixel value in the latches matches the background value. Setting the
  6027.       Mask Polarity bit to 1 inverts the result; that is, bits are 1 where a
  6028.       pixel value in the latches does not match the background value.
  6029.  
  6030.  
  6031.  Reading a Pixel's Value
  6032.  
  6033.  
  6034.       Now it is time to turn to some specific programming techniques for
  6035.       manipulating pixels on the various PC and PS/2 video subsystems. Once
  6036.       you calculate the byte and bit offsets of a particular pixel in the
  6037.       video buffer, determining the pixel's value is a matter of isolating
  6038.       the bits that represent it in the buffer. This is as true on the CGA
  6039.       and HGC, with their simpler video RAM architecture, as it is on more
  6040.       complicated video subsystems that use bit planes.
  6041.  
  6042.  
  6043.  CGA
  6044.  
  6045.       In 640-by-200 2-color mode, the value of a pixel is determined simply
  6046.       by reading the byte that contains the pixel from the video buffer and
  6047.       testing the value of the bit that represents the pixel (see Listing
  6048.       5-2).
  6049.  
  6050.  
  6051.  ───────────────────────────────────────────────────────────────────────────
  6052.  
  6053.       Listing 5-2.  Determining a pixel value in CGA 640-by-200 2-color
  6054.       mode.
  6055.  
  6056.  ───────────────────────────────────────────────────────────────────────────
  6057.  
  6058.  
  6059.       The technique for determining the value of a pixel in 320-by-200
  6060.       4-color graphics mode, as shown in Listing 5-3, is similar. After
  6061.       isolating the bits that represent the pixel, however, your program
  6062.       must shift them rightward so that the value returned represents the
  6063.       actual pixel value.
  6064.  
  6065.  
  6066.  ───────────────────────────────────────────────────────────────────────────
  6067.  
  6068.       Listing 5-3.  Determining a pixel value in CGA 320-by-200 4-color
  6069.       mode.
  6070.  
  6071.  ───────────────────────────────────────────────────────────────────────────
  6072.  
  6073.  
  6074.  HGC and HGC+
  6075.  
  6076.       The only difference between the pixel-read routines for the Hercules
  6077.       mono- chrome adapters and the ones used in the CGA's 640-by-200 2-
  6078.       color mode lies in how the pixel's address is computed. For example,
  6079.       you can adapt the CGA routine shown in Listing 5-2 for the HGC simply
  6080.       by substituting PixelAddrHGC for PixelAddr06.
  6081.  
  6082.  
  6083.  EGA
  6084.  
  6085.       In CGA-emulation modes, the routines used for the CGA work unchanged.
  6086.       However, in 16-color 200-line modes and in 350-line modes, you must
  6087.       program the Graphics Controller to isolate the bits that represent a
  6088.       pixel in the video buffer's bit planes, as the routine in Listing 5-4
  6089.       does.
  6090.  
  6091.  
  6092.  ───────────────────────────────────────────────────────────────────────────
  6093.  
  6094.       Listing 5-4.  Determining a pixel value in native EGA graphics
  6095.       modes.
  6096.  
  6097.  ───────────────────────────────────────────────────────────────────────────
  6098.  
  6099.  
  6100.       This routine uses the Graphics Controller's read mode 0 to read a
  6101.       single byte from each of the EGA's planes. As the bytes are read, the
  6102.       desired pixel's bits are masked and concatenated to form the pixel's
  6103.       value.
  6104.  
  6105.         ╔═══╗     In 640-by-350 monochrome graphics mode, only bit planes 0
  6106.         ║ T ║     and 2 are used to represent pixel values. In these modes,
  6107.         ║ I ║     only bits from these two planes are concatenated to form a
  6108.         ║ P ║     pixel value (see Listing 5-5).
  6109.         ╚═══╝
  6110.  
  6111.                   As described in Chapter 4, 640-by-350 graphics modes are
  6112.                   mapped differently on an EGA with only 64 KB of video RAM
  6113.                   than on an EGA with more memory. Memory maps 0 through 1
  6114.                   and 2 through 3 are chained to form two bit planes. Pixels
  6115.                   at even byte addresses are represented in maps 0 and 2,
  6116.                   while pixels at odd byte addresses are represented in maps
  6117.                   1 and 3. A routine to read pixel values in these modes
  6118.                   must use the pixel's byte address to determine which maps
  6119.                   to read (see Listing 5-6).
  6120.  
  6121.  
  6122.  ───────────────────────────────────────────────────────────────────────────
  6123.  
  6124.       Listing 5-5.  Determining a pixel value in EGA monochrome graphics
  6125.       mode.
  6126.  
  6127.  ───────────────────────────────────────────────────────────────────────────
  6128.  
  6129.  
  6130.  ───────────────────────────────────────────────────────────────────────────
  6131.  
  6132.       Listing 5-6.  Determining a pixel value in 640-by-350 modes on an EGA
  6133.       with 64 KB.
  6134.  
  6135.  ───────────────────────────────────────────────────────────────────────────
  6136.  
  6137.  
  6138.  InColor Card
  6139.  
  6140.       As with the EGA, to read a pixel's value on the InColor Card requires
  6141.       reading each bit plane separately. To do this, you must use the "don't
  6142.       care" bits in the Read/Write Control register along with the
  6143.       background value in the Read/Write Color register to isolate the
  6144.       contents of each latch.
  6145.  
  6146.       The routine in Listing 5-7 accumulates a pixel's 4-bit value by
  6147.       concatenating one bit from each of the InColor card's four bit planes.
  6148.       The routine determines the contents of each of the bit planes by
  6149.       setting the background value in the Read/Write Color register to 0FH
  6150.       (1111B) and by individually zeroing each Read/Write Control register
  6151.       "don't care" bit. When each CPU read is executed (with the
  6152.       AND CH,ES:[SI] instruction), the value returned to the CPU is thus
  6153.       the 8-bit value in one of the four latches. This value is ANDed with
  6154.       the bit mask in CH, and the isolated bits are accumulated in BL.
  6155.  
  6156.  
  6157.  ───────────────────────────────────────────────────────────────────────────
  6158.  
  6159.       Listing 5-7.  Determining a pixel value in InColor graphics mode.
  6160.  
  6161.  ───────────────────────────────────────────────────────────────────────────
  6162.  
  6163.  
  6164.         ╔═══╗     As usual in bit-plane programming, the tricky part of this
  6165.         ║ T ║     process is in setting up the control register values to
  6166.         ║ I ║     produce the desired result. For example, here is what
  6167.         ║ P ║     happens when the AND CH,ES:[SI] instruction executes:
  6168.         ╚═══╝
  6169.  
  6170.                   1. One byte from each bit plane is copied into the
  6171.                      latches.
  6172.  
  6173.                   2. Each of the eight pixels in the latches is compared
  6174.                      with the background value (1111B), and the eight bits
  6175.                      that reflect the result of the eight comparisons are
  6176.                      returned to the CPU. Because only one of the four
  6177.                      "don't care" bits in the Read/Write Control register
  6178.                      is 0, only one of the four bits in each pixel value
  6179.                      participates in each comparison. If this bit is 1, the
  6180.                      comparison is true, and the Decoder returns a 1 in the
  6181.                      bit position corresponding to this pixel value.
  6182.  
  6183.                   3. The eight bits returned to the CPU are ANDed with the
  6184.                      bit mask in CH to give the desired result.
  6185.  
  6186.                   That's a lot of action for a single AND instruction.
  6187.  
  6188.  
  6189.  MCGA
  6190.  
  6191.       In 640-by-200 2-color and 320-by-200 4-color modes, the routines
  6192.       written for the CGA (shown in Listings 5-2 and 5-3) also work on the
  6193.       MCGA. The two other MCGA graphics modes pose no additional problems
  6194.       (see Listings 5-8 and 5-9), because they use no buffer interleave as
  6195.       do CGA-compatible modes, and because there are no bit planes to worry
  6196.       about.
  6197.  
  6198.  
  6199.  ───────────────────────────────────────────────────────────────────────────
  6200.  
  6201.       Listing 5-8.  Determining a pixel value in MCGA and VGA 640-by-480
  6202.       2-color mode.
  6203.  
  6204.  ───────────────────────────────────────────────────────────────────────────
  6205.  
  6206.  
  6207.  ───────────────────────────────────────────────────────────────────────────
  6208.  
  6209.       Listing 5-9.  Determining a pixel value in MCGA and VGA 320-by-200
  6210.       256-color mode.
  6211.  
  6212.  ───────────────────────────────────────────────────────────────────────────
  6213.  
  6214.  
  6215.  VGA
  6216.  
  6217.       Once you write pixel-read routines for the CGA, the EGA, and the MCGA,
  6218.       you have covered all the bases as far as the VGA is concerned. The
  6219.       only VGA graphics mode not available on the other subsystems is 640-
  6220.       by-480 16-color mode. However, pixel representation and addressing are
  6221.       the same in this mode as in the EGA's 640-by-350 16-color mode, so you
  6222.       can use the routine in Listing 5-4 for both.
  6223.  
  6224.  
  6225.  Setting a Pixel's Value
  6226.  
  6227.  
  6228.       In some ways, setting a pixel's value is the converse of determining
  6229.       its value. Once the byte and bit offsets of a particular pixel have
  6230.       been calculated, setting its value is a simple matter of putting the
  6231.       right bits in the right places in the video buffer.
  6232.  
  6233.       What complicates pixel-setting routines is that you may not always
  6234.       wish simply to replace a pixel's old value with a new value. It is
  6235.       sometimes desirable to derive a pixel's new value by performing a
  6236.       bitwise logical operation on its old value. This is why the EGA and
  6237.       the VGA Graphics Controllers directly support logical AND, OR, and XOR
  6238.       operations on pixel values, as well as direct replacement of old
  6239.       values with new ones.
  6240.  
  6241.         ╔═══╗     Since the bulk of the overhead in a pixel-setting routine
  6242.         ║ T ║     is in calculating the pixel's location in the video
  6243.         ║ I ║     buffer, you can keep your code small and modular by
  6244.         ║ P ║     integrating different pixel-value manipulations into a
  6245.         ╚═══╝     single routine rather than writing separate routines to
  6246.                   replace pixels and to perform bitwise logical operations
  6247.                   on them. The examples in this chapter combine these
  6248.                   different pixel-value operations into unified routines.
  6249.  
  6250.                   Where each bitwise operation requires a different
  6251.                   subroutine, the subroutine's address is stored in a
  6252.                   variable (SetPixelOp). This technique is more flexible
  6253.                   than coding a jump to the desired pixel operation
  6254.                   (replace, AND, OR, or XOR), because you can change the
  6255.                   address in the variable with another independent
  6256.                   subroutine.
  6257.  
  6258.                   The examples in this chapter do not include code for
  6259.                   updating a pixel's value by performing a bitwise NOT
  6260.                   operation. You can use the XOR operation to obtain the
  6261.                   same result as NOT without decreasing performance and
  6262.                   without writing additional code.
  6263.  
  6264.  
  6265.  CGA
  6266.  
  6267.       To set a pixel in 640-by-200 2-color mode, mask the appropriate bit in
  6268.       a byte in the video buffer and then set the bit's value. The routine
  6269.       in Listing 5-10 implements four different ways of setting the value--
  6270.       by replacing the old pixel value with a new value and by using the
  6271.       logical operations OR, AND, and XOR.
  6272.  
  6273.  
  6274.  ───────────────────────────────────────────────────────────────────────────
  6275.  
  6276.       Listing 5-10.  Setting a pixel value in CGA and 640-by-200 2-color
  6277.       mode.
  6278.  
  6279.  ───────────────────────────────────────────────────────────────────────────
  6280.  
  6281.  
  6282.       The routine for 320-by-200 4-color mode is similar. This routine,
  6283.       shown in  Listing 5-11, differs from the routine for 640-by-200
  6284.       2-color mode (see Listing 5-10) only in its technique for computing
  6285.       pixel addresses and in its representation of pixels in bit fields that
  6286.       are two bits wide.
  6287.  
  6288.  
  6289.  ───────────────────────────────────────────────────────────────────────────
  6290.  
  6291.       Listing 5-11.  Setting a pixel value in CGA 320-by-200 2-color mode.
  6292.  
  6293.  ───────────────────────────────────────────────────────────────────────────
  6294.  
  6295.  
  6296.  HGC and HGC+
  6297.  
  6298.       As you might expect, a routine for writing a pixel in the HGC's 720-
  6299.       by-348 monochrome graphics mode can be derived from the equivalent
  6300.       routine for the CGA's 640-by-200 2-color mode in Listing 5-10 by
  6301.       substituting the HGC's pixel-address computation routine
  6302.       (PixelAddrHGC) for the CGA's.
  6303.  
  6304.  
  6305.  EGA
  6306.  
  6307.       You don't need to worry about CGA-emulation modes (640-by-200 2-color
  6308.       and 320-by-200 4-color), because the routines that work on the CGA
  6309.       work equally well on the EGA. However, things become considerably more
  6310.       complicated in the EGA's native graphics modes. In these modes, there
  6311.       are several different ways you can program the Graphics Controller to
  6312.       set the value of an individual pixel. Also, the pixel-setting routine
  6313.       must properly handle the video memory maps in monochrome and 640-by-
  6314.       350 4-color graphics modes (on an EGA with 64 KB).
  6315.  
  6316.  
  6317.       Write mode 0
  6318.       The method for setting a pixel's value in write mode 0 is shown in
  6319.       Listing 5-12. First, as usual, you calculate the byte offset and bit
  6320.       mask, which identify the pixel's location in the video buffer. Then
  6321.       you program the Graphics Controller: Set up write mode 0, store the
  6322.       bit mask value in the Bit Mask register, and configure the Set/Reset
  6323.       and Enable Set/Reset registers for the pixel value. Then you can
  6324.       perform a CPU read to latch the bit planes, followed by a CPU write to
  6325.       copy the contents of the latches and the new pixel value into the bit
  6326.       planes.
  6327.  
  6328.  
  6329.  ───────────────────────────────────────────────────────────────────────────
  6330.  
  6331.       Listing 5-12.  Setting a pixel value in native EGA graphics modes
  6332.       using write mode 0.
  6333.  
  6334.  ───────────────────────────────────────────────────────────────────────────
  6335.  
  6336.  
  6337.       Note how the contents of the Graphics Controller registers determine
  6338.       how the bit planes are updated during the CPU write in the OR
  6339.       instruction. The value in the Bit Mask register has only one nonzero
  6340.       bit, so only one pixel is updated. This pixel takes its value from the
  6341.       Set/Reset register. (The other seven pixels are updated from the
  6342.       latches; since the CPU read loaded the latches with these same pixels,
  6343.       the CPU write doesn't change them.) The Enable Set/Reset value is
  6344.       1111B, so the CPU data byte in AL plays no part in the operation.
  6345.  
  6346.         ╔═══╗     IBM's EGA BIOS uses write mode 0 to set the values of
  6347.         ║ T ║     individual pixels in INT 10H function 0CH, but the BIOS
  6348.         ║ I ║     routine does not use the Set/Reset register to specify the
  6349.         ║ P ║     pixel value. Instead, it first zeroes the pixel by using
  6350.         ╚═══╝     the Bit Mask register to isolate it and by writing a CPU
  6351.                   data byte of 0. Then the BIOS programs the Sequencer Map
  6352.                   Mask register to select only those bit planes in which the
  6353.                   desired pixel value contains a nonzero bit. The routine
  6354.                   then performs a second CPU write to set the nonzero bits,
  6355.                   as shown in Listing 5-13.
  6356.  
  6357.                   This technique has two weaknesses: There are easier ways
  6358.                   to do the same job, and the routine requires extra coding
  6359.                   if you want to AND, OR, or XOR the pixel value in the
  6360.                   video buffer. For both reasons, video BIOS INT 10H
  6361.                   function 0CH is limited in both speed and flexibility.
  6362.  
  6363.  
  6364.  ───────────────────────────────────────────────────────────────────────────
  6365.  
  6366.       Listing 5-13.  Setting a pixel value in native EGA graphics modes
  6367.       using the Sequencer Map Mask.
  6368.  
  6369.  ───────────────────────────────────────────────────────────────────────────
  6370.  
  6371.  
  6372.       Write mode 2
  6373.       A somewhat simpler way to set the value of an individual pixel is to
  6374.       use write mode 2. The routine in Listing 5-14 demonstrates this
  6375.       technique. As in write mode 0, the Bit Mask register determines how
  6376.       each of the eight pixels is updated. In write mode 2, however, new
  6377.       pixel values are derived by combining the CPU data byte with the
  6378.       latched pixel values; this avoids the need to program the Set/Reset
  6379.       and Enable Set/Reset registers and leads to shorter, faster code.
  6380.  
  6381.  
  6382.  ───────────────────────────────────────────────────────────────────────────
  6383.  
  6384.       Listing 5-14.  Setting a pixel value in native EGA graphics modes
  6385.       using write mode 2.
  6386.  
  6387.  ───────────────────────────────────────────────────────────────────────────
  6388.  
  6389.  
  6390.       The routines in Listings 5-12 and 5-14 are designed to work correctly
  6391.       when the Function Select register specifies the AND, OR, or XOR
  6392.       function. Thus, you need write no extra code to perform these
  6393.       alternative pixel manipulations in the EGA's native graphics modes.
  6394.  
  6395.       Furthermore, if you are careful to use the proper pixel values, the
  6396.       routines in Listings 5-12 and 5-14 can be used in any native EGA
  6397.       graphics mode. To ensure that the appropriate bits in the memory maps
  6398.       are updated in 640-by-350 monochrome mode, use pixel values of 0, 1,
  6399.       4, and 5 only. On an EGA with 64 KB of RAM, use pixel values 0, 3,
  6400.       0CH, and 0FH.
  6401.  
  6402.  
  6403.  InColor Card
  6404.  
  6405.       The routine in Listing 5-15 updates a single pixel in the InColor
  6406.       Card's 720-by-348 16-color mode. The InColor Card lacks a functional
  6407.       equivalent of the EGA's Function Select register, so this routine
  6408.       contains four separate subroutines which perform AND, OR, or XOR
  6409.       operations on pixel values.
  6410.  
  6411.  
  6412.  ───────────────────────────────────────────────────────────────────────────
  6413.  
  6414.       Listing 5-15.  Setting a pixel value in InColor graphics
  6415.       mode.
  6416.  
  6417.  ───────────────────────────────────────────────────────────────────────────
  6418.  
  6419.  
  6420.       Each one of these subroutines begins by programming the Read/Write
  6421.       Control, Read/Write Color, and Plane Mask registers. Then a CPU read
  6422.       loads the latches, and a subsequent CPU write updates the bit planes.
  6423.  
  6424.       Each subroutine starts by programming the Read/Write Control register
  6425.       for one of the four graphics write modes. At the same time, the "don't
  6426.       care" bits are all set to 1 and the Mask Polarity bit is zeroed so
  6427.       that the Decoder always returns 11111111B as the result of a CPU read.
  6428.       Then the Plane Mask and Read/Write Color foreground values are set up;
  6429.       these values depend upon whether the pixel value is to be replaced or
  6430.       manipulated by an AND, OR, or XOR operation.
  6431.  
  6432.       The instruction AND ES:[BX],CH (or XOR ES:[BX],CH for the pixel XOR
  6433.       operation) causes the CPU read and write. During the CPU read, the
  6434.       latches are loaded and the value 11111111B is returned to the CPU; the
  6435.       CPU ANDs (or XORs) this value with the bit mask in CH and writes the
  6436.       result back to the same address in the video buffer. In this way, the
  6437.       bit mask in CH selects which pixel value is updated during the CPU
  6438.       write.
  6439.  
  6440.       Except for the pixel that the bit mask specifies, the contents of the
  6441.       latches are copied back into the bit planes from which they were just
  6442.       read; the value of the pixel being updated derives from the foreground
  6443.       value in the Read/Write Color register. Only the bit planes that the
  6444.       Plane Mask register specifies are modified, so the only bits in the
  6445.       bit planes that are updated are those that the replace, AND, OR, or
  6446.       XOR operation modifies.
  6447.  
  6448.         ╔═══╗     It is instructive to compare the interaction of the write
  6449.         ║ T ║     mode, foreground color, and Plane Mask values within each
  6450.         ║ I ║     of the subroutines. The logical operation that takes place
  6451.         ║ P ║     (replace, AND, OR, or XOR) is not programmed explicitly
  6452.         ╚═══╝     with an 80x86 instruction. It is implicit in the contents
  6453.                   of the graphics control registers, which are programmed to
  6454.                   emulate the logical operation by modifying the individual
  6455.                   bits in the updated pixel.
  6456.  
  6457.  
  6458.  MCGA
  6459.  
  6460.       In CGA-compatible graphics modes, the same routines for setting pixel
  6461.       values run unchanged on both the CGA and the MCGA. The two non-CGA
  6462.       modes (640-by-480 2-color and 320-by-200 256-color) can be handled
  6463.       easily with simple modifications to the routine for 640-by-200 2-color
  6464.       mode. Listings 5-16 and 5-17 show the necessary changes.
  6465.  
  6466.  
  6467.  ───────────────────────────────────────────────────────────────────────────
  6468.  
  6469.       Listing 5-16.  Setting a pixel value in MCGA or VGA 640-by-480 2-color
  6470.       mode.
  6471.  
  6472.  ───────────────────────────────────────────────────────────────────────────
  6473.  
  6474.  
  6475.  ───────────────────────────────────────────────────────────────────────────
  6476.  
  6477.       Listing 5-17.  Setting a pixel value in MCGA or VGA 320-by-200 256-
  6478.       color mode.
  6479.  
  6480.  ───────────────────────────────────────────────────────────────────────────
  6481.  
  6482.  
  6483.  VGA
  6484.  
  6485.       Once you create routines to update pixels on the MCGA and EGA, doing
  6486.       the same for the VGA is easy. The only VGA video mode that does not
  6487.       exist on the other subsystems is 640-by-480 16-color mode. Pixel
  6488.       addressing in this mode is the same as in the EGA's 640-by-350 16-
  6489.       color mode, so the routines in Listings 5-12 through 5-14 may
  6490.       be used.
  6491.  
  6492.  
  6493.  Filling the Video Buffer
  6494.  
  6495.  
  6496.       Usually the first thing you do after selecting a new video mode is
  6497.       clear the video buffer by filling it with a uniform background of
  6498.       repetitive data. In alphanumeric modes, it is easy and efficient to
  6499.       fill the buffer with blanks or nulls by using the 80x86 STOSW
  6500.       instruction.
  6501.  
  6502.       Filling the video buffer in graphics modes is more of a challenge.
  6503.       Zeroing the entire buffer is relatively easy, but filling the screen
  6504.       with a solid color or pixel pattern is more difficult, particularly on
  6505.       the EGA, the VGA, and the InColor Card.
  6506.  
  6507.  
  6508.  CGA
  6509.  
  6510.       On the CGA, you can set the entire buffer to a single pixel value or a
  6511.       pattern of vertical stripes with a REP STOSW operation, as the routine
  6512.       in Listing 5-18 does. Because of the two-way interleave in the video
  6513.       buffer map, this technique fills all even-numbered scan lines before
  6514.       filling the odd-numbered lines. You might prefer to clear the buffer
  6515.       from the top down by filling it a line at a time. This technique, used
  6516.       in Listing 5-19, achieves a slightly smoother appearance, but requires
  6517.       slower and bulkier code.
  6518.  
  6519.  
  6520.  ───────────────────────────────────────────────────────────────────────────
  6521.  
  6522.       Listing 5-18.  Simple CGA graphics buffer fill.
  6523.  
  6524.  ───────────────────────────────────────────────────────────────────────────
  6525.  
  6526.  
  6527.  ───────────────────────────────────────────────────────────────────────────
  6528.  
  6529.       Listing 5-19.  CGA graphics buffer fill using two-way interleave.
  6530.  
  6531.  ───────────────────────────────────────────────────────────────────────────
  6532.  
  6533.  
  6534.       You can exploit the two-way interleave in the video buffer map to
  6535.       create a color blend or a simple pattern (see Listing 5-20). In this
  6536.       case, the pixel pattern in the even-numbered scan lines is shifted in
  6537.       position from the pattern in the odd-numbered scan lines. This creates
  6538.       a dithered or halftone pattern on the screen. Because the pixels are
  6539.       so close together, the eye blends them, perceiving the dithered
  6540.       pattern as gray in 640-by-200 2-color mode or as an intermediate color
  6541.       blend in 320-by-200 4-color mode.
  6542.  
  6543.  
  6544.  ───────────────────────────────────────────────────────────────────────────
  6545.  
  6546.       Listing 5-20.  CGA graphics buffer fill with different pixel pattern
  6547.       in odd and even rows.
  6548.  
  6549.  ───────────────────────────────────────────────────────────────────────────
  6550.  
  6551.  
  6552.  HGC and HGC+
  6553.  
  6554.       You can use the same basic techniques for clearing the video buffer in
  6555.       the HGC's 720-by-348 monochrome graphics mode as in the CGA's 640-by-
  6556.       200 2-color mode. However, your routine must be able to clear either
  6557.       of the two displayable portions of the HGC's video buffer. +Listing
  6558.       5-21 demonstrates how you can do this. Again, you can take advantage
  6559.       of the interleaved video memory map to create a dithered pattern as
  6560.       you clear the buffer.
  6561.  
  6562.  
  6563.  ───────────────────────────────────────────────────────────────────────────
  6564.  
  6565.       Listing 5-21.  HGC graphics buffer fill using four-way interleave.
  6566.  
  6567.  ───────────────────────────────────────────────────────────────────────────
  6568.  
  6569.  
  6570.  EGA and VGA
  6571.  
  6572.       The Graphics Controller can provide a certain amount of hardware
  6573.       assistance in filling the EGA and VGA video buffer. Also, because the
  6574.       buffer holds more data than can be displayed on the screen, you can
  6575.       choose to clear only the displayed portion, an undisplayed portion, or
  6576.       the entire buffer.
  6577.  
  6578.       In 640-by-200 2-color and 320-by-200 4-color modes you can use the
  6579.       routines for the CGA (see Listings 5-18 through 5-20). Remember,
  6580.       however, that the EGA and the VGA have enough video RAM to support two
  6581.       screens of data in 320-by-200 4-color mode. Your routine should
  6582.       therefore be capable of clearing any designated area of the buffer.
  6583.       Filling the video buffer in 640-by-480 2-color mode (see Listing 5-22)
  6584.       and 320-by-200 256-color mode (see Listing 5-23) is also a relatively
  6585.       easy task, because pixel addressing in these modes is simple.
  6586.  
  6587.  
  6588.  ───────────────────────────────────────────────────────────────────────────
  6589.  
  6590.       Listing 5-22.  MCGA and VGA 640-by-480 2-color graphics buffer fill.
  6591.  
  6592.  ───────────────────────────────────────────────────────────────────────────
  6593.  
  6594.  
  6595.  ───────────────────────────────────────────────────────────────────────────
  6596.  
  6597.       Listing 5-23.  MCGA and VGA 320-by-200 256-color graphics buffer fill.
  6598.       This routine fills alternate pixel rows separately to allow dithered
  6599.       pixel patterns.
  6600.  
  6601.  ───────────────────────────────────────────────────────────────────────────
  6602.  
  6603.  
  6604.       In 16-color 200-line graphics modes and all 350-line graphics modes,
  6605.       your routines should program the Graphics Controller to exploit its
  6606.       parallel processing capabilities. The most efficient way to fill the
  6607.       video buffer with a solid color is to use write mode 0 to repeatedly
  6608.       copy the Set/Reset value into the video buffer. Because no CPU read is
  6609.       required for this operation, you can set the entire video buffer to a
  6610.       solid color with a single REP STOSW instruction as shown in Listing
  6611.       5-24.
  6612.  
  6613.  
  6614.  ───────────────────────────────────────────────────────────────────────────
  6615.  
  6616.       Listing 5-24.  Solid buffer fill for EGA and VGA native graphics
  6617.       modes. The code assumes that the Graphics Controller is already in
  6618.       write mode 0 (the BIOS default).
  6619.  
  6620.  ───────────────────────────────────────────────────────────────────────────
  6621.  
  6622.  
  6623.       Filling the video buffer with an arbitrary pixel pattern is more
  6624.       difficult. Although the basic technique is the same, each component of
  6625.       the pattern must be written separately to the bit planes. The example
  6626.       in Listing 5-25 fills the video buffer with an 8-by-2 pattern of
  6627.       pixels in the VGA's 640-by-480 16-color mode. You can adapt the
  6628.       routine to 200-line and 350-line 16-color modes on both the EGA and
  6629.       VGA.
  6630.  
  6631.  
  6632.  ───────────────────────────────────────────────────────────────────────────
  6633.  
  6634.       Listing 5-25.  Patterned buffer fill for EGA and VGA native graphics
  6635.       modes. The code assumes that the desired pixel pattern is already
  6636.       stored in the first eight pixels of the first two rows of the video
  6637.       buffer (that is, at A000:0000 and A000:0050).
  6638.  
  6639.  ───────────────────────────────────────────────────────────────────────────
  6640.  
  6641.  
  6642.  InColor Card
  6643.  
  6644.       As with the EGA and the VGA, you should use the InColor Card's
  6645.       graphics data latches to update the four bit planes in parallel.
  6646.       Filling the video buffer with a solid color is straightforward, as
  6647.       shown in Listing 5-26. Filling it with a pixel pattern demands the
  6648.       same sort of logic used in the equivalent routine for the EGA and VGA
  6649.       (shown in Listing 5-27).
  6650.  
  6651.  
  6652.  ───────────────────────────────────────────────────────────────────────────
  6653.  
  6654.       Listing 5-26.  Solid buffer fill for Hercules InColor graphics mode.
  6655.  
  6656.  ───────────────────────────────────────────────────────────────────────────
  6657.  
  6658.  
  6659.  ───────────────────────────────────────────────────────────────────────────
  6660.  
  6661.       Listing 5-27.  Patterned buffer fill for InColor Card. The code
  6662.       assumes that the desired pixel pattern is already stored in the first
  6663.       eight pixels of the first two rows of the video buffer (that is, at
  6664.       offsets 0 and 2000H in BufferSeg).
  6665.  
  6666.  ───────────────────────────────────────────────────────────────────────────
  6667.  
  6668.  
  6669.  MCGA
  6670.  
  6671.       You can use the routines written for the CGA and the VGA to fill the
  6672.       video buffer in equivalent graphics modes on the MCGA.
  6673.  
  6674.  
  6675.  
  6676.                               6  Lines
  6677.  
  6678.  
  6679.                   An Efficient Line-drawing Algorithm
  6680.         Scan-converting a Straight Line ■ Bresenham's Algorithm
  6681.  
  6682.                              Optimization
  6683.                       Efficient Pixel Addressing
  6684.                 Performance Comparisons ■ Special Cases
  6685.  
  6686.                       PC and PS/2 Implementations
  6687.                            Modular Routines
  6688.                    Minimizing Video Buffer Accesses
  6689.                     Efficient Address Calculations
  6690.                             CGA ■ HGC ■ EGA
  6691.                               MCGA ■ VGA
  6692.                              InColor Card
  6693.  
  6694.                             Line Attributes
  6695.  
  6696.                                Clipping
  6697.            Pixel-by-Pixel Clipping ■ A Brute-Force Approach
  6698.                           A Better Algorithm
  6699.  
  6700.  
  6701.  
  6702.       Most video graphics applications rely on routines that draw straight
  6703.       lines on the screen. Straight lines are components of many graphics
  6704.       images, including polygons, filled areas (made up of groups of
  6705.       contiguous lines), and curves (made up of a series of short line
  6706.       segments joined end to end). Because lines are used frequently in
  6707.       video graphics, you need fast line-drawing subroutines to obtain high-
  6708.       performance video graphics. This chapter describes how to construct
  6709.       efficient and flexible line-drawing routines for IBM video subsystems.
  6710.  
  6711.  
  6712.  An Efficient Line-drawing Algorithm
  6713.  
  6714.  
  6715.       Imagine what would happen if you tried to draw a straight line on a
  6716.       piece of paper by painting through the square holes in a sieve (see
  6717.       Figure 6-1). The result would not really be a line, but a group of
  6718.       square dots that approximates a line.
  6719.  
  6720.  
  6721.               ╔══════════════════════════════════════════╗
  6722.               ║                                          ║
  6723.               ║    Figure 6-1 is found on page 162       ║
  6724.               ║    in the printed version of the book.   ║
  6725.               ║                                          ║
  6726.               ╚══════════════════════════════════════════╝
  6727.  
  6728.       Figure 6-1.  Line painted through a sieve.
  6729.  
  6730.  
  6731.       A raster video display's rectangular grid of pixels resembles an
  6732.       electronic "sieve" when it comes to drawing straight lines and
  6733.       geometric curves. The best you can do is to represent each line or
  6734.       curve with a group of pixels that closely approximates it. The process
  6735.       of determining which set of pixels in the video buffer best
  6736.       approximate a particular geometric figure is called scan-conversion.
  6737.  
  6738.         ╔═══╗     The visual consequence of scan-conversion is that
  6739.         ║ T ║     mathematically smooth lines and curves appear jagged on
  6740.         ║ I ║     the screen. Consider the nearly horizontal line in Figure
  6741.         ║ P ║     6-2a. The only way to represent such a line within a grid
  6742.         ╚═══╝     of pixels is as a series of connected horizontal line
  6743.                   segments. The more nearly horizontal or vertical the line,
  6744.                   the more jagged it appears. Although sophisticated
  6745.                   software techniques can minimize the jagged appearance of
  6746.                   a scan-converted line, the easiest way to smooth out a
  6747.                   line is to "use a finer sieve"; that is, to use a higher-
  6748.                   resolution video mode or higher-resolution video display
  6749.                   hardware (see Figure 6-2b).
  6750.  
  6751.  
  6752.               ╔══════════════════════════════════════════╗
  6753.               ║                                          ║
  6754.               ║    Figure 6-2 is found on page 163       ║
  6755.               ║    in the printed version of the book.   ║
  6756.               ║                                          ║
  6757.               ╚══════════════════════════════════════════╝
  6758.  
  6759.       Figure 6-2.  A nearly horizontal line displayed with (a) low
  6760.       resolution and (b) higher resolution.
  6761.  
  6762.  
  6763.  Scan-converting a Straight Line
  6764.  
  6765.       The simplest way to draw a line is to use the equation of the line
  6766.  
  6767.       y = mx + b
  6768.  
  6769.       where m is the slope of the line and b is the y-intercept (the value
  6770.       of y at the point where the line crosses the y-axis). You can use this
  6771.       equation to calculate the corresponding y-coordinate for each pixel x-
  6772.       coordinate between the line's endpoints as shown in Listing 6-1. This
  6773.       technique is slow, but it is easy to implement.
  6774.  
  6775.  
  6776.  ───────────────────────────────────────────────────────────────────────────
  6777.  
  6778.       Listing 6-1.  Drawing a line using the equation of the
  6779.       line.
  6780.  
  6781.  ───────────────────────────────────────────────────────────────────────────
  6782.  
  6783.  
  6784.       The problem is that the computational overhead in performing the
  6785.       multiplication, addition, and rounding necessary to generate y for
  6786.       each x in the line is considerable. Furthermore, the slope m must be
  6787.       maintained as a floating-point number, and using floating-point
  6788.       arithmetic in the calculations slows them down.
  6789.  
  6790.  
  6791.  Bresenham's Algorithm
  6792.  
  6793.       Incrementally calculating the appropriate y-coordinates is much more
  6794.       efficient. Given the x- and y-coordinates of the first pixel in the
  6795.       line, you can calculate the location of each subsequent pixel by
  6796.       incrementing the x- and y-coordinates in proportion to the line's
  6797.       slope. The arithmetic is simpler and faster than that involved in
  6798.       directly using the equation of the line.
  6799.  
  6800.       The algorithm presented by J. E. Bresenham in 1965 (IBM Systems
  6801.       Journal 4 (1) 1965, pp. 25-30) plots the set of pixels that lie
  6802.       closest to the line between two given pixels--(x1,y1) and (x2,y2)--
  6803.       assuming that x1 is less than x2 and that the slope of the line is
  6804.       between 0 and 1. To simplify the equation of the line, the algorithm
  6805.       assumes the location of the first endpoint (x1,y1) is (0,0). The
  6806.       equation of the resulting line is
  6807.  
  6808.       y = (dy/dx) * x
  6809.  
  6810.       where
  6811.  
  6812.       dy = y2 - y1
  6813.  
  6814.       and
  6815.  
  6816.       dx = x2 - x1
  6817.  
  6818.       To visualize how Bresenham's algorithm works, consider the portion of
  6819.       a line shown in Figure 6-3. The algorithm proceeds by iteratively
  6820.       determining the corresponding y-coordinate for each value of x from x1
  6821.       to x2. After plotting the pixel at (x(sub i-1),y(sub i-1)), for
  6822.       example, the algorithm determines whether pixel A or pixel B is closer
  6823.       to the exact line and plots the closer pixel.
  6824.  
  6825.  
  6826.               ╔══════════════════════════════════════════╗
  6827.               ║                                          ║
  6828.               ║    Figure 6-3 is found on page 165       ║
  6829.               ║    in the printed version of the book.   ║
  6830.               ║                                          ║
  6831.               ╚══════════════════════════════════════════╝
  6832.  
  6833.       Figure 6-3.  Bresenham's incremental line-drawing algorithm. Given the
  6834.       pixel at (x(sub i-1),y(sub i-1)), the algorithm selects either pixel
  6835.       A or B depending on the values of a and b.
  6836.  
  6837.  
  6838.       The difference between pixel A's y-coordinate and the y-coordinate on
  6839.       the exact line at x(sub i) is
  6840.  
  6841.       a = (y(sub i)+1) - (dy/dx)*x(sub i)
  6842.  
  6843.       where (dy/dx) represents the line's slope. Similarly, the distance b
  6844.       from pixel B to the line is
  6845.  
  6846.       b = (dy/dx)*x(sub i) - y(sub i)
  6847.  
  6848.       If distance b is smaller than distance a, pixel B lies closer to the
  6849.       exact line. If a is smaller than b, pixel A is closer. In other words,
  6850.       the sign of the difference (b - a) determines whether pixel A or pixel
  6851.       B is closer to the line.
  6852.  
  6853.       Now, this may seem like much more work than simply using the equation
  6854.       for the line. However, the values of a and b can be compared
  6855.       implicitly for each x(sub i) by iteratively computing the value of
  6856.       (b - a) for each succeeding x(sub i) in terms of simpler quantities
  6857.       like dy and dx. The resulting computation is simple, although deriving
  6858.       it requires a bit of algebra.
  6859.  
  6860.       To derive the computation, combine the equations for a and b:
  6861.  
  6862.       (b-a) = 2*(dy/dx)*x(sub i) - 2*y(sub i) - 1
  6863.  
  6864.       Since x1 is less than x2, dx is always positive, so dx * (b - a) can
  6865.       be used instead of (b - a) to decide whether to plot pixel A or pixel
  6866.       B:
  6867.  
  6868.       dx*(b-a) = 2*dy*x(sub i) - 2*y(sub i)*dx - dx
  6869.  
  6870.                = 2*(dy*x(sub i) - dx*y(sub i) - dx
  6871.  
  6872.       Let d(sub i) represent the quantity dx*(b-a). To calculate d(sub i)
  6873.       iteratively, you need to know how to compute it from d(sub i-1):
  6874.  
  6875.       (d(sub i)-d(sub i-1)) = (2*(dy*x(sub i) - dx*y(sub i))) -
  6876.                                 (2*(dy*x(sub i-1) - dx*y(sub i-1)))
  6877.  
  6878.                             = 2*(dy*(x(sub i)-x(sub i-1))) - dx*(y(sub i)-
  6879.                                  y(sub i-1))
  6880.  
  6881.       x(sub i) - x(sub i-1) is always 1, and y(sub i) - y(sub i-1 is either
  6882.       1 (if pixel A at (x(sub i),y(sub i + 1) is plotted) or 0 (if pixel B
  6883.       at (x(sub i),y(sub i) is plotted). Thus, computing the difference
  6884.       between d(sub i) and d(sub i-1) is easy, and d(sub i) can be
  6885.       calculated simply by incrementing d(sub i-1) with one of two
  6886.       constants:
  6887.  
  6888.       If d(sub i-1) >= 0, plot pixel A at (x(sub i),y(sub i + 1)). The
  6889.       increment for d(sub i-1) is then
  6890.  
  6891.       (d(sub i)-d(sub i-1)) = 2*(dy-dx)
  6892.  
  6893.       If d(sub i-1) < 0, plot pixel B at (x(sub i),y(sub i)). The
  6894.       increment for d(sub i-1) is then
  6895.  
  6896.       (d(sub i)-d(sub i-1)) = 2*dy
  6897.  
  6898.       To calculate d(sub i)'s initial value, remember that the first pixel
  6899.       in the line is assumed to be at (0,0). Substituting x(sub i) = 1 and
  6900.       y(sub i) = 0 into the equation for d(sub i) gives
  6901.  
  6902.       d(sub i) = 2*dy - dx
  6903.  
  6904.       The resulting algorithm is efficient, because the most complicated
  6905.       calculations are performed only once, outside the loop that plots the
  6906.       pixels (see Listing 6-2). Within the loop, incrementally determining
  6907.       which pixels lie closest to the desired line (using the decision
  6908.       variable d(sub i) eliminates the need for time-consuming floating-
  6909.       point arithmetic. The result is a faster line-drawing algorithm.
  6910.  
  6911.  
  6912.  Optimization
  6913.  
  6914.  
  6915.       Nevertheless, there is still room for improvement. The slowest part of
  6916.       the line-drawing primitive in Listing 6-2 is the call to SetPixel(),
  6917.       which calculates the pixel's address in the video buffer and then sets
  6918.       the pixel's value. The pixel address calculation is clearly the
  6919.       slowest part of the procedure.
  6920.  
  6921.  
  6922.  ───────────────────────────────────────────────────────────────────────────
  6923.  
  6924.       Listing 6-2.  A high-level implementation of Bresenham's
  6925.       algorithm.
  6926.  
  6927.  ───────────────────────────────────────────────────────────────────────────
  6928.  
  6929.  
  6930.  Efficient Pixel Addressing
  6931.  
  6932.       Fortunately, you can optimize the pixel address calculation
  6933.       significantly: The pixel addresses themselves can be calculated
  6934.       incrementally, in the same way you increment the decision variable
  6935.       d(sub i). After calculating the address of the first pixel in the
  6936.       line, you can find its neighbors in the video buffer either by
  6937.       incrementing the pixel's byte offset or by rotating the bit mask that
  6938.       represents its bit offset. Calculating pixel addresses incrementally
  6939.       is significantly faster than performing the computation from scratch
  6940.       for each (x,y) coordinate pair in the line.
  6941.  
  6942.       For example, you can identify the pixel immediately to the right of a
  6943.       given pixel by rotating the given pixel's bit mask one pixel position
  6944.       to the right. (If the given pixel is the rightmost pixel in its byte,
  6945.       increment the byte offset as well.) To find the pixel immediately
  6946.       above a given pixel, decrement the byte offset by the number of bytes
  6947.       per row of pixels, but keep the bit mask the same. This calculation is
  6948.       slightly more complicated in video modes with an interleaved video
  6949.       buffer map, but the principle is the same.
  6950.  
  6951.  
  6952.  Performance Comparisons
  6953.  
  6954.       When you compare the techniques for scan-converting lines, the
  6955.       performance gains from using an incremental line-drawing algorithm and
  6956.       incremental address calculations are remarkable (see Figure 6-4).
  6957.       Writing your line-drawing routines in assembly language also helps.
  6958.       Coding and optimizing bit mask manipulation and address computations
  6959.       is much easier in assembly language than in a high-level language.
  6960.  
  6961.  
  6962. ╓┌──────────────────────────────────────┌────────────────┌───────────────────╖
  6963.  Algorithm                              Language         Pixels per Second
  6964.  ──────────────────────────────────────────────────────────────────────────
  6965.  Algorithm based on the equation
  6966.  of a line                              C                 4,800
  6967.  Bresenham's algorithm                  C                16,000
  6968.  Bresenham's algorithm                  Assembler        26,000
  6969.  Bresenham's algorithm with             Assembler        70,000
  6970.  incremental pixel address calculation
  6971.  
  6972.       Figure 6-4.  Performance of line-drawing algorithms in C and in
  6973.       assembly language. Timings were obtained on a 6 MHz IBM PC/AT with a
  6974.       Hercules Graphics Card.
  6975.  
  6976.  
  6977.  Special Cases
  6978.  
  6979.       To further improve the overall performance of your video graphics
  6980.       drivers, use special routines for drawing horizontal and vertical
  6981.       lines. In many applications, these special cases account for a
  6982.       surprising percentage of the calls to the line-drawing primitive. This
  6983.       is especially true if you use lines to fill regions.
  6984.  
  6985.  
  6986.  ───────────────────────────────────────────────────────────────────────────
  6987.  
  6988.       Listing 6-3.  A routine that draws horizontal lines.
  6989.  
  6990.  ───────────────────────────────────────────────────────────────────────────
  6991.  
  6992.  
  6993.       For example, the routine FilledRectangle() in Listing 6-3 calls on the
  6994.       line-drawing function to draw horizontal lines exclusively. If you
  6995.       fill a rectangle that is 100 pixels high, the line-drawing function is
  6996.       called 100 times to draw a horizontal line. When the line-drawing
  6997.       function recognizes the special case of horizontal lines, functions
  6998.       such as FilledRectangle() run significantly faster.
  6999.  
  7000.       A special-purpose routine can draw horizontal lines 10 times faster
  7001.       than a general-purpose line-drawing routine. For vertical lines, a
  7002.       special-purpose routine is about 25 percent faster. Horizontal lines
  7003.       are represented in the video buffer by contiguous sequences of bytes
  7004.       you can fill with an 80x86 REP STOSB instruction, which runs much
  7005.       faster than the iterative loop the general line-drawing primitive
  7006.       requires. In drawing vertical lines, no logic is required to determine
  7007.       pixel locations. You simply increment the pixel address. Again, the
  7008.       resulting code is simpler and faster.
  7009.  
  7010.  
  7011.  PC and PS/2 Implementations
  7012.  
  7013.  
  7014.       Implementations of Bresenham's line-drawing algorithm on IBM video
  7015.       hardware are strongly influenced by the CPU's capabilities and by the
  7016.       idiosyncrasies of pixel mapping in the video buffer in various
  7017.       graphics modes. Nevertheless, once you write a line-drawing routine
  7018.       for one graphics mode, you can adapt the source code to other graphics
  7019.       modes or to other video hardware with little difficulty.
  7020.  
  7021.  
  7022.  Modular Routines
  7023.  
  7024.       You should build your line-drawing routines with a modular structure.
  7025.       One practical way to break your code into modules is to write separate
  7026.       routines for horizontal lines, vertical lines, lines with slope less
  7027.       than 1, and lines with slope greater than 1. Each module itself
  7028.       comprises a set of modules for performing each of the necessary pixel
  7029.       manipulations--XOR, AND, OR, and pixel replacement.
  7030.  
  7031.         ╔═══╗     Bresenham's algorithm as derived in this chapter is
  7032.         ║ T ║     applicable only to lines whose slope lies between 0 and 1.
  7033.         ║ I ║     However, it is easy to use the same algorithm for lines
  7034.         ║ P ║     with other slopes. For lines with slopes between -1 and 0,
  7035.         ╚═══╝     simply change the sign of the y-increment (see Listing
  7036.                   6-2). For lines with slopes less than -1 or greater than 1
  7037.                   (that is, |(dy/dx)|> 1), use the same algorithm but
  7038.                   exchange the x- and y- coordinates.
  7039.  
  7040.                   For example, each of the assembly-language line-drawing
  7041.                   routines in this chapter contains two similar subroutines,
  7042.                   one for |(dy/dx)|<= 1 and another for |(dy/dx)|> 1. Each
  7043.                   routine contains a prologue that detects the special
  7044.                   cases of horizontal and vertical lines, initializes the
  7045.                   appropriate increment values, and selects the proper
  7046.                   subroutine  for the slope.
  7047.  
  7048.       Breaking your routines into modules helps when you customize your code
  7049.       for an application. It also simplifies the task of writing code to run
  7050.       symmetrically in different graphics modes. For example, a routine that
  7051.       draws a vertical line in 640-by-200 2-color mode on a CGA requires
  7052.       little modification to run properly in 320-by-200 4-color mode.
  7053.  
  7054.  
  7055.  Minimizing Video Buffer Accesses
  7056.  
  7057.       In the 8086 family of microprocessors, data transfer instructions of
  7058.       the form MOV mem,reg are among the slowest. Try to minimize use of
  7059.       this CPU instruction within your line-drawing primitives. Neighboring
  7060.       pixels in a line frequently are grouped in the same byte in the video
  7061.       buffer. (Obviously, such groups occur more frequently in more nearly
  7062.       horizontal lines.) You can speed your line-drawing routines by
  7063.       updating all neighboring pixels in each byte you store in the video
  7064.       buffer.
  7065.  
  7066.  
  7067.  Efficient Address Calculations
  7068.  
  7069.       To maximize performance, use CPU registers carefully to hold the
  7070.       values most frequently updated in the inner loops of your routines:
  7071.       the pixel bit mask, the buffer offset, and the decision variable. In
  7072.       Listing 6-4, for example, registers DH and DL hold bit masks,
  7073.       register BX holds the buffer offset, and the decision variable d is
  7074.       maintained in register DI. These values are the ones most frequently
  7075.       updated in these routines, so they are the ones you should try to keep
  7076.       in registers rather than in memory variables.
  7077.  
  7078.       If you neglect to use the CPU registers effectively, your routines may
  7079.       run much slower than necessary. Consider what would happen if you
  7080.       rewrote the routine in Listing 6-4 to store the decision variable in
  7081.       a memory variable instead of in register DI. Just this minor change
  7082.       would cause the routine to run about 20 percent slower. (Not only does
  7083.       this emphasize why you must make the best possible use of the CPU
  7084.       registers, but it also suggests why writing highly optimized video
  7085.       graphics primitives in a high-level language is very difficult.)
  7086.  
  7087.  
  7088.  CGA
  7089.  
  7090.       Listing 6-4 contains code for drawing lines in the CGA's 640-by-200
  7091.       2-color graphics mode. The routine consists of a prologue and four
  7092.       line-drawing modules. The prologue puts the endpoints in ascending
  7093.       order by their x-coordinates, sets  up appropriate vertical increments
  7094.       for computing the pixel address within the  inner loop, and selects an
  7095.       appropriate line-drawing module according to the  slope of the line.
  7096.       The line-drawing modules (VertLine06, HorizLine06, LoSlopeLine06, and
  7097.       HiSlopeLine06) contain the inner loops that actually update pixels and
  7098.       increment addresses.
  7099.  
  7100.  
  7101.  ───────────────────────────────────────────────────────────────────────────
  7102.  
  7103.       Listing 6-4.  A line-drawing routine for CGA 640-by 200 2-color mode.
  7104.  
  7105.  ───────────────────────────────────────────────────────────────────────────
  7106.  
  7107.  
  7108.       Most of the execution time in this routine is spent in the inner loops
  7109.       of the four line-drawing modules. To optimize the speed of the inner
  7110.       loops, as much computation as possible is performed outside of them.
  7111.       In particular, the inner loop of HorizLine06 (at label L43) is very
  7112.       fast because it consists only of a single 80x86 machine instruction.
  7113.  
  7114.       The routines LoSlopeLine06 and HiSlopeLine06 implement Bresenham's
  7115.       algorithm. The inner loop of HiSlopeLine06 (at L21) is simpler than
  7116.       the inner loop of LoSlopeLine06 (at L11). This is because
  7117.       HiSlopeLine06 increments the pixel y-coordinate, and thus the buffer
  7118.       offset, on every iteration, so the only other code needed in the loop
  7119.       is the code to increment the decision variable and update the pixel
  7120.       bit mask. In LoSlopeLine06, the x-coordinate is incremented on each
  7121.       iteration by rotating the pixel bit mask. This necessitates some extra
  7122.       code to update the bit mask and buffer offset in accordance with the
  7123.       decision variable's value.
  7124.  
  7125.       The routine for 320-by-200 4-color mode, shown in Listing 6-5, is
  7126.       similar to the one for 640-by-200 2-color mode. In fact, you could
  7127.       write a single routine that works in either mode without undue
  7128.       sacrifice in performance. The differences lie in how the address of
  7129.       the first pixel in the line is calculated (that is, a call to
  7130.       PixelAddr04 versus one to PixelAddr06) and in how many bits are masked
  7131.       and updated for each pixel in the buffer. The bit mask is 1 bit wide
  7132.       in 640-by-200 2-color mode and 2 bits wide in 320-by-200 4-color mode.
  7133.  
  7134.  
  7135.  ───────────────────────────────────────────────────────────────────────────
  7136.  
  7137.       Listing 6-5.  A line-drawing routine for CGA 320-by-200 4-color
  7138.       mode.
  7139.  
  7140.  ───────────────────────────────────────────────────────────────────────────
  7141.  
  7142.  
  7143.       On the CGA, the code that handles vertical increments is complicated
  7144.       by the need to step across the interleaves in the video buffer. The
  7145.       pixel address is incremented by 2000H to move from the first
  7146.       interleave (even y-coordinates) to the second interleave (odd y-
  7147.       coordinates). To increment from a pixel at an odd y-coordinate to the
  7148.       pixel just below it, you add -2000H (to increment from the second to
  7149.       the first interleave) plus 80 (the number of bytes in each pixel row
  7150.       in the buffer). The increment is thus 0E050H (80 - 2000H).
  7151.  
  7152.         ╔═══╗     The routines for the CGA presented in Listings 6-4 and
  7153.         ║ T ║     6-5 can only copy the specified pixel value into the
  7154.         ║ I ║     video buffer. To perform a XOR, an OR, or an AND operation
  7155.         ║ P ║     on the preexisting values in the buffer using the
  7156.         ╚═══╝     specified pixel value, change the inner loops of each of
  7157.                   the four line-drawing modules.
  7158.  
  7159.                   In selecting among pixel operations (XOR, AND, and so on),
  7160.                   you face the usual trade-off between speed and code size.
  7161.                   To maximize speed, write a separate line-drawing module
  7162.                   for each pixel operation (AND, OR, XOR, and replace). To
  7163.                   minimize redundant code, call a short subroutine, or add
  7164.                   some branching logic to perform one of the pixel
  7165.                   operations.
  7166.  
  7167.  
  7168.  HGC
  7169.  
  7170.       The routine for the HGC, contained in Listing 6-6, is similar to the
  7171.       one for the CGA's 640-by-200 2-color mode. The important difference is
  7172.       in how the HGC's video buffer is mapped. Because of the Hercules video
  7173.       buffer's four-way interleave, the pixel address is incremented by
  7174.       adding the buffer interleave value (2000H or -2000H) until the result
  7175.       exceeds the limit of valid buffer offsets. Because valid buffer
  7176.       offsets lie in the range 0 through 7FFFH, the routine detects the
  7177.       overflow condition by examining the high-order bit of the result. When
  7178.       the result overflows, it adds another value (90 - 8000H or 8000H - 90)
  7179.       to it, so that the new result is the proper offset in the next buffer
  7180.       interleave.
  7181.  
  7182.  
  7183.  ───────────────────────────────────────────────────────────────────────────
  7184.  
  7185.       Listing 6-6.  A line-drawing routine for Hercules monochrome graphics
  7186.       mode.
  7187.  
  7188.  ───────────────────────────────────────────────────────────────────────────
  7189.  
  7190.  
  7191.       The routines for the HGC never access the video buffer with 16-bit
  7192.       read/write operations such as MOVSW or AND [BX],DX. Avoiding these
  7193.       16-bit operations ensures that the routines will run on the InColor
  7194.       Card as well as on the HGC and HGC+.
  7195.  
  7196.         ╔═══╗     You can use the same line-drawing routines on either of
  7197.         ║ T ║     the HGC's video pages by setting the appropriate value for
  7198.         ║ I ║     VideoBufferSeg in PixelAddrHGC. For video page 0, set
  7199.         ║ P ║     VideoBufferSeg to B000H. For video page 1, use B800H.
  7200.         ╚═══╝
  7201.  
  7202.  
  7203.  EGA
  7204.  
  7205.       For the EGA, three line-drawing routines can cover all available
  7206.       graphics modes. The routines for the CGA's 640-by-200 2-color and 320-
  7207.       by-200 4-color modes work equally well in equivalent modes on the EGA.
  7208.       The routine for the remaining graphics modes (200-line 16-color modes
  7209.       and all 350-line modes) is complicated by the need to program the
  7210.       Graphics Controller, but simplified in that the Graphics Controller
  7211.       hardware handles some pixel manipulations that must be performed in
  7212.       software on the CGA.
  7213.  
  7214.       The routine in Listing 6-7 uses Graphics Controller write mode 0 to
  7215.       update the video buffer. The routine stores the pixel value for the
  7216.       line in the Set/Reset register. For each pixel updated in the buffer,
  7217.       the routine writes the appropriate bit mask to the Bit Mask register.
  7218.       Thus, a single 80x86 instruction can read, update, and rewrite up to 8
  7219.       pixels at a time.
  7220.  
  7221.  
  7222.  ───────────────────────────────────────────────────────────────────────────
  7223.  
  7224.       Listing 6-7.  A line-drawing routine for native EGA graphics modes.
  7225.  
  7226.  ───────────────────────────────────────────────────────────────────────────
  7227.  
  7228.  
  7229.       Within the line-drawing modules, the value 3CEH (the port for the
  7230.       Graphics Controller Address register) is maintained in DX, the value 8
  7231.       (the Bit Mask register number) is kept in AL, and the current pixel
  7232.       bit mask is kept in AH. This lets you update the bit planes with only
  7233.       two machine instructions: OUT DX,AX to update the Bit Mask register
  7234.       and a MOVSB or OR ES:[DI],AL instruction that causes a CPU read and
  7235.       CPU write to occur.
  7236.  
  7237.       This routine makes careful use of the 80x86 registers and the Graphics
  7238.       Controller. The Graphics Controller's parallel processing helps the
  7239.       routine run at about the same speed as do CGA and HGC line-drawing
  7240.       routines.
  7241.  
  7242.       Native EGA graphics modes use no video buffer interleave, so locating
  7243.       a pixel's vertical neighbors in the video buffer is easy. If each line
  7244.       contains n bytes of pixels, the next pixel up from a given pixel is -n
  7245.       bytes away, and the next pixel down is n bytes away. The code for
  7246.       incrementing pixel addresses vertically is thus simpler than the
  7247.       corresponding code for the CGA or the HGC. (Compare, for example, the
  7248.       code in the loop at label L32 in Listings 6-4 and 6-7.)
  7249.  
  7250.       The Graphics Controller handles any of four pixel operations for you
  7251.       (XOR, AND, OR, and replace), so the only extra code required to
  7252.       support these functions consists of a few instructions to load the
  7253.       Data Rotate/Function Select register (03H). This task is part of the
  7254.       "configure the Graphics Controller" code near the beginning of the
  7255.       routine in Listing 6-7.
  7256.  
  7257.         ╔═══╗     You can use this line-drawing routine in 640-by-350
  7258.         ║ T ║     4-color and monochrome modes. Be sure to specify the
  7259.         ║ I ║     proper pixel value in these modes so that the routine sets
  7260.         ║ P ║     bits in the proper bit planes (see Chapter 4).
  7261.         ╚═══╝
  7262.  
  7263.  
  7264.  MCGA
  7265.  
  7266.       In CGA-compatible modes, you can use the CGA line-drawing routines on
  7267.       the MCGA. The non-CGA modes (640-by-480 2-color and 320-by-200 256-
  7268.       color) require their own routines, as shown in Listings 6-8 and 6-9,
  7269.       but these are easily derived from the code for 640-by-200 2-color
  7270.       mode.
  7271.  
  7272.  
  7273.  ───────────────────────────────────────────────────────────────────────────
  7274.  
  7275.       Listing 6-8.  A line-drawing routine for MCGA and VGA 640-by-480
  7276.       2-color mode.
  7277.  
  7278.  ───────────────────────────────────────────────────────────────────────────
  7279.  
  7280.  
  7281.  ───────────────────────────────────────────────────────────────────────────
  7282.  
  7283.       Listing 6-9.  A Line-drawing routine for MCGA and VGA 320-by-200
  7284.       256-color mode.
  7285.  
  7286.  ───────────────────────────────────────────────────────────────────────────
  7287.  
  7288.  
  7289.  VGA
  7290.  
  7291.       Once you implement routines for the EGA and the MCGA, you can draw
  7292.       lines in any of the VGA's graphics modes. To draw lines in 640-by-480
  7293.       16-color mode, use the 640-by-350 16-color routine.
  7294.  
  7295.  
  7296.  InColor Card
  7297.  
  7298.       Because pixel addressing in the video buffer is the same on the
  7299.       InColor Card as on Hercules monochrome cards, the only significant
  7300.       difference in the line-drawing routines for the InColor Card, as
  7301.       you'll see in Listing 6-10, is some extra code to select the
  7302.       specified pixel value. Note how the InColor Card's write mode 1 is
  7303.       used along with an appropriate foreground value in the Read/Write
  7304.       Color register to set the values of neighboring pixels in each byte of
  7305.       the buffer. This technique parallels the use of write mode 0 and the
  7306.       Set/Reset register on the EGA.
  7307.  
  7308.  
  7309.  ───────────────────────────────────────────────────────────────────────────
  7310.  
  7311.       Listing 6-10.  A line-drawing routine for the InColor Card's 720-by-
  7312.       348 16-color mode.
  7313.  
  7314.  ───────────────────────────────────────────────────────────────────────────
  7315.  
  7316.  
  7317.  Line Attributes
  7318.  
  7319.  
  7320.       The line-drawing algorithm in this chapter draws lines that are
  7321.       exactly one pixel wide. Consequently, diagonal lines appear less
  7322.       bright than horizontal or vertical lines. You can fatten diagonal
  7323.       lines by modifying the pixel-setting inner loop of a Bresenham line-
  7324.       drawing routine so that it always sets pixel B before selecting the
  7325.       next pixel in the line. The resulting lines are fatter, but the
  7326.       modified routine runs more slowly because it must update more pixels,
  7327.       particularly in lines with slopes near 1 or -1.
  7328.  
  7329.       To draw wider lines, simply draw contiguous, neighboring parallel
  7330.       lines. If you are using a pointing device to draw a wide line
  7331.       interactively, use a series of neighboring horizontal or vertical
  7332.       lines. After implementing a fast routine for drawing horizontal lines,
  7333.       you can write a high-level routine that paints wide lines by calling
  7334.       the horizontal line primitive.
  7335.  
  7336.       In some applications, you may wish to draw dashed lines or
  7337.       multicolored lines that incorporate a pattern of pixel values. To do
  7338.       this, modify the inner loop of your line-drawing routine to select
  7339.       pixel values from a circular list of possible values. Rotate the list
  7340.       each time you set a pixel.
  7341.  
  7342.  
  7343.  Clipping
  7344.  
  7345.  
  7346.       Not one of the assembly-language routines in this chapter validates
  7347.       the pixel coordinates you supply as endpoints. For example, if you
  7348.       call the 640-by-200 2-color routine to draw a line from (0,0) to
  7349.       (1000,1000), the routine blithely updates about 800 pixels at memory
  7350.       locations that don't exist in the available video RAM, all the way
  7351.       from (200,200) through (1000,1000). To avoid this sort of error, you
  7352.       must determine which part of any arbitrary line lies within a given
  7353.       area of the video buffer. This process is known as clipping.
  7354.  
  7355.       In the case of 640-by-200 2-color mode, the area into which lines must
  7356.       be clipped is the rectangular region defined by (0,0) in the upper
  7357.       left corner and (639,199) in the lower right corner. You would
  7358.       therefore clip a line with endpoints at (0,0) and (1000,1000) so that
  7359.       only the portion from (0,0) to (199,199) is drawn. In avoiding the
  7360.       error of updating nonexistent RAM, you might also improve your
  7361.       program's performance, since the line-drawing primitive will not
  7362.       attempt to update those nonexistent pixels.
  7363.  
  7364.  
  7365.  Pixel-by-Pixel Clipping
  7366.  
  7367.       A simplistic approach to clipping is to include a clipping test in the
  7368.       inner loop of your line-drawing routines. Just before setting the
  7369.       value of each pixel, your routine could compare the current pixel bit
  7370.       mask and buffer address with a set of precalculated limits. If the
  7371.       address, the bit mask, or both exceeded the limits, the routine would
  7372.       not update the video buffer. However, the overhead involved in
  7373.       clipping in this manner can be considerably greater than the work
  7374.       required to calculate and draw the pixels in the line.
  7375.  
  7376.         ╔═══╗     In general, avoid integrating code for line clipping into
  7377.         ║ T ║     low-level line-drawing routines, regardless of how
  7378.         ║ I ║     efficient the code might be. Keeping the functions
  7379.         ║ P ║     separate can improve performance, because an application
  7380.         ╚═══╝     can invoke the line-drawing routines directly, bypassing
  7381.                   the clipping code altogether when it's not needed.
  7382.  
  7383.  
  7384.  A Brute-Force Approach
  7385.  
  7386.       Another way to clip a line is to use its equation to calculate where,
  7387.       if anywhere, the line segment to be drawn intersects the edges of the
  7388.       rectangular display region. For example, in Figure 6-5, the slope m
  7389.       of the line is
  7390.  
  7391.       m = dy/dx = (y2-y1)/(x2-x1) = (100-40)/(750-150) = 0.1
  7392.  
  7393.       The y-intercept can be calculated by substituting x1 and y1 into the
  7394.       equation of  the line:
  7395.  
  7396.       b = y1 - m*x1 = 40 - (0.1*150) = 25
  7397.  
  7398.       The equation of the line is thus
  7399.  
  7400.       y = 0.1*x + 25
  7401.  
  7402.       To calculate the coordinates of the intersections of the line and the
  7403.       edges of the window, substitute the x-coordinates of the window's
  7404.       vertical edges and the y-coordinates of its horizontal edges into the
  7405.       equation. Each time you solve the equation for one side of the
  7406.       rectangle, check the result to see whether the intersection point
  7407.       actually lies within the line segment to be drawn as well as within
  7408.       the rectangle.
  7409.  
  7410.  
  7411.               ╔══════════════════════════════════════════╗
  7412.               ║                                          ║
  7413.               ║    Figure 6-5 is found on page 216       ║
  7414.               ║    in the printed version of the book.   ║
  7415.               ║                                          ║
  7416.               ╚══════════════════════════════════════════╝
  7417.  
  7418.       Figure 6-5.  Line segment (150,40)-(750,100) clipped at (639,89) in
  7419.       640-by-200 2-color mode.
  7420.  
  7421.  
  7422.       This approach to line clipping involves a lot of computation,
  7423.       primarily because the equation of the line must be solved four times
  7424.       for every line segment you clip. You must also check the results
  7425.       against the limits of the line segment to determine whether the
  7426.       intersection points fall between the endpoints. Furthermore, you must
  7427.       still handle special cases such as horizontal and vertical lines or
  7428.       degenerate "lines" consisting of a single pixel. This computational
  7429.       burden makes brute-force clipping impractical.
  7430.  
  7431.  
  7432.  A Better Algorithm
  7433.  
  7434.       A more efficient algorithm for line clipping compares the endpoints of
  7435.       the line segment with the boundaries of the rectangular region before
  7436.       computing intersection points. Thus, little computation is performed
  7437.       for lines that need not be clipped. The Sutherland-Cohen algorithm,
  7438.       which uses this approach, is widely known because of its simplicity
  7439.       and computational efficiency. (See Sproull and Sutherland, "A Clipping
  7440.       Divider," Conference Proceedings, Fall Joint Computer Conference,
  7441.       volume 33, pp. 765-776. AFIPS Press, 1968.)
  7442.  
  7443.       Conceptually, the algorithm extends the edges of the rectangular
  7444.       clipping region, dividing the plane into nine regions (see Figure
  7445.       6-6). Each endpoint of the line segment to be clipped falls into
  7446.       one of these regions. Identifying the region that corresponds to each
  7447.       endpoint makes it easy to determine the location of the line segment
  7448.       relative to the rectangular clipping area.
  7449.  
  7450.  
  7451.                    │              │
  7452.           0011     │     0010     │     0110
  7453.                    │              │                Mapping of bits
  7454.                    │              │                in 4-bit codes
  7455.       ─────────────■──────────────┼─────────────
  7456.                    │(x(sub ul),y(sub ul))          bit 0:1 = left
  7457.                    │              │                    1:1 = above
  7458.           0001     │     0000     │     0100           2:1 = right
  7459.                    │              │                    3:1 = below
  7460.       ─────────────┼──────────────■─────────────
  7461.                    │              │(x(sub lr),y(sub lr))
  7462.                    │              │
  7463.           1001     │     1000     │     1100
  7464.                    │              │
  7465.  
  7466.       Figure 6-6.  Rectangular clipping using the Sutherland-Cohen
  7467.       algorithm.
  7468.  
  7469.  
  7470.       The algorithm uses a computational shortcut to determine the relative
  7471.       location of the line segment. Each of the nine regions is represented
  7472.       by a 4-bit code; each bit corresponds to whether the region is above,
  7473.       below, left, or right of the clipping rectangle. The relationship of
  7474.       the endpoints to the rectangle is then quickly determined by bitwise
  7475.       combination of the 4-bit codes.
  7476.  
  7477.       If the logical OR of the two codes is 0 (that is, the 4-bit code for
  7478.       both endpoints is 0), both endpoints lie within the rectangle, and no
  7479.       clipping is needed. If the logical AND of the two 4-bit codes is
  7480.       nonzero, both endpoints lie on the same side of the rectangle, and the
  7481.       entire line is clipped out. These tests can be performed rapidly,
  7482.       because both the bitwise AND and OR operations can be implemented in
  7483.       single 80x86 machine instructions.
  7484.  
  7485.       If the logical OR of the endpoints' 4-bit codes is nonzero but the
  7486.       logical AND is 0, then the line segment is clipped against one of the
  7487.       rectangle's edges. The values of the 4-bit codes then determine which
  7488.       edge is used. The resulting intersection point becomes a new endpoint
  7489.       for the line segment.
  7490.  
  7491.       This process is repeated for the new line segment. The 4-bit codes are
  7492.       recalculated, the bitwise comparison is again performed, and, if
  7493.       necessary, a new endpoint is calculated. Since a rectangle has four
  7494.       sides, the algorithm requires at most four iterations.
  7495.  
  7496.       The routine Clip() in Listing 6-11 is a C illustration of the
  7497.       Sutherland-Cohen algorithm. The while block repeats while neither of
  7498.       the two termination conditions (Inside or Outside) is true. The 4-bit
  7499.       codes are used to determine which of the four sides of the rectangle
  7500.       the clipping calculation uses. The intersection point between the line
  7501.       segment and the side of the rectangle becomes a new endpoint. At the
  7502.       bottom of the while block, the 4-bit code for the new endpoint is
  7503.       calculated, and the loop repeats.
  7504.  
  7505.  
  7506.  ───────────────────────────────────────────────────────────────────────────
  7507.  
  7508.       Listing 6-11.  An implementation of the Sutherland-Cohen clipping
  7509.       algorithm.
  7510.  
  7511.  ───────────────────────────────────────────────────────────────────────────
  7512.  
  7513.  
  7514.       A program could call Clip() before drawing a line with a fast
  7515.       primitive such as Line(). If you are careful to define the values XUL,
  7516.       YUL, XLR, and YLR as variables rather than constants, you can use
  7517.       Clip() in any video mode. Furthermore, line clipping need not be
  7518.       limited to clipping lines to the limits of available RAM in the video
  7519.       buffer. You may instead want to define an arbitrary rectangular region
  7520.       in the video buffer and clip lines against it. A good high-level video
  7521.       graphics interface supports clipping into such arbitrary regions.
  7522.  
  7523.  
  7524.  
  7525.                        7  Circles and Ellipses
  7526.  
  7527.                        Circles and Pixel Scaling
  7528.  
  7529.                      An Ellipse-drawing Algorithm
  7530.                       Scan-converting an Ellipse
  7531.                        An Incremental Algorithm
  7532.                        A Typical Implementation
  7533.                          Problems and Pitfalls
  7534.                                Accuracy
  7535.  
  7536.                              Optimization
  7537.  
  7538.                                Clipping
  7539.  
  7540.                              True Circles
  7541.  
  7542.  
  7543.  
  7544.       Circles and ellipses are probably the most common graphics elements
  7545.       other than straight lines. This chapter describes techniques for
  7546.       displaying circles, ellipses, and arcs with IBM video hardware. These
  7547.       techniques are similar to the algorithms and programming examples for
  7548.       displaying straight lines (described in Chapter 6). Although an
  7549.       ellipse-drawing routine is somewhat more complicated than a routine
  7550.       for drawing straight lines, the algorithmic design and programming
  7551.       techniques are similar.
  7552.  
  7553.  
  7554.  Circles and Pixel Scaling
  7555.  
  7556.  
  7557.       The only way to draw a circle on the IBM video subsystems discussed in
  7558.       this book is to calculate and draw an ellipse. The reason is that the
  7559.       horizontal scale in which pixels are displayed differs from the
  7560.       vertical scale in most graphics modes (Chapter 4). If you display a
  7561.       "circle" whose pixels are computed without scaling, what you see on
  7562.       the screen is an ellipse. For example, Figure 7-1a shows a "circle"
  7563.       with a radius of 100 pixels in both horizontal and vertical directions
  7564.       as displayed in a 640-by-200 graphics mode.
  7565.  
  7566.       Because of this problem of pixel scaling, drawing a circle on the
  7567.       screen requires that you compute the pixels that correspond to a
  7568.       mathematical ellipse. In other words, to draw a circle that really
  7569.       looks like a circle, you must compute an ellipse whose major and
  7570.       minor axes are in the same ratio as the pixel coordinate scaling
  7571.       factor. On the screen, such an ellipse appears circular (see Figure
  7572.       7-1b). For this reason, this chapter concentrates on a practical
  7573.       algorithm for drawing ellipses.
  7574.  
  7575.  
  7576.               ╔══════════════════════════════════════════╗
  7577.               ║                                          ║
  7578.               ║    Figure 7-1 is found on page 222       ║
  7579.               ║    in the printed version of the book.   ║
  7580.               ║                                          ║
  7581.               ╚══════════════════════════════════════════╝
  7582.  
  7583.       Figure 7-1.  In Figure 7-1a, a mathematical circle with a 100-pixel
  7584.       radius appears elliptical in 640-by-200 2-color mode. In Figure 7-1b,
  7585.       an ellipse whose axes have been properly scaled appears circular when
  7586.       displayed in this mode.
  7587.  
  7588.  
  7589.  An Ellipse-drawing Algorithm
  7590.  
  7591.  
  7592.  Scan-converting an Ellipse
  7593.  
  7594.       You can use the algebraic formula for an ellipse to compute x- and y-
  7595.       coordinates for all of the pixels that represent a given ellipse. As
  7596.       in the case of scan-converting a straight line, many of these pixel
  7597.       coordinates will necessarily approximate the actual values, and the
  7598.       resulting figure will be jagged. This effect is especially noticeable
  7599.       when displaying a very thin ellipse (see Figure 7-2), but in most
  7600.       cases this side effect is acceptable.
  7601.  
  7602.  
  7603.               ╔══════════════════════════════════════════╗
  7604.               ║                                          ║
  7605.               ║    Figure 7-2 is found on page 223       ║
  7606.               ║    in the printed version of the book.   ║
  7607.               ║                                          ║
  7608.               ╚══════════════════════════════════════════╝
  7609.  
  7610.       Figure 7-2.  A thin ellipse can appear jagged when it is scan-
  7611.       converted.
  7612.  
  7613.  
  7614.       You can use the equation of the ellipse
  7615.  
  7616.       (x - xc)^2  +  (y - yc)^2 = 1
  7617.          a^2            b^2
  7618.  
  7619.       to scan-convert and display an ellipse. This equation describes an
  7620.       ellipse centered at (xc,yc) with major and minor axes a and b parallel
  7621.       to the x- and y-axes. However, the computational overhead of drawing
  7622.       ellipses by solving this equation, as in Listing 7-1, is very large.
  7623.       The multiplication, division, and square-root operations to determine
  7624.       each pixel's coordinates are very time-consuming. A better approach is
  7625.       to compute pixel coordinates incrementally in a manner similar to that
  7626.       used in the line-drawing algorithm in Chapter 6.
  7627.  
  7628.  
  7629.  ───────────────────────────────────────────────────────────────────────────
  7630.  
  7631.       Listing 7-1.  Drawing an ellipse using the equation of the
  7632.       ellipse.
  7633.  
  7634.  ───────────────────────────────────────────────────────────────────────────
  7635.  
  7636.  
  7637.  An Incremental Algorithm
  7638.  
  7639.       The derivation of an incremental algorithm for drawing ellipses
  7640.       resembles the derivation of Bresenham's line algorithm. The ellipse-
  7641.       drawing algorithm draws an ellipse pixel by pixel. After drawing each
  7642.       pixel, the algorithm selects the next pixel by determining which of
  7643.       the current pixel's two neighbors is closer to the actual ellipse.
  7644.  
  7645.       Creating an ellipse-drawing algorithm is easiest for an ellipse
  7646.       centered at the origin of the coordinate system, with major and minor
  7647.       axes congruent with the x- and y-axes (see Figure 7-3). The equation
  7648.       of such an ellipse is
  7649.  
  7650.       b^2x^2 + a^2y^2 - a^2b^2 = 0
  7651.  
  7652.       Because the ellipse is symmetric in relation to both the x- and y-
  7653.       axes, you only need derive an algorithm to draw one of its quadrants.
  7654.       Your routine can then determine the pixel coordinates in the other
  7655.       three quadrants by symmetry.
  7656.  
  7657.         ╔═══╗     If you need an algorithm to draw ellipses with axes that
  7658.         ║ T ║     are not parallel to the video buffer's x- and y-axes,
  7659.         ║ I ║     refer to M. L. V. Pitteway, "Algorithm for Drawing
  7660.         ║ P ║     Ellipses or Hyperbolae with a Digital Plotter," Computer
  7661.         ╚═══╝     Journal vol. 11 no. 3 (November 1967), p. 282.
  7662.  
  7663.       The algorithm presented here is known as the "midpoint algorithm." It
  7664.       draws an ellipse iteratively, pixel by pixel. For each pixel it draws,
  7665.       the algorithm selects which of the pixel's neighbors is closer to the
  7666.       ellipse by computing whether the point halfway between the pixels lies
  7667.       inside or outside the ellipse (see Figure 7-4). (This algorithm was
  7668.       described by J. R. Van Aken in "An Efficient Ellipse-Drawing
  7669.       Algorithm," IEEE Computer Graphics and Applications, September 1984,
  7670.       p. 24, and improved by M. R. Kappel in "An Ellipse-Drawing Algorithm
  7671.       for Raster Displays," Fundamental Algorithms for Computer Graphics,
  7672.       R. A. Earnshaw [editor], Springer-Verlag 1985, p. 257.)
  7673.  
  7674.  
  7675.               ╔══════════════════════════════════════════╗
  7676.               ║                                          ║
  7677.               ║    Figure 7-3 is found on page 225       ║
  7678.               ║    in the printed version of the book.   ║
  7679.               ║                                          ║
  7680.               ╚══════════════════════════════════════════╝
  7681.  
  7682.       Figure 7-3.  An ellipse centered at the origin of the coordinate
  7683.       system.
  7684.  
  7685.  
  7686.       To determine which pixel lies closer to the ellipse, the algorithm
  7687.       uses the value of the equation of the ellipse at the midpoint between
  7688.       the pixels. If the value is 0, the midpoint lies on the ellipse. If
  7689.       the value is negative, then the midpoint lies inside the ellipse; if
  7690.       the value is positive, the midpoint is outside the ellipse. Thus, the
  7691.       algorithm can choose which of the two pixels lies closer to the
  7692.       ellipse by examining the value's sign.
  7693.  
  7694.       One complication lies in determining which pair of neighboring pixels
  7695.       to investigate at each step in the iteration. This depends on dy/dx,
  7696.       the slope of the tangent to the ellipse (see Figure 7-5). When dy/dx
  7697.       is greater than -1, the algorithm chooses between two vertically
  7698.       oriented pixels (see Figure 7-6a). When dy/dx is less than -1, the
  7699.       choice is between two horizontally oriented pixels (see Figure 7-6b).
  7700.  
  7701.       While dy/dx is greater than -1, the algorithm iteratively determines,
  7702.       for each pixel it draws, whether neighboring pixel A or B is
  7703.       closer to the ellipse. This is done by deciding whether the mid-
  7704.       point between A and B lies inside or outside the exact ellipse. In
  7705.       Figure 7-6a, the pixel selected in the previous iteration is at
  7706.       (x(sub i-1),y(sub i-1)). The midpoint between A and B is therefore
  7707.       (x(sub i-1)+1,y(sub i-1)-1/2).
  7708.  
  7709.       The algorithm chooses between pixel A and pixel B by examining the
  7710.       sign of the value of the ellipse equation evaluated at the midpoint:
  7711.  
  7712.       d = b^2(x(sub i-1)+1)^2 + a^2(y(sub i-1)-1/2)^2 - a^2b^2
  7713.  
  7714.  
  7715.               ╔══════════════════════════════════════════╗
  7716.               ║                                          ║
  7717.               ║    Figure 7-4 is found on page 226       ║
  7718.               ║    in the printed version of the book.   ║
  7719.               ║                                          ║
  7720.               ╚══════════════════════════════════════════╝
  7721.  
  7722.       Figure 7-4.  Three iterations of the midpoint algorithm. After drawing
  7723.       the black pixel in illustration 7-4a, the algorithm chooses to draw
  7724.       either pixel A or pixel B by comparing the midpoint M to the actual
  7725.       ellipse. Because M is inside the ellipse, it chooses pixel A.
  7726.       Illustrations 7-4b and 7-4c represent the next two iterations.
  7727.  
  7728.  
  7729.               ╔══════════════════════════════════════════╗
  7730.               ║                                          ║
  7731.               ║    Figure 7-5 is found on page 227       ║
  7732.               ║    in the printed version of the book.   ║
  7733.               ║                                          ║
  7734.               ╚══════════════════════════════════════════╝
  7735.  
  7736.       Figure 7-5.  The slope of the tangent to the ellipse within the first
  7737.       quadrant.
  7738.  
  7739.  
  7740.               ╔══════════════════════════════════════════╗
  7741.               ║                                          ║
  7742.               ║    Figure 7-6 is found on page 227       ║
  7743.               ║    in the printed version of the book.   ║
  7744.               ║                                          ║
  7745.               ╚══════════════════════════════════════════╝
  7746.  
  7747.       Figure 7-6.  The midpoint algorithm chooses between A and B by
  7748.       substituting x and y at the midpoint M into the formula for the
  7749.       ellipse and testing the sign of the result. If the result is
  7750.       positive, pixel B is chosen; if the result is  negative, pixel A is
  7751.       chosen.
  7752.  
  7753.  
  7754.       The variable d, the value of the function at the midpoint, is the
  7755.       algorithm's decision variable. As in Bresenham's line algorithm, the
  7756.       key to this algorithm's speed is that it can compute d iteratively on
  7757.       the basis of its value at each previous step in the iteration. The
  7758.       difference between the current value of d and its previous value is
  7759.  
  7760.       d(sub i)-d(sub i-1) = [b^2(x(sub i-1)+1)^2 +
  7761.                               a^2(y(sub i-1)-1/2)^2 - a^2b^2] -
  7762.                               [b^2(x(sub i-1))^2 + a^2(y(sub i-1)-1/2)^2 -
  7763.                               a^2b^2]
  7764.  
  7765.                           = b^2(2x(sub i-1)+1)
  7766.  
  7767.                           = 2b^2x(sub i-1) + b^2
  7768.  
  7769.       Now, finding the difference between d(sub i) and d(sub i-1) (that
  7770.       is, dx) still involves multiplying the previous value of x by a
  7771.       constant. You can avoid this multiplication, however, by computing dx,
  7772.       as well as d, incrementally; that is, by adding 2b^2 to dx at each
  7773.       step of the iteration.
  7774.  
  7775.       If pixel A is nearer to the ellipse (that is, d(sub i) > 0), the newly
  7776.       calculated value of d(sub i) can be used as d(sub i-1) in the next
  7777.       iteration. If pixel B is nearer, however, d(sub i) must be adjusted
  7778.       for the downward step in the y direction. In this case, the value of
  7779.       the equation of the ellipse for the midpoint below pixel B must be
  7780.       computed. If (x(sub i-1),y(sub i-1)+1/2) is the midpoint between
  7781.       pixels A and B, then (x(sub i-1),y(sub i-1)-1/2) is the midpoint below
  7782.       pixel B, and dy is then
  7783.  
  7784.       d(sub i)-d(sub i-1) = [b^2(x(sub i-1))^2 +
  7785.                               a^2(y(sub i-1)-1/2)^2 - a^2b^2] -
  7786.                               [b^2(x(sub i-1))^2 + a^2(y(sub i-1)+1/2)^2 -
  7787.                               a^2b^2]
  7788.  
  7789.                           = -2a^2y(sub i-1)
  7790.  
  7791.       When dy/dx is less than -1, pixels A and B are horizontal rather than
  7792.       vertical neighbors (see Figure 7-6b). The values of dy and dx are
  7793.       therefore computed somewhat differently. When pixel B is chosen, the
  7794.       midpoint at (x(sub i-1)+1/2,y(sub i-1)-1) is used, so the increment
  7795.       for d is
  7796.  
  7797.       d(sub i)-d(sub i-1) = [b^2(x(sub i-1)+1/2)^2 +
  7798.                               a^2(y(sub i-1)-1)^2 - a^2b^2] -
  7799.                               [b^2(x(sub i-1)+1/2)^2 + a^2(y(sub i-1))^2 -
  7800.                               a^2b^2]
  7801.  
  7802.                           = a^2(-2y(sub i-1)+1)
  7803.  
  7804.                           = -2a^2y(sub i-1) + a^2
  7805.  
  7806.       Also, when pixel A is chosen, d must be adjusted for the step in the
  7807.       rightward direction:
  7808.  
  7809.       d(sub i)-d(sub i-1) = [b^2(x(sub i-1)-1/2)^2 +
  7810.                               a^2(y(sub i-1))^2 - a^2b^2] -
  7811.                               [b^2(x(sub i-1)+1/2)^2 + a^2(y(sub i-1))^2 -
  7812.                               a^2b^2]
  7813.  
  7814.                           = -2b^2x(sub i-1)
  7815.  
  7816.       These derivations provide a way to draw an ellipse iteratively, with
  7817.       only simple addition and subtraction required within the iterative
  7818.       loops. The analysis distinguishes between the case where dy/dx is
  7819.       greater than -1 and the case where dy/dx is less than -1. You
  7820.       determine when dy/dx has reached -1 by differentiating the equation of
  7821.       the ellipse and setting dy/dx to -1.
  7822.  
  7823.       d    (b^2x^2 + a^2y^2 - a^2b^2) = 0
  7824.       dx
  7825.  
  7826.  
  7827.       2b^2x + 2a^2y dy = 0
  7828.                     dx
  7829.  
  7830.  
  7831.       dy  =  -2b^2x
  7832.       dx      2a^2y
  7833.  
  7834.       Thus, at the point where dy/dx = -1,
  7835.  
  7836.       2b^2x = 2a^2y
  7837.  
  7838.       Because the algorithm already keeps track of the quantities 2b^2x and
  7839.       2a^2y to compute the differentials dx and dy, these quantities can be
  7840.       used to detect where dy/dx reaches -1. The algorithm can then start at
  7841.       the point (0,b) on the y-axis and proceed clockwise around the ellipse
  7842.       until it reaches (a,0).
  7843.  
  7844.       Initially, the quantity dy/dx is greater than -1, and the choice is
  7845.       made iteratively between vertically oriented pixels (see Figure
  7846.       7-6a). When dy/dx reaches -1, the algorithm chooses between
  7847.       horizontally oriented pixels (see Figure 7-6b) and continues to do
  7848.       so until it reaches the x-axis.
  7849.  
  7850.       The only remaining computation occurs when the algorithm reaches the
  7851.       pixel for which dy/dx = -1. At this point, a new value for d will
  7852.       already have been computed (M(sub old) in Figure 7-7) under
  7853.       the assumption that the next midpoint would have been between two
  7854.       vertically oriented pixels. Therefore, the value of d must be adjusted
  7855.       to reflect the value of the ellipse function at the midpoint between
  7856.       two horizontally oriented pixels (M(sub new) in Figure 7-7). The
  7857.       increment for d (from M(sub old) to M(sub new)) in this
  7858.       case is
  7859.  
  7860.       d(sub i)-d(sub i-1) = [b^2(x(sub i-1)+1/2)^2 +
  7861.                               a^2(y(sub i-1)-1)^2 - a^2b^2] -
  7862.                               [b^2(x(sub i-1)+1)^2 + a^2(y(sub i-1)-1/2)^2 -
  7863.                               a^2b^2]
  7864.  
  7865.                           = b^2(-x(sub i-1)-3/4) + a^2(-y(sub i-1)+3/4)
  7866.  
  7867.                           = 3(a^2-b^2)/4 - (b^2x(sub i-1) + a^2y(sub i-1))
  7868.  
  7869.       Again, since the algorithm already uses the quantities 2b^2x and
  7870.       2a^2y, the increment for d at this point can be computed by
  7871.  
  7872.       d(sub i)-d(sub i-1) = 3(a^2-b^2)/4 - (2b^2x(sub i-i)+2a^2y(sub i-1))/2
  7873.  
  7874.       Adding this value to d at the point where dy/dx = -1 gives the new
  7875.       value for d.
  7876.  
  7877.  
  7878.               ╔══════════════════════════════════════════╗
  7879.               ║                                          ║
  7880.               ║    Figure 7-7 is found on page 230       ║
  7881.               ║    in the printed version of the book.   ║
  7882.               ║                                          ║
  7883.               ╚══════════════════════════════════════════╝
  7884.  
  7885.       Figure 7-7.  When the value of dy/dx  reaches -1, a new
  7886.       midpoint (M(sub new)) is selected, and d, which has
  7887.       already been computed for M(sub old), is adjusted to reflect the
  7888.       value of the equation of the ellipse at M(sub new).
  7889.  
  7890.  
  7891.  A Typical Implementation
  7892.  
  7893.       The C routine in Listing 7-2 is fast and efficient because all
  7894.       decision-variable computation within the inner iterative loops has
  7895.       been reduced to addition and subtraction. The routine eliminates
  7896.       multiplication within the inner loops by precalculating the values of
  7897.       a^2, b^2, 2a^2, and 2b^2. The initial values for the decision
  7898.       variables are computed assuming that the first pixel to be drawn is at
  7899.       (0,b). Thus, the initial value of d is calculated for the midpoint
  7900.       between the pixels at (1,b) and (1,b-1); that is, at (1,b-1/2):
  7901.  
  7902.       d = b^2(1)^2 + a^2(b-1/2)^2 - a^2b^2
  7903.  
  7904.         = b^2 - a^2b + a^2/4
  7905.  
  7906.       The initial values for dx and dy are
  7907.  
  7908.       dx = 2b^2(x(sub 0)) = 0
  7909.  
  7910.       and
  7911.  
  7912.       dy = 2a^2(y(sub 0)) = 2a^2b
  7913.  
  7914.       The routine Ellipse() follows the algorithm closely. It first draws
  7915.       all the pixels between (0,b) and the point where dy/dx becomes -1.
  7916.       Then it updates d as in Figure 7-7. Iterative pixel selection
  7917.       continues until the routine reaches the x-axis. The routine calls the
  7918.       function Set4Pixels() to replicate each pixel in each of the four
  7919.       quadrants of the ellipse. Set4Pixels() also translates each pixel's
  7920.       coordinates relative to the actual center of the ellipse.
  7921.  
  7922.  
  7923.  ───────────────────────────────────────────────────────────────────────────
  7924.  
  7925.       Listing 7-2.  A high-level implementation of the midpoint
  7926.       algorithm.
  7927.  
  7928.  ───────────────────────────────────────────────────────────────────────────
  7929.  
  7930.  
  7931.  Problems and Pitfalls
  7932.  
  7933.       One difficult problem you'll encounter is that tiny ellipses appear
  7934.       somewhat angular rather than elliptical when they are scan-converted.
  7935.       When an ellipse is small and comparatively few pixels are used to
  7936.       display it, the best approximation generated by the algorithm can
  7937.       appear polygonal.
  7938.  
  7939.       Although it is possible to redesign the ellipse-drawing algorithm to
  7940.       draw "fatter" or "thinner" ellipses in this situation, a better
  7941.       solution is to display the ellipses with higher resolution. Tiny
  7942.       ellipses look much better with 640-by-480 resolution than they do with
  7943.       320-by-200 resolution.
  7944.  
  7945.       A related problem is that very eccentric ellipses may be drawn
  7946.       inaccurately at the points where they curve most sharply. This
  7947.       happens when the point where  dy/dx = -1 lies nearly adjacent to
  7948.       either the x-axis or the y-axis. Again, you can modify the algorithm
  7949.       to accommodate this situation, but if your application requires
  7950.       accurate representations of very thin ellipses, a better solution is
  7951.       to display them at higher resolution.
  7952.  
  7953.       A further consideration involves "degenerate" ellipses for which the
  7954.       length of either the major or minor axis is 0 (that is, a = 0 or b =
  7955.       0). Because either dy or  dx is 0 in this situation, the iterative
  7956.       routines do not terminate correctly. In these cases, either test for
  7957.       the special condition before executing the loops (and draw the
  7958.       appropriate straight line) or modify the termination conditions of the
  7959.       loops.
  7960.  
  7961.  
  7962.  Accuracy
  7963.  
  7964.       As does Bresenham's line algorithm, the midpoint algorithm attempts to
  7965.       minimize the vertical or horizontal distance to the ellipse from the
  7966.       pixels it selects. This is faster than minimizing the distance between
  7967.       each pixel and the nearest point to it on the ellipse, but if you
  7968.       examine its performance closely, you may find rare occasions when the
  7969.       pixel that the midpoint algorithm selects is not the one closest to
  7970.       the ellipse. Nevertheless, the accuracy of the midpoint algorithm in
  7971.       selecting the best pixels to represent the ellipse is sufficient for
  7972.       nearly all applications. (For more discussion of this topic, see Van
  7973.       Aken and Novak, "Curve-Drawing Algorithms for Raster Displays," ACM
  7974.       Transactions on Graphics, April 1985, p. 147, or Kappel, "An Ellipse-
  7975.       Drawing Algorithm for Raster Displays," Fundamental Algorithms for
  7976.       Computer Graphics, p. 257.)
  7977.  
  7978.       Although the source code in Listing 7-2 is a straightforward
  7979.       implementation of the algorithm, you need to remember a few details if
  7980.       you plan to modify the code or translate it into another language. It
  7981.       is important to compute all decision variables as 32-bit integers.
  7982.       Because these values involve the squaring of pixel coordinates, 16
  7983.       bits are inadequate to maintain precision.
  7984.  
  7985.       Another detail to remember is that this routine can draw the same
  7986.       pixels twice. This is an artifact of the ellipse's four-way symmetry.
  7987.       For example, the pixels at (±a,0) and (0,±b) are updated twice by
  7988.       Set4Pixels() in Listing 7-2. This becomes a problem when you use the
  7989.       routine to XOR pixels into the video buffer. If you perform a XOR on
  7990.       these pixels twice, they disappear. To avoid this, either test for
  7991.       these special cases in Set4Pixels() (Listing 7-3) or modify Ellipse()
  7992.       to draw these pixels separately.
  7993.  
  7994.  
  7995.  ───────────────────────────────────────────────────────────────────────────
  7996.  
  7997.       Listing 7-3.  A modified version of Set4Pixels that avoids updating
  7998.       the same pixel twice.
  7999.  
  8000.  ───────────────────────────────────────────────────────────────────────────
  8001.  
  8002.  
  8003.  Optimization
  8004.  
  8005.  
  8006.       For many applications, a high-level language implementation such as
  8007.       the one in Listing 7-2 is fast enough. The slowest part of the high-
  8008.       level version of Ellipse() is its repeated calls to the pixel-setting
  8009.       routine, which recomputes pixel addresses with every iteration. By
  8010.       writing Ellipse() in assembly language, you can calculate the pixel
  8011.       addresses much more efficiently. The resulting assembly-language
  8012.       routine is about three times faster than the equivalent high-level
  8013.       version.
  8014.  
  8015.       Listing 7-4 is a typical assembly-language implementation, in this
  8016.       case for the EGA. Note how the routine Set4Pixels maintains a set of
  8017.       four buffer offsets and bit masks instead of (x,y) coordinates for the
  8018.       four pixels it updates. When  Set4Pixels increments a pixel x-
  8019.       coordinate, it rotates a bit mask in the proper direction. The y-
  8020.       coordinates are incremented by adding the number of bytes in each line
  8021.       of pixels to the buffer offset. (This is the same technique used in
  8022.       the line routines in Chapter 6.) This method of video buffer
  8023.       addressing is much faster than making a call to a SetPixel() function
  8024.       for every pixel in the ellipse.
  8025.  
  8026.  
  8027.  ───────────────────────────────────────────────────────────────────────────
  8028.  
  8029.       Listing 7-4.  An assembly-language implementation of the midpoint
  8030.       algorithm.
  8031.  
  8032.  ───────────────────────────────────────────────────────────────────────────
  8033.  
  8034.  
  8035.       One optimization technique used in Chapter 6 is omitted here. In
  8036.       practice, minimizing video buffer accesses by setting more than one
  8037.       pixel at a time in each byte of the buffer is not worthwhile. The
  8038.       overhead involved in keeping track of which bytes contain more than
  8039.       one updated pixel is greater than the time saved in reducing video
  8040.       buffer accesses. Besides, the code is complicated enough already.
  8041.  
  8042.  
  8043.  Clipping
  8044.  
  8045.  
  8046.       If you clip an ellipse within a rectangular window, the result is an
  8047.       arc (see Figure 7-8). The place to perform the clipping is in the
  8048.       Set4Pixels() routine. You can clip each pixel's (x,y) coordinates
  8049.       against the window boundary before you call SetPixel() to update the
  8050.       video buffer.
  8051.  
  8052.  
  8053.               ╔══════════════════════════════════════════╗
  8054.               ║                                          ║
  8055.               ║    Figure 7-8 is found on page 241       ║
  8056.               ║    in the printed version of the book.   ║
  8057.               ║                                          ║
  8058.               ╚══════════════════════════════════════════╝
  8059.  
  8060.       Figure 7-8.  Clipping an ellipse produces an arc.
  8061.  
  8062.  
  8063.       Implementing clipping in this way slows the ellipse-drawing routine
  8064.       somewhat. If your application rarely requires clipping, consider
  8065.       implementing two different versions of Set4Pixels(), one that performs
  8066.       clipping and one that omits it. Before calling Ellipse(), you can
  8067.       compare the maximum and minimum coordinate values of the pixels in the
  8068.       ellipse (xc ± a,yc ± b) with the clipping boundaries to determine
  8069.       whether it can be drawn without clipping. Only if clipping is required
  8070.       do you need to use the slower, clipping version of Set4Pixels().
  8071.  
  8072.  
  8073.  True Circles
  8074.  
  8075.  
  8076.       After you implement the ellipse routine, you can draw true circles in
  8077.       all graphics modes on PC and PS/2 video subsystems. To display a
  8078.       circle, draw an ellipse with its major and minor axes scaled in
  8079.       proportion to your video display's horizontal and vertical
  8080.       resolutions. Listing 7-5 shows how you might do this in a 640-by-350
  8081.       graphics mode on an EGA.
  8082.  
  8083.       Because the scaling varies with the video mode, the same routine
  8084.       cannot draw circles in different video modes unless it accommodates
  8085.       the pixel coordinate scaling in each mode. Figure 4-9 in Chapter 4
  8086.       is a table of pixel scaling factors for all graphics modes.
  8087.  
  8088.  
  8089.  ───────────────────────────────────────────────────────────────────────────
  8090.  
  8091.       Listing 7-5.  Using pixel coordinate scaling to display a circle in
  8092.       640-by-350 16-color mode.
  8093.  
  8094.  ───────────────────────────────────────────────────────────────────────────
  8095.  
  8096.  
  8097.  
  8098.                             8  Region Fill
  8099.  
  8100.  
  8101.                            What Is a Region?
  8102.                Interior and Border Pixels ■ Connectivity
  8103.  
  8104.                   Simple Fills with Horizontal Lines
  8105.  
  8106.                      Three Region Fill Algorithms
  8107.                          Simple Recursive Fill
  8108.                           Line-Adjacency Fill
  8109.                               Border Fill
  8110.  
  8111.                        Comparing the Algorithms
  8112.  
  8113.  
  8114.  
  8115.       This chapter describes several methods for filling a region of the
  8116.       video buffer with a pattern of pixels. Region fill techniques are used
  8117.       in many areas of computer graphics programming, including color
  8118.       manipulation, shading, and representation of three-dimensional
  8119.       objects, as well as in applications such as image processing, image
  8120.       data transmission, and computer animation.
  8121.  
  8122.       This chapter contains working source code for three region fill
  8123.       algorithms, but the discussion is by no means comprehensive. These
  8124.       algorithms and implementations are intended to be working models that
  8125.       you can experiment with, modify, and optimize for your own
  8126.       applications.
  8127.  
  8128.  
  8129.  What Is a Region?
  8130.  
  8131.  
  8132.       A region is a connected group of pixels in the video buffer that is
  8133.       delineated by some sort of boundary. You can think of a region in the
  8134.       video buffer as comprising an interior and a border. To understand how
  8135.       the algorithms in this chapter are implemented, however, it is worth
  8136.       considering how a region can be clearly defined in terms of pixel
  8137.       values and pixel geometry in the video buffer.
  8138.  
  8139.  
  8140.  Interior and Border Pixels
  8141.  
  8142.       In this chapter, a region is assumed to be surrounded by pixels whose
  8143.       values distinguish them from the pixels in the interior. You could
  8144.       assume, for instance, that all interior pixels have the same value, in
  8145.       which case a border pixel is simply any pixel whose value differs from
  8146.       the values of pixels in the interior (see Figure 8-1a). You could
  8147.       also assign a range of allowable pixel values to both interior and
  8148.       border pixels. The algorithms in this chapter adhere to the convention
  8149.       that all pixels in the border have one specified value and pixels in
  8150.       the interior can be of any other value (see Figure 8-1b).
  8151.  
  8152.       In many applications, it is practical to use a range of pixel
  8153.       coordinates to define all or part of a region's border. The definition
  8154.       of a "border pixel" can thus be broadened to include pixels outside a
  8155.       predetermined range of (x,y) coordinates. In this way a region can be
  8156.       bounded by the limits of the screen buffer or by a software window, as
  8157.       well as by pixels of a predetermined value or range of values.
  8158.  
  8159.  
  8160.         ▒▒    ▒▒    ▒▒        ▒▒
  8161.             ▒▒  ▒▒        ▒▒                      ▒▒▒▒▒▒▒▒▒▒▒▒
  8162.       ▒▒    ████████████▒▒                      ▒▒████████████▒▒
  8163.         ▒▒▒▒████████████  ▒▒                    ▒▒████████████▒▒
  8164.     ▒▒      ████████████                        ▒▒████████████▒▒
  8165.         ▒▒  ████████████    ▒▒                  ▒▒████████████▒▒
  8166.             ▒▒          ▒▒                        ▒▒▒▒▒▒▒▒▒▒▒▒
  8167.                 ▒▒         ▒▒
  8168.  
  8169.     a.                                          b.
  8170.  
  8171.       Figure 8-1.  In Figure 8-1a, a region is defined by interior pixels of
  8172.       a given value. In Figure 8-1b, a region is defined by border pixels of
  8173.       a given value.
  8174.  
  8175.  
  8176.  Connectivity
  8177.  
  8178.       To distinguish border pixels from interior pixels, you must also
  8179.       specify the way the pixels are connected. If you allow interior pixels
  8180.       to be connected diagonally as well as orthogonally (horizontally and
  8181.       vertically), you must assume that the border pixels surrounding the
  8182.       region are always connected orthogonally (see Figure 8-2a).
  8183.       Conversely, if you allow border pixels to be diagonally connected, you
  8184.       must constrain interior pixels to orthogonal connections (see Figure
  8185.       8-2b). Consider the reason for this constraint: If both border and
  8186.       interior pixels could be diagonally connected, then interior pixels
  8187.       could be connected to pixels outside the border at places where border
  8188.       pixels are diagonally connected.
  8189.  
  8190.  
  8191.       ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
  8192.       │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │
  8193.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8194.       │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │
  8195.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8196.       │  │  │  │  │██│██│██│██│██│██│██│██│  │  │  │  │
  8197.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8198.       │  │  │  │  │██│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒ ██│  │  │  │  │
  8199.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8200.       │  │  │  │  │██│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│██│  │  │  │  │
  8201.       ├──┼──┼──┼──┼──┴──┴──┴──┴──┴──┴──┴──┼──┼──┼──┼──┤
  8202.       │  │  │  │  │██│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│██│  │  │  │  │
  8203.       ├──┼──┼──┼──┼──┼──┼──┼──├──┼──┼──┼──┼──┼──┼──┼──┤
  8204.       │  │  │  │  │██│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│██│  │  │  │  │
  8205.       ├──┼──┼──┼──┼──┼──┼──┼──├──┼──┼──┼──┼──┼──┼──┼──┤
  8206.       │  │  │  │  │██│██│██│██│██│██│██│██│  │  │  │  │
  8207.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8208.       │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │
  8209.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8210.       │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │
  8211.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8212.       │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │
  8213.       └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
  8214.  
  8215.       a.
  8216.  
  8217.       ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
  8218.       │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │
  8219.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8220.       │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │
  8221.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8222.       │  │  │  │  │  │██│██│██│██│██│██│  │  │  │  │  │
  8223.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8224.       │  │  │  │  │██│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│██│  │  │  │  │
  8225.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8226.       │  │  │  │██│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│██│  │  │  │
  8227.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8228.       │  │  │  │██│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│██│  │  │  │
  8229.       ├──┼──┼──┼──┼──┼──┼──┼──├──┼──┼──┼──┼──┼──┼──┼──┤
  8230.       │  │  │  │██│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│██│  │  │  │
  8231.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8232.       │  │  │  │  │██│▒▒│▒▒│▒▒│▒▒│▒▒│▒▒│██│  │  │  │  │
  8233.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8234.       │  │  │  │  │  │██│██│██│██│██│██│  │  │  │  │  │
  8235.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8236.       │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │
  8237.       ├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
  8238.       │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │  │
  8239.       └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
  8240.  
  8241.       b.
  8242.  
  8243.       Figure 8-2.  Connectivity of pixels. In Figure 8-2a, border pixels
  8244.       (black) are orthogonally connected, while interior pixels (gray) are
  8245.       both orthogonally and diagonally connected. In Figure 8-2b, border
  8246.       pixels are both orthogonally and diagonally connected, so interior
  8247.       pixels are only connected orthogonally.
  8248.  
  8249.  
  8250.  Simple Fills with Horizontal Lines
  8251.  
  8252.  
  8253.       Before you become involved with the intricacies of region fill
  8254.       algorithms, remember that you can fill many regular geometric shapes
  8255.       without using a specialized algorithm. A common application of this
  8256.       technique is shown in Listing 8-1. This routine fills a rectangular
  8257.       region in the video buffer with pixels of a specified value. It is
  8258.       fast, because the subroutine that draws horizontal lines is fast.
  8259.  
  8260.  
  8261.  ───────────────────────────────────────────────────────────────────────────
  8262.  
  8263.       Listing 8-1.  Filling a rectangle with horizontal lines.
  8264.  
  8265.  ───────────────────────────────────────────────────────────────────────────
  8266.  
  8267.  
  8268.       Creating similar routines to draw filled triangles, hexagons, and
  8269.       circles is not difficult, because of these objects' regularity and
  8270.       symmetry. Writing a general-purpose routine that can fill convex or
  8271.       irregular polygons is more difficult; in  this case, you must scan-
  8272.       convert each of the polygon's sides (using, for example, Bresenham's
  8273.       algorithm from Chapter 6) to create a list of the pixels that define
  8274.       the border of the polygon. This list contains pairs of pixels that can
  8275.       then be connected with horizontal lines to fill the interior of the
  8276.       polygon.
  8277.  
  8278.         ╔═══╗     Several good textbooks deal with the problem of scan-
  8279.         ║ T ║     converting and filling arbitrary polygons. For example,
  8280.         ║ I ║     see Fundamentals of Interactive Computer Graphics by J. D.
  8281.         ║ P ║     Foley and A. VanDam (Addison-Wesley 1982).
  8282.         ╚═══╝
  8283.  
  8284.       Though polygon fill techniques have many uses, some applications
  8285.       require filling a region with completely arbitrary borders, such as a
  8286.       map or an irregular shape that was drawn interactively. In this case,
  8287.       your fill routine must define the region using only the pixel values
  8288.       in the video buffer. The remainder of this chapter presents algorithms
  8289.       and working source code for three such routines.
  8290.  
  8291.  
  8292.  Three Region Fill Algorithms
  8293.  
  8294.  
  8295.       The three algorithms described here are all designed with IBM video
  8296.       subsystems in mind. They use the pixel manipulation and line-drawing
  8297.       subroutines developed in Chapters 4, 5, and 6. Also, all three
  8298.       algorithms assume that border pixels can be diagonally connected
  8299.       and that interior pixels must be orthogonally connected (as in Figure
  8300.       8-2b). You can thus fill regions with boundaries drawn using the
  8301.       line-drawing and ellipse-drawing routines in Chapters 6 and 7, since
  8302.       those line and ellipse routines draw diagonally connected figures.
  8303.  
  8304.       Furthermore, all three algorithms can fill a region that contains a
  8305.       hole in its interior (see Figure 8-3). Such holes are collections of
  8306.       border pixels that are  not contiguous with the pixels in the region's
  8307.       outer border. Each algorithm is designed to detect the presence of
  8308.       holes and to properly fill the interior pixels surrounding them.
  8309.  
  8310.  
  8311.               ╔══════════════════════════════════════════╗
  8312.               ║                                          ║
  8313.               ║    Figure 8-3 is found on page 247       ║
  8314.               ║    in the printed version of the book.   ║
  8315.               ║                                          ║
  8316.               ╚══════════════════════════════════════════╝
  8317.  
  8318.       Figure 8-3.  A region whose interior (gray pixels) contains two holes.
  8319.  
  8320.  
  8321.  Simple Recursive Fill
  8322.  
  8323.       One way to fill a region is to start by filling a given "seed" pixel
  8324.       in its interior, and then to fill each of the seed's immediate
  8325.       neighbors, each of the neighbors' neighbors, and so on until the
  8326.       entire region is filled. The C routine in Listing 8-2, PixelFill(),
  8327.       shows how to do this. In PixelFill(), as in the other algorithms in
  8328.       this chapter, pixels in the interior of the region are assumed to be
  8329.       connected horizontally and vertically, but not diagonally.
  8330.       (PixelFill() can be easily modified to fill diagonally connected
  8331.       regions if so desired.)
  8332.  
  8333.  
  8334.  ───────────────────────────────────────────────────────────────────────────
  8335.  
  8336.       Listing 8-2.  A simple recursive region fill.
  8337.  
  8338.  ───────────────────────────────────────────────────────────────────────────
  8339.  
  8340.  
  8341.       Before it fills a pixel, PixelFill() examines the pixel's value to
  8342.       determine whether filling is required. If the pixel is neither a
  8343.       border pixel nor a previously filled pixel, the routine updates the
  8344.       pixel value and calls itself recursively. Because PixelFill() does not
  8345.       fill previously filled pixels, the routine works properly even in
  8346.       regions with holes.
  8347.  
  8348.       Although simple, PixelFill() is inefficient. One reason is that on
  8349.       average only one of the four recursive calls to PixelFill() ever does
  8350.       anything. (Each pixel can only be filled once, but each time a pixel
  8351.       is filled, four recursive calls are made to the function. The only
  8352.       exception is in the case of the seed pixel.) Thus, PixelFill()
  8353.       accomplishes nothing about 75 percent of the time, which is not very
  8354.       efficient.
  8355.  
  8356.         ╔═══╗     Another problem with PixelFill() is that the depth of
  8357.         ║ T ║     recursion can increase beyond the limits of available
  8358.         ║ I ║     stack memory. For example, the default stack space for
  8359.         ║ P ║     code generated with the Microsoft C compiler is 2 KB. You
  8360.         ╚═══╝     can easily exceed this limit by using PixelFill() to fill
  8361.                   even relatively small regions.
  8362.  
  8363.  
  8364.  Line-Adjacency Fill
  8365.  
  8366.       A better approach is to regard the interior of the region as a group
  8367.       of adjacent line segments that are connected vertically instead of as
  8368.       a group of pixels connected both vertically and horizontally. An
  8369.       algorithm that fills adjacent line segments tends to be much more
  8370.       efficient than a pixel-by-pixel recursive fill, because it inspects
  8371.       and fills pixels more efficiently. Also, this conception of the region
  8372.       is closer to the physical representation of pixels in the video
  8373.       buffer, in which pixels are arranged in horizontal rows to be
  8374.       displayed during the raster scan.
  8375.  
  8376.       The routine in Listing 8-3, LineAdjFill(), implements a line-adjacency
  8377.       algorithm for filling a region. Its general strategy is to locate each
  8378.       group of horizontally connected pixels in the interior of the region.
  8379.       Like the simple recursive fill, this algorithm also starts at a seed
  8380.       pixel known to be in the region's interior. It scans left and right to
  8381.       find the ends of the seed pixel's row, then fills the entire row.
  8382.  
  8383.  
  8384.  ───────────────────────────────────────────────────────────────────────────
  8385.  
  8386.       Listing 8-3.  A line-adjacency fill routine.
  8387.  
  8388.  ───────────────────────────────────────────────────────────────────────────
  8389.  
  8390.  
  8391.       The algorithm proceeds by locating all groups of horizontally
  8392.       connected pixels that are vertically adjacent to the group it just
  8393.       scanned. Each time it finds an adjacent group of not-yet-filled
  8394.       pixels, LineAdjFill() is called recursively to fill them. The
  8395.       algorithm terminates when all interior pixels have been filled.
  8396.  
  8397.       Figure 8-4 illustrates the order in which LineAdjFill() fills a
  8398.       simple region comprising seven line segments. The seed pixel is
  8399.       assumed to lie inside line segment 1, and the routine is initially
  8400.       called with an upward search direction. The routine first searches the
  8401.       row of pixels above the seed (that is, line segment 2) for unfilled
  8402.       pixels. Because the row has not yet been filled, the routine is called
  8403.       recursively to fill it. Similarly, line segments 3 and 4 are filled by
  8404.       subsequent recursive calls to LineAdjFill(). At this point, neither
  8405.       line segment 4 nor line segment 3 has any unfilled pixels adjacent to
  8406.       it, but when the pixels below line segment 2 are scanned, line segment
  8407.       5 is discovered and filled. Finally, line segments 6 and 7 are filled
  8408.       recursively.
  8409.  
  8410.  
  8411.               ╔══════════════════════════════════════════╗
  8412.               ║                                          ║
  8413.               ║    Figure 8-4 is found on page 250       ║
  8414.               ║    in the printed version of the book.   ║
  8415.               ║                                          ║
  8416.               ╚══════════════════════════════════════════╝
  8417.  
  8418.       Figure 8-4.  Given a seed pixel in line segment 1, LineAdjFill() fills
  8419.       the adjacent line segments in this region in numerical order.
  8420.  
  8421.  
  8422.         ╔═══╗     A line-adjacency graph (LAG) is essentially a diagram of
  8423.         ║ T ║     the connections between the adjacent line segments in the
  8424.         ║ I ║     interior of a region (see Figure 8-5). The problem of
  8425.         ║ P ║     filling a region is equivalent to traversing its LAG in
  8426.         ╚═══╝     such a way that all nodes in the graph are visited. In
  8427.                   practice, traversing the LAG is relatively easy (there are
  8428.                   several textbook algorithms for graph traversal) compared
  8429.                   to generating the graph given only the pixels in the video
  8430.                   buffer (which is essentially what LineAdjFill() does). For
  8431.                   more information see "Filling Regions in Binary Raster
  8432.                   Images: A Graph-Theoretic Approach" by U. Shani (SIGGRAPH
  8433.                   Proceedings 1980, pp. 321-327).
  8434.  
  8435.  
  8436.               ╔══════════════════════════════════════════╗
  8437.               ║                                          ║
  8438.               ║    Figure 8-5 is found on page 251       ║
  8439.               ║    in the printed version of the book.   ║
  8440.               ║                                          ║
  8441.               ╚══════════════════════════════════════════╝
  8442.  
  8443.       Figure 8-5.  A simple line-adjacency graph (LAG).
  8444.  
  8445.  
  8446.       LineAdjFill() is much more efficient than PixelFill(), because it
  8447.       rarely visits a pixel more than once to determine whether it needs to
  8448.       be filled. Each time the routine is called, it fills one line segment
  8449.       and then inspects the adjacent rows of pixels for unfilled pixels. The
  8450.       routine does not examine pixels that were inspected during the
  8451.       previous invocation of the routine (that is, pixels between PrevXL and
  8452.       PrevXR), nor does it inspect pixels to be filled by subsequent
  8453.       invocations (that is, pixels between the current value of x and the
  8454.       value returned from a call to LineAdjFill()). The recursive logic
  8455.       becomes clear when you trace the execution of the routine as it fills
  8456.       a region such as the one diagrammed in Figure 8-4.
  8457.  
  8458.         ╔═══╗     If you implement a line-adjacency fill algorithm in
  8459.         ║ T ║     assembly language, you can improve its efficiency by
  8460.         ║ I ║     maintaining a push-down stack of parameters and executing
  8461.         ║ P ║     the function iteratively rather than recursively. The
  8462.         ╚═══╝     skeleton of the algorithm then becomes
  8463.  
  8464.                   push ( .. initial parameters on stack .. );
  8465.                   while ( .. stack not empty .. )
  8466.                     LineAdjFill();
  8467.  
  8468.                   The fill routine pops the topmost parameters off the stack
  8469.                   and pushes new sets of parameters instead of calling
  8470.                   itself recursively.
  8471.  
  8472.                   LineAdjFill
  8473.                   {
  8474.                     pop ( .. current parameters off of stack .. )
  8475.                     .
  8476.                     .
  8477.                     .
  8478.                     if ( .. adjacent line needs to be filled .. )
  8479.                       push ( .. new parameters .. )
  8480.                   }
  8481.  
  8482.                   In assembly language, a single machine instruction can
  8483.                   perform each push and pop, so the algorithm's performance
  8484.                   is greatly improved.
  8485.  
  8486.       A line-adjacency algorithm can be adapted to fill a region with a
  8487.       pattern of pixels as well as with a single pixel value. For this
  8488.       reason, it is used commonly in commercial graphics packages. (IBM
  8489.       BASICA and Microsoft GW-BASIC are examples.) Modifying the algorithm
  8490.       to do patterned fills requires that the horizontal line-drawing
  8491.       routine be replaced with a pattern-drawing routine and that the test
  8492.       that determines whether a pixel has been filled take into account the
  8493.       pixel values in the fill pattern.
  8494.  
  8495.       These modifications may seem innocuous, but they can significantly
  8496.       degrade the fill routine's performance. The logic required to detect
  8497.       the presence of previously filled pixels can be complicated,
  8498.       particularly if you allow the fill pattern to contain pixels with the
  8499.       same value as border pixels.
  8500.  
  8501.  
  8502.  Border Fill
  8503.  
  8504.       Because the border of a region defines the extent of its interior, it
  8505.       is possible to fill a region by following the connected border pixels
  8506.       at the ends of the adjacent line segments that make up the interior.
  8507.       (See "Contour Filling in Raster Graphics" by T. Pavlidis, Computer
  8508.       Graphics, August 1981, p. 29). As long as you fill the region at the
  8509.       same time that you trace the border, however, this kind of border-
  8510.       tracing fill algorithm offers no clear advantage over a line-adjacency
  8511.       algorithm.
  8512.  
  8513.       However, if you separate the problem of tracing the border from that
  8514.       of filling the region's interior, the resulting algorithm becomes more
  8515.       flexible. The process of filling a region then breaks down into three
  8516.       discrete steps:
  8517.  
  8518.       1)  Create an ordered list of the border pixels (trace the border).
  8519.  
  8520.       2)  Scan the interior of the region for holes.
  8521.  
  8522.       3)  "Connect the dots" in the list from left to right with horizontal
  8523.           lines, thereby filling the region.
  8524.  
  8525.       The routine BorderFill() in Listing 8-4 performs a region fill using
  8526.       this three-step method. The algorithm executes the three steps
  8527.       iteratively, once for the boundary of the region and once for each
  8528.       hole in the interior of the region.
  8529.  
  8530.  
  8531.  ───────────────────────────────────────────────────────────────────────────
  8532.  
  8533.       Listing 8-4.  A region fill routine that traces a region's
  8534.       border.
  8535.  
  8536.  ───────────────────────────────────────────────────────────────────────────
  8537.  
  8538.  
  8539.       The module TraceBorder() creates a table that contains the pixel
  8540.       address  of every pixel in the region's border. SortBP() then sorts
  8541.       the table of border pixels by increasing y- and x-coordinates. The
  8542.       routine ScanRegion() examines the interior line segment between each
  8543.       pair of border pixels in the table. If it detects a border pixel
  8544.       within the line segment, ScanRegion() assumes it has encountered a
  8545.       hole in the region; it then returns the border pixel's (x,y)
  8546.       coordinates so that TraceBorder() and SortBP() can update the table
  8547.       with the hole's border pixels. This process continues until the entire
  8548.       interior of the region has been scanned. Then FillRegion() uses the
  8549.       sorted list of border pixels to fill the region by drawing a
  8550.       horizontal line between each pair of pixels in the list.
  8551.  
  8552.       TraceBorder() starts with a seed pixel on the right-hand border of the
  8553.       region. It steps clockwise from pixel to pixel in the border. Because
  8554.       the search proceeds clockwise, the interior of the region is always to
  8555.       the right of the direction in which the search is moving. If a pixel
  8556.       is not adjacent to the interior, the algorithm does not identify it as
  8557.       a border pixel. The algorithm ensures that the border pixels it
  8558.       detects are indeed adjacent to the interior by always examining pixels
  8559.       to the right of the search direction first.
  8560.  
  8561.       The algorithm identifies its search direction with one of the eight
  8562.       numeric codes shown in Figure 8-6. (This technique is taken from
  8563.       "Algorithms for Graphics and Image Processing" by T. Pavlidis
  8564.       [Computer Science Press, 1982].) Thus, in Figure 8-7, the algorithm
  8565.       moves from pixel b to pixel c in direction 6 (downward). To  find the
  8566.       next pixel in the border, the algorithm starts by examining the pixel
  8567.       to the right of direction 6; that is, direction 4. This pixel is not a
  8568.       border pixel, but the pixel in direction 5 (pixel d) is, so d is added
  8569.       to the list. The algorithm continues to trace the border until it
  8570.       returns to the starting pixel. (The search terminates immediately in
  8571.       the case of a degenerate "border" consisting of only one pixel.)
  8572.  
  8573.       TraceBorder() performs another task in addition to identifying the
  8574.       pixels in the border. It also indicates whether each border pixel
  8575.       defines the left or right endpoint of a horizontal interior line
  8576.       segment. (Because FillRegion() draws horizontal lines from left to
  8577.       right, TraceBorder() marks each border pixel with a flag indicating
  8578.       whether the pixel can be used as a left border.) Furthermore, if a
  8579.       pixel can serve as both a left and a right border (see Figure 8-8),
  8580.       TraceBorder() adds it to the table twice. The logic in SameDirection()
  8581.       and DifferentDirection() accomplishes these tasks.
  8582.  
  8583.  
  8584.       ┌───────┬───────┬───────┐
  8585.       │       │   2   │       │
  8586.       │   3   │       │   1   │
  8587.       │      │      │      │
  8588.       │      \│   │   │/      │
  8589.       ├───────┼───────┼───────┤
  8590.       │       │       │       │
  8591.       │  4 ──│       │──  0 │
  8592.       │       │       │       │
  8593.       ├───────┼───────┼───────┤
  8594.       │      /│   │   │\      │
  8595.       │      │      │      │
  8596.       │   5   │       │   7   │
  8597.       │       │   6   │       │
  8598.       └───────┴───────┴───────┘
  8599.  
  8600.       Figure 8-6.  Numeric codes for border pixel trace directions.
  8601.  
  8602.  
  8603.                       ┌───┐                  Direction
  8604.                       │ a │           ab        6
  8605.                      ├───┼───┐       bc        6
  8606.                       │ b │   │       cd        5
  8607.                      ├───┼───┘       de        5
  8608.                 /     │ c │           ef        4
  8609.                  ┌───┼───┤           fg        4
  8610.                 │ d │   │
  8611.       ┌───┬───┬───┼───┼───┘
  8612.       │ g │ f │ e │   │
  8613.       └───┴───┼───┼───┘
  8614.               │   │
  8615.               └───┘
  8616.  
  8617.       Figure 8-7.  Border pixel identification in TraceBorder().
  8618.  
  8619.  
  8620.           ┌───┬───┬───┐                       ┌───┬───┐
  8621.           │   │   │   │                       │   │   │
  8622.       ┌───┼───┴───┴───┼───┐               ┌───┼───┴───┼───┐
  8623.       │   │           │   │               │   │       │   │
  8624.       ├───┤           └───┼───┬───┬───┬───┼───┘       ├───┤
  8625.       │   │               │ a │   │   │   │           │   │
  8626.       ├───┤               └───┴───┴───┴───┘           ├───┤
  8627.       │   │                                           │   │
  8628.       ├───┤                                           ├───┤
  8629.       │   │                                           │   │
  8630.       ├───┤                   ┌───┐                   ├───┤
  8631.       │   │                   │ b │                   │   │
  8632.       └───┼───┐           ┌───┼───┼───┐           ┌───┼───┘
  8633.           │   │           │   │   │   │           │   │
  8634.           └───┼───┬───┬───┼───┘   └───┼───┬───┬───┼───┘
  8635.               │   │   │   │           │   │   │   │
  8636.               └───┴───┴───┘           └───┴───┴───┘
  8637.  
  8638.       Figure 8-8.  Pixels may border the interior on the left, right, or
  8639.       both directions: Pixel a is a border pixel on the right of a
  8640.       row of interior pixels; it is blocked to its right by other border
  8641.       pixels. Pixel b serves as both a left and a right border.
  8642.  
  8643.  
  8644.       TraceBorder() may seem complex, but it is a relatively fast routine.
  8645.       The slowest steps in BorderFill() are actually SortBP(), which sorts
  8646.       the table of border pixels, and ScanRegion(), which searches for
  8647.       border pixels in the interior of the region. If SortBP() and
  8648.       ScanRegion() are slow, BorderFill() will be slow, because these
  8649.       routines are executed iteratively, once for each hole in the region.
  8650.  
  8651.       You can significantly improve BorderFill()'s performance by modifying
  8652.       TraceBorder() so that it builds its list of border pixels in the
  8653.       proper order to begin with, avoiding the sort altogether. You can
  8654.       build the ordered list efficiently using any of several data
  8655.       structures, including a linked list, a heap, or a fixed-size table.
  8656.       This type of modification is particularly effective when the algorithm
  8657.       is used to fill regions that contain one or more holes. Instead of
  8658.       sorting the list each time it detects a hole, the modified algorithm
  8659.       simply inserts the hole's border pixels into the list.
  8660.  
  8661.       Writing ScanRegion() in a high-level language is relatively easy, but
  8662.       because the routine examines all pixels in the interior of the region,
  8663.       you should write it in assembly language so it will execute rapidly.
  8664.       Furthermore, using assembly language on the EGA, the VGA, and the
  8665.       InColor Card offers a distinct advantage, because the graphics control
  8666.       hardware in these subsystems can examine eight pixels at a time and
  8667.       indicate which, if any, match the border pixel value. The assembly-
  8668.       language routine ScanRight() in Listing 8-5, which can be used in EGA
  8669.       and VGA 16-color graphics modes, runs 50 times faster than the C
  8670.       version in Listing 8-4.
  8671.  
  8672.  
  8673.  ───────────────────────────────────────────────────────────────────────────
  8674.  
  8675.       Listing 8-5.  An assembly-language version of ScanRight().
  8676.  
  8677.  ───────────────────────────────────────────────────────────────────────────
  8678.  
  8679.  
  8680.       The fastest step in BorderFill() is the fill itself, because
  8681.       horizontal lines can be drawn rapidly. Thus, if you need to fill the
  8682.       same region repeatedly or to copy the same filled region several
  8683.       times, you can preserve the list of border pixels generated the first
  8684.       time you execute BorderFill(). This greatly accelerates subsequent
  8685.       fills, because you can skip the border-tracing and sorting steps.
  8686.  
  8687.  
  8688.  Comparing the Algorithms
  8689.  
  8690.  
  8691.       Which region fill algorithm is best? Each algorithm described in this
  8692.       chapter has its pros and cons. You can compare them in several ways. A
  8693.       valid comparison considers the simplicity of the algorithm, the speed
  8694.       of the compiled code, and the suitability of each algorithm for
  8695.       particular types of region fills.
  8696.  
  8697.       The recursive, pixel-by-pixel algorithm implemented as PixelFill() is
  8698.       about as simple as you can get. The source code is short and easy to
  8699.       implement in assembly language as well as in a high-level language.
  8700.       However, PixelFill() is too inefficient and too highly recursive to be
  8701.       generally useful.
  8702.  
  8703.       The line-adjacency fill algorithm LineAdjFill() is more complicated
  8704.       than PixelFill(). Nevertheless, LineAdjFill() improves on the
  8705.       performance of PixelFill() because it examines pixel groups instead of
  8706.       individual pixels. LineAdjFill() also runs faster when it is written
  8707.       to access the video buffer in one-byte increments instead of one-pixel
  8708.       increments. LineAdjFill() is also much less recursive than
  8709.       PixelFill(), so its runtime memory requirements are smaller than those
  8710.       of PixelFill().
  8711.  
  8712.       The three-step algorithm implemented in BorderFill() is more
  8713.       complicated and somewhat slower than the other two algorithms. The
  8714.       advantage of using BorderFill() is its generality. Its modules can be
  8715.       readily adapted to alternate types of region fills, including pattern
  8716.       fills and fills of regions defined as numeric lists of (x,y)
  8717.       coordinates.
  8718.  
  8719.       The performance of BorderFill() depends on the number of holes in the
  8720.       region. It is as fast as LineAdjFill() in filling a region without
  8721.       holes. However, when the region to be filled looks like Swiss cheese,
  8722.       BorderFill() slows down because it must update the sorted list of
  8723.       border pixels whenever it fills around a hole.
  8724.  
  8725.       Nevertheless, BorderFill() can do several things that the other
  8726.       algorithms cannot. For example, it can reliably fill regions that
  8727.       contain previously filled pixels. Unlike BorderFill(), both
  8728.       PixelFill() and LineAdjFill() rely on the implicit assumption tht no
  8729.       interior pixels have the same value as the fill value. Thus,
  8730.       BorderFill() correctly fills the region shown in Figure 8-9, but both
  8731.       of the other routines fail.
  8732.  
  8733.  
  8734.               ╔══════════════════════════════════════════╗
  8735.               ║                                          ║
  8736.               ║    Figure 8-9 is found on page 264       ║
  8737.               ║    in the printed version of the book.   ║
  8738.               ║                                          ║
  8739.               ╚══════════════════════════════════════════╝
  8740.  
  8741.       Figure 8-9.  A test case for fill algorithms. Neither PixelFill() nor
  8742.       LineAdjFill() can correctly fill this region with gray pixels, because
  8743.       the "holes" are treated as if they have been already filled.
  8744.  
  8745.  
  8746.         ╔═══╗     You could modify a routine such as LineAdjFill() so that
  8747.         ║ T ║     its detection of holes in the region does not depend on
  8748.         ║ I ║     the presence of previously filled pixels. This means the
  8749.         ║ P ║     algorithm must somehow keep track of pixels it has already
  8750.         ╚═══╝     filled. One way to do this is to keep track of points
  8751.                   where the border reaches a local minimum or maximum (see
  8752.                   Figure 8-10). These locations can identify the top and
  8753.                   bottom of a hole in the region, enabling the fill
  8754.                   algorithm to determine when to stop working its way around
  8755.                   the hole.
  8756.  
  8757.  
  8758.               ╔══════════════════════════════════════════╗
  8759.               ║                                          ║
  8760.               ║    Figure 8-10 is found on page 265      ║
  8761.               ║    in the printed version of the book.   ║
  8762.               ║                                          ║
  8763.               ╚══════════════════════════════════════════╝
  8764.  
  8765.       Figure 8-10.  An algorithm can detect the presence of a hole in a
  8766.       region by locating the border's local maximum and minimum. Pixels
  8767.       marked a identify a local maximum. Pixels marked b identify a
  8768.       local minimum.
  8769.  
  8770.  
  8771.       For some applications, BorderFill() has a strong advantage over the
  8772.       other algorithms, because its border-tracing and sorting steps
  8773.       generate a list of numeric pixel coordinates. This list completely
  8774.       defines a two-dimensional region of pixels. You can translate or
  8775.       change the scale of the region by applying the appropriate conversions
  8776.       to the list of border pixels. As long as you preserve the pixels'
  8777.       order in the list, you can use the FillRegion() routine in
  8778.       BorderFill() to fill the region the list defines. For this reason, the
  8779.       BorderFill() algorithm is best suited for applications that must copy
  8780.       arbitrary regions, change their scale or size, or draw them repeatedly
  8781.       into the video buffer.
  8782.  
  8783.       Furthermore, by modifying the horizontal line routine in BorderFill()
  8784.       you can easily fill a region with an arbitrary pattern or allow pixel
  8785.       AND, OR, and XOR functions. Although you can augment PixelFill() or
  8786.       LineAdjFill() in this way, the source code can become complicated
  8787.       because these algorithms inspect pixels to determine whether they have
  8788.       been filled.
  8789.  
  8790.       The trade-offs in complexity and performance in these algorithms leave
  8791.       a great deal to your programming judgment. No single region fill
  8792.       algorithm is best for all possible graphics applications. Your choice
  8793.       of implementation should depend on your performance demands, the
  8794.       requirements of the application itself, the capabilities of your video
  8795.       display hardware, and the effort you can afford to expend in
  8796.       integrating and optimizing the code.
  8797.  
  8798.  
  8799.  
  8800.                           9  Graphics Text
  8801.  
  8802.  
  8803.                       Character Definition Tables
  8804.                           Video BIOS Support
  8805.                  Creating a Character Definition Table
  8806.  
  8807.                      Software Character Generators
  8808.                   Video BIOS Support ■ Pixel Handling
  8809.  
  8810.                Designing a Software Character Generator
  8811.                          Horizontal Alignment
  8812.                   Variable Character Sizes ■ Clipping
  8813.                          Character Orientation
  8814.                     Cooperating with the Video BIOS
  8815.                       More Power, More Complexity
  8816.  
  8817.               Implementing a Software Character Generator
  8818.                        CGA ■ HGC and HCG+ ■ MCGA
  8819.                       EGA and VGA ■ InColor Card
  8820.  
  8821.  
  8822.  
  8823.       Few programs are complete without some sort of text display. Most
  8824.       graphics applications incorporate text with graphics images. In
  8825.       graphics modes, the software that draws characters requires the same
  8826.       thoughtful design and construction as do routines that draw geometric
  8827.       figures such as lines and ellipses.
  8828.  
  8829.       In alphanumeric video modes, of course, displaying text is easy. You
  8830.       simply place a character code and attribute in the video buffer and
  8831.       let the hardware character generator put pixels on the screen. In
  8832.       graphics modes, however, your program must store every pixel of every
  8833.       character in the video buffer.
  8834.  
  8835.       This chapter discusses how to translate character codes into the pixel
  8836.       patterns that form characters in graphics modes. The programming
  8837.       examples are hardware-specific, of course, but you can adapt the
  8838.       table-driven character generator described here for use with other
  8839.       computers and in other graphics applications.
  8840.  
  8841.  
  8842.  Character Definition Tables
  8843.  
  8844.  
  8845.       Every character that an IBM video subsystem displays is made up of a
  8846.       pattern of contiguous pixels. The pixels are arranged to appear as
  8847.       coherent, recognizable characters on the screen. The pixel pattern
  8848.       that represents a character is the same no matter where in the buffer
  8849.       or on the screen the character is located.
  8850.  
  8851.       The most convenient way to describe the pixel patterns that represent
  8852.       the characters in a character set is to create a table in which bit
  8853.       patterns represent the pixel patterns. Such a character definition
  8854.       table contains a bit pattern for every displayable character (see
  8855.       Figure 9-1). Each character's bit pattern is defined within a
  8856.       rectangular matrix. When the character matrix is the same size for all
  8857.       characters in the table, and the definitions in the table are
  8858.       organized by character code, converting a character code to an offset
  8859.       into the table is easy.
  8860.  
  8861.       You can use a character definition table formatted in this way in
  8862.       alphanumeric as well as graphics modes in video subsystems that
  8863.       support RAM-based alphanumeric character definitions. Chapter 10
  8864.       covers this topic in detail.
  8865.  
  8866.  
  8867.                                             Hex    Binary  ┌─┬─┬─┬─┬─┬─┬─┬─┐
  8868.                                         ┌───7E    01111110 │ │█│█│█│█│█│█│ │
  8869.                                         │                  ├─┼─┼─┼─┼─┼─┼─┼─┤
  8870.                                         │   81    10000001 │█│ │ │ │ │ │ │█│
  8871.  FOOO:FA6E  00 00 00 00 00 00 00 00     │                  ├─┼─┼─┼─┼─┼─┼─┼─┤
  8872.            ┌────────────────────────────┘   A5    10100101 │█│ │█│ │ │█│ │█│
  8873.  FOOO:FA76 │7E 81 A5 81 BD 99 81 7E                        ├─┼─┼─┼─┼─┼─┼─┼─┤
  8874.            └────────────────────────────┐   81    10000001 │█│ │ │ │ │ │ │█│
  8875.  FOOO:FA7E  7E FF DB FF C3 E7 FF 7E     │                  ├─┼─┼─┼─┼─┼─┼─┼─┤
  8876.  FOOO:FA86  6C FE FE FE 7C 38 10 00     │   B0    10111101 │█│ │█│█│█│█│ │█│
  8877.  FOOO:FA8E  10 38 7C FE 7C 38 10 00     │                  ├─┼─┼─┼─┼─┼─┼─┼─┤
  8878.  FOOO:FA96  38 7C 38 FE FE 7C 38 7C     │   99    10011001 │█│ │ │█│█│ │ │█│
  8879.  FOOO:FA9E  10 10 38 7C FE 7C 38 7C     │                  ├─┼─┼─┼─┼─┼─┼─┼─┤
  8880.  FOOO:FAA6  00 00 18 3C 3C 18 00 00     │   81    10000001 │█│ │ │ │ │ │ │█│
  8881.  FOOO:FAAE  FF FF E7 C3 C3 E7 FF FF     │                  ├─┼─┼─┼─┼─┼─┼─┼─┤
  8882.                      .                  └───7E    01111110 │ │█│█│█│█│█│█│ │
  8883.                      .                                     └─┴─┴─┴─┴─┴─┴─┴─┘
  8884.                      .
  8885.  
  8886.       Figure 9-1.  The beginning of the bit patterns that define IBM's ROM
  8887.       BIOS 8-by-8 character definitions.
  8888.  
  8889.  
  8890.  Video BIOS Support
  8891.  
  8892.       The PC and PS/2 ROM BIOS contains default character definition tables
  8893.       for use in graphics modes. The size of the characters in the table
  8894.       depends on the vertical resolution of the video mode. In 200-line,
  8895.       CGA-compatible video modes, the default character matrix is 8 pixels
  8896.       wide and 8 pixels high; in 350-line graphics modes, it is 8 wide by 14
  8897.       high; in 400-line and 480-line modes, it is 8 by 16. In all graphics
  8898.       modes, the default characters are 8 pixels wide simply because there
  8899.       are 8 bits in a byte. Because each byte in a character definition
  8900.       table represents 8 horizontal pixels, defining characters as a
  8901.       multiple of 8 pixels in width makes the table easy to manipulate in
  8902.       software.
  8903.  
  8904.       No equivalent constraint applies to the height of characters defined
  8905.       in a character definition table. In practice, however, the character
  8906.       matrix used with IBM video subsystems should rarely be smaller than 8
  8907.       by 6 pixels or larger than 8 by 16 pixels. With a character matrix
  8908.       outside this range, the displayed height and width of the characters
  8909.       become disproportionate and the characters tend to appear too short or
  8910.       too elongated to be easily read.
  8911.  
  8912.  
  8913.       Default CGA Characters
  8914.       Figure 9-1 shows the beginning of the character definition table for
  8915.       the default character set in CGA graphics modes. The table contains an
  8916.       8-byte definition for each of the first 128 ASCII characters (0
  8917.       through 7FH). The first eight bytes of the table correspond to
  8918.       character code 0, the second eight bytes to character code 1, and so
  8919.       forth. The bit pattern in each group of eight bytes represents the
  8920.       pixel pattern displayed for the corresponding row of pixels in the
  8921.       character. The first of the eight bytes in each group corresponds to
  8922.       the topmost row of eight pixels.
  8923.  
  8924.       This table of 8-by-8 character definitions is located at F000:FA6E in
  8925.       the motherboard ROM on all PCs and PS/2s. However, the table defines
  8926.       only the first 128 ASCII characters. Character definitions for the
  8927.       second group of 128 ASCII codes (80H through 0FFH) are found in a
  8928.       table whose address is stored in interrupt vector 1FH (0000:007C).
  8929.       Because the motherboard BIOS contains no definitions for these
  8930.       characters, the address is initialized to 0000:0000. If you use the
  8931.       ROM BIOS to display ASCII characters between 80H and 0FFH in CGA
  8932.       graphics modes without pointing this interrupt vector to a character
  8933.       definition table, the "characters" you see on the screen are whatever
  8934.       binary patterns happen to lie in the first 1024 bytes of RAM.
  8935.  
  8936.         ╔═══╗     The MS-DOS utility GRAFTABL leaves a table of definitions
  8937.         ║ T ║     for characters 80H through 0FFH resident in RAM and
  8938.         ║ I ║     updates the interrupt 1FH vector to point to it. The
  8939.         ║ P ║     characters defined in GRAFTABL are the same as those the
  8940.         ╚═══╝     alphanumeric character generator displays for ASCII codes
  8941.                   80H through 0FFH.
  8942.  
  8943.  
  8944.       Default EGA, VGA, and MCGA Characters
  8945.       The ROM BIOS in the EGA, VGA, and MCGA subsystems contains definitions
  8946.       for all 256 ASCII codes for all graphics modes. (You can access these
  8947.       tables directly; their addresses may be obtained by calling INT 10H
  8948.       function 11H with AL = 30H.) When you select a graphics mode with INT
  8949.       10H function 0, the video BIOS loads the address of the appropriate
  8950.       character definition table for the graphics mode into interrupt vector
  8951.       43H (0000:010C). In CGA-compatible 200-line graphics modes, the BIOS
  8952.       also points the interrupt 1FH vector to the definitions for characters
  8953.       80H through  0FFH.
  8954.  
  8955.  
  8956.  Creating a Character Definition Table
  8957.  
  8958.       The easiest way to obtain a character definition table is to use one
  8959.       of the default BIOS tables. If the staid, placid characters in those
  8960.       tables aren't to your liking, you can find many others commercially
  8961.       available or in the public domain.
  8962.  
  8963.         ╔═══╗     Several standard character sets are defined and registered
  8964.         ║ T ║     with the International Standards Organization (ISO). IBM
  8965.         ║ I ║     refers to these character sets as code pages and has
  8966.         ║ P ║     assigned arbitrary identification numbers to them. For
  8967.         ╚═══╝     example, the standard IBM PC ASCII character set is
  8968.                   designated by code page 437; the Canadian French code page
  8969.                   is 863; and code page 850 is the general-purpose
  8970.                   "multilingual" character set devised by IBM for languages
  8971.                   that use a Latin alphabet.
  8972.  
  8973.                   Both MS-DOS (starting in version 3.3) and OS/2 allow
  8974.                   applications to switch between code pages on an EGA or
  8975.                   VGA. When a program displays characters with operating
  8976.                   system function calls, the operating system uses the
  8977.                   character definitions in the currently selected code page.
  8978.                   Applications that use foreign language character sets
  8979.                   should, whenever possible, exploit the code pages
  8980.                   supported by the operating system.
  8981.  
  8982.  
  8983.       When you define your own character set, you can select among several
  8984.       alternative methods. The ugly alternative is to build your character
  8985.       definition table by specifying every byte in source code. Figure 9-2
  8986.       shows the beginning of such a table. A more elegant alternative is to
  8987.       use a character-set editing program. With such editors, you use
  8988.       cursor-control keys or a pointing device such as a light pen or mouse
  8989.       to specify the bit patterns in the table. Character-set editors are
  8990.       also available both commercially and in the public domain. (You can
  8991.       even write your own, using the routines in this book.)
  8992.  
  8993.       Another approach is to start with one of the BIOS character sets and
  8994.       transform the bit patterns in a regular way. For example, you could
  8995.       reverse the bit patterns in a table by converting 0s to 1s and 1s to
  8996.       0s (that is, apply a bitwise logical NOT to each byte in the table),
  8997.       thus creating a "reverse" character set.
  8998.  
  8999.  
  9000.       CharDefs    db   000h,000h,000h,000h,000h,000h,000h,000h ; character 0
  9001.                   db   03Ch,066h,0C0h,0C0h,0C0h,066h,03Ch,000h ; character 1
  9002.                   db   0FCh,066h,066h,07Ch,06Ch,066h,0E6h,000h ; character 2
  9003.                   db   0FEh,062h,068h,078h,068h,062h,0FEh,000h ; character 3
  9004.                   db   078h,0CCh,0CCh,078h,0CCh,0CCh,078h,000h ; character 4
  9005.                   db   078h,030h,030h,030h,030h,030h,078h,000h ; character 5
  9006.                   db   0CCh,0CCh,0CCh,0CCh,0CCh,078h,030h,000h ; character 6
  9007.                   db   0FEh,062h,068h,078h,068h,062h,0FEh,000h ; character 7
  9008.                                        .
  9009.                                        .
  9010.                                        .
  9011.  
  9012.       Figure 9-2.  A hand-coded character definition table.
  9013.  
  9014.  
  9015.  Software Character Generators
  9016.  
  9017.  
  9018.       A software routine that uses the bit patterns in a character
  9019.       definition table to draw characters in the video buffer is called a
  9020.       software character generator. A software character generator performs
  9021.       several functions. It locates the bit pattern for a given character
  9022.       code, translates the bit pattern into a corresponding pattern of
  9023.       pixels, and updates pixels at a specified location in the video
  9024.       buffer.
  9025.  
  9026.  
  9027.  Video BIOS Support
  9028.  
  9029.       The video BIOS provides a software character generator that is used
  9030.       whenever INT 10H functions 09H, 0AH, 0EH, and 13H are called in
  9031.       graphics modes. The software character generator in the IBM PC and AT
  9032.       uses only the 8-by-8 characters defined at F000:FA6E and at the
  9033.       address indicated by interrupt vector 1FH. The version in the EGA and
  9034.       PS/2 BIOS uses the table to which interrupt vector 43H points; this
  9035.       version determines the height of displayed characters from the BIOS
  9036.       variable POINTS at 0040:0085.
  9037.  
  9038.       You can use the BIOS software character generator to display
  9039.       characters from any character definition table by updating the
  9040.       appropriate interrupt vectors with the address of the table. On the
  9041.       EGA and PS/2s, use INT 10H function 11H to do this.
  9042.  
  9043.       The BIOS character generator is convenient to use, but it is somewhat
  9044.       limited in its capabilities. In particular, it can only store byte-
  9045.       aligned characters in the video buffer. If you are willing to
  9046.       sacrifice compatibility with the INT 10H interface, you can write a
  9047.       faster software character generator that is more powerful than the
  9048.       default video BIOS version.
  9049.  
  9050.  
  9051.  Pixel Handling
  9052.  
  9053.       You store characters in the video buffer by changing the values of the
  9054.       appropriate pixel groups. You can update the video buffer simply by
  9055.       replacing old pixel values with new ones. You can also perform bitwise
  9056.       logical operations (AND, OR, or XOR) to update the pixels.
  9057.  
  9058.       Your routine to display text in graphics modes can handle the
  9059.       background pixels in the character matrix in one of two ways. One is
  9060.       to preserve the contents of the video buffer as much as possible by
  9061.       updating only foreground pixels; that is, by updating only those
  9062.       pixels that represent the character itself (see Figure 9-3a). The
  9063.       other is to update all foreground and background pixels within the
  9064.       bounds of the rectangular character matrix (see Figure 9-3b).
  9065.  
  9066.  
  9067.               ╔══════════════════════════════════════════╗
  9068.               ║                                          ║
  9069.               ║    Figure 9-3 is found on page 272       ║
  9070.               ║    in the printed version of the book.   ║
  9071.               ║                                          ║
  9072.               ╚══════════════════════════════════════════╝
  9073.  
  9074.       Figure 9-3.  Characters written without background pixels (a.) and
  9075.       with background pixels (b.).
  9076.  
  9077.  
  9078.       Updating only the character's foreground pixels preserves as many
  9079.       pixels in the video buffer as possible. This may be the best way to
  9080.       display text in front of a detailed or patterned graphics image.
  9081.       However, reading the displayed characters can be difficult if the
  9082.       graphics image in some way blends with the character. For example,
  9083.       text is invisible against a region filled with pixels having the same
  9084.       value as the character's foreground pixels.
  9085.  
  9086.       To avoid such problems, you can update all foreground and background
  9087.       pixels in the character matrix each time you store a character in the
  9088.       buffer. This avoids a background pattern inadvertently masking the
  9089.       characters. The trade-off is that each time you store a character in
  9090.       the buffer you must replace the previous contents of the buffer with a
  9091.       rectangular blot.
  9092.  
  9093.       The source code for the two types of graphics text routines is
  9094.       similar. The examples in this chapter demonstrate the second type,
  9095.       which makes them more complicated than routines that draw only
  9096.       foreground pixels. You can convert the routines to draw only the
  9097.       foreground pixels by eliminating the code for incorporating the
  9098.       background pixels.
  9099.  
  9100.  
  9101.  Designing a Software Character Generator
  9102.  
  9103.  
  9104.       Software character generators for IBM PC video subsystems have a
  9105.       number of design considerations in common. Because the performance of
  9106.       your character generator strongly influences the overall performance
  9107.       of many graphics applications, always consider the trade-offs between
  9108.       function and simplicity in your character generator routines.
  9109.  
  9110.  
  9111.  Horizontal Alignment
  9112.  
  9113.       In graphics modes, the left edge of a character is not necessarily
  9114.       byte-aligned. When a character is written so that its leftmost pixels
  9115.       fall somewhere in the middle of a byte in the video buffer (see Figure
  9116.       9-4a), the character generator must shift and mask the character
  9117.       matrix so that only pixels that are part of the character are updated.
  9118.  
  9119.       Usually, however, characters are written into the video buffer at
  9120.       byte-aligned pixel addresses (see Figure 9-4b). This is the case, for
  9121.       example, whenever the display is used in a "teletype mode"; that is,
  9122.       when each line of characters starts at the left edge of the display.
  9123.       Generating byte-aligned characters requires no rotation or masking of
  9124.       pixels, so using a separate routine for byte-aligned characters
  9125.       improves the character generator's performance.
  9126.  
  9127.  
  9128.               ╔══════════════════════════════════════════╗
  9129.               ║                                          ║
  9130.               ║    Figure 9-4 is found on page 273       ║
  9131.               ║    in the printed version of the book.   ║
  9132.               ║                                          ║
  9133.               ╚══════════════════════════════════════════╝
  9134.  
  9135.       Figure 9-4.  Alignment of characters in the video buffer. In Figure
  9136.       9-4a, characters are not aligned; in Figure 9-4b, characters are byte-
  9137.       aligned.
  9138.  
  9139.  
  9140.  Variable Character Sizes
  9141.  
  9142.       Writing a character generator that accommodates characters of
  9143.       different heights is relatively easy. The height of a character
  9144.       corresponds to the number of bytes in its definition in the character
  9145.       definition table. You can thus use the height of your characters as a
  9146.       loop limit inside the character generator routine without
  9147.       significantly affecting the complexity of the routine.
  9148.  
  9149.       Handling characters of different widths is more difficult. If the
  9150.       width of a character does not fit exactly into an integer number of
  9151.       bytes, you must mask each row of pixels in the character as you store
  9152.       it in the video buffer. Again, the extra overhead of forming the
  9153.       appropriate bit mask and masking pixels in the video buffer
  9154.       complicates and slows the character generator routine.
  9155.  
  9156.  
  9157.  Clipping
  9158.  
  9159.       You can clip characters in several ways. The simplest is to clip the
  9160.       entire character before you store it in the video buffer; if any
  9161.       portion of the character matrix would lie outside the clipping area,
  9162.       don't write the character.
  9163.  
  9164.       Clipping a character so that only a portion of it is stored in the
  9165.       video buffer is more difficult. One way to do this is to modify the
  9166.       character generator so that any clipped portion of a character is not
  9167.       written to the buffer. Another approach is to write the entire
  9168.       character into an auxiliary buffer and then copy the clipped character
  9169.       into the video buffer with a pixel block copy routine (see Chapter
  9170.       11).
  9171.  
  9172.  
  9173.  Character Orientation
  9174.  
  9175.       Usually, characters are displayed so that they can be read from left
  9176.       to right and from the top down. To change this orientation, apply the
  9177.       appropriate transformation to the bit patterns in the character
  9178.       definition table. For example, the subroutine in Listing 9-1 rotates
  9179.       the 8-byte bit pattern that represents an 8-by-8 character so that the
  9180.       displayed characters read upward. With this transformation, you can
  9181.       use the same character generator to display vertically or horizontally
  9182.       oriented characters. Only the bit patterns differ.
  9183.  
  9184.  
  9185.  ───────────────────────────────────────────────────────────────────────────
  9186.  
  9187.       Listing 9-1.  A routine that rotates an 8-by-8 character
  9188.       definition by 90 degrees.
  9189.  
  9190.  ───────────────────────────────────────────────────────────────────────────
  9191.  
  9192.  
  9193.  Cooperating with the Video BIOS
  9194.  
  9195.       Even if your character definition tables and character generator
  9196.       software avoid using video BIOS functions, you should nevertheless try
  9197.       to preserve compatibility by cooperating with the BIOS routines when
  9198.       possible. In 200-line graphics modes, you should update the address in
  9199.       interrupt vector 1FH whenever you use an 8-by-8 character definition
  9200.       table that includes the second 128 ASCII characters. On the EGA, VGA,
  9201.       and MCGA, you should generally use INT 10H function 11H to keep the
  9202.       BIOS interrupt vectors and Video Display Data Area variables up to
  9203.       date.
  9204.  
  9205.  
  9206.  More Power, More Complexity
  9207.  
  9208.       You can add functionality to a software character generator in several
  9209.       ways. You might, for example, write a character generator that refers
  9210.       to a table of relative character widths to display proportionally
  9211.       spaced characters. As your routine reads bit patterns from the
  9212.       character definition table, you might have it shift them to the right
  9213.       by a predetermined number of pixels to generate bold or italic
  9214.       character sets. You might apply a pattern of pixel values to the
  9215.       foreground pixels you update. You might allow a character definition
  9216.       table to extend beyond the usual range of 256 characters; the more
  9217.       characters you define, the wider range of characters you can display
  9218.       at one time. Any of these possibilities adds power and flexibility to
  9219.       your software character generator, but all of them complicate your
  9220.       source code and ultimately slow it down.
  9221.  
  9222.  
  9223.  Implementing a Software Character Generator
  9224.  
  9225.  
  9226.       All software character generator examples in this chapter require that
  9227.       you specify the x- and y-coordinates of the pixel in the upper left
  9228.       corner of the displayed character matrix. Each routine detects the
  9229.       special case where the character matrix is byte-aligned in the video
  9230.       buffer, but the routines do not validate pixel coordinates or perform
  9231.       any clipping. All the routines except DisplayChar10() update pixels in
  9232.       the video buffer by replacing their values. To perform a bitwise AND,
  9233.       OR, or XOR operation, you must modify the routines (see Chapter 5).
  9234.  
  9235.  
  9236.  CGA
  9237.  
  9238.       In 640-by-200 2-color mode on the CGA, the software character
  9239.       generator applies the bit patterns in the character definition table
  9240.       directly to the pixels in the video buffer (see Listing 9-2). When
  9241.       the character is byte-aligned in the video buffer, the routine copies
  9242.       pixel values directly from the character definition table. Otherwise,
  9243.       for each row of eight pixels in the character, a rotated 16-bit mask
  9244.       is used to zero the proper eight pixels in the buffer. Then the pixels
  9245.       from the character definition table are rotated into position and
  9246.       stored in the buffer using a bitwise OR operation.
  9247.  
  9248.  
  9249.  ───────────────────────────────────────────────────────────────────────────
  9250.  
  9251.       Listing 9-2.  A software character generator for 640-by-200 2-color
  9252.       mode.
  9253.  
  9254.  ───────────────────────────────────────────────────────────────────────────
  9255.  
  9256.  
  9257.       The routine for 320-by-200 4-color mode in Listing 9-3 is more
  9258.       complicated because each bit in the character definition must be
  9259.       expanded into the appropriate 2-bit pixel value. A 0 bit in the
  9260.       character definition table becomes a 2-bit background pixel value; a
  9261.       1 bit in the table is expanded into a 2-bit foreground pixel value.
  9262.       Thus, each byte in the table is transformed into a word of pixels.
  9263.  
  9264.  
  9265.  ───────────────────────────────────────────────────────────────────────────
  9266.  
  9267.       Listing 9-3.  A software character generator for 640-by-200
  9268.       4-color mode.
  9269.  
  9270.  ───────────────────────────────────────────────────────────────────────────
  9271.  
  9272.  
  9273.       In Listing 9-3, when the character is byte-aligned in the video
  9274.       buffer, the routine moves the 16-bit word of pixels directly into the
  9275.       buffer. A character that is not byte-aligned spans three bytes in the
  9276.       buffer. In this case, the routine must rotate the eight pixels in each
  9277.       row of the character into position. Then the first two bytes of the
  9278.       character in the buffer are masked and updated, followed by the third
  9279.       (rightmost) byte of the character.
  9280.  
  9281.  
  9282.  HGC and HGC+
  9283.  
  9284.       A routine for the 720-by-348 monochrome graphics mode on the HGC and
  9285.       the HGC+ can use the same bit-masking technique that the CGA 640-by-
  9286.       200 2-color routine uses. You could convert DisplayChar06() into a
  9287.       Hercules-compatible routine by revising the call to PixelAddr06() and
  9288.       by changing video buffer addressing to accommodate the different
  9289.       buffer interleaves on the two adapters.
  9290.  
  9291.       It is worthwhile, however, to exploit the HGC's 720-pixel horizontal
  9292.       resolution by displaying characters in a matrix that is 9 pixels wide,
  9293.       so that each row on the screen contains 80 evenly spaced characters.
  9294.       The routine in Listing 9-4 does this by appending a ninth bit to each
  9295.       8-bit pattern it reads from the character definition table. The
  9296.       ninth bit is 0 except for box-drawing characters (ASCII 0C0-0DFH).
  9297.       For these characters, the ninth bit is a copy of the rightmost
  9298.       bit in the bit pattern. (This mimics the function of the hardware
  9299.       character generator in alphanumeric modes. See Chapter 10.)
  9300.  
  9301.  
  9302.  ───────────────────────────────────────────────────────────────────────────
  9303.  
  9304.       Listing 9-4.  A software character generator for Hercules monochrome
  9305.       graphics mode.
  9306.  
  9307.  ───────────────────────────────────────────────────────────────────────────
  9308.  
  9309.  
  9310.         ╔═══╗     Note how the CGA and Hercules routines use interrupt
  9311.         ║ T ║     vector 43H to point to the start of the current character
  9312.         ║ I ║     definition table. This is the interrupt vector the EGA and
  9313.         ║ P ║     VGA ROM BIOS uses for this purpose. Also, the routines
  9314.         ╚═══╝     determine the size of the displayed character matrix by
  9315.                   inspecting the variables POINTS (0040:0085) and CRT_COLS
  9316.                   (0040:004A) in the BIOS Video Display Data Area. If you
  9317.                   are not using an EGA, MCGA, or VGA, the BIOS won't keep
  9318.                   the interrupt vector and POINTS up to date; in this case,
  9319.                   your program should either update these values explicitly
  9320.                   or maintain equivalent values elsewhere.
  9321.  
  9322.  
  9323.  MCGA
  9324.  
  9325.       In 640-by-480 2-color mode on the MCGA, pixels are stored eight to a
  9326.       byte, so you can adapt the 640-by-200 2-color character generator for
  9327.       use in this mode by modifying its video buffer addressing. A character
  9328.       generator for 320-by-200 256-color mode is a little different, because
  9329.       each bit in the character definition table expands into a byte in the
  9330.       video buffer (see Listing 9-5).
  9331.  
  9332.  
  9333.  ───────────────────────────────────────────────────────────────────────────
  9334.  
  9335.       Listing 9-5.  A character generator for MCGA and VGA 320-by-200 256-
  9336.       color mode.
  9337.  
  9338.  ───────────────────────────────────────────────────────────────────────────
  9339.  
  9340.  
  9341.  EGA and VGA
  9342.  
  9343.       The routine for the EGA and VGA in Listing 9-6 uses the Graphics
  9344.       Controller to update pixels in the video buffer. The routine is
  9345.       similar in some ways to the routine for the CGA's 640-by-200 2-color
  9346.       mode, because each byte of the video buffer represents eight pixels.
  9347.       Of course, the code is complicated by the need to program the Graphics
  9348.       Controller to handle the foreground and background pixel values.
  9349.  
  9350.       The routine writes each row of pixels in the character by latching the
  9351.       bit planes, updating the foreground pixels, updating the background
  9352.       pixels, and then writing the latches back to the bit planes. The
  9353.       Graphics Controller cannot conveniently update both foreground and
  9354.       background pixels at the same time, so the routine must perform these
  9355.       operations separately.
  9356.  
  9357.  
  9358.  ───────────────────────────────────────────────────────────────────────────
  9359.  
  9360.       Listing 9-6.  A software character generator for native EGA and VGA
  9361.       graphics modes.
  9362.  
  9363.  ───────────────────────────────────────────────────────────────────────────
  9364.  
  9365.  
  9366.  InColor Card
  9367.  
  9368.       The technique for storing characters in the video buffer on the
  9369.       Hercules InColor Card, shown in Listing 9-7, is different from that
  9370.       on the EGA or VGA because you can use the InColor Card's Read/Write
  9371.       Color register (1AH) and write mode 0 to update both foreground and
  9372.       background pixel values in one operation. Thus, the actual process of
  9373.       updating the bit planes collapses into relatively few machine
  9374.       instructions.
  9375.  
  9376.       However, the InColor Card cannot perform pixel AND, OR, or XOR
  9377.       operations in hardware. To do this, you must write additional
  9378.       subroutines that use the Plane Mask register to map logical operations
  9379.       onto the bit planes (see Chapter 5).
  9380.  
  9381.  
  9382.  ───────────────────────────────────────────────────────────────────────────
  9383.  
  9384.       Listing 9-7.  A software character generator for Hercules InColor
  9385.       graphics modes.
  9386.  
  9387.  ───────────────────────────────────────────────────────────────────────────
  9388.  
  9389.  
  9390.  
  9391.                     10  Alphanumeric Character Sets
  9392.  
  9393.  
  9394.                       Character Definition Tables
  9395.                Alphanumeric Character Definitions in ROM
  9396.                Alphanumeric Character Definitions in RAM
  9397.  
  9398.                    Updating Character Generator RAM
  9399.                EGA and VGA ■ HGC+ ■ InColor Card ■ MCGA
  9400.  
  9401.                     Using RAM-based Character Sets
  9402.                          ASCII Character Sets
  9403.                         Extended Character Sets
  9404.          Compatibility Problems with Extended Character Codes
  9405.  
  9406.                 Changing the Displayed Character Matrix
  9407.                EGA ■ VGA ■ MCGA ■ HGC+ and InColor Card
  9408.  
  9409.                 Graphics Windows in Alphanumeric Modes
  9410.               HGC+ and InColor Card ■ EGA and VGA ■ MCGA
  9411.  
  9412.  
  9413.  
  9414.       One of the easiest ways to speed up a program's video interface is to
  9415.       use an alphanumeric video mode. To gain this speed advantage, however,
  9416.       you must accept the limitations of the video subsystem's alphanumeric
  9417.       character generator.
  9418.  
  9419.       On the original MDA and CGA, the only characters you could display in
  9420.       alphanumeric mode were those defined in a table located in ROM on the
  9421.       adapter. The hardware character generator on these adapters was not
  9422.       designed to use a character definition table located in RAM. However,
  9423.       the EGA, the MCGA, the VGA, the HGC+, and the InColor Card can all
  9424.       display alphanumeric characters defined in RAM.
  9425.  
  9426.       This chapter shows you how to exploit RAM-based alphanumeric character
  9427.       sets on these subsystems. It describes how to format character
  9428.       definition tables and where to place them in RAM to be used in
  9429.       alphanumeric modes. It discusses the pros and cons of using extended
  9430.       character sets that contain more than the usual 256 ASCII characters.
  9431.       The chapter concludes with techniques for displaying true graphics
  9432.       images in an alphanumeric video mode.
  9433.  
  9434.  
  9435.  Character Definition Tables
  9436.  
  9437.  
  9438.       Like the software graphics character generators described in Chapter
  9439.       9, the hardware alphanumeric character generator in all IBM video
  9440.       subsystems references a memory-resident character definition table
  9441.       that contains bit-pattern representations of the pixels in each
  9442.       displayable character. Unlike the graphics-mode tables, whose location
  9443.       in memory may vary, the alphanumeric tables must lie in a
  9444.       predesignated portion of memory to allow the alphanumeric character
  9445.       generator to access them.
  9446.  
  9447.  
  9448.  Alphanumeric Character Definitions in ROM
  9449.  
  9450.       The MDA, the CGA, and the Hercules adapters have an alphanumeric
  9451.       character definition table located in ROM that is not within the CPU's
  9452.       address space. Only the character generator hardware can access it.
  9453.       The character set that these adapters display in alphanumeric modes is
  9454.       therefore not controlled by software.
  9455.  
  9456.       On the EGA, the MCGA, and the VGA, the alphanumeric character
  9457.       generator uses a table of bit patterns stored in RAM rather than in
  9458.       dedicated ROM. The video ROM BIOS contains tables with which it
  9459.       initializes character generator RAM whenever it establishes an
  9460.       alphanumeric video mode. Because these video subsystems can set up
  9461.       alphanumeric modes with different vertical resolutions, the sizes of
  9462.       the default alphanumeric characters vary (see Figure 10-1).
  9463.  
  9464.  
  9465.       200-Line Modes
  9466.       The CGA's 200-line alphanumeric modes use an 8-by-8 character matrix.
  9467.       In  80-by-25 alphanumeric mode, the screen is thus 640 pixels wide; in
  9468.       40-by-25 alphanumeric mode, the screen is 320 pixels wide. Although
  9469.       the CGA uses the same character set and font in its alphanumeric and
  9470.       graphics modes, the character definitions for alphanumeric modes
  9471.       reside in dedicated ROM, accessible only to the hardware character
  9472.       generator. (As described in Chapter 9, the graphics-mode definitions
  9473.       are found in the ROM BIOS and in a table in RAM addressed by the
  9474.       vector for interrupt 1FH.)
  9475.  
  9476.  
  9477. ╓┌────────────┌──────────────────────┌───────────────────────────────────────╖
  9478.                                      Character Matrix
  9479.  Adapter      Video Mode             (width by height in pixels)
  9480.  ──────────────────────────────────────────────────────────────────────────
  9481.  MDA, HGC     Monochrome             9-by-14
  9482.  CGA          40-by-25 16-color      8-by-8
  9483.               80-by-25 16-color      8-by-8
  9484.  EGA          80-by-25 16-color      8-by-8  (200-line resolution)
  9485.                                      8-by-14 (350-line resolution)
  9486.               80-by-25 monochrome    9-by-14
  9487.  MCGA         40-by-25 16-color      8-by-16
  9488.               80-by-25 16-color      8-by-16
  9489.  VGA          40-by-25 16-color      8-by-8  (200-line resolution)
  9490.                                      8-by-14 (350-line resolution)
  9491.                                      9-by-16 (400-line resolution)
  9492.               80-by-25 16-color      8-by-8  (200-line resolution)
  9493.                                      8-by-14 (350-line resolution)
  9494.                                      Character Matrix
  9495.  Adapter      Video Mode             (width by height in pixels)
  9496.                                     8-by-14 (350-line resolution)
  9497.                                      9-by-16 (400-line resolution)
  9498.               80-by-25 monochrome    9-by-14 (350-line resolution)
  9499.                                      9-by-16 (400-line resolution)
  9500.  HGC+         80-by-25 monochrome    9-by-14
  9501.  InColor Card 80-by-25 16-color      9-by-14
  9502.  
  9503.       Figure 10-1.  The default alphanumeric character matrix in various
  9504.       video modes.
  9505.  
  9506.  
  9507.         ╔═══╗     The CGA comes with two tables of 8-by-8 characters in the
  9508.         ║ T ║     alphanumeric character generator's ROM. A jumper on the
  9509.         ║ I ║     adapter selects which table the alphanumeric character
  9510.         ║ P ║     generator uses. By default, jumper P3 on the CGA is not
  9511.         ╚═══╝     connected, and the usual "double-dot" 8-by-8 characters
  9512.                   are displayed. If you connect jumper P3, the CGA's
  9513.                   alphanumeric character generator uses a "single-dot" font
  9514.                   (see Figure 10-2). The "single-dot" characters appear
  9515.                   sharper on some monitors because their vertical strokes
  9516.                   are only one pixel wide.
  9517.  
  9518.  
  9519.               ╔══════════════════════════════════════════╗
  9520.               ║                                          ║
  9521.               ║    Figure 10-2 is found on page 299      ║
  9522.               ║    in the printed version of the book.   ║
  9523.               ║                                          ║
  9524.               ╚══════════════════════════════════════════╝
  9525.  
  9526.       Figure 10-2.  Double-dot and single-dot alphanumeric character sets on
  9527.       the CGA.
  9528.  
  9529.  
  9530.       350-Line Modes
  9531.       In 350-line alphanumeric modes on the MDA and the Hercules adapters,
  9532.       the characters are defined in an 8-by-14 matrix. Again, the character
  9533.       definition table resides in ROM outside the CPU address space that is
  9534.       dedicated to the hardware character generator. Because the horizontal
  9535.       resolution is 720 pixels on these adapters, each 8-by-14 character
  9536.       actually is displayed in a matrix 9 pixels wide. Thus, each row on the
  9537.       screen contains 720/9, or 80, characters.
  9538.  
  9539.       If characters are defined in ROM in an 8-by-14 matrix but displayed in
  9540.       a 9-by-14 matrix, where does the extra pixel come from? The hardware
  9541.       character generator in the MDA, the Hercules cards, the EGA, and the
  9542.       VGA (in monochrome mode) adds an extra pixel to the right of each row
  9543.       of eight pixels in each character. For the block graphics characters
  9544.       (ASCII 0C0H through 0DFH), the value of the rightmost pixel is
  9545.       replicated in each row. For all remaining character codes, the extra
  9546.       pixel is displayed with the character's background attribute.
  9547.  
  9548.       Since the ninth (rightmost) pixel in block graphics characters is a
  9549.       copy of the eighth, these characters abut and can be used to draw
  9550.       horizontal lines. All other displayable characters are separated from
  9551.       each other by that ninth pixel. The resulting display appears less
  9552.       crowded than it would be without the extra space.
  9553.  
  9554.         ╔═══╗     With the EGA and the VGA, you can control whether or not
  9555.         ║ T ║     the alphanumeric character generator replicates the eighth
  9556.         ║ I ║     pixel of block graphics characters. When bit 2 of the
  9557.         ║ P ║     Attribute Controller's Mode Control register (10H) is set
  9558.         ╚═══╝     to 1, the ninth pixel is the same as the eighth. When bit
  9559.                   2 is set to 0, the ninth pixel is a background pixel.
  9560.  
  9561.  
  9562.       400-Line Modes
  9563.       The default alphanumeric modes of both the MCGA and the VGA have 400-
  9564.       line vertical resolution. The characters used in these modes are
  9565.       defined in an 8-by-16 matrix. On the VGA, the 8-by-16 characters are
  9566.       displayed in a 9-by-16 matrix, just as on an MDA or an EGA with a
  9567.       monochrome display.
  9568.  
  9569.  
  9570.  Alphanumeric Character Definitions in RAM
  9571.  
  9572.       The EGA, the VGA, the MCGA, the HGC+, and the InColor Card all have
  9573.       alphanumeric character generators that use character definition tables
  9574.       located in predesignated areas of RAM. In all these subsystems, this
  9575.       RAM lies within the address space of the video buffer. If you know how
  9576.       character generator RAM is mapped, you can write programs that read or
  9577.       update the alphanumeric character definition tables and thereby change
  9578.       the displayed alphanumeric character set.
  9579.  
  9580.  
  9581.       EGA and VGA
  9582.       In alphanumeric modes on the EGA and the VGA, the video buffer is
  9583.       organized as four parallel memory maps, just as in graphics modes. In
  9584.       alphanumeric modes, however, only maps 0 and 1 contain displayable
  9585.       data (see Figure 10-3). Even-numbered bytes (character codes) in the
  9586.       CPU's address space are located in map 0, and odd-numbered bytes
  9587.       (attribute bytes) are located in map 1. This mapping is invisible to
  9588.       the CPU; the CRTC internally translates odd addresses to offsets into
  9589.       map 1 and even addresses into references to map 0.
  9590.  
  9591.  
  9592.                             ┌───────────────────────┐
  9593.                             │                       │
  9594.                             │                       │
  9595.                         ┌───┴───────────────────┐   │
  9596.                         │ Character definitions │   │
  9597.                         │                       │   │
  9598.                     ┌───┴───────────────────┐   │   │
  9599.                     │    Attribute bytes    │   │   │
  9600.                     │                       │   │   │
  9601.                 ┌───┴───────────────────┐   │   │   │
  9602.                 │    Character codes    │   │   │   │
  9603.                 │                       │   │   ├───┘ Map 3
  9604.                 │                       │   │   │
  9605.                 │                       │   │   │
  9606.                 │                       │   ├───┘ Map 2
  9607.                 │                       │   │
  9608.                 │                       │   │
  9609.                 │                       ├───┘ Map 1
  9610.       B800:0000 │                       │
  9611.          or     │                       │
  9612.       B000:0000 └───────────────────────┘ Map 0
  9613.  
  9614.       Figure 10-3.  Video RAM layout in EGA and VGA alphanumeric modes.
  9615.  
  9616.  
  9617.       The alphanumeric character generator uses a set of 256-character
  9618.       tables stored in map 2. The EGA supports four such tables (see Figure
  9619.       10-4); the VGA supports eight (see Figure 10-5). Each table consists
  9620.       of 256 32-byte bit patterns, so the maximum height of the character
  9621.       matrix is 32 scan lines. When the displayed character matrix contains
  9622.       fewer than 32 lines, the character generator ignores the extra bytes
  9623.       in each character definition.
  9624.  
  9625.       On the EGA, each of the four alphanumeric character definition tables
  9626.       starts at a 16 KB boundary. Since only 8 KB (256 characters * 32 bytes
  9627.       per character) are used, 8 KB of unused RAM follows each table. On the
  9628.       VGA, these unused areas in map 2 can contain additional character
  9629.       definitions. Of course, in writing an application that must run on
  9630.       both the EGA and the VGA, you should avoid using these extra tables
  9631.       because the EGA does not support them.
  9632.  
  9633.         ╔═══╗     On the IBM EGA, which may be equipped with less than 256
  9634.         ║ T ║     KB of video RAM, the number of character definition tables
  9635.         ║ I ║     you can load into video RAM depends on the amount of RAM
  9636.         ║ P ║     installed on the card. For example, without IBM's Graphics
  9637.         ╚═══╝     Memory Expansion Card, an IBM EGA has only 64 KB of video
  9638.                   RAM, so each video memory map in alphanumeric modes
  9639.                   contains only 16 KB, and only one character definition
  9640.                   table will fit in map 2.
  9641.  
  9642.  
  9643.                     ┌─────────────────────────────┐
  9644.                     │          (Unused)           │
  9645.                     ├─────────────────────────────┤
  9646.                     │  256 character definitions  │
  9647.               C000H ├─────────────────────────────┤
  9648.                     │          (Unused)           │
  9649.                     ├─────────────────────────────┤
  9650.                     │  256 character definitions  │
  9651.       Offset  8000H ├─────────────────────────────┤
  9652.                     │          (Unused)           │
  9653.                     ├─────────────────────────────┤
  9654.                     │  256 character definitions  │
  9655.               4000H ├─────────────────────────────┤
  9656.                     │          (Unused)           │
  9657.                     ├─────────────────────────────┤
  9658.                     │  256 character definitions  │
  9659.               0000H └─────────────────────────────┘
  9660.  
  9661.       Figure 10-4.  Character generator RAM in EGA video memory map 2.
  9662.  
  9663.  
  9664.                     ┌─────────────────────────────┐
  9665.                     │  256 character definitions  │
  9666.               E000H ├─────────────────────────────┤
  9667.                     │  256 character definitions  │
  9668.               C000H ├─────────────────────────────┤
  9669.                     │  256 character definitions  │
  9670.               A000H ├─────────────────────────────┤
  9671.                     │  256 character definitions  │
  9672.       Offset  8000H ├─────────────────────────────┤
  9673.                     │  256 character definitions  │
  9674.               6000H ├─────────────────────────────┤
  9675.                     │  256 character definitions  │
  9676.               4000H ├─────────────────────────────┤
  9677.                     │  256 character definitions  │
  9678.               2000H ├─────────────────────────────┤
  9679.                     │  256 character definitions  │
  9680.               0000H └─────────────────────────────┘
  9681.  
  9682.       Figure 10-5.  Character generator RAM in VGA video memory map 2.
  9683.  
  9684.  
  9685.       HGC+
  9686.       Character generator RAM on the HGC+ starts at B000:4000 and extends to
  9687.       the end of available video RAM at B000:FFFF (see Figure 10-6). You can
  9688.       fill this entire 48 KB area with character definitions. Each character
  9689.       definition is 16 bytes long, so a table that defines 256 characters
  9690.       occupies 4 KB. Thus, this RAM can hold 3072 character definitions.
  9691.  
  9692.  
  9693.       B000:FFFF ┌────────────────────────────────┐
  9694.                 │                                │
  9695.                 │      Character definitions     │
  9696.                 │                                │
  9697.                 │                                │
  9698.       B000:4000 ├────────────────────────────────┤
  9699.                 │ Character codes and attributes │
  9700.       B000:0000 └────────────────────────────────┘
  9701.  
  9702.       Figure 10-6.  Video RAM layout in alphanumeric modes on the HGC+.
  9703.  
  9704.  
  9705.         ╔═══╗     If the HGC+ is configured so that video RAM above
  9706.         ║ T ║     B000:8000 is masked out of the CPU address space (that is,
  9707.         ║ I ║     bit 1 of the Configuration Switch at 3BFH is set to 0),
  9708.         ║ P ║     then only the 16 KB of RAM between B000:4000 and B000:7FFF
  9709.         ╚═══╝     can be used for character definitions.
  9710.  
  9711.  
  9712.       InColor Card
  9713.       Character generator RAM occupies the same range of addresses on the
  9714.       InColor Card as on the HGC+, that is, B000:4000 through B000:FFFF.
  9715.       Also, each InColor character definition is 16 bytes long. Unlike the
  9716.       HGC+, however, the 16-color InColor Card uses all four bit planes in
  9717.       this range of addresses for character definitions (see Figure 10-7).
  9718.  
  9719.       Because of this, you can control the value of each pixel in each
  9720.       character you define. You can also program the InColor Card so that
  9721.       different bit planes define different characters; when the characters
  9722.       are displayed, their attribute bytes select which bit plane is used.
  9723.       By loading each of the four bit planes with different character
  9724.       definitions, you can maintain as many as 12,288 (3072 x 4) character
  9725.       definitions in RAM. Or, to preserve compatibility with the HGC+, you
  9726.       can load all four bit planes with the same bit patterns.
  9727.  
  9728.         ╔═══╗     In using both the EGA and the Hercules cards, be careful
  9729.         ║ T ║     in changing from an alphanumeric mode that uses a RAM-
  9730.         ║ I ║     based character definition table to a graphics mode. The
  9731.         ║ P ║     same RAM that contains pixel data in graphics modes is
  9732.         ╚═══╝     used to store character definitions in alphanumeric modes.
  9733.                   You can corrupt or erase your character definition tables
  9734.                   by updating the video buffer in a graphics mode and then
  9735.                   returning to an alphanumeric mode.
  9736.  
  9737.  
  9738.       MCGA
  9739.       Unlike the EGA and VGA, the MCGA has no parallel memory maps in which
  9740.       to store character definitions. Instead, alphanumeric character
  9741.       definitions are maintained in the 32 KB of video RAM between A000:0000
  9742.       and A000:7FFF. You can store as many as four 8 KB character definition
  9743.       tables at A000:0000, A000:2000, A000:4000, and A000:6000 (see Figure
  9744.       10-8).
  9745.  
  9746.  
  9747.                             ┌───────────────────────┐
  9748.                             │                       │
  9749.                             │ Character definitions │
  9750.                         ┌───┴───────────────────┐   │
  9751.                         │                       │   │
  9752.                         │ Character definitions │   │
  9753.                     ┌───┴───────────────────┐   ├───┤
  9754.                     │                       │   │   │
  9755.                     │ Character definitions │   │   │
  9756.                 ┌───┴───────────────────┐   ├───┤   │
  9757.                 │                       │   │   ├───┘ Bit plane 3
  9758.                 │ Character definitions │   │   │
  9759.                 │                       ├───┤   │
  9760.                 │                       │   ├───┘ Bit plane 2
  9761.                 │                       │   │
  9762.       B800:4000 ├───────────────────────│   │
  9763.                 │                       ├───┘ Bit plane 1
  9764.                 │    Character codes    │
  9765.                 │    and attributes     │
  9766.       B000:0000 └───────────────────────┘ Bit plane 0
  9767.  
  9768.       Figure 10-7.  Video RAM layout in alphanumeric modes on the Hercules
  9769.       InColor Card. Character definitions start at B000:4000 in all four bit
  9770.       planes.
  9771.  
  9772.  
  9773.                  ┌────────────────────────────────┐
  9774.                  │                                │
  9775.                  │ Character codes and attributes │
  9776.                  │                                │
  9777.         A000:8000│                                │
  9778.       (B800:0000)├────────────────────────────────┤
  9779.                  │    256 character definitions   │ 30H
  9780.         A000:6000├────────────────────────────────┤
  9781.                  │    256 character definitions   │ 20H
  9782.         A000:4000├────────────────────────────────┤
  9783.                  │    256 character definitions   │ 10H
  9784.         A000:2000├────────────────────────────────┤
  9785.                  │    256 character definitions   │ 00H
  9786.         A000:0000└────────────────────────────────┘ ▒▒▒
  9787.                                                      │
  9788.                             Value in Character Font Pointer register
  9789.  
  9790.       Figure 10-8.  Layout of video RAM in MCGA alphanumeric modes.
  9791.  
  9792.  
  9793.       The format of the MCGA's character definition tables is very different
  9794.       from that of any other tables discussed thus far. Each 8 KB table is
  9795.       divided into 16 512-byte lists of character codes and bit patterns
  9796.       (see Figure 10-9). Each list corresponds to one scan line of the
  9797.       characters being defined; the first list represents the bit patterns
  9798.       in the topmost scan line of each character, the second list
  9799.       corresponds to the second scan line, and so on (see Figure 10-10).
  9800.       Since there are 16 lists, the maximum height of a character is 16
  9801.       lines.
  9802.  
  9803.  
  9804.             0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 0123456789ABCDEF
  9805.  A000:0400 00 00 01 7E 02 7E 03 00 04 00 05 00 06 00 07 00 ...~.~..........
  9806.  A000:0410 08 FF 09 00 0A FF 0B IE 0C 3C 0D 3F 0E 7F 0F 00 .........<.?....
  9807.  A000:0420 10 C0 11 06 12 18 13 66 14 7F 15 C6 16 00 17 18 .......f........
  9808.  A000:0430 18 18 19 18 1A 00 1B 00 1C 00 1D 00 1E 00 1F 00 ................
  9809.  A000:0440 20 00 21 18 22 66 23 00 24 7C 25 00 26 38 27 30  .!."F#.$|%.&8'0
  9810.  A000:0450 28 0C 29 30 2A 00 2B 00 2C 00 2D 00 2E 00 2F 00 (.)0*.+.,.-.../.
  9811.  A000:0460 30 7C 31 18 32 7C 33 7C 34 0C 35 FE 36 38 37 FE 0|1.2|3|4.5.687.
  9812.  A000:0470 38 7C 39 7C 3A 00 3B 00 3C 00 3D 00 3E 00 3F 7C 8|9|:.;.<.=.>.?|
  9813.  A000:0480 40 00 41 10 42 FC 43 3C 44 F8 45 FE 46 FE 47 3C @.A.B.C<D.E.F.G<
  9814.  A000:0490 48 C6 49 3C 4A 1E 4B E6 4C F0 4D C6 4E C6 4F 38 H.I<J.K.L.M.N.O8
  9815.  A000:04A0 50 FC 51 7C 52 FC 53 7C 54 7E 55 C6 56 C6 57 C6 P.Q|R.S|T~U.V.W.
  9816.  A000:04B0 58 C6 59 66 5A FE 5B 3C 5C 00 5D 3C 5E 6C 5F 00 X.YfZ.{<\.}<^1_.
  9817.  A000:04C0 60 18 61 00 62 E0 63 00 64 1C 65 00 66 38 67 00 `.a.b.c.d.e.f8g.
  9818.  A000:04D0 68 E0 69 18 6A 06 6B E0 6C 38 6D 00 6E 00 6F 00 h.i.j.k.l9m.n.o.
  9819.  A000:04E0 70 00 71 00 72 00 73 00 74 10 75 00 76 00 77 00 p.q.r.s.t.u.v.w.
  9820.  A000:04F0 78 00 79 00 7A 00 7B 0E 7C 18 7D 70 7E 76 7F 00 x.y.z.{.|.}p~v..
  9821.  A000:0500 80 3C 81 CC 82 18 83 38 84 CC 85 30 86 6C 87 00 .<.....8...0.1..
  9822.  A000:0510 88 38 89 CC 8A 30 8B 66 8C 3C 8D 30 8E C6 8F 38 .8...0.f.<.0...8
  9823.  A000:0520 90 60 91 00 92 3E 93 38 94 C6 95 30 96 78 97 30 .`...>.8...0.X.0
  9824.  A000:0530 98 C6 99 C6 9A C6 9B 18 9C 6C 9D 66 9E CC 9F 1B .........l.f....
  9825.  A000:0540 A0 30 A1 18 A2 30 A3 30 A4 76 A5 00 A6 6C A7 6C .0...0.0.v...l.l
  9826.  A000:0550 A8 30 A9 00 AA 00 AB C0 AC C0 AD 18 AE 00 AF 00 .0..............
  9827.  
  9828.       Figure 10-9.  One of 16 lists of character codes and bit patterns in
  9829.       MCGA character generator RAM. This table defines the bit patterns for
  9830.       the third scan line of each character. Character codes are in the
  9831.       even-numbered bytes. The odd-numbered bytes contain the corresponding
  9832.       bit patterns.
  9833.  
  9834.  
  9835.             0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 0123456789ABCDEF
  9836.  A000:0000 00 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 ................
  9837.  A000:0010 08 FF 09 00 0A FF 0B 00 0C 00 0D 00 0E 00 0F 00 ................
  9838.  
  9839.  A000:0200 00 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 ................
  9840.  A000:0210 08 FF 09 00 0A FF 0B 00 0C 00 0D 00 0E 00 0F 00 ................
  9841.  
  9842.  A000:0400 00 00 01 7E 02 7E 03 00 04 00 05 00 06 00 07 00 ...~ ~..........
  9843.  A000:0410 08 FF 09 00 0A FF 0B 1E 0C 3C 0D 3F 0E 7F 0F 00 .........<.?....
  9844.  
  9845.  A000:0600 00 00 01 81 02 FF 03 00 04 00 05 18 06 18 07 00 ................
  9846.  A000:0610 08 FF 09 00 0A FF 0B 0E 0C 66 0D 33 0E 63 0F 18 ................
  9847.  
  9848.  A000:0800 00 00 01 A5 02 DB 03 6C 04 10 05 3C 06 3C 07 00 .......l...<.<..
  9849.  A000:0810 08 FF 09 00 0A FF 0B 1A 0C 66 0D 3F 0E 7F 0F 18 .........f.?....
  9850.  
  9851.  A000:0A00 00 00 01 81 02 FF 03 FE 04 38 05 3C 06 7E 07 00 .........8.<.~..
  9852.  A000:0A10 08 FF 09 3C 0A C3 0B 32 0C 66 0D 30 0E 63 0F DB ...<...2.f.0.c..
  9853.  
  9854.  A000:0C00 00 00 01 81 02 FF 03 FE 04 7C 05 E7 06 FF 07 18 .........|......
  9855.  A000:0C10 08 E7 09 66 0A 99 0B 78 0C 66 0D 30 0E 63 0F 3C ...f...x.f.0.c.<
  9856.  
  9857.  A000:0E00 00 00 01 BD 02 C3 03 FE 04 FE 05 E7 06 FF 07 3C ...............<
  9858.  A000:0E10 08 C3 09 42 0A BD 0B CC 0C 3C 0D 30 0E 63 0F E7 ...B.....<.0.c..
  9859.  
  9860.  A000:1000 00 00 01 99 02 E7 03 FE 04 7C 05 E7 06 7E 07 3C .........|...~.<
  9861.  A000:1010 08 C3 09 42 0A BD 0B CC 0C 18 0D 30 OE 63 0F 3C ...B.......0.c.<
  9862.  
  9863.  A000:1200 00 00 01 81 02 FF 03 7C 04 38 05 18 06 18 07 18 .......|.8......
  9864.  A000:1210 08 E7 09 66 0A 99 0B CC 0C 7E 0D 70 0E 67 0F DB ...f.....~.p.g..
  9865.  
  9866.  A000:1400 00 00 01 81 02 FF 03 38 04 10 05 18 06 18 07 00 .......8........
  9867.  A000:1410 08 FF 09 3C 0A C3 0B CC 0C 18 0D F0 0E E7 0F 18 ...<............
  9868.  
  9869.  A000:1600 00 00 01 7E 02 7E 03 10 04 00 05 3C 06 3C 07 00 ...~.~.....<.<..
  9870.  A000:1610 08 FF 09 00 0A FF 0B 78 0C 18 0D E0 0E E6 0F 18 .......x........
  9871.  
  9872.       Figure 10-10.  MCGA character definitions for the first 12 scan lines
  9873.       of the first 16 characters. The top scan line for each character is
  9874.       defined starting at A000:0000, the second scan line starting at
  9875.       A000:2000, and so on. (Only the first 32 bytes of each 512-byte list
  9876.       are shown.)
  9877.  
  9878.  
  9879.  Updating Character Generator RAM
  9880.  
  9881.  
  9882.       After you create a table of character definitions (discussed in
  9883.       Chapter 9), you must make the table accessible to the hardware
  9884.       character generator by properly locating it in the video buffer. One
  9885.       way to do this is to create the table in RAM (outside the video
  9886.       buffer) and then copy it to character generator RAM. You can also read
  9887.       the table directly from a disk file into character generator RAM.
  9888.       Either technique works on any of the video subsystems discussed here.
  9889.  
  9890.  
  9891.  EGA and VGA
  9892.  
  9893.       To copy a character definition table into video memory map 2, you must
  9894.       program both the Sequencer's Memory Mode register and its Map Mask
  9895.       register, as well as the Graphics Controller's Mode and Miscellaneous
  9896.       registers, to make memory map 2 directly addressable. You can then
  9897.       copy character definitions to any of the available table locations in
  9898.       map 2. After you update map 2, restore the Sequencer and Graphics
  9899.       Controller registers to values appropriate for the alphanumeric video
  9900.       mode you are using.
  9901.  
  9902.       Listing 10-1a demonstrates how the Sequencer and Graphics Controller
  9903.       are programmed on both the EGA and the VGA to make character generator
  9904.       RAM in map 2 accessible. Listing 10-1b is the converse routine; it
  9905.       restores the Sequencer and Graphics Controller registers to their
  9906.       alphanumeric mode default values. You can use the routines in Listings
  9907.       10-1a and 10-1b in a program that copies character definitions
  9908.       directly from a file into character generator RAM (as shown in
  9909.       Listings 10-2a and 10-2b).
  9910.  
  9911.  
  9912.  ───────────────────────────────────────────────────────────────────────────
  9913.  
  9914.       Listing 10-1a.  Using character generator RAM on the EGA and
  9915.       VGA.
  9916.  
  9917.  ───────────────────────────────────────────────────────────────────────────
  9918.  
  9919.  
  9920.  ──────────────────────────────────────────────────────────────────────────
  9921.  
  9922.       Listing 10-1b.  Restoring character generator RAM on the EGA and
  9923.       VGA.
  9924.  
  9925.  ───────────────────────────────────────────────────────────────────────────
  9926.  
  9927.  
  9928.  ───────────────────────────────────────────────────────────────────────────
  9929.  
  9930.       Listing 10-2a.  Loading character definitions on an EGA or
  9931.       VGA.
  9932.  
  9933.  ───────────────────────────────────────────────────────────────────────────
  9934.  
  9935.  
  9936.  ───────────────────────────────────────────────────────────────────────────
  9937.  
  9938.       Listing 10-2b.  Calling CGenRead1 from a C program.
  9939.  
  9940.  ───────────────────────────────────────────────────────────────────────────
  9941.  
  9942.  
  9943.       A faster and more portable way to load character definitions into RAM
  9944.       is to use INT 10H function 11H with AL = 0 (see Listings 10-3a and 10-
  9945.       3b). When you use the INT 10H function, you can selectively update any
  9946.       portion of a table in map 2 by choosing appropriate values for DX (the
  9947.       character offset into the table) and CX (the number of character
  9948.       definitions to update). To use this video BIOS function, you must
  9949.       first store the character definition table in an intermediate buffer.
  9950.       This technique consumes more memory than reading character definitions
  9951.       directly from disk, but it results in faster code.
  9952.  
  9953.  
  9954.  ───────────────────────────────────────────────────────────────────────────
  9955.  
  9956.       Listing 10-3a.  Using the BIOS to load character definitions.
  9957.  
  9958.  ───────────────────────────────────────────────────────────────────────────
  9959.  
  9960.  
  9961.  ───────────────────────────────────────────────────────────────────────────
  9962.  
  9963.       Listing 10-3b.  Calling CGenRead2 from a C
  9964.       program.
  9965.  
  9966.  ───────────────────────────────────────────────────────────────────────────
  9967.  
  9968.  
  9969.       The INT 10H function 11H services can also update character generator
  9970.       RAM from the character tables in the ROM BIOS. To use one of the ROM
  9971.       BIOS character definition tables, call INT 10H function 11H with AL =
  9972.       1 (for 8-by-14 character definitions) or AL = 2 (for 8-by-8
  9973.       definitions). (See Listing 10-4.)
  9974.  
  9975.  
  9976.  ───────────────────────────────────────────────────────────────────────────
  9977.  
  9978.       Listing 10-4.  Using a ROM BIOS character definition table.
  9979.  
  9980.  ───────────────────────────────────────────────────────────────────────────
  9981.  
  9982.  
  9983.  HGC+
  9984.  
  9985.       Moving a character definition table into RAM is easier on the HGC+,
  9986.       because memory addressing is simpler. Character generator RAM is
  9987.       mapped linearly, starting at B000:4000. Since each 256-character table
  9988.       occupies 4 KB (256 x 16), subsequent 256-character tables start at
  9989.       B000:5000, B000:6000, and so on.
  9990.  
  9991.       Because HGC+ memory has no bit planes, you can access character
  9992.       generator RAM as easily as any other system RAM. You can, for example,
  9993.       use a single REP MOVSB instruction to move bit patterns into character
  9994.       generator RAM  from elsewhere in system RAM, or you can read a
  9995.       character definition table directly into RAM from a disk file. For
  9996.       example, you can modify Listing 10-2a to read a file directly into
  9997.       HGC+ character generator RAM by changing the  values of CGenRAMSeg to
  9998.       B000H, CGenStartOffset to 4000H, and CGenDefSize to 16.
  9999.  
  10000.  
  10001.  InColor Card
  10002.  
  10003.       Although the InColor Card uses all four bit planes to store character
  10004.       definitions, you can use virtually the same routine to copy bit
  10005.       patterns into its character generator RAM that you use on the HGC+.
  10006.       The only difference is that you can select which of the four bit
  10007.       planes to update. Do this by setting bits 4 through 7  of the Plane
  10008.       Mask register (18H) to write-protect one or more of the bit planes.
  10009.       For compatibility with the HGC+, set these four bits to 0 so that all
  10010.       four bit planes contain the same bit patterns.
  10011.  
  10012.  
  10013.  MCGA
  10014.  
  10015.       As on the Hercules adapters, character generator RAM on the MCGA is
  10016.       mapped linearly in the video buffer. Thus, you can update MCGA
  10017.       character definitions simply by writing the bit patterns in the
  10018.       appropriate format in the character definition tables.
  10019.  
  10020.       If you update the MCGA character definition tables directly, however,
  10021.       your program must store bit patterns and character codes in the format
  10022.       expected by the MCGA character generator. It is usually better to use
  10023.       INT 10H function 11H to copy character definitions into MCGA character
  10024.       generator RAM. This video BIOS function translates character
  10025.       definition tables from the linear format used on the EGA and VGA into
  10026.       the formatted lists used on the MCGA.
  10027.  
  10028.       The MCGA is different from the other video subsystems discussed here
  10029.       in that its alphanumeric character generator does not fetch bit
  10030.       patterns from the tables at A000:0000 as it generates characters.
  10031.       Instead, the character generator uses two internal character
  10032.       definition tables, called font pages. To display the characters from
  10033.       one of the four tables in video RAM, you must load the table into one
  10034.       of the character generator's font pages. Listing 10-5 shows how this
  10035.       is done.
  10036.  
  10037.  
  10038.  ───────────────────────────────────────────────────────────────────────────
  10039.  
  10040.       Listing 10-5.  Loading font pages on an MCGA.
  10041.  
  10042.  ───────────────────────────────────────────────────────────────────────────
  10043.  
  10044.  
  10045.       Thus, displaying a new alphanumeric character set on the MCGA is a
  10046.       two-step process. First, you store character definition tables in one
  10047.       or more of the four 8 KB blocks of video RAM reserved for this
  10048.       purpose. Then you update the character generator's font pages to
  10049.       display the characters.
  10050.  
  10051.  
  10052.  Using RAM-based Character Sets
  10053.  
  10054.  
  10055.       When you use characters defined in a RAM-based table, you must choose
  10056.       how the alphanumeric character generator is to decode the character
  10057.       codes and attributes stored in the displayed portion of the video
  10058.       buffer. Using the usual 256-character ASCII set, with 8-bit
  10059.       character codes and 8-bit attributes, is simplest. However,
  10060.       to display more than 256 different characters at once or to switch
  10061.       rapidly between character sets, you must use a wider range of
  10062.       "extended" character codes and a different set of attributes.
  10063.  
  10064.  
  10065.  ASCII Character Sets
  10066.  
  10067.       The simplest way to customize alphanumeric characters is to use 8-bit
  10068.       ASCII character codes and attributes with a RAM-based character
  10069.       definition table. Because there are only 256 ASCII character codes,
  10070.       you can display only one 256-character set at a time. However, the
  10071.       character codes and attribute bytes stored in the displayed portion of
  10072.       the video buffer retain their usual format, so software that knows
  10073.       nothing about the RAM-based character definitions can run unchanged
  10074.       while displaying the RAM-based character set.
  10075.  
  10076.  
  10077.       EGA, VGA, and MCGA
  10078.       Whenever you select an alphanumeric video mode using the video BIOS,
  10079.       the alphanumeric character generator is configured to display the
  10080.       characters defined in the first table in character generator RAM.
  10081.       Thus, to display a different set of ASCII characters, all you need do
  10082.       is update the table. As described above, INT 10H function 11H provides
  10083.       a convenient mechanism for doing this. This same BIOS function also
  10084.       lets you display the 256 characters defined in any of the other
  10085.       character definition tables as described later in this chapter.
  10086.  
  10087.  
  10088.       HGC+ and InColor Card
  10089.       When you power up an HGC+ or an InColor Card, the alphanumeric
  10090.       character generator uses the ROM-based character definition table by
  10091.       default. To display a different ASCII character set, configure the
  10092.       alphanumeric character generator to use the RAM-based table (see
  10093.       Listing 10-6) and then load a character definition table into video
  10094.       RAM at B000:4000.
  10095.  
  10096.       To do this, set bit 0 of the adapter's xMode register (14H) to 1. This
  10097.       causes the adapter to display the characters defined in the table in
  10098.       RAM at B000:4000. Also, set bit 0 of the Configuration Switch register
  10099.       (3BFH) to 1 to make character generator RAM addressable at B000:4000.
  10100.       (This configuration is called "4K RamFont mode" in Hercules
  10101.       documentation.) After you update character generator RAM, you can
  10102.       protect it from subsequent modification by resetting bit 0 of the
  10103.       Configuration Switch register.
  10104.  
  10105.  
  10106.  ───────────────────────────────────────────────────────────────────────────
  10107.  
  10108.       Listing 10-6.  Configuring an HGC+ or InColor Card for updating
  10109.       character generator RAM.
  10110.  
  10111.  ───────────────────────────────────────────────────────────────────────────
  10112.  
  10113.  
  10114.       Updating character generator RAM is more complicated on the InColor
  10115.       Card because all four bit planes are used for character definitions.
  10116.       The complexity lies in the way colors are displayed for characters
  10117.       defined in the bit planes. A character's color is determined not only
  10118.       by its foreground and background attributes, but also by the bit
  10119.       planes used to define its pixel pattern.
  10120.  
  10121.       The InColor Card combines the pixel values in a character definition
  10122.       (in character generator RAM) with the character's foreground and
  10123.       background attributes (in the displayed portion of the video buffer)
  10124.       to produce a 4-bit attribute for every pixel in the character. The
  10125.       logic used is:
  10126.  
  10127.       (pixel_value AND foreground_attribute) OR
  10128.  
  10129.       (NOT pixel_value AND background_attribute)
  10130.  
  10131.       In the example in Figure 10-11, one of the pixels in a character has a
  10132.       value of 2 (0010B) in the character definition table. The character's
  10133.       attribute byte in the video buffer specifies a foreground value of 0
  10134.       and a background value of 7 (0111B). The InColor Card thus displays
  10135.       this pixel with an attribute of (2 AND 0) OR (NOT 2 AND 7), or 5.
  10136.  
  10137.  
  10138.                                   AND
  10139.        ┌───────────────────────────■───── 0101 ──┐
  10140.        │ Background                │              │
  10141.        │ attribute               1101             │
  10142.   ▒▒▒▒▒▒▒▒▒▒                        NOT          │
  10143.  ┌───────────────────────┐   ┌─────┴─────┐        │                 6-bit
  10144.  │0  1  1  1  0  0  0  0 │   │0  0  1  0 │     OR ■─0101─Palette─digital
  10145.  └───────────────────────┘   └─────┬─────┘        │                 output
  10146.               ▒▒▒▒▒▒▒▒▒▒           │ Character definition pixel
  10147.         Foreground │               │              │
  10148.          attribute └───────────────■───── 0000 ──┘
  10149.                                   AND
  10150.  
  10151.       Figure 10-11.  InColor foreground color attribute decoding using RAM-
  10152.       based character definitions (8-bit character codes). The pixel value
  10153.       in the character definition and both attributes in the character's
  10154.       attribute byte all contribute to foreground attribute decoding.
  10155.  
  10156.  
  10157.       Using colors on the InColor Card is simpler if you load all four bit
  10158.       planes with identical bit patterns so that all pixels in the character
  10159.       definitions have the value 0FH (1111B). Then a character's foreground
  10160.       and background attributes depend solely on the values in its attribute
  10161.       byte. Alternatively, you can specify a foreground attribute of 0FH
  10162.       (1111B) and a background attribute of 0 for every character in the
  10163.       video buffer. In this case, the displayed colors depend solely on the
  10164.       pixel values in the character definitions.
  10165.  
  10166.       A more practical use of the InColor Card's character definition RAM is
  10167.       to load each bit plane with a different character definition table.
  10168.       Then each bit in a character's foreground attribute acts as a mask to
  10169.       select a different character set. Of course, a 4-bit foreground
  10170.       attribute is still generated, as in Figure 10-11, so in effect each
  10171.       character set is associated with the color that corresponds to its bit
  10172.       plane. You can, of course, display the character sets in any colors
  10173.       you want by programming the palette registers.
  10174.  
  10175.       To load the bit planes separately, use the high-order nibble in the
  10176.       Plane Mask register (18H) to write-protect the bit planes each time
  10177.       you load a different character set. This permits you to use different
  10178.       foreground attributes to display the different character sets. For
  10179.       example, if all four bit planes contain different character sets, you
  10180.       can select each of the four character sets by using the foreground
  10181.       attributes 1, 2, 4, and 8.
  10182.  
  10183.  
  10184.  Extended Character Sets
  10185.  
  10186.       All of the video subsystems discussed in this chapter have enough
  10187.       character generator RAM to store definitions for more than 256
  10188.       characters, so they all provide a way for the character generator to
  10189.       recognize extended character codes larger than the usual eight bits.
  10190.  
  10191.  
  10192.       EGA and VGA
  10193.       On the EGA and the VGA, the usual range of 256 ASCII codes is doubled
  10194.       by using bit 3 of a character's attribute byte to designate one of the
  10195.       character definition tables in map 2 (see Figure 10-12). In this way,
  10196.       512 different characters can be displayed in an alphanumeric mode.
  10197.  
  10198.       Normally, the value of bit 3 of a character's attribute byte does not
  10199.       affect the character set displayed. This is why: The value of this bit
  10200.       selects one of two bit fields in the Sequencer Character Map Select
  10201.       register. In turn, the value in each of these two bit fields
  10202.       designates one of the available character definition tables in RAM.
  10203.       When the video BIOS establishes a video mode, it loads a default set
  10204.       of character definitions into the first character definition table in
  10205.       map 2 and clears both bit fields in the Character Map Select register.
  10206.       Thus, default alphanumeric characters are defined by the bit patterns
  10207.       in the first table in map 2, regardless of the value of bit 3 of the
  10208.       attribute bytes of the characters displayed.
  10209.  
  10210.       Changing the value in the Character Map Select register, however,
  10211.       changes the character definition tables associated with bit 3 of each
  10212.       character's attribute byte. If two different values appear in the bit
  10213.       fields in the Character Map Select register, the value of bit 3
  10214.       designates one of two different character definition tables.
  10215.  
  10216.  
  10217.       7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
  10218.      ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
  10219.      │High-order byte│Low-order byte │
  10220.      └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
  10221.       ║ ║ ║ ║ ║ ║ ║ ║ │ │ │ │ │ │ │ │
  10222.       ║ ║ ║ ║ ║ ║ ║ ║ └─┴─┴─┴─┴─┴─┴─┴─────8-bit character code
  10223.       ║ ║ ║ ║ ╚═╩═╩═╩═════════════════════4-bit foreground attribute
  10224.       ╚═╩═╩═╩═════════════════════════════4-bit background attribute
  10225.     a.
  10226.  
  10227.       7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
  10228.      ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
  10229.      │High-order byte│Low-order byte │
  10230.      └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
  10231.       ║ ║ ║ ║ ║│║ ║ ║ │ │ │ │ │ │ │ │
  10232.       ║ ║ ║ ║ ║└╫─╫─╫─┴─┴─┴─┴─┴─┴─┴─┴─────9-bit extended character code
  10233.       ║ ║ ║ ║ ╚═╩═╩═╩═════════════════════4-bit foreground attribute
  10234.       ╚═╩═╩═╩═════════════════════════════4-bit background attribute
  10235.     b.
  10236.  
  10237.       7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
  10238.      ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
  10239.      │High-order byte│Low-order byte │
  10240.      └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
  10241.       ║ ║ ║ ║ │ │ │ │ │ │ │ │ │ │ │ │
  10242.       ║ ║ ║ ║ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─────12-bit extended character code
  10243.       ╚═╩═╩═╩═════════════════════════════4-bit attribute
  10244.     c.
  10245.  
  10246.       Figure 10-12.  Character codes and attributes. Figure 10-12a shows the
  10247.       usual 8-bit format. Figure 10-12b shows the extended 9-bit format used
  10248.       on the EGA, VGA, and MCGA. Figure 10-12c shows the extended 12-bit
  10249.       format used on the HGC+ and InColor Card.
  10250.  
  10251.  
  10252.       For example, in Figure 10-13, bit 3 is set to 1, so bits 2, 3, and 5
  10253.       of the Character Map Select register designate which character
  10254.       definition table to use. (This example pertains to the VGA; on the
  10255.       EGA, only bits 2 and 3 of the Character Map Select value would be
  10256.       meaningful.)
  10257.  
  10258.  
  10259.                 ┌────────────┐
  10260.   ┌─────────────┴──────────┐ │
  10261.   │ x  x  x  x  1  x  x  x │ │
  10262.   └────────────────────────┘ │                                      Offset
  10263.         Attribute byte       │               3-bit value            in map 2
  10264.                                                  ┌─────────────────┐
  10265.                 Character Map Select register  111│                 │
  10266.                  ┌────────────────────────┐       ├─────────────────┤E000H
  10267.                  │ 0   x   1   0    x   x │    011│                 │
  10268.                  └────────────────────────┘       ├─────────────────┤C000H
  10269.                    ▒▒▒▒▒▒▒▒▒▒▒▒▒               110│                 │
  10270.                          │                        ├─────────────────┤A000H
  10271.                          └────────────────────010│                 │
  10272.                                                   ├─────────────────┤8000H
  10273.                                                101│                 │
  10274.                                                   ├─────────────────┤6000H
  10275.                                                001│                 │
  10276.                                                   ├─────────────────┤4000H
  10277.                                                100│                 │
  10278.                                                   ├─────────────────┤2000H
  10279.                                                000│                 │
  10280.                                                   └─────────────────┘0
  10281.  
  10282.       Figure 10-13.  Function of the VGA Character Map Select register.
  10283.  
  10284.  
  10285.       Listing 10-7 illustrates two methods of updating this register.
  10286.       Although the technique of using an INT 10H function call generally
  10287.       requires less code and is more portable, you might prefer to program
  10288.       the Sequencer directly in applications that require rapid switching
  10289.       between character sets.
  10290.  
  10291.  
  10292.  ───────────────────────────────────────────────────────────────────────────
  10293.  
  10294.       Listing 10-7.  Programming the Sequencer Character Map Select
  10295.       register on the EGA and VGA.
  10296.  
  10297.  ───────────────────────────────────────────────────────────────────────────
  10298.  
  10299.  
  10300.       If both bit fields in the Character Map Select register contain the
  10301.       same value, the value of bit 3 of a character's attribute byte does
  10302.       not affect which character set is used. If the bit fields designate
  10303.       different character definition tables, then the value of bit 3 of each
  10304.       character's attribute byte selects between two different character
  10305.       sets. Keep in mind, however, that bit 3 is also part of each
  10306.       character's 4-bit foreground attribute. When bit 3 of a character's
  10307.       foreground attribute is set to 0, the character's displayed color is
  10308.       taken from one of the first eight palette registers (0000B through
  10309.       0111B). When bit 3 is set to 1, the color derives from one of the
  10310.       second eight palette registers (1000B through 1111B).
  10311.  
  10312.       Thus, the two 256-character sets selected by bit 3 are displayed with
  10313.       two different sets of eight palette register values. This is handy if
  10314.       you want to associate a particular set of colors with a character set.
  10315.       Otherwise, you might prefer to load the second eight palette registers
  10316.       with the same set of values as the first eight so that the value of
  10317.       bit 3 of a character's attribute byte has no effect on its displayed
  10318.       color. Another technique is to mask bit 3 of the foreground attribute
  10319.       by zeroing bit 3 of the Attribute Controller's Color Plane Enable
  10320.       register, as in Listing 10-8. Because the value in the Color Plane
  10321.       Enable register masks the 4-bit attribute value, zeroing bit 3 in this
  10322.       register allows only the first eight palette registers to be
  10323.       referenced, regardless of the value of bit 3 in a character's
  10324.       attribute byte.
  10325.  
  10326.  
  10327.  ───────────────────────────────────────────────────────────────────────────
  10328.  
  10329.       Listing 10-8.  Zeroing bit 3 of the Color Plane Enable register. This
  10330.       causes bit 3 of a character's attribute byte to have no effect on its
  10331.       displayed attribute.
  10332.  
  10333.  ───────────────────────────────────────────────────────────────────────────
  10334.  
  10335.  
  10336.       MCGA
  10337.       The MCGA supports 8-bit and 9-bit character codes with the same BIOS
  10338.       interface as the EGA and VGA, although the hardware implementation is
  10339.       different. On the MCGA, the two character definition tables selected
  10340.       by bit 3 of a character's attribute byte are the ones in the MCGA's
  10341.       two internal font pages. Although you can load the font pages by
  10342.       programming the MCGA's Character Generator Interface register (12H),
  10343.       Character Font Pointer register (13H), and Number of Characters to
  10344.       Load register (14H), it is easier to use INT 10H function 11H with
  10345.       AL = 3.
  10346.  
  10347.       As on the EGA and VGA, bit 3 of a character's attribute byte does
  10348.       double duty as part of the 9-bit character code as well as the high-
  10349.       order bit of the character's foreground attribute. If you want to use
  10350.       the same colors for both 256-character sets, you can call INT 10H
  10351.       function 10H to store the same set of color values in the second eight
  10352.       video DAC color registers as you do in the first eight. You can also
  10353.       call INT 10H function 10H to mask bit 3 out of alphanumeric attribute
  10354.       decoding (see Listing 10-8).
  10355.  
  10356.  
  10357.       HGC+ and InColor Card
  10358.       On the HGC+ and the InColor Cards, you can configure the character
  10359.       generator to regard the four low-order bits of each character's
  10360.       attribute byte as part of the character code. Do this by setting both
  10361.       bit 2 and bit 1 of the xMode register to 1. (Hercules calls this
  10362.       configuration "48K RamFont mode.")
  10363.  
  10364.       By using 12-bit character codes, you can display all characters
  10365.       defined anywhere in the Hercules adapter's 48 KB of character
  10366.       generator RAM. In practice, you can regard all 48 KB of character
  10367.       generator RAM as one continuous character definition table. However,
  10368.       in some applications, you might find it more convenient to think of
  10369.       character generator RAM as a set of twelve 256-character tables, where
  10370.       the four high-order bits of the character code designate one of the
  10371.       tables, and the eight low-order bits designate a character definition
  10372.       within a table.
  10373.  
  10374.       When 12 bits are used as an extended character code, only bits 4
  10375.       through 7 of the high-order byte specify a character's attribute (see
  10376.       Figure 10-12c). The attributes that Hercules assigned to these bits
  10377.       differ somewhat from the usual monochrome display attributes (see
  10378.       Figure 10-14).
  10379.  
  10380.  
  10381. ╓┌──────────────┌─────────────────────┌──────────────────────────────────────╖
  10382.                 Enable Blink Bit = 1  Enable Blink Bit = 0
  10383.  Attribute Bit  (blink enabled)       (blink disabled)
  10384.  ──────────────────────────────────────────────────────────────────────────
  10385.  7              High-intensity        Boldface
  10386.  6              Blink                 Reverse
  10387.  5              Overstrike            No overstrike
  10388.  4              Underline             No underline
  10389.  
  10390.       Figure 10-14.  Extended attribute set on the HGC+ and the InColor
  10391.       Card.
  10392.  
  10393.  
  10394.         ╔═══╗     When using 12-bit character codes on the HGC+ and the
  10395.         ║ T ║     InColor Card, you can specify the scan line on which the
  10396.         ║ I ║     overstrike and underscore attributes appear. Bits 0
  10397.         ║ P ║     through 3 of the Underscore register (15H) control the
  10398.         ╚═══╝     position of the underscore. Bits 0 through 3 of the
  10399.                   Overstrike register (16H) control the position of the
  10400.                   overstrike. On the InColor Card, you can also control the
  10401.                   displayed color of the underscore and overstrike by
  10402.                   storing a value between 1 and 0FH in bits 4 through 7 of
  10403.                   the corresponding control register.
  10404.  
  10405.       As on the HGC+, the 12-bit character codes on the InColor Card
  10406.       designate locations in the character definition tables. Attribute
  10407.       decoding is more complicated on the InColor Card, however (see Figure
  10408.       10-15). The 4-bit foreground attribute generated for each pixel in a
  10409.       character is derived by combining the character's 4-bit attribute with
  10410.       the pixel's value in the character definition table.
  10411.  
  10412.  
  10413.  ───────────────────────────────────────────────────────────────────────────
  10414.  MDA-compatible Attributes (Exception register bit 5 = 1)
  10415.                Enable Blink On                 Enable Blink Off
  10416.  ───────────────────────────────────────────────────────────────────────────
  10417.  Foreground    (pixel value) OR (background)   (pixel value) XOR
  10418.                                                (background)
  10419.  Background    0 if bit 7 of attribute = 0     0 if bit 6 of attribute = 0
  10420.                8 if bit 7 of attribute = 1     0FH if bit 6 of attribute = 1
  10421.  ───────────────────────────────────────────────────────────────────────────
  10422.  Color Attributes (Exception register bit 5 = 0)
  10423.  ───────────────────────────────────────────────────────────────────────────
  10424.  Foreground    (pixel value) AND (NOT attribute)
  10425.  Background    0
  10426.  
  10427.       Figure 10-15.  InColor Card color attribute decoding using 12-bit
  10428.       character codes.
  10429.  
  10430.  
  10431.       As was the case when using 8-bit character codes, the peculiar
  10432.       interaction of character attributes with the pixel values in the
  10433.       character definition table makes controlling colors difficult. To
  10434.       simplify matters, you can store the same character definitions in all
  10435.       four bit planes when using color attribute decoding; this allows each
  10436.       character's 4-bit attribute to specify all 16 colors. When using MDA-
  10437.       compatible attributes, you can store the same bit patterns in bit
  10438.       planes 0 through 2 and zero bit plane 3. Again, this allows each
  10439.       character's 4-bit attribute to completely control the displayed
  10440.       attributes.
  10441.  
  10442.       If you elect to store different character definition tables in each
  10443.       bit plane, each of a character's attribute bits can select one of the
  10444.       bit planes. Again, you should program the palette registers carefully
  10445.       so that characters from different bit planes are displayed with
  10446.       appropriate colors.
  10447.  
  10448.  
  10449.  Compatibility Problems with Extended Character Codes
  10450.  
  10451.       Most PC and PS/2 programs, including the BIOS, MS-DOS, and most
  10452.       commercially available applications, expect you to use 8-bit ASCII
  10453.       character codes. This means you can update character generator RAM
  10454.       with an 8-bit ASCII character set in a different font, but you cannot
  10455.       take advantage of the extended 9-bit or 12-bit character codes
  10456.       supported by IBM and Hercules.
  10457.  
  10458.       If you use the INT 10H interface to display characters with extended
  10459.       character codes, you must be careful when you use certain ROM BIOS
  10460.       functions. For example, INT 10H function 0AH, which stores an 8-bit
  10461.       character code in the video buffer, is not very useful for writing
  10462.       characters with a 9-bit or a 12-bit extended character code. On the
  10463.       other hand, you can use INT 10H function 9, which handles a 16-bit
  10464.       character code and attribute combination, to process extended
  10465.       character codes and attributes.
  10466.  
  10467.       When you run an application that uses extended character codes, you
  10468.       can encounter problems when your application interacts inadvertently
  10469.       with software that doesn't recognize the different character-attribute
  10470.       format. Consider what might happen if a RAM-resident utility program
  10471.       popped up in the middle of your application without being "aware" that
  10472.       you were using extended character codes. When the utility program
  10473.       placed 8-bit character codes and attributes in the buffer, the
  10474.       alphanumeric character generator would interpret them as extended
  10475.       character codes and attributes. The results would probably be
  10476.       unusable.
  10477.  
  10478.  
  10479.  Changing the Displayed Character Matrix
  10480.  
  10481.  
  10482.       There is another dimension to customizing a RAM-based character
  10483.       definition table: You can control the height of the character matrix
  10484.       in which characters are displayed. The height of the displayed
  10485.       character matrix determines how many rows of characters appear on the
  10486.       screen. For example, a 350-line display accommodates 43 rows of 8-by-8
  10487.       characters but only 25 rows of 8-by-14 characters.
  10488.  
  10489.       With all of the subsystems discussed in this chapter, you can vary the
  10490.       displayed height of alphanumeric characters by programming the CRT
  10491.       Controller to display characters the same size as the characters
  10492.       defined in character generator RAM. Thus, to display 8-by-8 characters
  10493.       on a 350-line display, you place 8-by-8 character definitions into
  10494.       character generator RAM and then program the CRTC to display
  10495.       characters that are 8 pixels high.
  10496.  
  10497.       On the EGA and the VGA, you can perform both these tasks by calling
  10498.       INT 10H function 11H, although in some situations you may prefer to
  10499.       update the character definitions or program the CRTC explicitly.
  10500.       Hercules adapters, of course, have no ROM BIOS, so you must do the
  10501.       work yourself.
  10502.  
  10503.  
  10504.  EGA
  10505.  
  10506.       Consider how you would display 43 rows of 8-by-8 characters in an EGA
  10507.       alphanumeric mode with 350-line vertical resolution, as in Listing 10-
  10508.       9. In this example, the call to INT 10H function 11H with AL = 12H
  10509.       copies the ROM's 8-by-8 character set (normally used in 200-line video
  10510.       modes) into the first of the four tables in map 2 and then calculates
  10511.       the proper CRTC register values based on the values of POINTS and ROWS
  10512.       in the BIOS Video Display Data Area.
  10513.  
  10514.  
  10515.  ───────────────────────────────────────────────────────────────────────────
  10516.  
  10517.       Listing 10-9.  Establishing an 80-by-43 alphanumeric mode on an EGA.
  10518.  
  10519.  ───────────────────────────────────────────────────────────────────────────
  10520.  
  10521.  
  10522.       INT 10H function 11H calls INT 10H function 1 to set the position of
  10523.       the alphanumeric cursor in the displayed character matrix. As
  10524.       described in Chapter 3, the EGA BIOS version of INT 10H function 1
  10525.       computes this cursor position incorrectly, leading to an improperly
  10526.       displayed cursor. Therefore, the routine in Listing 10-9 updates the
  10527.       CRTC Cursor Start and Cursor End registers directly.
  10528.  
  10529.         ╔═══╗     If your program changes the number of displayed character
  10530.         ║ T ║     rows, it should also call INT 10H function 12H to select
  10531.         ║ I ║     the EGA BIOS's alternate print screen routine. This
  10532.         ║ P ║     routine functions identically to the one in the
  10533.         ╚═══╝     motherboard BIOS except that it uses the Video Display
  10534.                   Data Area value ROWS to determine how many lines to print.
  10535.                   (The motherboard BIOS routine disregards ROWS and always
  10536.                   prints 25 lines.)
  10537.  
  10538.  
  10539.  VGA
  10540.  
  10541.       You can also use INT 10H function 11H on the VGA to establish an
  10542.       alphanumeric mode with a nondefault character matrix (see Listing
  10543.       10-10). On the VGA, you set the vertical resolution of the video mode
  10544.       using INT 10H function 12H (with BL = 30H) before calling function
  10545.       11H. Also, the cursor emulation computations are performed properly in
  10546.       the VGA BIOS, so no extra code is required to avoid cursor emulation
  10547.       on the VGA.
  10548.  
  10549.  
  10550.  ───────────────────────────────────────────────────────────────────────────
  10551.  
  10552.       Listing 10-10.  Establishing an 80-by-50 alphanumeric mode on a VGA.
  10553.  
  10554.  ───────────────────────────────────────────────────────────────────────────
  10555.  
  10556.  
  10557.  MCGA
  10558.  
  10559.       The MCGA can only display characters with 2, 4, 6, 8, 10, 12, 14, or
  10560.       16 scan lines. (This is a limitation of the MCGA's Memory Controller.)
  10561.       To change the displayed character matrix, use INT 10H function 11H to
  10562.       load a new character set into the character generator. Then program
  10563.       the Scan Lines per Character register (09H) with a value from 0
  10564.       through 7; if the value is n, the number of scan lines displayed in
  10565.       the character matrix is (n + 1) * 2. Listing 10-11 shows how to set up
  10566.       an 8-by-10 character matrix using the MCGA's 400-line vertical
  10567.       resolution to produce 40 rows of 80 characters.
  10568.  
  10569.  
  10570.  ───────────────────────────────────────────────────────────────────────────
  10571.  
  10572.       Listing 10-11.  Establishing an 80-by-40 alphanumeric mode on an
  10573.       MCGA.
  10574.  
  10575.  ───────────────────────────────────────────────────────────────────────────
  10576.  
  10577.  
  10578.       For some values in the Scan Lines per Character register, the MCGA
  10579.       incorrectly displays the bottommost scan line of the screen.
  10580.       Specifically, when the value in the Scan Lines per Character register
  10581.       is 1, 3, 5, or 6, the MCGA replicates part of the topmost scan line on
  10582.       the screen at the bottom of the screen. Thus, you should generally
  10583.       avoid using these values for the Scan Lines per Character register.
  10584.  
  10585.  
  10586.  HGC+ and InColor Card
  10587.  
  10588.       You must program the HGC+ CRTC explicitly to change the number of
  10589.       displayed lines in alphanumeric characters. The subroutine SetHercCRTC
  10590.       in Listing 10-14 illustrates a table-driven technique for setting up
  10591.       the CRTC's vertical timing parameters for a variety of character
  10592.       sizes. Figure 10-16 summarizes the CRTC timing parameters recommended
  10593.       by Hercules for any character matrix between 4 and 16 scan lines high
  10594.       as well as for characters that are either 8 or 9 pixels wide.
  10595.  
  10596.  
  10597.  ───────────────────────────────────────────────────────────────────────────
  10598.                      Width of Character Matrix
  10599.  CRTC register      8 Pixels           9 Pixels
  10600.  ───────────────────────────────────────────────────────────────────────────
  10601.  00H                6DH                61H
  10602.  01H                5AH                50H
  10603.  02H                5CH                52H
  10604.  03H                0FH                0FH
  10605.  
  10606.  ───────────────────────────────────────────────────────────────────────────
  10607.  CRTC                  Height of Character Matrix (in pixels)
  10608.  register  4    5    6    7    8    9    10   11   12   13   14   15   16
  10609.  ───────────────────────────────────────────────────────────────────────────
  10610.  04H       5CH  4Ah  3DH  34H  2DH  28H  24H  20H  1DH  1BH  19H  17H  16H
  10611.  05H       02H  00H  04H  06H  02H  01H  00H  07H  0AH  06H  06H  0AH  02H
  10612.  06H       58H  46H  3AH  32H  2BH  26H  23H  1FH  1DH  1AH  19H  17H  15H
  10613.  07H       59H  46H  3BH  33H  2CH  27H  23H  20H  1DH  1BH  19H  17H  16H
  10614.  
  10615.       Figure 10-16.  CRTC timing parameters for height and width of the
  10616.       alphanumeric character matrix (HGC+ and InColor Card).
  10617.  
  10618.  
  10619.       On the InColor Card, the techniques for changing the displayed
  10620.       character matrix parallel those used on the HGC+. The values you place
  10621.       in the CRTC registers for each possible character matrix are also the
  10622.       same.
  10623.  
  10624.  
  10625.       Programming Examples
  10626.       The routines on the following pages unify the programming techniques
  10627.       for changing the displayed character matrix on the EGA (see Listing
  10628.       10-12), on the VGA (see Listing 10-13), and on the HGC+ and InColor
  10629.       Card (see Listing 10-14). In each case, the function AlphaModeSet()
  10630.       programs the alphanumeric character generator and the CRTC to
  10631.       accommodate the dimensions of the specified character matrix and
  10632.       character code size.
  10633.  
  10634.  
  10635.  ───────────────────────────────────────────────────────────────────────────
  10636.  
  10637.       Listing 10-12.  Programming the EGA alphanumeric character
  10638.       size.
  10639.  
  10640.  ───────────────────────────────────────────────────────────────────────────
  10641.  
  10642.  
  10643.  ───────────────────────────────────────────────────────────────────────────
  10644.  
  10645.       Listing 10-13.  Programming the VGA alphanumeric character
  10646.       size.
  10647.  
  10648.  ───────────────────────────────────────────────────────────────────────────
  10649.  
  10650.  
  10651.  ───────────────────────────────────────────────────────────────────────────
  10652.  
  10653.       Listing 10-14.  Programming the alphanumeric character size on the
  10654.       HGC+ and InColor Card.
  10655.  
  10656.  ───────────────────────────────────────────────────────────────────────────
  10657.  
  10658.  
  10659.  Graphics Windows in Alphanumeric Modes
  10660.  
  10661.  
  10662.       When you update a RAM-resident character definition table, you alter
  10663.       the appearance of any characters displayed using those definitions.
  10664.       The contents of the displayed portion of the video buffer need not be
  10665.       updated. You can exploit this characteristic of RAM-based character
  10666.       definitions to display pixel-addressable graphics images in an
  10667.       alphanumeric mode, thereby displaying text with maximum speed while
  10668.       including pixel-by-pixel graphics images on the same screen.
  10669.  
  10670.       The technique is similar on both IBM and Hercules subsystems. Tile an
  10671.       area of the screen with a sequence of characters whose attribute
  10672.       selects a character definition table that contains the graphics image
  10673.       (see Figure 10-17). The graphics image is created and modified by
  10674.       updating the appropriate character definitions in the table. You can
  10675.       regard the character definition table as a sort of virtual graphics
  10676.       buffer and access individual pixels within it just as you do in the
  10677.       usual graphics modes.
  10678.  
  10679.       On the InColor Card, you can specify the value of each individual
  10680.       pixel you store in the character definition table as though you were
  10681.       using 720-by-348 16-color graphics mode. On other subsystems, however,
  10682.       only one memory map is used for character definitions, so you do not
  10683.       have pixel-by-pixel attribute control. Instead, pixels in the
  10684.       character definition table have a value of 0 or 1; the attributes with
  10685.       which the character codes are stored in the video buffer determine the
  10686.       appearance of the pixels.
  10687.  
  10688.  
  10689.               ╔══════════════════════════════════════════╗
  10690.               ║                                          ║
  10691.               ║    Figure 10-17 is found on page 338     ║
  10692.               ║    in the printed version of the book.   ║
  10693.               ║                                          ║
  10694.               ╚══════════════════════════════════════════╝
  10695.  
  10696.       Figure 10-17.  A tiled graphics window in an alphanumeric mode.
  10697.  
  10698.  
  10699.       Listing 10-15 illustrates the technique for producing a tiled graphics
  10700.       window in 80-column alphanumeric mode on the EGA and VGA. The first
  10701.       part of the program creates the tiled window by storing the second 128
  10702.       ASCII characters in four rows of 32 at the start of the video buffer
  10703.       (that is, in the upper left corner of the screen). Then the program
  10704.       clears the window by setting the second 128 character definitions to
  10705.       0.
  10706.  
  10707.       To update a pixel in the window, the subroutine SetPixel() computes a
  10708.       byte offset in the character definition table that corresponds to the
  10709.       pixel's location in the tiled window. As in graphics modes, the
  10710.       routine accesses each individual pixel with a bit mask.
  10711.  
  10712.  
  10713.  ───────────────────────────────────────────────────────────────────────────
  10714.  
  10715.       Listing 10-15.  Creating a tiled graphics window on the EGA or
  10716.       VGA.
  10717.  
  10718.  ───────────────────────────────────────────────────────────────────────────
  10719.  
  10720.  
  10721.  HGC+ and InColor Card
  10722.  
  10723.       Clearly, the size of a tiled graphics window is restricted if you use
  10724.       8-bit character codes because the 8-bit ASCII character set contains
  10725.       only 256 characters. If you configure a Hercules adapter for 12-bit
  10726.       character codes, however, you can create much larger tiled windows
  10727.       without running out of character codes. Also, you can create larger
  10728.       windows by displaying taller characters (that is, by increasing the
  10729.       height of the displayed character matrix). Of course, if you use
  10730.       taller characters you decrease the number of rows of text that you can
  10731.       display at the same time; this can be a drawback in some applications.
  10732.  
  10733.       You can use similar programming techniques for alphanumeric graphics
  10734.       on Hercules adapters and on IBM subsystems. For example, Listing
  10735.       10-15 can be modified for use with the HGC+ and InColor Card by
  10736.       changing the values of CGenDefSize and CharDefTable and removing the
  10737.       calls to the functions CGenModeSet() and CGenModeClear().
  10738.  
  10739.         ╔═══╗     In establishing a graphics window on a Hercules card,
  10740.         ║ T ║     avoid using a character matrix that is 9 pixels wide.
  10741.         ║ I ║     Because the ninth (rightmost) pixel in each character is
  10742.         ║ P ║     actually a hardware-generated copy of the eighth dot, you
  10743.         ╚═══╝     cannot control it independently by updating the character
  10744.                   definition table.
  10745.  
  10746.  
  10747.  EGA and VGA
  10748.  
  10749.       On the EGA and VGA, you can create larger tiled graphics windows if
  10750.       you use 9-bit extended character codes. For instance, you could
  10751.       dedicate one 256-character definition table to text characters and a
  10752.       second character definition table to graphics tiling characters.
  10753.       Nevertheless, the EGA and VGA are still limited to displaying no more
  10754.       than 512 different characters at a time, so the largest tiled graphics
  10755.       window is much smaller than it can be on a Hercules adapter.
  10756.  
  10757.         ╔═══╗     When you update pixels in the tiled window, you should
  10758.         ║ T ║     minimize  the number of times your program resets the
  10759.         ║ I ║     Sequencer (for example, in the routines CGenModeSet() and
  10760.         ║ P ║     CGenModeClear()). If you reset the Sequencer each time
  10761.         ╚═══╝     you update a pixel, you might create screen interference.
  10762.                   (Synchronizing Sequencer resets with the vertical retrace
  10763.                   interval can eliminate this interference but can also
  10764.                   greatly decrease the speed of a program.) If you draw a
  10765.                   complicated graphics figure containing many pixels, draw
  10766.                   the entire figure at one time as in Listing 10-15.
  10767.  
  10768.  
  10769.  MCGA
  10770.  
  10771.       Character definition tables in MCGA character generator RAM are
  10772.       formatted differently than those on the EGA and VGA, so a routine that
  10773.       manipulates pixels in character generator RAM must address the tables
  10774.       differently (see Listing 10-16). Also, remember that the screen does
  10775.       not reflect changes to the MCGA's character definition tables until
  10776.       you load the character generator's font pages (see Listing 10-5).
  10777.  
  10778.  
  10779.  ───────────────────────────────────────────────────────────────────────────
  10780.  
  10781.       Listing 10-16.  A routine to set pixels in a tiled graphics window on
  10782.       the MCGA.
  10783.  
  10784.  ───────────────────────────────────────────────────────────────────────────
  10785.  
  10786.  
  10787.  
  10788.                      11  Bit Blocks and Animation
  10789.  
  10790.  
  10791.                             Bit Block Move
  10792.             CGA and MCGA ■ EGA and VGA ■ HGC ■ InColor Card
  10793.  
  10794.                        Bitwise Pixel Operations
  10795.                          XOR ■ NOT ■ AND ■ OR
  10796.  
  10797.                            Bit Block Tiling
  10798.  
  10799.                                Animation
  10800.                              XOR Animation
  10801.                       Overlapping Bit Block Moves
  10802.  
  10803.                         A Graphics-Mode Cursor
  10804.                          XOR ■ Bit Block Move
  10805.  
  10806.  
  10807.  
  10808.       This chapter is about moving things around in the video buffer and on
  10809.       the screen. Some of the most useful and entertaining graphics-mode
  10810.       programs create the appearance of on-screen motion. Objects as mundane
  10811.       as a cursor or as unusual as an alien spaceship can appear to move
  10812.       across the screen if you erase them and then immediately redraw them
  10813.       in successive locations. PC and PS/2 video subsystems are not
  10814.       particularly well equipped to support this kind of real-time
  10815.       animation, but the techniques in this chapter should help you fully
  10816.       exploit their capabilities.
  10817.  
  10818.       You might think of video animation in the same context as video games,
  10819.       but animation has other uses in computer graphics. For instance, all
  10820.       interactive graphics programs require a moving cursor that allows the
  10821.       user to point to screen locations. Many drawing or design programs let
  10822.       the user move shapes and images around the screen. Robotic control
  10823.       programs indicate the status of a robot arm with an animated
  10824.       representation of its position. You can create such animation effects
  10825.       using the techniques in this chapter.
  10826.  
  10827.  
  10828.  Bit Block Move
  10829.  
  10830.  
  10831.       The basic software tool for many animation techniques is the bit block
  10832.       move--a routine that copies a rectangular block of pixels into, out
  10833.       of, or within the video buffer. The name "bit block move" describes
  10834.       this routine well. After all, a rectangle of pixels is in essence
  10835.       nothing more than a block of bits. Still, a bit block move routine can
  10836.       do more than simply copy pixel values. As can other video graphics
  10837.       drawing routines, a bit block move routine can update pixel values
  10838.       using the bitwise logical operations AND, OR, and XOR. These
  10839.       operations can create attractive effects when used as part of bit
  10840.       block moves.
  10841.  
  10842.       To copy a bit block from one location to another within the video
  10843.       buffer in PC and PS/2 video subsystems, it is usually more efficient
  10844.       to use an intermediate buffer in system RAM. You first copy pixel
  10845.       values from the video buffer into the intermediate buffer, then copy
  10846.       the values from this buffer to the desired position in the video
  10847.       buffer.
  10848.  
  10849.       Creating an intermediate copy of the pixels in a bit block might seem
  10850.       superfluous, but in most situations it is preferable to trying to move
  10851.       the bit block entirely within the video buffer. For example, neither
  10852.       the EGA nor the InColor Card supports direct logical operations (AND,
  10853.       OR, and XOR) between pixels in the bit planes. Also, CPU accesses to
  10854.       video RAM are slower than equivalent accesses to system RAM. Thus,
  10855.       when multiple copies of the same bit block are to be stored in the
  10856.       video buffer, making a single copy in system RAM and then making
  10857.       multiple copies from system RAM to video RAM is more efficient.
  10858.  
  10859.  
  10860.  CGA and MCGA
  10861.  
  10862.       Listing 11-1 is a bit block move routine for the CGA. The routine
  10863.       GetBitBlock() copies a block of pixels from the video buffer to a
  10864.       buffer in system RAM. The complementary routine StoreBitBlock(), in
  10865.       Listing 11-2, copies pixels from system RAM to the video buffer.
  10866.       StoreBitBlock()contains subroutines to perform AND, OR, or XOR
  10867.       operations on the pixels in system RAM using the previous contents of
  10868.       the video buffer.
  10869.  
  10870.  
  10871.  ───────────────────────────────────────────────────────────────────────────
  10872.  
  10873.       Listing 11-1.  A routine to copy a block of pixels from the CGA video
  10874.       buffer to system RAM.
  10875.  
  10876.  ──────────────────────────────────────────────────────────────────────────
  10877.  
  10878.  
  10879.  ───────────────────────────────────────────────────────────────────────────
  10880.  
  10881.       Listing 11-2.  A routine to copy a block of pixels from system RAM to
  10882.       the CGA video buffer.
  10883.  
  10884.  ───────────────────────────────────────────────────────────────────────────
  10885.  
  10886.  
  10887.       In the MCGA's 640-by-480 2-color and 320-by-200 256-color modes, pixel
  10888.       addressing is different than in the two CGA-compatible modes.
  10889.       Otherwise, versions of GetBitBlock() and StoreBitBlock() are similar
  10890.       in all MCGA modes.
  10891.  
  10892.  
  10893.  EGA and VGA
  10894.  
  10895.       In native EGA and VGA graphics modes, the bit block move routine must
  10896.       move the contents of all four bit planes to system RAM. The
  10897.       GetBitBlock() routine in Listing 11-3 extracts bytes from each
  10898.       bit plane using read mode 0 and selecting each bit plane in turn with
  10899.       the Graphics Controller's Read Map Mask register. StoreBitBlock(), in
  10900.       Listing 11-4, then uses write mode 0 to copy data into the bit
  10901.       planes. The bit planes are isolated in write mode 0 by programming the
  10902.       Sequencer's Map Mask register.
  10903.  
  10904.       Do not use the routines in Listings 11-3 and 11-4 on an EGA with
  10905.       only 64 KB of video RAM. Because the memory maps are chained together
  10906.       to form the two bit planes used in 640-by-350 graphics modes, these
  10907.       routines will not work properly in this situation. (Chapter 4
  10908.       discusses this in greater detail.)
  10909.  
  10910.  
  10911.  ───────────────────────────────────────────────────────────────────────────
  10912.  
  10913.       Listing 11-3.  A routine to copy a block of pixels from the EGA or VGA
  10914.       video buffer to system RAM in native graphics modes.
  10915.  
  10916.  ───────────────────────────────────────────────────────────────────────────
  10917.  
  10918.  
  10919.  ───────────────────────────────────────────────────────────────────────────
  10920.  
  10921.       Listing 11-4.  A routine to copy a block of pixels from system RAM
  10922.       to the EGA or VGA video buffer in native graphics mode.
  10923.  
  10924.  ───────────────────────────────────────────────────────────────────────────
  10925.  
  10926.  
  10927.  HGC
  10928.  
  10929.       Bit block move routines for HGC and HGC+ 720-by-348 monochrome
  10930.       graphics mode are similar routines for CGA 640-by-200 2-color mode.
  10931.       The differences  are in how they calculate pixel addresses and in the
  10932.       way the video buffer is interleaved.
  10933.  
  10934.  
  10935.  InColor Card
  10936.  
  10937.       The routines for the InColor Card's 720-by-348 16-color mode resemble
  10938.       the EGA routines in Listings 11-3 and 11-4, because both adapters'
  10939.       video buffers are mapped in parallel bit planes. Differences between
  10940.       the routines lie in the way pixel addresses are computed, in how the
  10941.       video buffer is interleaved, and in how individual bit planes are
  10942.       accessed. On the InColor Card, you can use the same technique as
  10943.       ReadPixelInC() (discussed in Chapter 5) to program the Read/Write
  10944.       Control and Color registers and isolate the contents of each bit
  10945.       plane. Similarly, a bit block store routine for the InColor Card
  10946.       follows  StorePixelInC() in its use of the Plane Mask register and the
  10947.       Read/Write Control and Color registers.
  10948.  
  10949.  
  10950.  Bitwise Pixel Operations
  10951.  
  10952.  
  10953.       If you experimented with the pixel-programming and line-drawing
  10954.       examples in previous chapters, you probably know why the bitwise
  10955.       logical operations--XOR, AND, and OR--are useful in video graphics
  10956.       programming. In this case, you can skip the next few paragraphs.
  10957.       Otherwise, read on to see how video graphics programs can exploit the
  10958.       ability to perform XOR, AND, and OR on pixel values.
  10959.  
  10960.  
  10961.  XOR
  10962.  
  10963.       The XOR operation is useful because it is reversible. When you change
  10964.       a pixel's value in the video buffer using the XOR function, you can
  10965.       restore its original value by repeating the operation. For example, if
  10966.       a pixel in the video buffer has the value 9, setting its value by
  10967.       XORing it with a value of 5 results in a pixel value of 0CH. XORing
  10968.       the resulting pixel value (0CH) with a value of 5 restores the
  10969.       original pixel value of 9.
  10970.  
  10971.       This implies that you can XOR objects into the video buffer and onto
  10972.       the screen, and then erase them, without worrying about saving and
  10973.       restoring the contents of the video buffer. The use of XOR has
  10974.       limitations, however. One is that an image containing zero-value
  10975.       pixels cannot be XORed into the video buffer. Because XORing a pixel
  10976.       with 0 leaves the pixel's value unchanged, only nonzero pixels in the
  10977.       image affect the video buffer.
  10978.  
  10979.       Another more serious limitation is that a patterned background can
  10980.       obscure the image you are trying to XOR into the video buffer.
  10981.       Consider Figure 11-1, in which a text string is XORed against
  10982.       progressively distracting backgrounds. The text is perfectly readable
  10983.       against a solid background, but a striped background significantly
  10984.       obscures the letters. In the worst case, XORing a single-color image
  10985.       into a pattern of random pixels results only in another pattern of
  10986.       random pixels.
  10987.  
  10988.  
  10989.               ╔══════════════════════════════════════════╗
  10990.               ║                                          ║
  10991.               ║    Figure 11-1 is found on page 361      ║
  10992.               ║    in the printed version of the book.   ║
  10993.               ║                                          ║
  10994.               ╚══════════════════════════════════════════╝
  10995.  
  10996.       Figure 11-1.  Effects of XORing a text string against various
  10997.       backgrounds.
  10998.  
  10999.  
  11000.  NOT
  11001.  
  11002.       A bitwise NOT operation on a pixel value toggles all 1 bits to 0 and
  11003.       all 0 bits to 1. Obviously, two sequential NOT operations will leave
  11004.       the pixel value unchanged. A common programming practice in monochrome
  11005.       graphics modes is to use NOT to toggle a reverse video state. For
  11006.       instance, a black-on-white character can be reversed to white-on-black
  11007.       by performing NOT operations on its pixels.
  11008.  
  11009.       The effect of NOT on multibit pixel values is less clear. In this
  11010.       situation, the NOT operation converts one pixel value into some other
  11011.       pixel value, but the colors corresponding to these two values may be
  11012.       unrelated. Thus, in a color graphics mode, performing a NOT operation
  11013.       on all pixels in a character matrix changes both the foreground and
  11014.       background values, but the resulting color combination may not be
  11015.       particularly attractive or even readable. In manipulating pixels in
  11016.       color graphics, use NOT with caution.
  11017.  
  11018.         ╔═══╗     A bitwise NOT is equivalent to performing a bitwise XOR
  11019.         ║ T ║     using a binary value of all 1 bits. This means you can use
  11020.         ║ I ║     any of the pixel XOR routines developed in this book to
  11021.         ║ P ║     perform NOT operations as well. Thus, little can be gained
  11022.         ╚═══╝     by writing special-purpose NOT routines for pixel
  11023.                   manipulation.
  11024.  
  11025.  
  11026.  AND
  11027.  
  11028.       The bitwise logical operation AND is also useful in manipulating
  11029.       graphics images. Consider, for instance, how you might go about
  11030.       drawing the striped circle in Figure 11-2b. You could do it the hard
  11031.       way, by intersecting a set of parallel lines with the circle. This
  11032.       procedure would be laborious, however, because of the extra
  11033.       programming and increased computational overhead involved in
  11034.       determining the intersection points.
  11035.  
  11036.  
  11037.               ╔══════════════════════════════════════════╗
  11038.               ║                                          ║
  11039.               ║    Figure 11-2 is found on page 362      ║
  11040.               ║    in the printed version of the book.   ║
  11041.               ║                                          ║
  11042.               ╚══════════════════════════════════════════╝
  11043.  
  11044.       Figure 11-2.  Using AND to draw a striped circle. The circle in Figure
  11045.       11-2a consists of pixels of the maximum possible value. The lines are
  11046.       drawn across the circle using a pixel AND operation to produce the
  11047.       striped circle in Figure 11-2b.
  11048.  
  11049.  
  11050.       It is much easier to draw a filled circle (see Figure 11-2a) with
  11051.       pixels of the maximum possible value (that is, all bits set to 1)
  11052.       against a background of zero-value pixels. This circle is used as a
  11053.       mask against which you AND the pixels in the parallel lines. When
  11054.       pixels in each line are ANDed with pixels inside the circle, their
  11055.       original values are stored intact in the video buffer. Outside the
  11056.       circle, the result of ANDing the line pixels with the zero background
  11057.       always results in zero-value pixels being stored in the buffer. The
  11058.       result: a striped circle.
  11059.  
  11060.       You can apply this technique to any graphics form, but it is
  11061.       particularly attractive in conjunction with a bit block move routine.
  11062.       You can superimpose patterned images with a short sequence of bit
  11063.       block moves using pixel AND and OR operations. In Figure 11-3, a
  11064.       circular chunk of pattern B is superimposed on pattern A by using a
  11065.       mask to isolate a "hole" in pattern A. The inverse of the same mask
  11066.       extracts the congruent piece of pattern B. The two masked patterns are
  11067.       then superimposed by a third bit block move that uses OR (or XOR) to
  11068.       update pixels.
  11069.  
  11070.  
  11071.               ╔══════════════════════════════════════════╗
  11072.               ║                                          ║
  11073.               ║    Figure 11-3 is found on page 362      ║
  11074.               ║    in the printed version of the book.   ║
  11075.               ║                                          ║
  11076.               ╚══════════════════════════════════════════╝
  11077.  
  11078.       Figure 11-3.  Masking patterned images with pixel AND operations.
  11079.  
  11080.  
  11081.  OR
  11082.  
  11083.       The bitwise OR operator is less frequently used than XOR for
  11084.       manipulating pixel values. The OR operation, unlike XOR or NOT, is not
  11085.       reversible. The result of ORing pixels always depends on their
  11086.       previous values in the video buffer.
  11087.  
  11088.       One typical use of the pixel OR operation is to accentuate
  11089.       intersections of forms in the video buffer. Consider what happens when
  11090.       you OR two different-colored areas into a 16-color video buffer (see
  11091.       Figure 11-4). If one rectangle is filled with pixels of value 3 and
  11092.       the other rectangle with pixels of value 5, the pixels at the
  11093.       intersection points have the value 7 (3 OR 5). With the usual default
  11094.       color palette, the upper rectangle appears cyan, the lower rectangle
  11095.       is violet, and the intersection is white.
  11096.  
  11097.  
  11098.       ┌────────────────────┐
  11099.       │░░░░░░░░░░░░░░░░░░░░│
  11100.       │░Cyan░░░░░░░░░░░░░░░│
  11101.       │░░░░░░░░░░░░░░░░░░░░│
  11102.       │░░░░░░░░░░┌─────────┼─────────┐
  11103.       │░░░░░░░░░░│         │▒▒▒▒▒▒▒▒▒│
  11104.       │░░░░░░░░░░│ White   │▒▒▒▒▒▒▒▒▒│
  11105.       │░░░░░░░░░░│         │▒▒▒▒▒▒▒▒▒│
  11106.       └──────────┼─────────┘▒▒▒▒▒▒▒▒▒│
  11107.                  │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│
  11108.                  │▒▒▒▒▒▒▒▒▒▒▒▒Violet▒│
  11109.                  │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│
  11110.                  └───────────────────┘
  11111.  
  11112.       Figure 11-4.  ORing two colored areas into a 16-bit video buffer.
  11113.  
  11114.  
  11115.  Bit Block Tiling
  11116.  
  11117.  
  11118.       You can use bit block move routines to fill an area of the video
  11119.       buffer with any arbitrary pattern. Do this by tiling the buffer
  11120.       through bit block moves to adjoining rectangular areas of the buffer
  11121.       (see Figure 11-5). Using the AND mask technique, you can fill any
  11122.       arbitrary form, such as the circle in Figure 11-6, with a pattern
  11123.       contained in a bit block.
  11124.  
  11125.  
  11126.               ╔══════════════════════════════════════════╗
  11127.               ║                                          ║
  11128.               ║    Figure 11-5 is found on page 363      ║
  11129.               ║    in the printed version of the book.   ║
  11130.               ║                                          ║
  11131.               ╚══════════════════════════════════════════╝
  11132.  
  11133.       Figure 11-5.  Bit block tiling.
  11134.  
  11135.  
  11136.               ╔══════════════════════════════════════════╗
  11137.               ║                                          ║
  11138.               ║    Figure 11-6 is found on page 364      ║
  11139.               ║    in the printed version of the book.   ║
  11140.               ║                                          ║
  11141.               ╚══════════════════════════════════════════╝
  11142.  
  11143.       Figure 11-6.  Tiling with AND mask.
  11144.  
  11145.  
  11146.         ╔═══╗     You can use a variation of bit block tiling as a sort of
  11147.         ║ T ║     software character generator. If you define a group of bit
  11148.         ║ I ║     blocks, each of which represents a character in a
  11149.         ║ P ║     character set, you can tile the screen with characters.
  11150.         ╚═══╝     This is one technique for displaying proportionally
  11151.                   spaced characters.
  11152.  
  11153.  
  11154.  Animation
  11155.  
  11156.  
  11157.       PC and PS/2 video subsystems have no built-in hardware to support
  11158.       animation. Consequently, moving images across the screen is a task
  11159.       relegated to software. (This is a good reason to make your video
  11160.       graphics routines as efficient as possible.) Several software
  11161.       techniques can produce real-time video animation. Each technique is
  11162.       best suited to a particular type of animation.
  11163.  
  11164.  
  11165.  XOR Animation
  11166.  
  11167.       You can take advantage of the reversibility of the logical XOR
  11168.       operation to make any pixel or set of pixels appear to move across the
  11169.       display. To make an object appear to move, XOR it into the video
  11170.       buffer twice. The object flickers onto the screen the first time it is
  11171.       drawn. It immediately disappears the second time it is drawn. If you
  11172.       repeatedly redraw the object in a slightly different position, it
  11173.       appears to move across the screen.
  11174.  
  11175.  
  11176.       Outlining
  11177.       Consider the C fragment in Listing 11-5. This bit of code makes a
  11178.       circle appear to grow outward from its center by repeatedly XORing it
  11179.       into the video buffer with a gradually increasing radius.
  11180.  
  11181.  
  11182.  ───────────────────────────────────────────────────────────────────────────
  11183.  
  11184.       Listing 11-5.  XORing a circle into the video buffer.
  11185.  
  11186.  ───────────────────────────────────────────────────────────────────────────
  11187.  
  11188.  
  11189.       This technique is frequently used interactively to outline a
  11190.       rectangular area of the display. The outline is rapidly XORed into and
  11191.       out of the video buffer as the user moves a pointing device such as a
  11192.       mouse. Just as the circle created by the routine in Listing 11-5
  11193.       appears to grow, a rectangular outline can appear to move, grow, or
  11194.       shrink in response to the user's actions.
  11195.  
  11196.       The routine in Listing 11-6 slides a rectangle across the screen. At
  11197.       each iteration, the rectangle is drawn and then erased using lines
  11198.       that are XORed into the video buffer. In this example, the rectangle's
  11199.       onscreen location is changed within an iterative loop. In practice,
  11200.       however, the rectangle's size and location could be changed in
  11201.       response to input from the keyboard or from a pointing device. In this
  11202.       case, the rectangle would be erased and redrawn whenever the input
  11203.       indicated a change in position.
  11204.  
  11205.  
  11206.  ───────────────────────────────────────────────────────────────────────────
  11207.  
  11208.       Listing 11-6.  XORing a rectangle into the video buffer.
  11209.  
  11210.  ───────────────────────────────────────────────────────────────────────────
  11211.  
  11212.  
  11213.       Rubberbanding
  11214.       A related technique based on the XOR operation is rubberbanding, in
  11215.       which a moving object remains attached to a stationary object by a
  11216.       straight line. The technique is called rubberbanding because the line
  11217.       that connects the two objects appears to stretch as it moves. Listing
  11218.       11-7 is similar to Listing 11-6, but moves a rubberbanded line around
  11219.       the point at (150,100).
  11220.  
  11221.  
  11222.  ───────────────────────────────────────────────────────────────────────────
  11223.  
  11224.       Listing 11-7.  XORing a line into the video buffer.
  11225.  
  11226.  ───────────────────────────────────────────────────────────────────────────
  11227.  
  11228.  
  11229.       Bit Block Moves
  11230.       You can use XOR with a bit block move to animate any arbitrary group
  11231.       of pixels. But use this technique only with a relatively small bit
  11232.       block, since generally a bit block contains many more pixels to be
  11233.       drawn and redrawn than does a line or a rectangle. The longer it takes
  11234.       to maneuver the bit block around the screen, the slower your video
  11235.       routine performs.
  11236.  
  11237.  
  11238.       Problems with XOR Animation
  11239.       Objects that are animated by XOR operations always flicker. The reason
  11240.       is obvious: An object is visible only after you first XOR it into the
  11241.       buffer. The second XOR makes it disappear. The resulting flicker draws
  11242.       attention to the animated object, and may be desirable, particularly
  11243.       if the object is repeatedly XORed even when you aren't moving it. On
  11244.       the other hand, the flickering can be distracting, particularly on
  11245.       color displays where the XORed object alternates between two garish
  11246.       colors.
  11247.  
  11248.       You can sometimes alleviate flickering during XOR animation by
  11249.       inserting a software "pause" between the first and second XOR
  11250.       operations. This pause can be an empty loop, a call to some short
  11251.       subroutine, or perhaps a wait for the next vertical blanking interval.
  11252.       In any case, because the XORed object remains on the screen slightly
  11253.       longer, it may flicker less.
  11254.  
  11255.       The animated image can disappear if the loop that performs the XOR
  11256.       operations inadvertently becomes synchronized with the display refresh
  11257.       cycle. In this situation, the animated object is never visible if both
  11258.       XOR operations occur outside the relatively brief time interval when
  11259.       the raster is displaying the relevant portion of the video buffer.
  11260.       Solving this sort of problem is tricky because it involves both the
  11261.       speed of your program and the size of the animated image.
  11262.  
  11263.  
  11264.  Overlapping Bit Block Moves
  11265.  
  11266.       In some applications, you can avoid XOR animation problems by rapidly
  11267.       redrawing a block of pixels in overlapping locations in the video
  11268.       buffer (see Figure 11-7 and Listing 11-8). The bit block in Figure
  11269.       11-7 has a margin of background pixels along its left edge. Each time
  11270.       you store the bit block in the video buffer, this margin overlaps the
  11271.       foreground pixels in the previously drawn block. Without this margin,
  11272.       unexpected streaks of foreground pixels trail the bit block as it
  11273.       moves to the right across the screen.
  11274.  
  11275.       Although they are fast enough for most purposes, the bit block move
  11276.       routines in this chapter are too slow for such performance-intensive
  11277.       applications as arcade-style video games. You can tailor the code in
  11278.       several ways to increase the animation speed if you're willing to
  11279.       sacrifice their general-purpose approach.
  11280.  
  11281.  
  11282.       ┌──┬──────────────────┐        ┌──┬──┬────────────┬──┐
  11283.       │░░│                  │        │░░│░░│            |  │
  11284.       |░░│                  │        |░░│░░│            |  │
  11285.       │░░│                  │        │░░│░░│            |  │
  11286.       |░░│                  │        |░░│░░│            |  │
  11287.       │░░│                  ├───────│░░│░░│            |  │
  11288.       |░░│                  │        |░░│░░│            |  │
  11289.       │░░│                  │        │░░│░░│            |  │
  11290.       |░░│                  │        |░░│░░│            |  │
  11291.       └──┴──────────────────┘        └──┴──┴────────────┴──┘
  11292.                                        
  11293.        │                              │  │ Margin
  11294.        │ Margin of                    │
  11295.        │ background pixels            │ Previous margin
  11296.      a.            b.
  11297.  
  11298.       Figure 11-7.  Overlapping bit block moves. The bit block is drawn
  11299.       (Figure 11-7a), then drawn again slightly to the right (Figure
  11300.       11-7b). The margin of background pixels restores the background as
  11301.       the bit block is "moved" to the right.
  11302.  
  11303.  
  11304.  ───────────────────────────────────────────────────────────────────────────
  11305.  
  11306.       Listing 11-8.  A program to move a block of pixels using the
  11307.       overlapping technique.
  11308.  
  11309.  ───────────────────────────────────────────────────────────────────────────
  11310.  
  11311.  
  11312.       One technique is to limit the bit block routines to byte-aligned (or,
  11313.       on the CGA and the HGC, word-aligned) blocks of pixels. This
  11314.       eliminates much of the bit-mask logic and lets you make full use of
  11315.       the 80x86 MOVs instruction. Another approach is to write routines that
  11316.       handle bit blocks of a fixed, predetermined size. This lets you
  11317.       replace some iterative loops in the routines with repetitive sequences
  11318.       of in-line code. Unfortunately, even highly optimized CGA and EGA
  11319.       animation routines rarely come close to the speed you can expect from
  11320.       arcade-style video display hardware.
  11321.  
  11322.  
  11323.  A Graphics-Mode Cursor
  11324.  
  11325.  
  11326.       In alphanumeric modes, the on-screen cursor indicates the location
  11327.       where your program expects the user's next input. Most alphanumeric-
  11328.       mode programs rely on the hardware-generated blinking cursor to
  11329.       indicate the current input location. In graphics modes, on the other
  11330.       hand, hardware does not support a cursor; your software must generate
  11331.       one.
  11332.  
  11333.       Implementing a cursor in a graphics mode is somewhat complicated,
  11334.       because you must draw the form that represents the cursor directly
  11335.       into the video buffer, while preserving the pixels that the operation
  11336.       overwrites. You can do this in two ways: by using XOR to display the
  11337.       cursor, or by saving and restoring the bit block that is overlaid by
  11338.       the cursor.
  11339.  
  11340.  
  11341.  XOR
  11342.  
  11343.       The simplest way to display a graphics cursor is to XOR it into and
  11344.       then out of the video buffer. This technique is the same one used to
  11345.       animate graphics images, and the same pros and cons apply.
  11346.  
  11347.       Probably the worst side effect of XORing a graphics cursor into the
  11348.       video buffer is that the color displayed for the XORed cursor can
  11349.       change with the background. The cursor can all but disappear on a
  11350.       patterned background or on a background with a displayed color near
  11351.       that of the XORed cursor.
  11352.  
  11353.       Palette programming can prevent this problem. For example, the EGA
  11354.       palette in Figure 11-8 is set up assuming that all pixels in the
  11355.       cursor shape have the value 8 (1000B) and that all preexisting pixels
  11356.       in the video buffer have a value from 0 through 7. With this
  11357.       arrangement, XORing the cursor into the video buffer causes it always
  11358.       to be displayed with color value 3FH (high-intensity white). The
  11359.       obvious drawback is that this technique halves the number of colors
  11360.       you can display.
  11361.  
  11362.  
  11363. ╓┌──────────────────┌────────────────────────────────────────────────────────╖
  11364.  Palette Register   Color Value
  11365.  ──────────────────────────────────────────────────────────────────────────
  11366.  00H                0
  11367.  01H                1
  11368.  02H                2
  11369.  03H                3
  11370.  04H                4
  11371.  05H                5
  11372.  06H                6
  11373.  07H                7
  11374.  08H                3FH
  11375.  09H                3FH
  11376.  0AH                3FH
  11377.  0BH                3FH
  11378.  0CH                3FH
  11379.  0DH                3FH
  11380.  0EH                3FH
  11381.  0FH                3FH
  11382.  
  11383.       Figure 11-8.  EGA palette values for a high-intensity white XOR
  11384.       graphics cursor.
  11385.  
  11386.  
  11387.  Bit Block Move
  11388.  
  11389.       Another approach is to make a copy of the bit block of pixels that the
  11390.       cursor replaces. You can then erase the cursor by restoring the pixels
  11391.       in the video buffer from the copy. This technique is attractive
  11392.       because it lets you use any means you choose to draw the cursor.
  11393.  
  11394.       A good way to draw the cursor, once you have made a copy of the
  11395.       underlying pixels in the video buffer, is to copy the cursor shape
  11396.       into the buffer with a bit block move. Obviously, this technique works
  11397.       best with a rectangular cursor. To draw a cursor of any arbitrary
  11398.       shape, use a two-step process (see Figure 11-9). First, zero a group
  11399.       of pixels in the shape of the cursor in the video buffer with a bit
  11400.       block AND operation. Then draw the cursor with a bit block OR or XOR
  11401.       operation.
  11402.  
  11403.         ╔═══╗     Whenever you use a graphics-mode cursor, you must ensure
  11404.         ║ T ║     that the cursor is erased before updating the video
  11405.         ║ I ║     buffer. If you do not, your program may inadvertently
  11406.         ║ P ║     update the portion of the video buffer that contains the
  11407.         ╚═══╝     cursor image. The next cursor move will restore the
  11408.                   contents of the buffer to what they were before the cursor
  11409.                   was drawn, leaving a "hole" where the cursor was (see
  11410.                   Figure 11-10).
  11411.  
  11412.  
  11413.       ███████████████                         ███████████████████████████
  11414.       ███████████████                         ███████████████████████████
  11415.       █████     █████                         ███████████     ███████████
  11416.       █████     █████           ███           ███████████ ███ ███████████
  11417.       ███         ███           ███           █████████   ███   █████████
  11418.       ███         ███         ███████         █████████ ███████ █████████
  11419.       ███         ███         ███████         █████████ ███████ █████████
  11420.       ███         ███           ███           █████████   ███   █████████
  11421.       █████     █████           ███           ███████████ ███ ███████████
  11422.       █████     █████                         ███████████     ███████████
  11423.       ███████████████                         ███████████████████████████
  11424.       ███████████████                         ███████████████████████████
  11425.       a.                    b.                c.
  11426.  
  11427.       Figure 11-9.  Drawing a graphics cursor with a 2-step mask-and-replace
  11428.       technique: First, a mask (Figure 11-9a) is ANDed into the video
  11429.       buffer. Then the cursor shape (Figure 11-9b) is ORed into the buffer
  11430.       to give the result in Figure 11-9c.
  11431.  
  11432.  
  11433.               ╔══════════════════════════════════════════╗
  11434.               ║                                          ║
  11435.               ║    Figure 11-10 is found on page 371     ║
  11436.               ║    in the printed version of the book.   ║
  11437.               ║                                          ║
  11438.               ╚══════════════════════════════════════════╝
  11439.  
  11440.       Figure 11-10.  If a graphics cursor is accidentally overwritten
  11441.       (Figure 11-10a), a "hole" appears when the cursor is erased (Figure
  11442.       11-10b).
  11443.  
  11444.  
  11445.  
  11446.             12  Some Advanced Video Programming Techniques
  11447.  
  11448.  
  11449.                      A Vertical Interrupt Handler
  11450.                           EGA and VGA ■ MCGA
  11451.  
  11452.                       Panning on the EGA and VGA
  11453.                 Positioning the Screen Window ■ Panning
  11454.                        Resizing the Video Buffer
  11455.  
  11456.                           Bit-Plane Layering
  11457.  
  11458.                        EGA and VGA Split Screen
  11459.  
  11460.                         The Light Pen Interface
  11461.                  Light Pen Position ■ Light Pen Switch
  11462.                    Determining Hercules Video Modes
  11463.  
  11464.  
  11465.  
  11466.       This chapter deals with some of the less frequently exploited
  11467.       capabilities of PC and PS/2 video subsystems. Most programmers do not
  11468.       concern themselves with these hardware features, because they are
  11469.       infrequently used in most video software. Still, each of these
  11470.       hardware features lends itself to programming techniques that can be
  11471.       used in certain applications where nothing else is as effective.
  11472.  
  11473.       Nothing in this chapter requires "gonzo programming" or any magical
  11474.       knowledge of the hardware. You should nevertheless be comfortable with
  11475.       80x86 assembly-language programming before tackling the details of
  11476.       this material. Most of the chapter describes programming techniques
  11477.       for the EGA and the VGA, but the discussions of the light pen
  11478.       interface and bit-plane layering are pertinent to Hercules adapters as
  11479.       well.
  11480.  
  11481.  
  11482.  A Vertical Interrupt Handler
  11483.  
  11484.  
  11485.       It's neither the interrupt nor the handler that's vertical--it's the
  11486.       fact that the CRTC on the EGA, the VGA, and the MCGA can generate a
  11487.       hardware interrupt at the start of the vertical blanking interval,
  11488.       that is, at the start of the scan line after the bottom line of
  11489.       displayed video buffer data. An interrupt handler for this Vertical
  11490.       Interrupt can thus update the video buffer or program the video
  11491.       hardware without interfering with the display.
  11492.  
  11493.       The interrupt is generated on interrupt request line 2 (IRQ2). The
  11494.       computer's programmable interrupt controller (PIC) is set up during
  11495.       the ROM BIOS coldstart to map IRQ2 to interrupt vector 0AH, so a
  11496.       Vertical Interrupt handler should be designed to handle interrupt 0AH.
  11497.  
  11498.         ╔═══╗     The programmable interrupt controller used in the IBM PC,
  11499.         ║ T ║     PC/AT, and PS/2 Models 50, 60, and 80 is the Intel 8259A;
  11500.         ║ I ║     in the PS/2 Model 30, the same functions are supported in
  11501.         ║ P ║     a proprietary VLSI chip, the I/O Support Gate Array. In
  11502.         ╚═══╝     all cases, however, the programming interface to the PIC
  11503.                   for managing Vertical Interrupts is the same.
  11504.  
  11505.  
  11506.  EGA and VGA
  11507.  
  11508.       The scan line number at which the interrupt is issued is 1 greater
  11509.       than the value in the CRTC's Vertical Display Enable End register
  11510.       (12H). The value in this register specifies the number of scan lines
  11511.       of video buffer data that are displayed, so the CRTC generates
  11512.       Vertical Interrupts at the start of the vertical blanking interval.
  11513.  
  11514.       Bits 4 and 5 of CRTC's Vertical Retrace End register (11H) control
  11515.       whether and when the CRTC  signals a Vertical Interrupt. You set bit 5
  11516.       to 1 to enable the CRTC to generate the interrupt. Bit 4 controls a 1-
  11517.       bit latch whose status appears in bit 7 of Input Status Register Zero
  11518.       (3C2H). You must zero bit 4 to clear the status latch. When you set
  11519.       bit 4 to 1, the latch status bit changes from 0 to 1 when the next
  11520.       vertical interrupt occurs, and remains set to 1 until you again clear
  11521.       the latch.
  11522.  
  11523.       To use the Vertical Interrupt feature, you must perform the following
  11524.       actions:
  11525.  
  11526.       ■  Point the interrupt 0AH vector to a Vertical Interrupt handler.
  11527.  
  11528.       ■  Enable IRQ2.
  11529.  
  11530.       ■  Enable the Vertical Interrupt.
  11531.  
  11532.       The routine in Listing 12-1 shows how to do this. Note how this
  11533.       routine is coordinated with the interrupt handler itself. The routine
  11534.       preserves the interrupt 0AH vector so the interrupt handler can chain
  11535.       to the previous handler if necessary, and so the routine can
  11536.       eventually restore the previous interrupt vector when the interrupt
  11537.       handler is no longer needed.
  11538.  
  11539.  
  11540.  ───────────────────────────────────────────────────────────────────────────
  11541.  
  11542.       Listing 12-1.  Handling Vertical Interrupts on the EGA and VGA.
  11543.  
  11544.  ───────────────────────────────────────────────────────────────────────────
  11545.  
  11546.  
  11547.       The handler itself, in procedure ISR0A, gains control whenever
  11548.       interrupt 0AH occurs. To distinguish between the hardware Vertical
  11549.       Interrupt on IRQ2 and a possible software interrupt 0AH, the handler
  11550.       examines bit 7 of Input Status Register Zero. If this bit is 1, a
  11551.       Vertical Interrupt has occurred, and the handler continues about its
  11552.       business. If the bit is 0, no Vertical Interrupt has occurred, so the
  11553.       handler chains to the previous interrupt 0AH handler.
  11554.  
  11555.         ╔═══╗     A drawback to using the Vertical Interrupt is that any
  11556.         ║ T ║     hardware interrupt on IRQ2 causes the status bit in Input
  11557.         ║ I ║     Status Register Zero to be set. Thus, although the status
  11558.         ║ P ║     bit can be used to detect software interrupt 0AH, an
  11559.         ╚═══╝     interrupt handler cannot distinguish between EGA Vertical
  11560.                   Interrupts and IRQ2 interrupts generated by other hardware
  11561.                   unless the other hardware can be reliably interrogated.
  11562.                   Since some other IBM PC adapters can use IRQ2 (for
  11563.                   example, the bus version of the Microsoft Mouse), you can
  11564.                   reliably use the Vertical Interrupt only when certain
  11565.                   about the exact hardware configuration of the PC on which
  11566.                   your program is running.
  11567.  
  11568.       Once the handler detects a Vertical Interrupt (that is, bit 7 of
  11569.       Input  Status Register Zero is 1), it issues a nonspecific end-of-
  11570.       interrupt (EOI) instruction to the interrupt controller so that
  11571.       subsequent IRQ2 interrupts can be processed. Reentrance is not a
  11572.       problem, because additional Vertical Interrupts will not be signalled
  11573.       until the handler itself clears and reenables the status latch. Once
  11574.       the EOI has been issued, the handler is free to perform some useful
  11575.       action. In this example, it simply increments a counter. Just before
  11576.       exiting, the handler reprograms the Vertical Retrace End register to
  11577.       enable the next Vertical Interrupt.
  11578.  
  11579.       The example in Listing 12-2 shows how you can integrate a Vertical
  11580.       Interrupt handler into a high-level program. The example is
  11581.       intentionally simple. It does nothing but count a designated number of
  11582.       Vertical Interrupts and display a message. Of course, your own
  11583.       Vertical Interrupt handler might perform more complicated actions than
  11584.       simply updating a variable. For instance, you could perform animation
  11585.       by updating the video buffer each time the interrupt occurs. You might
  11586.       also update the CRT and Attribute controllers to produce a panning
  11587.       effect using techniques described later in this chapter.
  11588.  
  11589.  
  11590.  ───────────────────────────────────────────────────────────────────────────
  11591.  
  11592.       Listing 12-2.  Using a Vertical Interrupt handler in a C program.
  11593.  
  11594.  ───────────────────────────────────────────────────────────────────────────
  11595.  
  11596.  
  11597.         ╔═══╗     Hardware support for the Vertical Interrupt feature can
  11598.         ║ T ║     vary. IBM's VGA adapter, for example, does not support
  11599.         ║ I ║     Vertical Interrupts at all. On some EGA clones, the
  11600.         ║ P ║     polarity of bit 7 in Input Status Register Zero is
  11601.         ╚═══╝     opposite to that of the equivalent EGA bit; that is, a
  11602.                   Vertical Interrupt has occurred when bit 7 is 0. (Second-
  11603.                   source manufacturers of EGA-compatible adapters do not
  11604.                   always emulate every detail of the EGA's occasionally
  11605.                   inscrutable hardware design.) To ensure that your Vertical
  11606.                   Interrupt handler works correctly on EGA clones, determine
  11607.                   the status bit's polarity when the bit is in a known state
  11608.                   and devise your test for the Vertical Interrupt
  11609.                   accordingly.
  11610.  
  11611.  
  11612.  MCGA
  11613.  
  11614.       A Vertical Interrupt handler for the MCGA, such as the one in Listing
  11615.       12-3, is similar to the handler for the EGA and the VGA. On the MCGA,
  11616.       the Interrupt Control register (11H) contains the control and status
  11617.       bits used to set up and detect a Vertical Interrupt. Zeroing bit 5 of
  11618.       the Interrupt Control register enables the MCGA to generate a Vertical
  11619.       Interrupt. Zeroing bit 4 clears the interrupt status latch. Setting
  11620.       bit 4 to 1 allows the MCGA to detect subsequent interrupts. Bit 6 is
  11621.       the interrupt status bit. The MCGA sets this bit to 1 to indicate that
  11622.       a Vertical Interrupt has occurred.
  11623.  
  11624.  
  11625.  ───────────────────────────────────────────────────────────────────────────
  11626.  
  11627.       Listing 12-3.  Handling Vertical Interrupts on the MCGA.
  11628.  
  11629.  ───────────────────────────────────────────────────────────────────────────
  11630.  
  11631.  
  11632.         ╔═══╗     On the EGA and MCGA, if a Vertical Interrupt handler gains
  11633.         ║ T ║     control while a video BIOS (INT 10H) function is
  11634.         ║ I ║     executing, the interrupt handler may inadvertently disrupt
  11635.         ║ P ║     BIOS CRTC programming. The reason can be traced to a
  11636.         ╚═══╝     subroutine buried in the IBM BIOS in these video
  11637.                   subsystems. This subroutine is called by several video
  11638.                   BIOS routines to perform I/O port output to video hardware
  11639.                   registers, including CRT Controller, Sequencer, Graphics
  11640.                   Controller, and Attribute Controller registers.
  11641.  
  11642.                   Unfortunately, this subroutine is not impervious to
  11643.                   interrupts. It contains a sequence of two 8-bit port
  11644.                   writes (OUT DX,AL). The first OUT loads the designated
  11645.                   address register. The second OUT writes a data byte to the
  11646.                   corresponding data register. If an interrupt occurs
  11647.                   between the two port writes, and if the interrupt handler
  11648.                   itself writes to the same port, the BIOS subroutine's
  11649.                   second port write may be invalid.
  11650.  
  11651.                   To avoid this situation on the EGA and MCGA, the Vertical
  11652.                   Interrupt handlers in Listings 12-1 and 12-3 read the
  11653.                   value of the CRTC Address register at port 3D4H (3B4H on
  11654.                   an EGA with a monochrome display). On the EGA, this value
  11655.                   is only readable for about 15 milliseconds after the port
  11656.                   has been written, but this is enough time for the Vertical
  11657.                   Interrupt handler to read and preserve the value of the
  11658.                   CRTC Address register. The handler can thus restore the
  11659.                   value before it returns from the interrupt.
  11660.  
  11661.  
  11662.  Panning on the EGA and VGA
  11663.  
  11664.  
  11665.       The 256 KB video buffer of the EGA and the VGA can store several
  11666.       screens of data. Thus, in a sense, what is displayed represents a
  11667.       "screen window," a sort of hardware window into the contents of the
  11668.       video buffer.
  11669.  
  11670.  
  11671.  Positioning the Screen Window
  11672.  
  11673.       On an adapter such as the MDA or the CGA, the CRT Controller's Start
  11674.       Address registers control which portion of the video buffer is
  11675.       displayed. Because these registers contain a byte offset into the
  11676.       video buffer, you can control the position of the screen window only
  11677.       to the nearest byte. On the other hand, the CRT Controller on the EGA
  11678.       and the VGA can position the start of the screen window at any given
  11679.       pixel position.
  11680.  
  11681.       In graphics modes, the contents of the CRTC's Start Address High and
  11682.       Start Address Low registers (0CH and 0DH) locate the screen window to
  11683.       the nearest byte offset in the video buffer. The contents of the
  11684.       CRTC's Preset Row Scan register (08H) and the Attribute Controller's
  11685.       Horizontal Pel Pan register (13H) "fine-tune" the screen window's
  11686.       position pixel by pixel (see Figure 12-1).
  11687.  
  11688.       When you change the screen window's position smoothly, pixel by pixel,
  11689.       the displayed image appears to pan across the screen. A convenient way
  11690.       to do this is to write a routine that locates the screen window at a
  11691.       specified pixel position and then call the routine iteratively from
  11692.       within a loop. This routine, as demonstrated in Listing 12-4, must
  11693.       distinguish between alphanumeric and graphics modes. It must also
  11694.       handle a 9-pixel-wide character matrix in VGA and EGA monochrome
  11695.       alphanumeric modes.
  11696.  
  11697.  
  11698.                ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
  11699.       ┌──────0│   │ │   │ │   │ │▓▓▓│ │▓▓▓│ │   │ │   │ │   │
  11700.       │        └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘
  11701.  Start Address ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
  11702.  registers    1│   │ │   │ │▓▓▓│ │   │ │   │ │▓▓▓│ │   │ │   │
  11703.  specify this  └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘
  11704.  character     ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
  11705.               2│   │ │▓▓▓│ │   │ │   │ │   │ │   │ │▓▓▓│ │   │ Origin
  11706.                └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ of screen
  11707.                ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ window
  11708.               3│   │ │▓▓▓│ │   │ │   │ │   │ │   │ │▓▓▓│ │   │   ║
  11709.                └───┘ └───┘ └───┘ └───┘ └─╔═══╧═══════════════════╝
  11710.                ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌──┐ ┌───┐ ┌───┐ ┌───┐
  11711.         ┌────4│   │ │▓▓▓│ │▓▓▓│ │▓▓▓│ │███│ │▓▓▓│ │▓▓▓│ │   │══════╗
  11712.         │      └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘       ║
  11713.         │      ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐       ║
  11714.         │     5│   │ │▓▓▓│ │   │ │   │ │   │ │   │ │▓▓▓│ │   │       ║
  11715.         │      └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘       ║
  11716.         │      ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐       ║
  11717.         │     6│   │ │▓▓▓│ │   │ │   │ │   │ │   │ │▓▓▓│ │   │       ║
  11718.         │      └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘       ║
  11719.         │      ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐       ║
  11720.         │     7│   │ │   │ │   │ │   │ │   │ │   │ │   │ │   │       ║
  11721.         │      └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘       ║
  11722.         │        0     1     2     3     4     5     6     7         ║
  11723.         │                                                           ║
  11724.         │                                │                           ║
  11725.  Value for Preset               Value for Horizontal                 ║
  11726.  Row Scan register              Pel Pan register                     ║
  11727.                                              ╔═══════════════════════╝
  11728.                                           ┌──╫──────────────────────┐
  11729.                                           │  ABCDEFG──────────┐     │
  11730.                                           │  HIJKLMN          │     │
  11731.                                           │  OPQR             │     │
  11732.                                           │  └────────────────┘     │
  11733.                                           │  Displayed portion      │
  11734.                                           │  of buffer              │
  11735.                                           └─────────────────────────┘
  11736.                                                  Video buffer
  11737.  
  11738.       Figure 12-1.  Control of the displayed portion of the video buffer in
  11739.       alphanumeric modes.
  11740.  
  11741.  
  11742.  ───────────────────────────────────────────────────────────────────────────
  11743.  
  11744.       Listing 12-4.  Setting the screen origin on the EGA and VGA.
  11745.  
  11746.  ───────────────────────────────────────────────────────────────────────────
  11747.  
  11748.  
  11749.       ScreenOrigin() accepts as input the x- and y-coordinates of the pixel
  11750.       that identifies the origin (the upper left corner) of the screen. The
  11751.       routine first updates the CRTC's Start Address registers. In effect,
  11752.       this positions the screen at the upper left pixel of the character
  11753.       that contains the origin in alphanumeric modes, or at the leftmost
  11754.       pixel in the byte that contains the origin in graphics modes. Then
  11755.       ScreenOrigin() positions the virtual screen exactly by updating
  11756.       the Horizontal Pel Panning and Preset Row Scan registers.
  11757.  
  11758.       The content of the Attribute Controller Horizontal Pel Panning
  11759.       register corresponds to the bit offset of the pixel in the screen's
  11760.       upper left corner. The value to store in this register is thus
  11761.  
  11762.       x MOD 8
  11763.  
  11764.       In the case of 9-pixel characters in VGA alphanumeric modes and in 80-
  11765.       by-25 monochrome mode on the EGA, the value is
  11766.  
  11767.       (x + 8) MOD 9
  11768.  
  11769.       The Horizontal Pel Panning register is programmed the same way in both
  11770.       alphanumeric and graphics modes. This is not the case, however, for
  11771.       the CRTC's Preset Row Scan register, which controls the vertical
  11772.       position of the start of the screen.
  11773.  
  11774.       In alphanumeric modes, the number of rows of pixels displayed for each
  11775.       row of characters in the video buffer depends on the height of the
  11776.       displayed character matrix. This is the value stored as POINTS in the
  11777.       ROM BIOS Video Display Data Area. The Start Address registers position
  11778.       the virtual screen to the particular character in the video buffer,
  11779.       and the Preset Row Scan register indicates which line in the character
  11780.       matrix contains the origin of the virtual screen. The Preset Row Scan
  11781.       register thus contains a value between 0 (the top line of the
  11782.       character) and POINTS-1 (the bottom line). In graphics modes, the
  11783.       pixels in each byte in the video buffer correspond one-to-one with
  11784.       pixels on the screen, so the Preset Row Scan register always contains
  11785.       0.
  11786.  
  11787.       To avoid interference with the display, updates to the Horizontal Pel
  11788.       Panning, Preset Row Scan, and Start Address registers should be
  11789.       synchronized with the display refresh cycle. The Horizontal Pel
  11790.       Panning register must be updated during the vertical blanking
  11791.       interval. On the other hand, the CRTC samples the values in the Start
  11792.       Address and Preset Row Scan registers at the beginning of vertical
  11793.       retrace, so these registers should be updated when vertical retrace is
  11794.       not active.
  11795.  
  11796.  
  11797.  Panning
  11798.  
  11799.       The routine in Listing 12-5 shows how you can call ScreenOrigin() to
  11800.       pan the screen up and down or across the video buffer. Because the
  11801.       position of the virtual screen always changes during a vertical
  11802.       blanking interval, the panning effect is smooth, with no interference
  11803.       on the screen.
  11804.  
  11805.  
  11806.  ───────────────────────────────────────────────────────────────────────────
  11807.  
  11808.       Listing 12-5.  A routine to perform smooth pixel-by-pixel panning on
  11809.       an EGA or VGA.
  11810.  
  11811.  ───────────────────────────────────────────────────────────────────────────
  11812.  
  11813.  
  11814.  Resizing the Video Buffer
  11815.  
  11816.       Horizontal panning introduces a problem. The way the video buffer is
  11817.       normally mapped, the first byte of each line of data in the buffer
  11818.       immediately follows the last byte of the previous line. If you try to
  11819.       pan horizontally with this map, each line appears to wrap around the
  11820.       screen as the screen window moves across the video buffer. To perform
  11821.       horizontal panning usefully, you should resize the video buffer so
  11822.       each line of data in it is wider than the screen window.
  11823.  
  11824.       The value in the CRT Controller's Offset register (13H) controls the
  11825.       way the CRTC maps lines in the video buffer. As it scans the raster,
  11826.       the CRTC uses the value in this register to locate the start of each
  11827.       line in the video buffer map. Normally, lines in the video buffer are
  11828.       the same width as displayed lines. Increasing the value in the Offset
  11829.       register widens the lines in the video buffer map so only part of each
  11830.       line can be displayed at one time. This lets you pan horizontally
  11831.       without wraparound.
  11832.  
  11833.       For example, consider how you could double the logical width of the
  11834.       video buffer in 80-by-25 alphanumeric mode. By default, the video BIOS
  11835.       stores the value 28H in the CRTC's Offset register, so the CRTC
  11836.       regards each line in the buffer as being 40 words (80 bytes) wide.
  11837.       Although each logical line in the buffer contains 160 bytes of data
  11838.       (80 character codes and 80 attribute bytes), character codes and
  11839.       attributes are stored in different video memory maps (see Figure 10-3
  11840.       in Chapter 10). Thus, to double the logical line width, store 50H (80
  11841.       decimal) in the CRTC's Offset register. The CRTC will still display 80
  11842.       characters in each row on the screen, but it skips 160 characters of
  11843.       data between rows of characters in the  video buffer.
  11844.  
  11845.       When you resize the video buffer by programming the CRTC's Offset
  11846.       register, be careful not to exceed the bounds of the 256 KB video
  11847.       buffer. For example, in 640-by-350 16-color graphics mode, one
  11848.       screen's worth of pixels occupies 28,000 bytes (80 bytes per line *
  11849.       350 lines) in each of the 64 KB video memory maps. If you resize the
  11850.       video buffer by increasing the value stored in the CRTC Offset
  11851.       register, you cannot go beyond 187 bytes per line in this video mode
  11852.       without exceeding the 64 KB limit.
  11853.  
  11854.       The routine BufferDims() in Listing 12-6a can be called to
  11855.       redimension the video buffer in either graphics or alphanumeric modes.
  11856.       It accepts as parameters the desired horizontal and vertical
  11857.       dimensions of the buffer in pixels. The routine updates the relevant
  11858.       variables in the video BIOS data area and then programs the CRTC
  11859.       Offset register with the appropriate value. The example in Listing 12-
  11860.       6b shows how BufferDims() could be called to transform a default
  11861.       80-by-25 alphanumeric mode into a 160-by-102 mode in which the Pan()
  11862.       routine in Listing 12-5 can be used.
  11863.  
  11864.  
  11865.  ───────────────────────────────────────────────────────────────────────────
  11866.  
  11867.       Listing 12-6a.  Redimensioning the video buffer.
  11868.  
  11869.  ───────────────────────────────────────────────────────────────────────────
  11870.  
  11871.  
  11872.  ───────────────────────────────────────────────────────────────────────────
  11873.  
  11874.       Listing 12-6b.  Creating a 160-by-102 alphanumeric mode.
  11875.  
  11876.  ───────────────────────────────────────────────────────────────────────────
  11877.  
  11878.  
  11879.  Bit-Plane Layering
  11880.  
  11881.  
  11882.       In EGA and VGA 16-color graphics modes and in the InColor Card's 720-
  11883.       by-348 16-color mode, you can display any combination of the four bit
  11884.       planes. On the EGA and VGA, the four low-order bits of the Attribute
  11885.       Controller's Color Plane Enable register (12H) control which bit
  11886.       planes are displayed. Similarly, on the InColor Card, the four low-
  11887.       order bits of the Plane Mask register (18H) determine which bit planes
  11888.       are displayed. In all three subsystems, all four bits are set to 1 to
  11889.       enable the display of all four bit planes. You can zero any
  11890.       combination of these bits to prevent display of the corresponding bit
  11891.       planes.
  11892.  
  11893.       When you disable a bit plane in this way, pixel values are interpreted
  11894.       as though the corresponding bit in each pixel were set to 0. The
  11895.       contents of a disabled bit plane are unaffected. This means you can
  11896.       draw different images into different bit planes and display them
  11897.       selectively. When bit planes containing different images are displayed
  11898.       together, the images appear to overlap, as if the bit planes were
  11899.       transparent and layered one above the other.
  11900.  
  11901.       Consider the example in Figure 12-2. The grid is drawn in bit plane 3
  11902.       and the cylinder in bit planes 0 through 2. (A quick way to draw both
  11903.       figures into the bit planes is to OR the appropriate pixel values into
  11904.       the video buffer.) If you use a default 16-color palette, the grid
  11905.       appears gray, and the cylinder can have any of the usual eight
  11906.       unintensified colors.
  11907.  
  11908.  
  11909.               ╔══════════════════════════════════════════╗
  11910.               ║                                          ║
  11911.               ║    Figure 12-2 is found on page 395      ║
  11912.               ║    in the printed version of the book.   ║
  11913.               ║                                          ║
  11914.               ╚══════════════════════════════════════════╝
  11915.  
  11916.       Figure 12-2.  Bit-plane layering. The cylinder's pixels have values
  11917.       between 0 and 7 (bit planes 0 through 2); the grid's pixels have the
  11918.       value 8 (bit plane 3 only). Selectively enabling or disabling bit
  11919.       planes 0 through 2 and bit plane 3 displays the cylinder, the grid, or
  11920.       both.
  11921.  
  11922.  
  11923.       If all four bit planes are displayed, both grid and cylinder appear on
  11924.       the screen. If you disable bit plane 3, the grid disappears. If you
  11925.       disable bit planes 0 through 2, displaying only bit plane 3, the
  11926.       cylinder disappears and only the grid is visible. In all three cases,
  11927.       the contents of the bit planes remain intact.
  11928.  
  11929.       In using the default palette register values with the grid and
  11930.       cylinder, you'll find the pixels at which the grid and cylinder
  11931.       intersect are displayed with intensified colors. You can avoid this by
  11932.       updating the palette so that the colors displayed for the intersection
  11933.       points (pixel values 9 through 0FH) are the same as the corresponding
  11934.       unintensified colors (1 through 7). Then, when both grid and cylinder
  11935.       are displayed, the cylinder appears in front of the grid.
  11936.  
  11937.  
  11938.  EGA and VGA Split Screen
  11939.  
  11940.  
  11941.       You can configure the CRT Controller on the EGA and the VGA to display
  11942.       two different portions of the video buffer on the same screen (see
  11943.       Figure 12-3). To do this, program the CRTC's Line Compare register
  11944.       (18H) with the raster line at which you want to split the screen, as
  11945.       shown in Listing 12-7a and Listing 12-7b.
  11946.  
  11947.  
  11948.                     ┌─────────────────────────────────────────────────────┐
  11949.  CRTC Start─────── │ ┌─────────────────────────────────────────────────┐ │
  11950.  Address registers  │ │  ──────                                         │ │
  11951.                     │ │  ─────────────                                  │ │
  11952.                     │ │  ────────  ──────                               │ │
  11953.                     │ │  ───── ──────── ──                              │ │
  11954.                     │ │                                                 │ │
  11955.  Start of video────│ ├─────────────────────────────────────────────────┤ │
  11956.  buffer             │ │  ─────  ─────                                   │ │
  11957.                     │ │  ════  ════  ════  ════  ════                   │ │
  11958.                     │ │  ════  ════  ════  ════  ════                   │ │
  11959.                     │ │  ════  ════  ════  ════  ════                   │ │
  11960.                     │ │  ════  ════  ════  ════  ════                   │ │
  11961.                     │ │                                                 │ │
  11962.                     │ └─────────────────────────────────────────────────┘ │
  11963.                     └─────────────────────────────────────────────────────┘
  11964.  
  11965.       Figure 12-3.  Appearance of an EGA or VGA split screen. The top part
  11966.       of the screen displays data from the location in the video buffer
  11967.       specified by the CRTC Start Address registers. The bottom part of the
  11968.       screen displays data from the start of the video buffer.
  11969.  
  11970.  
  11971.       The contents of the CRTC Start Address registers determine which
  11972.       portion of the video buffer is displayed in the top part of the
  11973.       screen. As the raster is drawn during each display refresh cycle, the
  11974.       CRTC compares the current scan line with the value in the Line Compare
  11975.       register. When the values are equal, the CRTC resets its internal
  11976.       address counter so that the remaining scan lines in the raster are
  11977.       drawn using data from the start of the video buffer. Thus, the top of
  11978.       the video buffer is always displayed in the bottom part of the split
  11979.       screen.
  11980.  
  11981.       Both the EGA and the VGA accommodate Line Compare values larger than
  11982.       eight bits (0FFH or 255 scan lines) by using other CRTC registers to
  11983.       contain additional high-order bits. Thus, bit 8 of the Line Compare
  11984.       value is represented in bit 4 of the CRTC Overflow register (07H).
  11985.       On the VGA, a ninth bit must also be specified for the Line Compare
  11986.       value; this bit is represented in bit 6 of the the Maximum Scan Line
  11987.       register (09H). Programming the CRTC with a Line Compare value thus
  11988.       requires you to update two different registers on the EGA and three
  11989.       different registers on the VGA.
  11990.  
  11991.  
  11992.  ───────────────────────────────────────────────────────────────────────────
  11993.  
  11994.       Listing 12-7a.  Splitting the screen on the EGA.
  11995.  
  11996.  ──────────────────────────────────────────────────────────────────────────
  11997.  
  11998.  
  11999.  ───────────────────────────────────────────────────────────────────────────
  12000.  
  12001.       Listing 12-7b.  Splitting the screen on the VGA.
  12002.  
  12003.  ───────────────────────────────────────────────────────────────────────────
  12004.  
  12005.  
  12006.         ╔═══╗     Because the CRTC uses the Line Compare value while it is
  12007.         ║ T ║     actively updating the raster, the best time to change this
  12008.         ║ I ║     value is during a vertical retrace interval as in Listings
  12009.         ║ P ║     12-7a and 12-7b.
  12010.         ╚═══╝
  12011.  
  12012.                   The video BIOS default Line Compare value is the maximum
  12013.                   possible value (1FFH on the EGA, 3FFH on the VGA). Use
  12014.                   this default value to "unsplit" the screen. There are also
  12015.                   certain values that the CRTC does not handle in a useful
  12016.                   manner. On both the EGA and VGA, do not specify a Line
  12017.                   Compare value that is between the Vertical Retrace Start
  12018.                   and Vertical Total values. Also, in 200-line modes on the
  12019.                   VGA, the Line Compare register value should be an even
  12020.                   number.
  12021.  
  12022.                   In native graphics modes in the IBM EGA, the CRTC
  12023.                   duplicates the scan line at which the screen is split.
  12024.                   This anomaly is also found in some EGA clones.
  12025.  
  12026.       You might find it convenient to regard the bottom portion of the split
  12027.       screen as a sort of window superimposed on the top portion. Use the
  12028.       first portion of the video buffer for the window foreground (the lower
  12029.       part of the split screen) and some other portion of the buffer for the
  12030.       background.
  12031.  
  12032.       One attractive way to use the split-screen feature is to scroll the
  12033.       split smoothly up or down the screen. Do this by incrementing or
  12034.       decrementing the value in the Line Compare register within a loop, as
  12035.       is done by the routine in Listing 12-8.
  12036.  
  12037.  
  12038.  ───────────────────────────────────────────────────────────────────────────
  12039.  
  12040.       Listing 12-8.  Smooth vertical scrolling of a split screen on the EGA
  12041.       or VGA.
  12042.  
  12043.  ───────────────────────────────────────────────────────────────────────────
  12044.  
  12045.  
  12046.  The Light Pen Interface
  12047.  
  12048.  
  12049.       On most video subsystems covered in this book, the CRT Controller can
  12050.       return the position of a light pen. When you trigger a light pen, it
  12051.       sends a signal to the CRTC at the moment the video display's electron
  12052.       beam sweeps past the pen's light sensor. The CRTC responds by storing
  12053.       the current value of its internal address counter into its Light Pen
  12054.       High and Light Pen Low registers. This value corresponds to the offset
  12055.       in the video buffer of the data displayed in the raster at the point
  12056.       where the light pen was triggered. Thus, the value in the Light Pen
  12057.       High and Low registers can be translated into row and column
  12058.       coordinates for screen locations.
  12059.  
  12060.         ╔═══╗     You can't attach a light pen to IBM's MDA, but Hercules
  12061.         ║ T ║     monochrome adapters can support one. However, a light pen
  12062.         ║ I ║     used with a monochrome display must be capable of
  12063.         ║ P ║     operating with the high-persistence P39 phosphor used in
  12064.         ╚═══╝     green monochrome displays.
  12065.  
  12066.  
  12067.  Light Pen Position
  12068.  
  12069.       The light pen position that the CRTC returns is not an exact pixel
  12070.       location. One reason is simply that the value returned in the CRTC's
  12071.       Light Pen registers is a byte offset into the video buffer, so the
  12072.       light pen's horizontal position can be determined only to the nearest
  12073.       byte of pixels. Another source of inaccuracy is that the CRTC chip
  12074.       itself introduces a small amount of delay between the time it receives
  12075.       a signal from the light pen and the time it stores a value in its
  12076.       Light Pen registers. The value returned in the Light Pen registers
  12077.       thus can be as much as 5 bytes too large; the actual amount of error
  12078.       must be determined empirically.
  12079.  
  12080.       The light pen programming interface, shown in Figure 12-4, is similar
  12081.       on all IBM and Hercules adapters. To determine a light pen's position,
  12082.       your program must first reset the CRTC's light pen latch by writing a
  12083.       0 to I/O port 3DBH (3BBH on an MDA, a Hercules adapter, or an EGA with
  12084.       a monochrome display). Then it must poll the Status Port at 3DAH (3BAH
  12085.       in monochrome modes). When bit 1 of the Status Port value changes from
  12086.       0 to 1, the light pen has been triggered and the routine can obtain
  12087.       its location from the CRTC (see Listing 12-9a).
  12088.  
  12089.       After reading the light pen location from the Light Pen registers, you
  12090.       must apply an empirical correction for the intrinsic delay in the
  12091.       CRTC. The routine in Listing 12-9b, for the EGA's 80-by-25
  12092.       alphanumeric mode, subtracts 7 from the value that the CRTC returns.
  12093.       To convert the result into a pixel location, subtract the value in the
  12094.       Start Address High and Start Address Low registers from the corrected
  12095.       CRTC value. (You can get the Start Address value by dividing the value
  12096.       in CRT_START in the Video Display Data Area by 2. You can also read it
  12097.       from the Start Address High and Start Address Low registers on the
  12098.       EGA, the HGC+, and the InColor Card.) Then divide the difference by
  12099.       the number of characters in each row of the video buffer. (This value
  12100.       is represented in the CRTC's Horizontal Displayed register, or in
  12101.       CRT_COLS on the EGA.) The quotient is the y-coordinate of the light
  12102.       pen location. The remainder is the character column corresponding to
  12103.       the position of the light pen.
  12104.  
  12105.  
  12106. ╓┌────────────────────────────────┌──────────────────────────────────────────╖
  12107.  I/O Port                         Function
  12108.  ───────────────────────────────────────────────────────────────────────────
  12109.  MDA, HGC, HGC+, and InColor Card
  12110.  3B9H                             Set light pen latch
  12111.  3BAH bit 1                       Light pen trigger
  12112.  3BAH bit 2                       Light pen switch (IBM adapters only)
  12113.  3BBH                             Reset light pen latch
  12114.  
  12115.  CGA, EGA
  12116.  3DAH bit 1                       Light pen trigger
  12117.  3DAH bit 2                       Light pen switch (IBM adapters only)
  12118.  3DBH                             Reset light pen latch
  12119.  I/O Port                         Function
  12120. 3DBH                             Reset light pen latch
  12121.  3DCH                             Set light pen latch
  12122.  
  12123.       Figure 12-4.  Light pen programming interface. Note: In EGA
  12124.       monochrome modes, read light pen trigger and switch status from 3BAH
  12125.       instead of 3DAH.
  12126.  
  12127.  
  12128.  ───────────────────────────────────────────────────────────────────────────
  12129.  
  12130.       Listing 12-9a.  Getting the light pen's location from the
  12131.       CRTC.
  12132.  
  12133.  ──────────────────────────────────────────────────────────────────────────
  12134.  
  12135.  
  12136.  ───────────────────────────────────────────────────────────────────────────
  12137.  
  12138.       Listing 12-9b.  Using GetLightPen in a C program.
  12139.  
  12140.  ───────────────────────────────────────────────────────────────────────────
  12141.  
  12142.  
  12143.       If this seems like more trouble than it's worth, you're probably
  12144.       right. On IBM video adapters, as well as in Hercules adapters'
  12145.       alphanumeric modes, you can call INT 10H function 4 to return the
  12146.       light pen location. If you plan to use a light pen in Hercules
  12147.       graphics modes, however, you're on your own.
  12148.  
  12149.  
  12150.  Light Pen Switch
  12151.  
  12152.       On IBM adapters, you can determine whether the light pen switch is
  12153.       depressed by examining bit 2 of the Status Port value returned from
  12154.       port 3DAH (3BAH in monochrome modes). This bit is set to 1 while the
  12155.       switch is closed. It returns to 0 when the switch is opened. You
  12156.       should usually test the status of the light pen switch before
  12157.       attempting to read the CRTC's Light Pen registers.
  12158.  
  12159.  
  12160.  Determining Hercules Video Modes
  12161.  
  12162.       The Light Pen registers can also be used to determine video modes on
  12163.       Hercules adapters. In most applications, determining the current video
  12164.       mode is not a problem, because the application itself establishes the
  12165.       mode. Sometimes, however, a program may not know the video mode a
  12166.       priori. For example, a screen dump program (see Appendix B) may need
  12167.       to determine the video mode to correctly interpret the contents of the
  12168.       video buffer. Similarly, a RAM-resident "pop up" program should save
  12169.       and then restore the video mode into which it "pops."
  12170.  
  12171.       You can easily determine the current ROM BIOS video mode by calling
  12172.       INT 10H function 0FH. The task is more difficult for the Hercules
  12173.       adapters, because the BIOS does not keep track of the video mode. You
  12174.       can sometimes infer the video mode from the Video Display Data Area
  12175.       variables CRT_COLS, CRT_LEN, and POINTS, but not everybody who writes
  12176.       programs for Hercules adapters keeps these variables updated.
  12177.  
  12178.       Moreover, there is no direct way to interrogate the hardware to
  12179.       determine the video mode. For example, the Mode Control register
  12180.       (3B8H), used to select the video mode, is unfortunately a write-only
  12181.       register. Nevertheless, you can infer a Hercules adapter's video mode
  12182.       by latching the 6845's Light Pen High and Low registers (10H and 11H)
  12183.       at the start of vertical retrace, as shown in Listing 12-10.
  12184.  
  12185.  
  12186.  ───────────────────────────────────────────────────────────────────────────
  12187.  
  12188.       Listing 12-10.  Identifying the current video mode on a Hercules
  12189.       adapter.
  12190.  
  12191.  ───────────────────────────────────────────────────────────────────────────
  12192.  
  12193.  
  12194.       The routine in Listing 12-10 waits for the start of vertical retrace
  12195.       and triggers the light pen at this point with an OUT instruction to
  12196.       port 3B9H. The Light Pen registers reflect the value of the CRTC's
  12197.       internal address counter at the point where vertical retrace begins.
  12198.       (This value is the product of the values in the CRTC Horizontal
  12199.       Displayed and Vertical Sync registers.) You can expect the Light Pen
  12200.       registers to contain at least 7D0H (80 words per character row x 25
  12201.       rows) in 80-by-25 alphanumeric mode and 0F4BH (45 words per character
  12202.       row x 87 rows) in 720-by-348 graphics mode. Inspecting the Light Pen
  12203.       value thus reveals whether the HGC is in alphanumeric or graphics
  12204.       mode.
  12205.  
  12206.         ╔═══╗     In practice, the Light Pen value returned is somewhat
  12207.         ║ T ║     larger than these expected values because of the delay in
  12208.         ║ I ║     the CRTC timing. This imprecision makes the technique
  12209.         ║ P ║     somewhat less useful on the HGC+ and InColor cards, where
  12210.         ╚═══╝     you must distinguish among all the different character
  12211.                   sizes that can be displayed by the CRTC in alphanumeric
  12212.                   mode. For example, the value returned by GetHercMode()
  12213.                   when 9-by-8 characters are displayed is near 0DC0H (80 x
  12214.                   44)  and near 0DB6H (90 * 39) when 8-by-9 characters are
  12215.                   displayed. Because the Light Pen value is inexact, you may
  12216.                   not be able to distinguish these two different CRTC
  12217.                   configurations.
  12218.  
  12219.  
  12220.  
  12221.            13  Graphics Subroutines in High-Level Languages
  12222.  
  12223.  
  12224.                      Linking Graphics Subroutines
  12225.                            Subroutine Calls
  12226.                  Interrupts to a Memory-Resident Driver
  12227.                               Inline Code
  12228.  
  12229.                            Global Data Areas
  12230.  
  12231.                       Layered Graphics Interfaces
  12232.                       Direct Hardware Programming
  12233.                         Extended BIOS Interface
  12234.                          High-Level Interface
  12235.  
  12236.  
  12237.  
  12238.       Most programming examples in this book are written in assembly
  12239.       language, the language of choice for programs that need to control
  12240.       hardware precisely and to run as fast as possible. Nevertheless, most
  12241.       IBM PC programmers prefer not to write large applications entirely in
  12242.       assembly language because they can write, debug, and maintain a
  12243.       program in a high-level language much more effectively.
  12244.  
  12245.       As you write the code for a program that produces video output, you
  12246.       must balance the convenience and conceptual clarity a high-level
  12247.       language provides against the speed and exact control provided by
  12248.       assembly language. A good rule of thumb is to use assembly language
  12249.       whenever you directly access the video buffer or the video subsystem's
  12250.       control registers. The rest of the time, you can generally obtain
  12251.       satisfactory performance using any compiled high-level language.
  12252.  
  12253.       This chapter focuses on the interface between programs written in
  12254.       high-level languages and the low-level, assembly-language drivers that
  12255.       actually access the video hardware. You can implement the interface in
  12256.       several ways. The method you select should depend on the language you
  12257.       are using, your familiarity with the memory models and parameter-
  12258.       passing techniques that your compiler uses, and (as always) your own
  12259.       good judgment in evaluating the alternatives.
  12260.  
  12261.       The last part of the chapter introduces several different high-level
  12262.       video programming interfaces. The focus is on the reasons why high-
  12263.       level programming interfaces are used and the programming approach
  12264.       involved in using them.
  12265.  
  12266.  
  12267.  Linking Graphics Subroutines
  12268.  
  12269.  
  12270.       You can tie low-level graphics subroutines to high-level applications
  12271.       in several ways. The three techniques discussed here--subroutine
  12272.       calls, calling a set of memory-resident routines, and using inline
  12273.       code in a high-level-language program--have all been proved in various
  12274.       graphics applications. As usual, the "best" method to use in any given
  12275.       application is a matter of judgment.
  12276.  
  12277.  
  12278.  Subroutine Calls
  12279.  
  12280.       This book contains numerous subroutines that are designed to be called
  12281.       from within a high-level-language program. Most are to be linked to
  12282.       programs compiled with the Microsoft C compiler. However, you can link
  12283.       these subroutines to any high-level-language program if you know the
  12284.       proper protocol for structuring executable code, and for passing
  12285.       parameters to a subroutine and returning values from it. The routines
  12286.       in Listings 13-1 through 13-4 show how to call the same assembly-
  12287.       language subroutine from Microsoft C, Microsoft FORTRAN, Turbo Pascal,
  12288.       and interpreted BASIC.
  12289.  
  12290.  
  12291.  ──────────────────────────────────────────────────────────────────────────
  12292.  
  12293.       Listing 13-1a.  The SetPixel subroutine (Microsoft C small-model
  12294.       calling conventions).
  12295.  
  12296.  ──────────────────────────────────────────────────────────────────────────
  12297.  
  12298.  
  12299.  ───────────────────────────────────────────────────────────────────────────
  12300.  
  12301.       Listing 13-1b.  Calling SetPixel() from a C program.
  12302.  
  12303.  ──────────────────────────────────────────────────────────────────────────
  12304.  
  12305.  
  12306.  ───────────────────────────────────────────────────────────────────────────
  12307.  
  12308.       Listing 13-2a.  The SETPEL subroutine (Microsoft FORTRAN calling
  12309.       conventions).
  12310.  
  12311.  ───────────────────────────────────────────────────────────────────────────
  12312.  
  12313.  
  12314.  ───────────────────────────────────────────────────────────────────────────
  12315.  
  12316.       Listing 13-2b.  Calling SETPEL() from a FORTRAN program.
  12317.  
  12318.  ───────────────────────────────────────────────────────────────────────────
  12319.  
  12320.  
  12321.  ───────────────────────────────────────────────────────────────────────────
  12322.  
  12323.       Listing 13-3a.  The SETPEL subroutine (Turbo Pascal calling
  12324.       conventions).
  12325.  
  12326.  ───────────────────────────────────────────────────────────────────────────
  12327.  
  12328.  
  12329.  ───────────────────────────────────────────────────────────────────────────
  12330.  
  12331.       Listing 13-3b.  Calling SETPEL() from a Turbo Pascal program.
  12332.  
  12333.  ───────────────────────────────────────────────────────────────────────────
  12334.  
  12335.  
  12336.  ───────────────────────────────────────────────────────────────────────────
  12337.  
  12338.       Listing 13-4a.  The SETPEL subroutine (BASICA calling convention).
  12339.  
  12340.  ───────────────────────────────────────────────────────────────────────────
  12341.  
  12342.  
  12343.  ───────────────────────────────────────────────────────────────────────────
  12344.  
  12345.       Listing 13-4b.  Calling SETPEL from a BASICA program.
  12346.  
  12347.  ───────────────────────────────────────────────────────────────────────────
  12348.  
  12349.  
  12350.       One of the ways these assembly-language subroutines differ is that
  12351.       they use different memory models. A memory model describes the segment
  12352.       organization of a program--whether executable code is separated from
  12353.       program data, and whether segments are accessed with 16-bit (near) or
  12354.       32-bit (far) addresses. For example, a small-model program has one
  12355.       near code and one near data segment; a large-model program can have
  12356.       multiple far code and far data segments. The subroutines in Listings
  12357.       13-1 through 13-4 conform to the default memory models used by the
  12358.       different language translators.
  12359.  
  12360.       The protocol for passing parameters also varies among compilers and
  12361.       programming languages. In Pascal, for example, parameters are pushed
  12362.       on the stack in the order they appear in the PROCEDURE statement,
  12363.       while in C, parameters are pushed in reverse order. Also, either the
  12364.       actual value of a parameter or its address may be passed; this depends
  12365.       on the programming language you use as well as on the type of data
  12366.       involved. Each compiler's reference manual contains details on its
  12367.       parameter-passing protocol.
  12368.  
  12369.  
  12370.       Microsoft C
  12371.       Source code examples in previous chapters that can be called from a C
  12372.       program are all designed to be linked with small- or compact-model
  12373.       programs. To call them from a medium- or large-model program, you must
  12374.       make three modifications to the source code to make it conform to
  12375.       these memory models' subroutine-calling conventions.
  12376.  
  12377.       ■  Change the name of the executable code segment.
  12378.  
  12379.       ■  Use the far keyword in assembler PROC directives.
  12380.  
  12381.       ■  Modify the stack frame addressing to accommodate the calling
  12382.          routine's 32-bit return address.
  12383.  
  12384.       For example, to call SetPixel10() within a medium-model C program,
  12385.       change the name of the _TEXT segment in SetPixel10()'s source
  12386.       code to a name of the form module_TEXT and use the far keyword
  12387.       in the routine's PROC directive. Also, adjust the stack frame
  12388.       addresses by two bytes to account for the 32-bit return address.
  12389.  
  12390.  
  12391.       Microsoft FORTRAN
  12392.       Microsoft's FORTRAN compiler does not generate small- or compact-model
  12393.       programs, so the far addressing conventions applicable to medium- and
  12394.       large-model programs apply to FORTRAN-callable assembly-language
  12395.       graphics subroutines. The C-callable version in Listing 13-1a and the
  12396.       FORTRAN equivalent in Listing 13-2a differ in several ways. These
  12397.       differences relate to the way parameters are passed on the stack to
  12398.       the subroutine.
  12399.  
  12400.       The C compiler passes the current values of each subroutine argument
  12401.       in reverse order, so the first argument is on top of the stack. The
  12402.       FORTRAN compiler passes the 32-bit address of each argument's value in
  12403.       the order in which the arguments appear in the subroutine's
  12404.       argument list. The C subroutine obtains the argument values directly
  12405.       from the stack; the FORTRAN routine must obtain the arguments'
  12406.       addresses from the stack, then use the addresses to obtain the values.
  12407.       Also, in C, the routine that called the subroutine discards the
  12408.       arguments on the stack. In contrast, in FORTRAN the called subroutine
  12409.       cleans up the stack when it exits.
  12410.  
  12411.         ╔═══╗     The Microsoft C, FORTRAN, and Pascal compilers let you
  12412.         ║ T ║     specify the parameter-passing protocol used to call a
  12413.         ║ I ║     particular subroutine. For  example, you can write a C-
  12414.         ║ P ║     callable subroutine and then access it using the
  12415.         ╚═══╝     appropriate compiler directive in your FORTRAN or Pascal
  12416.                   program. This interlanguage linking capability became
  12417.                   available in MS C version 3.00, MS Pascal version 3.3, and
  12418.                   MS FORTRAN version 3.3.
  12419.  
  12420.                   Including a compiler directive in your high-level source
  12421.                   code can be more convenient than modifying an assembly-
  12422.                   language subroutine. For example, a C subroutine can be
  12423.                   called from a FORTRAN program by declaring the subroutine
  12424.                   in a FORTRAN INTERFACE unit:
  12425.  
  12426.                   interface to subroutine SP10[C](x,y,n)
  12427.                   integer*2 x,y,n
  12428.                   end
  12429.  
  12430.                   This INTERFACE unit instructs the FORTRAN compiler to
  12431.                   generate code that calls the subroutine _sp10() using C's
  12432.                   parameter-passing protocol. However, this technique does
  12433.                   not affect the memory model used; the C-callable routine
  12434.                   is called with a far call, because it lies in a different
  12435.                   segment from the FORTRAN caller. Thus, _sp10() must still
  12436.                   be declared with the far keyword, and the stack frame must
  12437.                   be addressed with the assumption that a 32-bit far return
  12438.                   address lies on top of the stack when the procedure is
  12439.                   called.
  12440.  
  12441.                   If you intend to write graphics routines that can be
  12442.                   called from either Microsoft C, Pascal, or FORTRAN, you
  12443.                   should use a medium or large memory model, so the routine
  12444.                   can be called as a far procedure. You can use any
  12445.                   parameter-passing protocol; the Microsoft language
  12446.                   translators can generate code for all of them.
  12447.  
  12448.  
  12449.       Turbo Pascal
  12450.       Turbo Pascal links EXTERNAL assembly-language subroutines dynamically.
  12451.       However, Turbo Pascal's dynamic linker does not perform address
  12452.       relocation or resolve symbolic references between the main program and
  12453.       the subroutine. Thus, the assembly-language subroutine has a very
  12454.       simple structure. Listing 13-3a is an example of this type of
  12455.       subroutine. Note how the subroutine performs "self-relocation" by
  12456.       initializing a register with the starting offset of the subroutine
  12457.       (using a CALL L01 followed by a POP), then adding this value to all
  12458.       references to labels within the subroutine.
  12459.  
  12460.  
  12461.       BASIC
  12462.       IBM BASICA and Microsoft GWBASIC have their own intrinsic video output
  12463.       routines. However, you can use assembly-language subroutines to
  12464.       customize your BASIC programs for video modes or hardware not
  12465.       supported by these BASIC interpreters. Listings 13-4a and 13-4b
  12466.       show how to do this.
  12467.  
  12468.       Like Turbo Pascal, BASICA requires you to link your subroutine
  12469.       dynamically. In Listing 13-4a, the subroutine is assembled in the
  12470.       form of a binary file that can be loaded with the BASIC BLOAD command,
  12471.       as in lines 220-250 of Listing 13-4b. In BASICA, as in Pascal,
  12472.       parameters are passed to the subroutine in the order they are
  12473.       specified in the high-level source code. Unlike the Turbo Pascal
  12474.       subroutine, however, the BASIC subroutine is a far procedure. Also, in
  12475.       BASIC the addresses of parameters are passed instead of the values of
  12476.       the parameters themselves.
  12477.  
  12478.  
  12479.  Interrupts to a Memory-Resident Driver
  12480.  
  12481.       Another way to implement the interface between high-level-language
  12482.       programs and machine-language graphics routines is to make the
  12483.       graphics routines resident in memory. When they are, programs can
  12484.       access the graphics routines by executing a software interrupt. This
  12485.       is the design of the interface used by all video BIOS routines in the
  12486.       PC and PS/2 families. The routines reside at a fixed address in ROM.
  12487.       Interrupt vector 10H is initialized at bootup to point to a service
  12488.       routine that calls the BIOS routines.
  12489.  
  12490.       Your own video output routines can be accessed in a similar manner if
  12491.       you make them resident in RAM and set an interrupt vector to point to
  12492.       them. (On the PC and PS/2s, interrupt numbers 60H through 67H are
  12493.       reserved for such user-defined interrupts.) Listing 13-5 is an
  12494.       example of a simple RAM-resident routine that stores pixels in the
  12495.       EGA's 640-by-350 16-color mode. The source for this routine assembles
  12496.       to a .EXE file that installs the routine in RAM and sets interrupt
  12497.       vector 60H to point to the code that sets the pixel value. After the
  12498.       interrupt vector is initialized, any program can access the routine by
  12499.       loading the CPU registers with the pixel location and value and then
  12500.       executing interrupt 60H.
  12501.  
  12502.  
  12503.  ───────────────────────────────────────────────────────────────────────────
  12504.  
  12505.       Listing 13-5.  A RAM-resident routine to write pixels in 640-by-350
  12506.       graphics mode.
  12507.  
  12508.  ───────────────────────────────────────────────────────────────────────────
  12509.  
  12510.  
  12511.  Inline Code
  12512.  
  12513.       A technique familiar to many C, Modula-2, and Turbo Pascal programmers
  12514.       is to implement low-level subroutines as inline machine instructions
  12515.       in high-level source code. Doing so can simplify the problem of using
  12516.       consistent memory-model and parameter-passing protocols, because the
  12517.       high-level-language compiler handles these implicitly. However, inline
  12518.       code is rarely portable and can be difficult to adapt for use with
  12519.       other languages.
  12520.  
  12521.  
  12522.  Global Data Areas
  12523.  
  12524.  
  12525.       When you link video output subroutines to a high-level program, you
  12526.       face the problem of transferring information about the current state
  12527.       of the video hardware between the high-level program and the
  12528.       subroutines. Although you can pass such information to subroutines
  12529.       using argument lists, a better approach is to use a global data
  12530.       structure that both the high-level program and the low-level
  12531.       subroutines can access. Information contained in a global data area
  12532.       can include:
  12533.  
  12534.       ■  Hardware identification ("EGA with 350-line color display")
  12535.  
  12536.       ■  Hardware coordinate system (orientation of x- and y-axes,
  12537.          maximum x- and y-coordinates)
  12538.  
  12539.       ■  Video buffer status, including video mode, buffer dimensions
  12540.          (maximum x- and y-coordinates), and currently displayed
  12541.          portion of the buffer
  12542.  
  12543.       ■  Foreground and background pixel values for text and graphics
  12544.          output
  12545.  
  12546.       ■  Color values for palette registers
  12547.  
  12548.       ■  Current pixel operation (replace, XOR, AND, OR, or NOT)
  12549.  
  12550.       ■  Current region fill pattern
  12551.  
  12552.       ■  Current line-drawing style (thick or thin line, dashed or broken
  12553.          line)
  12554.  
  12555.       In many applications it is better to maintain several global areas
  12556.       instead of just one. Because almost all PC and PS/2 video hardware
  12557.       supports more than one display mode, you can create a separate global
  12558.       data block for each mode and make an entire block "current" when you
  12559.       select a video mode. In a windowing environment, a block of global
  12560.       data can apply to each displayable window. In addition to the above
  12561.       information, such a block can also describe the way graphics images
  12562.       and text are mapped into a window. This can include clipping
  12563.       boundaries, vertical and horizontal scaling, or window visibility
  12564.       (whether a window is on or off the screen, overlapping another window,
  12565.       and so on).
  12566.  
  12567.       Using a global data area has several advantages. Because both high-
  12568.       level and low-level routines can determine output hardware status, you
  12569.       can write hardware-independent programs that examine the descriptive
  12570.       information in the global data area to determine how to format their
  12571.       output. This information is relatively static, so maintaining it in a
  12572.       global area helps minimize redundant parameter passing between
  12573.       graphics routines. Moreover, global data areas can be used
  12574.       contextually: the contents of a global data area can be saved,
  12575.       modified transiently, and restored.
  12576.  
  12577.         ╔═══╗     Of course, the information in a global data area can
  12578.         ║ T ║     pertain to output devices other than video adapters and
  12579.         ║ I ║     displays. A graphics interface that accommodates printers
  12580.         ║ P ║     or plotters can also incorporate information about their
  12581.         ╚═══╝     status in a global data area.
  12582.  
  12583.  
  12584.  Layered Graphics Interfaces
  12585.  
  12586.  
  12587.       After implementing an interface between your low-level video output
  12588.       routines and your high-level program, you may still find that a
  12589.       certain amount of high-level source code is concerned with low-level
  12590.       hardware-dependent manipulations such as pixel coordinate scaling and
  12591.       clipping. You can insulate high-level application code from
  12592.       considerations about hardware capabilities by creating one or more
  12593.       intermediate layers of functionality between the high-level
  12594.       application and the hardware drivers.
  12595.  
  12596.       A simple layered graphics interface is depicted schematically in
  12597.       Figure 13-1. The bottom layer comprises a set of hardware driver
  12598.       routines like the ones in this book. The top layer provides a set of
  12599.       subroutines that can be called by a high-level application.The
  12600.       routines in the top layer may call the hardware drivers in the bottom
  12601.       layer directly, or there may be one or more intermediate binding
  12602.       layers interposed between the high-level routines and the hardware
  12603.       drivers. In any case, the top-level subroutines present a consistent,
  12604.       hardware-independent software interface to the programmer who uses a
  12605.       high-level language, and thereby insulate high-level programs from the
  12606.       vagaries of video hardware programming.
  12607.  
  12608.       The ROM video BIOS provides an example of this sort of layering. The
  12609.       set of routines that you invoke by issuing INT 10H serves as an
  12610.       intermediate layer between assembly-language applications and the low-
  12611.       level routines that actually program the hardware. From the
  12612.       application's point of view, the INT 10H interface is relatively
  12613.       hardware-independent; the video BIOS programs the graphics controller,
  12614.       updates the video buffer, and performs many other hardware-dependent
  12615.       programming tasks. Because the video BIOS routines contain the
  12616.       hardware-dependent code, a program that uses the BIOS is to some
  12617.       extent portable to different types of video hardware.
  12618.  
  12619.       You can, of course, build many more functions into a layered interface
  12620.       than the video BIOS provides. For example, commercially available
  12621.       video graphics interfaces can produce sophisticated graphics and
  12622.       perform video control functions, including geometric transformations
  12623.       (scaling, translation, rotation of graphics images), three-dimensional
  12624.       graphics (hidden-line removal, three-dimensional surface represen-
  12625.       tation), or sophisticated color mixing and shading. Such graphics
  12626.       packages can support output to printers or plotters, as well as to
  12627.       video displays. In this case, the layered interface provides a set of
  12628.       routines and data structures that allow a high-level program to
  12629.       determine the status of an output device and  to select appropriate
  12630.       output attributes (line style, drawing color, and so on) on each
  12631.       device.
  12632.  
  12633.  
  12634.       ┌─────────────────────────────┐
  12635.       │High-level-language interface│ "Top layer"
  12636.       │  (independent of hardware)  │
  12637.       └────────────────────────────┘
  12638.                      │
  12639.                      │
  12640.                      │Language binding
  12641.                      │
  12642.                      │
  12643.       ┌────────────────────────────┐
  12644.       │        Device driver        │ "Bottom layer"
  12645.       │    (hardware-dependent)     │
  12646.       └─────────────────────────────┘
  12647.  
  12648.       Figure 13-1.  A simple layered graphics interface.
  12649.  
  12650.  
  12651.       In an operating environment that relies heavily on a graphics-oriented
  12652.       video interface, access to operating system functions can be combined
  12653.       with video output routines in a high-level application program
  12654.       interface (API). This is the approach taken in Apple's Macintosh and
  12655.       in Microsoft Windows. In both these environments, support for system
  12656.       functions like windows, pull-down menus, and icons is integrated into
  12657.       a unified, graphics-oriented API.
  12658.  
  12659.       Most layered graphics interfaces comprise more than one intermediate
  12660.       layer. Furthermore, each layer can be broken into several independent
  12661.       modules. The desire to preserve software portability, particularly as
  12662.       existing software is adapted to new video hardware, is the main reason
  12663.       for this. Many PC graphics programs are designed so that the end-user
  12664.       can customize the hardware-dependent layer(s) to a particular hardware
  12665.       configuration. This is a great convenience for the user, since
  12666.       adapting a program with a layered video interface to a newly acquired
  12667.       piece of hardware is no more difficult than installing a new device
  12668.       driver or relinking the program with a new subroutine library.
  12669.  
  12670.       The price you pay for this flexibility is a certain amount of extra
  12671.       code needed to support the layered interface, so programs run somewhat
  12672.       slower. You must consider this trade-off whenever you write an
  12673.       application that relies on video display output. Although the benefits
  12674.       of using a layered graphics interface are great, many applications are
  12675.       simpler to develop and run faster when you dispense with the formal
  12676.       graphics interface and use only the necessary low-level drivers.
  12677.  
  12678.       To get an idea of the type of programming required when you use a
  12679.       layered graphics interface, consider how you might draw a filled
  12680.       rectangle in a video graphics mode. The following examples show how
  12681.       you could do this using one  of the routines developed earlier in this
  12682.       book and using two different layered graphics interfaces. As you
  12683.       compare the source code and the programming technique in each of the
  12684.       following examples, you will see where the advantages and
  12685.       disadvantages of each graphics interface might lie.
  12686.  
  12687.  
  12688.  Direct Hardware Programming
  12689.  
  12690.       The routine in Listing 13-6 draws a filled rectangle directly, by
  12691.       computing the endpoints of the set of adjacent line segments that make
  12692.       up the rectangle and using a horizontal line-drawing routine to update
  12693.       the video buffer. Strictly speaking, this routine could be written
  12694.       entirely in assembly language by adapting one of the line-drawing
  12695.       routines from Chapter 6. The high-level routine in Listing 13-6 runs
  12696.       nearly as fast, however, since most of the time is spent drawing the
  12697.       lines, not computing their endpoints.
  12698.  
  12699.  
  12700.  ───────────────────────────────────────────────────────────────────────────
  12701.  
  12702.       Listing 13-6.  Using C to draw a filled rectangle.
  12703.  
  12704.  ───────────────────────────────────────────────────────────────────────────
  12705.  
  12706.  
  12707.       If raw speed is the major constraint on your program, this is the best
  12708.       way to draw a rectangle. The code, however, is relatively nonportable,
  12709.       because it makes implicit assumptions about such hardware-dependent
  12710.       constraints as the (x,y) coordinate system and color capabilities of
  12711.       the video subsystem. You could not use a routine such as the one in
  12712.       Listing 13-6 in a multitasking or windowing operating environment,
  12713.       because it programs the video hardware directly and could therefore
  12714.       inadvertently corrupt video output from a concurrently executing
  12715.       program.
  12716.  
  12717.  
  12718.  Extended BIOS Interface
  12719.  
  12720.       As mentioned previously, the video ROM BIOS provides a certain amount
  12721.       of hardware independence and portability through the interrupt 10H
  12722.       interface. The tradeoff, of course, is speed and a certain amount of
  12723.       flexibility. Apart from inefficient implementations, the INT 10H
  12724.       routines are relatively unstructured and limited in their
  12725.       capabilities. As IBM video subsystems have become more complex,
  12726.       additional functionality has been grafted onto the INT 10H interface,
  12727.       making it more powerful but increasingly difficult to master.
  12728.  
  12729.       Direct Graphics Interface Standard (DGIS) is a firmware interface
  12730.       developed by Graphics Software Systems that extends the capabilities
  12731.       of the INT 10H interface in a structured manner. DGIS was designed to
  12732.       provide a uniform low-level interface to video hardware based on
  12733.       graphics coprocessors such as the Intel 82786 or the Texas Instruments
  12734.       TMS34010. Programming with DGIS is reminiscent of programming with
  12735.       IBM's video BIOS, but many elements of a high-level graphics interface
  12736.       have also been incorporated into DGIS.
  12737.  
  12738.       DGIS implements a hardware-independent interface by describing actual
  12739.       video subsystems, or devices, in terms of their possible display
  12740.       modes, or configurations. An application can interrogate DGIS to
  12741.       determine what devices are supported in the computer. It then selects
  12742.       a subsequent video output configuration, based on the configuration's
  12743.       resolution, number of colors, graphics and/or alphanumeric text
  12744.       support, and so on.
  12745.  
  12746.       For example, Listing 13-7 calls DGIS to draw the same filled
  12747.       rectangle as before. This time, however, instead of programming the
  12748.       hardware, the source code is concerned primarily with programming the
  12749.       interface. The routine first establishes the presence of a suitable
  12750.       graphics output device in the computer by calling the DGIS Inquire
  12751.       Available Devices function. This function returns a list of available
  12752.       DGIS devices; in a system with an EGA, for example, the configurations
  12753.       associated with the "EGA" device correspond to the EGA's video modes.
  12754.  
  12755.  
  12756.  ───────────────────────────────────────────────────────────────────────────
  12757.  
  12758.       Listing 13-7.  Using DGIS to draw a filled rectangle.
  12759.  
  12760.  ───────────────────────────────────────────────────────────────────────────
  12761.  
  12762.  
  12763.       The application program "connects" to an appropriate configuration,
  12764.       which DGIS identifies with a handle. The application can then
  12765.       associate an attribute context with the handle; the attribute context
  12766.       is a data structure that defines drawing colors, line styles, clipping
  12767.       boundaries, and so on. Subsequent calls to DGIS graphics output
  12768.       functions like OutputFilledRectangle refer to the attribute context
  12769.       associated with a specified handle.
  12770.  
  12771.       This general sequence of operations is inherently flexible. One reason
  12772.       is that it lets an application program access hardware features
  12773.       without actually programming the hardware. For example, an application
  12774.       can use DGIS functions to change a color palette or update pixels
  12775.       without writing directly to hardware control registers or to the video
  12776.       buffer.
  12777.  
  12778.       However, an application that performs video output through a DGIS
  12779.       interface runs slower than an equivalent application that programs the
  12780.       video hardware directly. As always, when you interpose a layer of
  12781.       functionality between your application and the hardware, you gain
  12782.       increased functionality and portability at the price of a decrease in
  12783.       speed. You must decide whether this trade-off is worthwhile in your
  12784.       own applications.
  12785.  
  12786.  
  12787.  High-Level Interface
  12788.  
  12789.       There are several high-level graphics interface implementations
  12790.       available for IBM video subsystems. These high-level interfaces differ
  12791.       from DGIS and the IBM video BIOS in that they are implemented as
  12792.       software libraries or RAM-loadable device drivers instead of firmware
  12793.       routines. All of them relieve you of the need to program the hardware
  12794.       directly, and all provide a structured programming interface that can
  12795.       be used in a program written in a high-level language.
  12796.  
  12797.       The differences between the high-level graphics interfaces lie in the
  12798.       amount and type of functionality built into them. For example, the
  12799.       Virtual Device Interface (VDI) is a proposed ANSI standard designed to
  12800.       promote hardware independence in programs written in high-level
  12801.       languages. VDI presents a consistent programming interface to all
  12802.       graphics output hardware, including video subsystems, printers, and
  12803.       plotters. (The Graphics Development Toolkit sold by Graphics Software
  12804.       Systems and IBM support VDI.)
  12805.  
  12806.       Another well-known interface is the Graphical Kernel System (GKS), an
  12807.       internationally recognized ANSI standard. GKS offers a highly
  12808.       structured interface with powerful graphics data manipulation
  12809.       features. GKS deals not with individual hardware devices but with
  12810.       workstations that can include several related input and output devices
  12811.       (such as a display, a keyboard, and a mouse). A GKS implementation can
  12812.       be layered above a lower-level interface like VDI; an application can
  12813.       then use either interface without sacrificing functionality or
  12814.       portability.
  12815.  
  12816.       Still another type of high-level interface integrates graphics output
  12817.       with the computer's operating environment, as does the Graphics Device
  12818.       Interface (GDI) in Microsoft Windows. In contrast to DGIS, which is
  12819.       designed to be a low-level interface to display hardware, GDI serves
  12820.       as a high-level interface to Windows' graphics-oriented operating
  12821.       environment. In a layered graphics interface, GDI would be closer to
  12822.       the topmost layer while an interface like DGIS would be near the
  12823.       bottom. In fact, you can install Windows to run on top of DGIS; a
  12824.       Windows application can then use GDI functions which in turn call DGIS
  12825.       functions to access the hardware (Figure 13-2).
  12826.  
  12827.       The C source code fragment in Listing 13-8 merely scratches the
  12828.       surface of GDI programming in Windows, but it should give you an idea
  12829.       of how the video interface is structured. Most of the code in the
  12830.       example establishes a device context for the Rectangle() function to
  12831.       use. In GDI, a device context is a global data structure that contains
  12832.       information on the colors with which text and graphics are drawn, as
  12833.       well as scaling factors for pixel (x,y) coordinates, clipping
  12834.       boundaries, and other information. Windows maintains a device context
  12835.       for each window on the screen. Each device context is identified by a
  12836.       16-bit handle. When an application calls a GDI output function like
  12837.       Rectangle() or Ellipse(), it passes the handle of a device context to
  12838.       the function; the function then refers to the information in the
  12839.       device context to produce output in a window.
  12840.  
  12841.       To produce graphics output in a window, a Windows application starts
  12842.       by calling the Windows function CreateWindow(), which returns a handle
  12843.       (hWnd) that identifies the window. The application then monitors
  12844.       Windows' applicaiton message queue to determine when to update the
  12845.       window.
  12846.  
  12847.       To generate output to the window, the application can use another
  12848.       Windows function, BeginPaint(), to associate a device context
  12849.       (identified with the handle hDC) with the window. The application then
  12850.       uses GDI functions to establish drawing attributes and pixel
  12851.       coordinate mapping in the device context. In the example in Listing
  12852.       13-8, the attributes of the rectangle's border (line style and color)
  12853.       are specified by creating a data structure that becomes part of the
  12854.       device context.
  12855.  
  12856.  
  12857.       ┌────────────────────┐
  12858.       │    Application     │
  12859.       └───────────────────┘
  12860.                 │
  12861.       ┌───────────────────┐
  12862.       │        GDI         │
  12863.       └───────────────────┘
  12864.                 │
  12865.       ┌───────────────────┐
  12866.       │        DGIS        │
  12867.       └───────────────────┘
  12868.                 │
  12869.       ┌───────────────────┐
  12870.       │      Hardware      │
  12871.       └────────────────────┘
  12872.  
  12873.       Figure 13-2.  Microsoft Windows GDI installed on DGIS.
  12874.  
  12875.  
  12876.  ───────────────────────────────────────────────────────────────────────────
  12877.  
  12878.       Listing 13-8.  Using Microsoft Windows GDI (version 1.03) to draw a
  12879.       filled rectangle.
  12880.  
  12881.  ───────────────────────────────────────────────────────────────────────────
  12882.  
  12883.  
  12884.       The function CreatePen() creates the data structure and returns an
  12885.       identifying handle that is assigned to the variable hpen. The
  12886.       function SelectObject() then updates the device context with this
  12887.       information. Similarly, calls to CreateSolidBrush() and SelectObject()
  12888.       establish the color and pattern used to fill the rectangle.
  12889.  
  12890.       When Rectangle() executes, it uses the "pen" and "brush" attributes in
  12891.       the device context to draw the rectangle's border and interior. The
  12892.       (x,y) coordinates specified in the call to Rectangle() indicate the
  12893.       rectangle's upper left and lower right corners. The coordinates do not
  12894.       indicate absolute pixel locations in the video buffer; they specify
  12895.       points in the coordinate system that relates to the window in which
  12896.       the rectangle is displayed.
  12897.  
  12898.       GDI's general design is similar to that of other high-level graphics
  12899.       interfaces--the hardware-dependent, machine-language routines are
  12900.       isolated in the lowest layer of the interface, and portable, hardware-
  12901.       independent functions are implemented in the interface's upper layers.
  12902.       The differences among GDI, VDI, and other high-level graphics
  12903.       interfaces lie not so much in implementation details as in the types
  12904.       and complexity of the graphics functions they can perform.
  12905.  
  12906.  
  12907.  
  12908.                        Appendix A  Video BIOS Summary
  12909.  
  12910.  
  12911.  
  12912.       All computers in the IBM PC and PS/2 family have a BIOS (Basic
  12913.       Input/Output System) in ROM. The ROM BIOS contains a set of assembly-
  12914.       language routines that provide a low-level programming interface for
  12915.       accessing various hardware features, including disk drives, the system
  12916.       timer, serial I/O ports, a parallel printer, and, of course, the video
  12917.       hardware. By building a video BIOS in ROM into every machine, IBM has
  12918.       attempted to provide a common software interface for the various
  12919.       machines, despite substantial hardware differences among the IBM PC,
  12920.       the PC/XT, the PC/AT, and the PS/2s.
  12921.  
  12922.       To a large extent, this endeavor succeeded. Transporting programs
  12923.       between IBM PCs with different hardware tends to be easier when the
  12924.       programs access the hardware only by calling ROM BIOS routines. This
  12925.       is particularly true of programs that manipulate the video display.
  12926.       When you consider the many video display configurations available, you
  12927.       might regard the BIOS as a sort of "lowest common denominator" for the
  12928.       software developer.
  12929.  
  12930.       Still, you might not always choose to use ROM BIOS routines for video
  12931.       output for several reasons. For one, ROM BIOS video support routines
  12932.       are not very fast. When performance is critical, you probably will not
  12933.       use them. The speed of the routines is rarely important for tasks
  12934.       performed infrequently, such as loading a character set into RAM or
  12935.       changing a video display mode. On the other hand, in displaying
  12936.       graphics images or producing animation effects, using the BIOS can
  12937.       substantially decrease performance.
  12938.  
  12939.       Many other tasks are better performed by your operating system rather
  12940.       than the BIOS. For example, when you call the BIOS to write characters
  12941.       to the screen, you bypass any operating system processing of those
  12942.       characters. The BIOS routines know nothing about input/output
  12943.       redirection, windowing, or other functions the operating system
  12944.       provides.
  12945.  
  12946.       Clearly, the video ROM BIOS is essential to IBM PC video programming,
  12947.       but the extent to which your programs use it is a matter for your
  12948.       judgment.
  12949.  
  12950.  
  12951.  Hardware Supported by ROM Video BIOS
  12952.  
  12953.  
  12954.  MDA and CGA
  12955.  
  12956.       The ROM BIOS on the motherboard of every IBM/PC, PC/XT, and PC/AT
  12957.       supports both the MDA and the CGA. Also, the PS/2 Model 30's video
  12958.       BIOS supports an MDA, in addition to its integrated MCGA. When you
  12959.       power a PC on, the vector for interrupt 10H is initialized to point to
  12960.       the video service routine in ROM.
  12961.  
  12962.       IBM's technical documentation frequently refers to the motherboard ROM
  12963.       BIOS  in the PCs and PS/2s as the "planar" BIOS. The planar BIOS
  12964.       routines start at F000:E000 in the CPU's address space.
  12965.  
  12966.  
  12967.  EGA
  12968.  
  12969.       IBM's EGA contains its own set of video drivers in ROM, located at
  12970.       C000:0000. The EGA's cold boot routines initialize interrupt 10H to
  12971.       point to its service routine in the EGA ROM BIOS. The EGA BIOS uses
  12972.       the interrupt 42H vector to point to the motherboard video service
  12973.       routine. Because the EGA's interrupt 10H routines access the
  12974.       motherboard BIOS routines whenever necessary through INT 42H, you
  12975.       rarely need to execute this interrupt explicitly.
  12976.  
  12977.  
  12978.  MCGA
  12979.  
  12980.       The video ROM BIOS in the PS/2 Models 25 and 30 supports the
  12981.       integrated MCGA subsystem in these computers. The Model 30's ROM BIOS
  12982.       supports the concurrent use of an MDA, but a CGA cannot be used in the
  12983.       same machine because its I/O port assignments and video memory usage
  12984.       conflict with those of the MCGA.
  12985.  
  12986.  
  12987.  VGA
  12988.  
  12989.       Video ROM routines in the PS/2 Models 50, 60, and 80, starting at
  12990.       E000:0000, support the VGA exclusively. The other video adapters
  12991.       described in this book cannot be installed in these computers because
  12992.       they are incompatible with the PS/2 MicroChannel bus.
  12993.  
  12994.  
  12995.  VGA Adapter
  12996.  
  12997.       The VGA Adapter's video ROM BIOS routines start at C000:0000. The BIOS
  12998.       routines on the VGA Adapter are the same as those in the PS/2 Model
  12999.       50, 60, and 80 video BIOS, except for minor differences related to the
  13000.       different hardware implementations of the adapter and the integrated
  13001.       VGA subsystem.
  13002.  
  13003.  
  13004.  Interrupt 10H
  13005.  
  13006.  
  13007.       The BIOS video routines are written in assembly language and accessed
  13008.       by performing 80x86 interrupt 10H. The INT 10H interface is designed
  13009.       for assembly-language programs, but you can call the BIOS routines
  13010.       directly from programs written in languages such as C or Pascal if
  13011.       your language compiler provides a way to execute the interrupt.
  13012.  
  13013.       You select a BIOS video support routine by loading a function number
  13014.       into register AH. To pass parameters to the BIOS routine, place their
  13015.       values in the 80x86 registers before executing INT 10H. Values that
  13016.       the BIOS routines return to your program are left in registers as
  13017.       well.
  13018.  
  13019.       The IBM PC motherboard BIOS routines explicitly preserve the contents
  13020.       of registers DS, ES, BX, CX, DX, SI, and DI (unless they are used for
  13021.       parameter passing). The EGA, MCGA, and VGA BIOS routines also preserve
  13022.       register BP.
  13023.  
  13024.         ╔═══╗     If you are using the IBM PC or PC/XT planar BIOS, preserve
  13025.         ║ T ║     register BP across INT 10H calls to the BIOS. For example:
  13026.         ║ I ║           push bp                ; preserve BP
  13027.         ║ P ║           int  10h               ; call the BIOS
  13028.         ╚═══╝           pop  bp                ; restore BP
  13029.  
  13030.  
  13031.       As a rule, BIOS video input/output routines do not validate data, nor
  13032.       do they return status codes or error flags. Thus, your programs should
  13033.       never attempt to access an invalid video buffer address, select a
  13034.       video page in a video mode that does not support them, or access
  13035.       hardware not installed in your system. The BIOS routines do not
  13036.       reliably detect any of these errors.
  13037.  
  13038.  
  13039.  Video BIOS Data Areas
  13040.  
  13041.  
  13042.  Video Display Data Area
  13043.  
  13044.       The BIOS routines maintain several dynamic variables in an area of
  13045.       memory called the Video Display Data Area. Figure A-1 contains a
  13046.       summary of these variables' addresses, their symbolic names, and their
  13047.       contents.
  13048.  
  13049.  
  13050. ╓┌───────────────────┌──────────────┌─────────┌──────────────────────────────╖
  13051.  Address             Name           Type      Description
  13052.  ──────────────────────────────────────────────────────────────────────────
  13053.  0040:0049           CRT_MODE       Byte      Current BIOS video mode number
  13054.  0040:004A           CRT_COLS       Word      Number of displayed character
  13055.                                                 columns
  13056.  0040:004C           CRT_LEN        Word      Size of video buffer in bytes
  13057.  0040:004E           CRT_START      Word      Offset of start of video
  13058.                                                 buffer
  13059.  0040:0050           CURSOR_POSN    Word      Array of eight words
  13060.                                                 containing the cursor
  13061.                                                 position for each of eight
  13062.                                                 possible video pages. The
  13063.                                                 high-order byte of each word
  13064.  Address             Name           Type      Description
  13065.                                                high-order byte of each word
  13066.                                                 contains the character row,
  13067.                                                 the low-order byte the
  13068.                                                 character column.
  13069.  0040:0060           CURSOR_MODE    Word      Starting and ending lines for
  13070.                                                 alphanumeric cursor. The
  13071.                                                 high-order byte contains the
  13072.                                                 starting (top) line; the
  13073.                                                 low- order byte contains the
  13074.                                                 ending (bottom) line.
  13075.  0040:0062           ACTIVE_PAGE    Byte      Currently displayed video page
  13076.                                                 number
  13077.  0040:0063           ADDR_6845      Word      I/O port address of CRT
  13078.                                                 Controller's Address
  13079.                                                 register (3B4H for
  13080.                                                 monochrome, 3D4H for color).
  13081.  0040:0065           CRT_MODE_SE    T         Byte Current value for Mode
  13082.                                                 Control register (3B8H on
  13083.                                                 MDA, 3D8H on CGA). On the
  13084.                                                 EGA and VGA, the value
  13085.  Address             Name           Type      Description
  13086.                                                EGA and VGA, the value
  13087.                                                 emulates those used on the
  13088.                                                 MDA and CGA.
  13089.  0040:0066           CRT_PALETTE    Byte      Current value for the CGA
  13090.                                                 Color Select register
  13091.                                                 (3D9H). On the EGA and VGA,
  13092.                                                 the value emulates those
  13093.                                                 used on the MDA and CGA.
  13094.  0040:0084           ROWS           Byte      Number of displayed character
  13095.                                                 rows - 1
  13096.  0040:0085           POINTS         Word      Height of character matrix
  13097.  0040:0087           INFO           Byte      (See Figure A-1a)
  13098.  0040:0088           INFO_3         Byte      (See Figure A-1b)
  13099.  0040:0089           Flags          Byte      Miscellaneous flags (see
  13100.                                                 Figure A-1c)
  13101.  0040:008A           DCC            Byte      Display Combination Code table
  13102.                                                 index
  13103.  0040:00A8           SAVE_PTR       Dword     Pointer to BIOS Save Area (see
  13104.                                                 Figure A-3)
  13105.  
  13106.       Figure A-1.  BIOS Video Display Data Area.
  13107.  
  13108.  
  13109. ╓┌─────────┌─────────────────────────────────────────────────────────────────╖
  13110.  Bit       Description
  13111.  ──────────────────────────────────────────────────────────────────────────
  13112.  7         Reflects bit 7 of video mode number passed to INT 10H
  13113.            function 0
  13114.  6-5       Amount of video RAM:
  13115.                       00b - 64K
  13116.                       01b - 128K
  13117.                       10b - 192K
  13118.                       11b - 256K
  13119.  4         (reserved)
  13120.  3         1 - video subsystem is inactive
  13121.  2         (reserved)
  13122.  1         1 - video subsystem is attached to monochrome display
  13123.  0         1 - alphanumeric cursor emulation is enabled
  13124.  
  13125.       Figure A-1a.  Mapping of INFO byte at 0040:0087 in the EGA and VGA
  13126.       Video Display Data Area.
  13127.  
  13128.  
  13129. ╓┌─────────┌─────────────────────────────────────────────────────────────────╖
  13130.  Bit       Description
  13131.  ──────────────────────────────────────────────────────────────────────────
  13132.  7         Input from feature connector on FEAT1 (bit 6 of Input Status
  13133.              register 0) in response to output on FC1 (bit 1 of Feature
  13134.              Control register)
  13135.  6         Input from feature connector on FEAT0 (bit 5 of Input Status
  13136.              register 0) in response to output on FC1 (bit 1 of Feature
  13137.              Control register)
  13138.  5         Input from feature connector on FEAT1 (bit 6 of Input Status
  13139.              register 0) in response to output on FC0 (bit 0 of Feature
  13140.              Control register)
  13141.  4         Input from feature connector on FEAT0 (bit 5 of Input Status
  13142.              register 0) in response to output on FC0 (bit 0 of Feature
  13143.              Control register)
  13144.  3         Configuration switch 4 (1 - off, 0 - on)
  13145.  2         Configuration switch 3 (1 - off, 0 - on)
  13146.  1         Configuration switch 2 (1 - off, 0 - on)
  13147.  0         Configuration switch 1 (1 - off, 0 - on)
  13148.  Bit       Description
  13149. 0         Configuration switch 1 (1 - off, 0 - on)
  13150.  
  13151.       Figure A-1b.  Mapping of INFO_3 byte at 0040:0088 in the EGA and VGA
  13152.       Video Display Data Area. Bits 4 through 7 reflect the power-on status
  13153.       of the feature connector. Bits 0 through 3 reflect the settings of the
  13154.       four EGA configuration switches. (The switch values are emulated by
  13155.       the VGA BIOS, depending on the type of display attached.)
  13156.  
  13157.  
  13158. ╓┌─────────┌─────────────────────────────────────────────────────────────────╖
  13159.  Bit       Description
  13160.  ──────────────────────────────────────────────────────────────────────────
  13161.  7         Alphanumeric scan lines (with bit 4):
  13162.            bit 7   bit 4
  13163.            0       0       350-line mode
  13164.            0       1       400-line mode
  13165.            1       0       200-line mode
  13166.            1       1       (reserved)
  13167.  6         1 - display switching is enabled
  13168.            0 - display switching is disabled
  13169.  Bit       Description
  13170.           0 - display switching is disabled
  13171.  5         (reserved)
  13172.  4         (see bit 7)
  13173.  3         1 - default palette loading is disabled
  13174.            0 - default palette loading is enabled
  13175.  2         1 - using monochrome monitor
  13176.            0 - using color monitor
  13177.  1         1 - gray scale summing is enabled
  13178.            0 - gray scale summing is disabled
  13179.  0         1 - VGA active
  13180.            0 - VGA not active
  13181.  
  13182.       Figure A-1c.  Mapping of Flags byte at 0040:0089 in MCGA and VGA Video
  13183.       Display Data Area.
  13184.  
  13185.  
  13186.       Video BIOS routines update the values in the Video Display Data Area
  13187.       to reflect the status of the video subsystem. If you alter the video
  13188.       environment without invoking an INT 10H routine, be sure you update
  13189.       the relevant variables in the Video Display Data Area. Failing to do
  13190.       so can cause the BIOS video routines to malfunction.
  13191.  
  13192.  
  13193.  Save Areas
  13194.  
  13195.       The ROM BIOS routines on the EGA, the MCGA, and the VGA support a set
  13196.       of save areas, which are dynamic tables of video hardware and BIOS
  13197.       information. The video BIOS can use these save areas to supplement the
  13198.       Video Display Data Area. You can also use them to override the usual
  13199.       video BIOS defaults for character sets, palette programming, and other
  13200.       configuration functions.
  13201.  
  13202.       The video BIOS save areas are linked by a set of doubleword
  13203.       (segment:offset) pointers (see Figure A-2). Use the variable SAVE_PTR
  13204.       (at 0040:00A8 in the Video Display Data Area) to locate the save
  13205.       areas. SAVE_PTR contains the address of the SAVE POINTER table (see
  13206.       Figure A-3). This table contains addresses of as many as seven data
  13207.       structures, each with a different format and a different set of data
  13208.       pertaining to operation of the video hardware or of the video BIOS
  13209.       routines.
  13210.  
  13211.       The fifth address in the SAVE POINTER table is that of the SECONDARY
  13212.       SAVE POINTER table (see Figure A-4), which only the VGA's BIOS uses.
  13213.       This table also contains the addresses of several data structures with
  13214.       contents relating to the functioning of the video hardware and the
  13215.       BIOS.
  13216.  
  13217.  
  13218.       ┌────────────────────┐
  13219.       │        PTR         │
  13220.       └─────────┬──────────┘
  13221.                 
  13222.       ┌────────────────────┐
  13223.       │                    ├─────Video Parameter table
  13224.       │ SAVE POINTER Table ├─────Parameter Save Area
  13225.       │                    ├─────Alphanumeric Character Set Override
  13226.       │                    ├─────Graphics Character Set Override
  13227.       │                    ├─────┐
  13228.       └────────────────────┘     │
  13229.                 ┌────────────────┘
  13230.                 
  13231.       ┌────────────────────┐
  13232.       │   SECONDARY SAVE   ├─────Display Combination Code
  13233.       │   POINTER Table    ├─────2nd Alphanumeric Character Set Override
  13234.       │     (VGA only)     ├─────User Palette Profile Table
  13235.       └────────────────────┘
  13236.  
  13237.       Figure A-2.  Video BIOS Save Areas.
  13238.  
  13239.  
  13240. ╓┌─────────┌────────────────┌────────────────────────────────────────────────╖
  13241.  Offset    Type             Description
  13242.  ──────────────────────────────────────────────────────────────────────────
  13243.  0         Dword            Address of Video Parameter table
  13244.  4         Dword            Address of Parameter Save Area (EGA, VGA only)
  13245.  8         Dword            Address of Alphanumeric Character Set Override
  13246.  0CH       Dword            Address of Graphics Character Set Override
  13247.  10H       Dword            Address of SECONDARY SAVE POINTER table
  13248.                               (VGA only)
  13249.  14H       Dword            (reserved)
  13250.  18H       Dword            (reserved)
  13251.  
  13252.       Figure A-3.  SAVE POINTER table (EGA, MCGA, VGA).
  13253.  
  13254.  
  13255. ╓┌─────────┌────────────────┌────────────────────────────────────────────────╖
  13256.  Offset      Type           Description
  13257.  ──────────────────────────────────────────────────────────────────────────
  13258.  0           Word           Length of SECONDARY SAVE POINTER table in
  13259.                               bytes
  13260.  2           Dword          Address of Display Combination Code table
  13261.  6           Dword          Address of second Alphanumeric Character
  13262.                               Set Override
  13263.  0AH         Dword          Address of User Palette Profile table
  13264.  0EH         Dword          (reserved)
  13265.  12H         Dword          (reserved)
  13266.  16H         Dword          (reserved)
  13267.  
  13268.       Figure A-4.  SECONDARY SAVE POINTER table (VGA only).
  13269.  
  13270.  
  13271.       Apart from the SAVE POINTER and SECONDARY SAVE POINTER tables, the
  13272.       only data structures provided in the ROM BIOS are the Video Parameter
  13273.       table and, on the VGA, the Display Combination Code table. Thus, the
  13274.       only initialized addresses in the SAVE POINTER table are those of the
  13275.       Video Parameter table and of the SECONDARY SAVE POINTER table. The
  13276.       only initialized address in the SECONDARY SAVE POINTER table belongs
  13277.       to the Display Combination Code table. Remaining addresses are
  13278.       initialized to 0.
  13279.  
  13280.  
  13281.  Video Parameter Table
  13282.  
  13283.       This data structure contains configuration parameters that the video
  13284.       BIOS video mode set routines use. The table contains entries for each
  13285.       available video mode. Its structure differs on the various models of
  13286.       the EGA, the MCGA, and the VGA. Figure A-5 is a typical entry in the
  13287.       VGA Video Parameter table. Formats for table entries in the EGA and
  13288.       MCGA BIOS are similar.
  13289.  
  13290.  
  13291. ╓┌─────────┌────────────────┌────────────────────────────────────────────────╖
  13292.  Offset    Type             Description
  13293.  ──────────────────────────────────────────────────────────────────────────
  13294.  0         Byte             Value for CRT_COLS
  13295.  Offset    Type             Description
  13296. 0         Byte             Value for CRT_COLS
  13297.  1         Byte             Value for ROWS
  13298.  2         Byte             Value for POINTS
  13299.  3         Word             Value for CRT_LEN
  13300.  5         4-byte array     Values for Sequencer registers 1-4
  13301.  9         Byte             Value for Miscellaneous Output register
  13302.  0AH       25-byte array    Values for CRTC registers 0-18H
  13303.  23H       20-byte array    Values for Attribute Controller registers 0-13H
  13304.  37H       9-byte array     Values for Graphics Controller registers 0-8
  13305.  
  13306.       Figure A-5.  Format of a VGA Video Parameter table entry. The VGA
  13307.       Video Parameter table comprises 29 such entries.
  13308.  
  13309.  
  13310.  Parameter Save Area
  13311.  
  13312.       When present, this table contains the values of the EGA or the VGA
  13313.       Graphics Controller palette registers (00H through 0FH) and the
  13314.       Overscan register (11H), as shown in Figure A-6. The video BIOS
  13315.       updates the Parameter Save Area whenever it updates the corresponding
  13316.       Attribute Controller registers.
  13317.  
  13318.  
  13319. ╓┌─────────┌────────────────┌────────────────────────────────────────────────╖
  13320.  Offset    Type             Description
  13321.  ──────────────────────────────────────────────────────────────────────────
  13322.  0         16-byte array    Current contents of Graphics Controller
  13323.                               Palette registers
  13324.  10H       Byte             Current contents of Graphics Controller
  13325.                               Overscan register
  13326.  11H-0FFH  (reserved)
  13327.  
  13328.       Figure A-6.  Parameter Save Area. This area is 256 bytes in size.
  13329.  
  13330.  
  13331.         ╔═══╗     When a User Palette Profile (see Figure A-10 later in
  13332.         ║ T ║     this discussion) overrides the default palette register
  13333.         ║ I ║     values, the Parameter Save Area is updated with default
  13334.         ║ P ║     values, not those in the User Palette Profile.
  13335.         ╚═══╝
  13336.  
  13337.  
  13338.  Alphanumeric Character Set Override
  13339.  
  13340.       This data structure (see Figure A-7) indicates an alphanumeric
  13341.       character set to be used instead of the BIOS default character set.
  13342.       The character set is loaded whenever the video BIOS is called to
  13343.       select one of the video modes that the data structure specifies.
  13344.  
  13345.  
  13346. ╓┌─────────┌────────────────┌────────────────────────────────────────────────╖
  13347.  Offset    Type             Description
  13348.  ──────────────────────────────────────────────────────────────────────────
  13349.  0         Byte             Length in bytes of each character definition
  13350.  1         Byte             Character generator RAM bank
  13351.  2         Word             Number of characters defined
  13352.  4         Word             First character code in table
  13353.  6         Dword            Address of character definition table
  13354.  0AH       Byte             Number of displayed character rows
  13355.  0BH       Byte array       Applicable video modes
  13356.            Byte             0FFH (end of list of video modes)
  13357.  
  13358.       Figure A-7.  Alphanumeric Character Set Override.
  13359.  
  13360.  
  13361.       On the VGA, you can specify a second 256-character set by creating a
  13362.       second Alphanumeric Character Set Override data structure and storing
  13363.       its address in the SECONDARY SAVE POINTER table.
  13364.  
  13365.  
  13366.  Graphics Character Set Override
  13367.  
  13368.       This data structure (see Figure A-8) overrides the default BIOS
  13369.       character set selection whenever the video BIOS sets up one of the
  13370.       specified video modes.
  13371.  
  13372.  
  13373. ╓┌─────────┌────────────────┌────────────────────────────────────────────────╖
  13374.  Offset    Type             Description
  13375.  ──────────────────────────────────────────────────────────────────────────
  13376.  0         Byte             Number of displayed character rows
  13377.  1         Word             Length in bytes of each character definition
  13378.  3         Dword            Address of character definition table
  13379.  Offset    Type             Description
  13380. 3         Dword            Address of character definition table
  13381.  7         Byte array       Applicable video modes
  13382.            Byte             0FFH (end of list of video modes)
  13383.  
  13384.       Figure A-8.  Graphics Character Set Override.
  13385.  
  13386.  
  13387.  Display Combination Code Table
  13388.  
  13389.       Figure A-9 lists all combinations of video subsystems that the video
  13390.       BIOS supports. The description of INT 10H function 1AH in this
  13391.       appendix explains how this table is used.
  13392.  
  13393.         ╔═══╗     The MCGA video BIOS contains a Display Combination Code
  13394.         ║ T ║     table in ROM to support INT 10H function 1AH. However, the
  13395.         ║ I ║     MCGA BIOS does not support a SECONDARY SAVE POINTER table,
  13396.         ║ P ║     so you can't modify its DCC table.
  13397.         ╚═══╝
  13398.  
  13399.  
  13400. ╓┌─────────┌────────────────┌────────────────────────────────────────────────╖
  13401.  Offset    Type             Description
  13402.  ──────────────────────────────────────────────────────────────────────────
  13403.  0         Byte             Number of entries in table
  13404.  1         Byte             DCC table version number
  13405.  2         Byte             Maximum display type code
  13406.  3         Byte             (reserved)
  13407.  4         Word array       Each pair of bytes in the array describes a
  13408.                               valid display combination (see INT 10H
  13409.                               function 1AH)
  13410.  
  13411.       Figure A-9.  Display Combination Code table.
  13412.  
  13413.  
  13414.  User Palette Profile Table
  13415.  
  13416.       This data structure contains user-specified overrides for the default
  13417.       Attribute Controller Palette and Overscan register values, for the
  13418.       default values in the 256 video DAC color registers, and for the
  13419.       default value in the CRTC Underline Location register (see Figure A-
  13420.       10). Only the VGA video BIOS supports this table.
  13421.  
  13422.  
  13423. ╓┌─────────┌────────────────┌────────────────────────────────────────────────╖
  13424.  Offset    Type             Description
  13425.  ──────────────────────────────────────────────────────────────────────────
  13426.  0         Byte             Underlining:  1 - Enable in all alphanumeric
  13427.                                                 modes
  13428.                                           0 - Enable in monochrome
  13429.                                                 alphanumeric mode
  13430.                                          -1 - Disable in all alphanumeric
  13431.                                                 modes
  13432.  1         Byte             (reserved)
  13433.  2         Word             (reserved)
  13434.  4         Word             Number of Attribute Controller registers in
  13435.                               table
  13436.  6         Word             First Attribute Controller register number
  13437.  8         Dword            Address of Attribute Controller register
  13438.                               table
  13439.  0CH       Word             Number of video DAC Color registers in
  13440.                               table
  13441.  0EH       Word             First video DAC Color register number
  13442.  Offset    Type             Description
  13443. 0EH       Word             First video DAC Color register number
  13444.  10H       Dword            Address of video DAC Color register table
  13445.  14H       Byte array       Applicable video modes
  13446.            Byte             0FFH (end of list of video modes)
  13447.  
  13448.       Figure A-10.  User Palette Profile table.
  13449.  
  13450.  
  13451.  Video BIOS Save Area Programming
  13452.  
  13453.       To use a data structure supported in the SAVE POINTER and SECONDARY
  13454.       SAVE POINTER tables, place the data structure in RAM and update the
  13455.       appropriate SAVE POINTER or SECONDARY SAVE POINTER addresses to point
  13456.       to it. Because the default SAVE POINTER and SECONDARY SAVE POINTER
  13457.       tables are located in ROM, you must copy these tables to RAM and
  13458.       update SAVE_PTR (0040:00A8) appropriately before you can modify them.
  13459.  
  13460.       Listings A-1 and A-2 demonstrate two uses of the video BIOS save
  13461.       areas. The routine in Listing A-1 provides a parameter save area for
  13462.       the EGA or VGA BIOS. Once the parameter save area is established, its
  13463.       first 17 bytes are updated with the contents of the Attribute
  13464.       Controller's 16 palette registers and its Overscan register each time
  13465.       the video BIOS writes to them.
  13466.  
  13467.  
  13468.  ───────────────────────────────────────────────────────────────────────────
  13469.  
  13470.       Listing A-1.  Using a Parameter Save Area to keep track of EGA or VGA
  13471.       palette registers.
  13472.  
  13473.  ───────────────────────────────────────────────────────────────────────────
  13474.  
  13475.  
  13476.       Listing A-2 shows how to specify the palette values to be used when
  13477.       the video BIOS routines are invoked to establish a new video mode.
  13478.       First, place the values in a table whose address is stored in a User
  13479.       Palette Profile data structure. Then place the address of this data
  13480.       structure in the SECONDARY SAVE POINTER table. (Since this example
  13481.       uses the SECONDARY SAVE POINTER table, you can run it only on the
  13482.       VGA.)
  13483.  
  13484.  
  13485.  ───────────────────────────────────────────────────────────────────────────
  13486.  
  13487.       Listing A-2.  Using a User Palette Profile to override the
  13488.       default VGA palette.
  13489.  
  13490.  ───────────────────────────────────────────────────────────────────────────
  13491.  
  13492.  
  13493.         ╔═══╗     Generally, your application should restore SAVE_PTR to its
  13494.         ║ T ║     original value when the SAVE POINTER tables and save areas
  13495.         ║ I ║     are no longer needed. If you want to preserve these tables
  13496.         ║ P ║     in RAM for use by subsequent applications, use the MS-DOS
  13497.         ╚═══╝     "Terminate-but-Stay-Resident" function (INT 21H function
  13498.                   31H) so that the RAM containing the tables is not freed
  13499.                   when the program that creates them terminates.
  13500.  
  13501.  
  13502.  Interrupt 1DH Vector
  13503.  
  13504.       This interrupt vector contains the address of a table of video
  13505.       initialization values (see Figure A-11). These values are useful only
  13506.       for the MDA and the CGA; however, the table is maintained for
  13507.       compatibility among all PCs and PS/2s.
  13508.  
  13509.  
  13510. ╓┌─────────┌────────────────┌────────────────────────────────────────────────╖
  13511.  Offset    Type             Description
  13512.  ──────────────────────────────────────────────────────────────────────────
  13513.  0         16-byte array    CRTC registers for 40-by-25 alphanumeric
  13514.                               mode (CGA)
  13515.  10H       16-byte array    CRTC registers for 80-by-25 alphanumeric
  13516.                               mode (CGA)
  13517.  20H       16-byte array    CRTC registers for 320-by-200 4-color or
  13518.                               640-by-200 2-color graphics modes (CGA)
  13519.  30H       16-byte array    CRTC registers for 80-by-25 monochrome
  13520.                               (MDA)
  13521.  40H       Word             Video buffer length (40-by-25 alphanumeric
  13522.                               mode)
  13523.  42H       Word             Video buffer length (80-by-25 alphanumeric
  13524.                               mode)
  13525.  44H       Word             Video buffer length (CGA graphics modes)
  13526.  Offset    Type             Description
  13527. 44H       Word             Video buffer length (CGA graphics modes)
  13528.  46H       Word             Video buffer length (CGA graphics modes)
  13529.  48H       8-byte array     Number of displayed character columns for
  13530.                               video BIOS modes 0 through 7
  13531.  50H       8-byte array     Values for CRT Mode Control register 3x8H
  13532.                               for video BIOS modes 0 through 7
  13533.  
  13534.       Figure A-11.  MDA and CGA Video Initialization table. This table's
  13535.       address is stored in the vector for INT 1DH.
  13536.  
  13537.  
  13538.  IBM PC and PS/2 Video BIOS Functions (INT 10H Interface)
  13539.  
  13540.  
  13541.       The following pages provide detailed descriptions of each BIOS
  13542.       function available through software interrupt 10H. The descriptions
  13543.       are intended to complement the function summaries and assembly-
  13544.       language source code listings in IBM's technical literature. The
  13545.       accompanying source code fragments represent typical programming
  13546.       examples that you can modify for your own purposes.
  13547.  
  13548.       This summary includes information on the ROM BIOS routines found on
  13549.       the motherboard, the EGA, the MCGA, and the VGA. However, not all the
  13550.       routines are available or function identically on all computers in the
  13551.       IBM PC and PS/2 family.
  13552.  
  13553.       All information in this chapter is based on IBM technical
  13554.       specifications and on the following dated versions of the video ROM:
  13555.  
  13556.       ■  IBM PC motherboard ROM: 10/27/82
  13557.  
  13558.       ■  IBM PC/AT motherboard ROM: 6/10/85
  13559.  
  13560.       ■  IBM EGA ROM: 9/13/84
  13561.  
  13562.       ■  IBM PS/2 Model 30 (MCGA) ROM: 9/2/86
  13563.  
  13564.       ■  IBM PS/2 Model 60 (VGA) ROM: 2/13/87
  13565.  
  13566.       ■  IBM PS/2 (VGA) Display Adapter ROM: 10/27/86
  13567.  
  13568.  
  13569.  
  13570.  ───────────────────────────────────────────────────────────────────────────
  13571.  Function 0: Select Video Mode
  13572.  
  13573.  Caller registers:
  13574.  
  13575.       AH   =    0
  13576.       AL   =    video mode number:
  13577.                 0      40-by-25 16-color alphanumeric, color burst disabled
  13578.                 1      40-by-25 16-color alphanumeric, color burst enabled
  13579.                 2      80-by-25 16-color alphanumeric, color burst disabled
  13580.                 3      80-by-25 16-color alphanumeric, color burst enabled
  13581.                 4      320-by-200 4-color graphics, color burst enabled
  13582.                 5      320-by-200 4-color graphics, color burst disabled
  13583.                 6      640-by-200 2-color graphics, color burst enabled
  13584.                 7      80-by-25 monochrome alphanumeric (MDA, Hercules, EGA,
  13585.                           and VGA only)
  13586.                 8      160-by-200 16-color graphics (PCjr only)
  13587.                 9      320-by-200 16-color graphics (PCjr only)
  13588.                 0AH    640-by-200 4-color graphics (PCjr only)
  13589.                 0BH    Reserved (used by EGA BIOS function 11H)
  13590.                 0CH    Reserved (used by EGA BIOS function 11H)
  13591.                 0DH    320-by-200 16-color graphics (EGA and VGA only)
  13592.                 0EH    640-by-200 16-color graphics (EGA and VGA only)
  13593.                 0FH    640-by-350 monochrome graphics (EGA and VGA only)
  13594.                 10H    640-by-350 16-color graphics (VGA, EGA with at least
  13595.                           128 KB)
  13596.                        640-by-350 4-color graphics (64 KB EGA)
  13597.                 11H    640-by-480 2-color graphics (MCGA, VGA only)
  13598.                 12H    640-by-480 16-color graphics (VGA only)
  13599.                 13H    320-by-200 256-color graphics (MCGA and VGA only)
  13600.  
  13601.  Returned values:
  13602.  
  13603.       (none)
  13604.  
  13605.  Video Display Data Area updates:
  13606.  
  13607.       0040:0049  CRT_MODE
  13608.       0040:004A  CRT_COLS
  13609.       0040:004C  CRT_LEN
  13610.       0040:004E  CRT_START
  13611.       0040:0050  CURSOR_POSN
  13612.       0040:0060  CURSOR_MODE
  13613.       0040:0062  ACTIVE_PAGE
  13614.       0040:0063  ADDR_6845
  13615.       0040:0065  CRT_MODE_SET
  13616.       0040:0066  CRT_PALETTE
  13617.       0040:0084  ROWS
  13618.       0040:0085  POINTS
  13619.       0040:0087  INFO
  13620.       0040:0088  INFO_3
  13621.  
  13622.       INT 10H function 0 puts the video subsystem in the video mode you
  13623.       specify with the value in register AL. Function 0 programs the CRT
  13624.       Controller, selects a default color palette, and optionally clears the
  13625.       video buffer. You can modify several default tasks that function 0
  13626.       performs by setting flags in the Video Display Data Area (see INT 10H
  13627.       function 12H) or by providing character set or palette attribute
  13628.       overrides in BIOS save areas.
  13629.  
  13630.       Video mode numbers 0BH and 0CH are reserved for the EGA BIOS support
  13631.       routine for RAM-loadable character sets, in which video memory map 2
  13632.       is selectively enabled so a table of character definitions can be
  13633.       loaded.
  13634.  
  13635.       On the EGA, the MCGA, and the VGA, composite video displays are not
  13636.       supported, and there is no color burst signal to control. Thus, mode 0
  13637.       is the same as mode 1, mode 2 = mode 3, and mode 4 = mode 5.
  13638.  
  13639.       If you use this BIOS routine to request a video mode your system
  13640.       hardware does not support, the results are unreliable. In particular,
  13641.       if you select mode 7 (monochrome alphanumeric) with a CGA, the
  13642.       motherboard BIOS programs the CGA's CRT Controller with parameters
  13643.       appropriate for an MDA, which results in incomprehensible noise on the
  13644.       CGA screen. The third example below shows how to solve this problem by
  13645.       setting bits 4 and 5 of EQUIP_FLAG (0040:0010) to indicate which
  13646.       subsystem the BIOS is to use.
  13647.  
  13648.       On the EGA, the MCGA, and the VGA, if bit 7 of the requested video
  13649.       mode number in AL is set to 1, the video buffer is not cleared when
  13650.       the new video mode is selected. Thus, a program can alternate between
  13651.       two video subsystems without losing the contents of their video
  13652.       buffers.
  13653.  
  13654.       The following example selects 320-by-200 4-color graphics mode.
  13655.  
  13656.          mov  ax,0004        ; AH := 0 (INT 10H function number)
  13657.                              ; AL := 4 (video mode number)
  13658.          int  10h
  13659.  
  13660.       This routine shows how to change modes on the EGA without clearing
  13661.       the video buffer.
  13662.  
  13663.          mov  ax,000EH       ; select a video mode (in this case,
  13664.                              ;  640x200 16-color mode)
  13665.          or   al,10000000b   ; set bit 7
  13666.          int  10h
  13667.  
  13668.       To select video modes in a system containing both a CGA and an MDA,
  13669.       use a routine such as the following.
  13670.  
  13671.          mov  ax,40h
  13672.          mov  es,ax
  13673.          and  byte ptr es:[10h],11001111b  ; zero bits 4 and 5 of EQUIP_FLAG
  13674.          or   byte ptr es:[10h],00110000b  ; set bits 4 and 5:
  13675.                                            ;  11b - monochrome
  13676.                                            ;  10b - color (80x25)
  13677.                                            ;  01b - color (40x25)
  13678.                                            ;  00b - (unused)
  13679.          mov  ax,0007
  13680.          int  10h                          ; select monochrome mode 7
  13681.  
  13682.          and  byte ptr es:[10],11001111b   ; zero those bits
  13683.          or   byte ptr es:[10],00100000b   ; bits for 80x25 16-color
  13684.          mov  ax,0003
  13685.          int  10h                          ; select 80x25 16-color mode 3
  13686.  
  13687.  
  13688.  
  13689.  ───────────────────────────────────────────────────────────────────────────
  13690.  Function 1: Set Alphanumeric Cursor Size
  13691.  
  13692.  Caller registers:
  13693.  
  13694.       AH   =    1
  13695.       CH   =    top line of cursor
  13696.       CL   =    bottom line of cursor
  13697.  
  13698.  Returned values:
  13699.  
  13700.       (none)
  13701.  
  13702.  Video Display Data Area update:
  13703.  
  13704.       0040:0060  CURSOR_MODE
  13705.  
  13706.       INT 10H function 1 programs the CRT Controller to display the
  13707.       specified alphanumeric cursor. It programs the CRT Controller's Cursor
  13708.       Start and Cursor End registers so that the alphanumeric cursor appears
  13709.       between the specified lines in the character matrix. The contents of
  13710.       register CX are copied into CURSOR_MODE.
  13711.  
  13712.       If the value in CH is 20H the alphanumeric cursor is disabled.
  13713.  
  13714.       On the EGA and the VGA, if bit 0 of the INFO byte (0040:0087) is set
  13715.       to 0, the BIOS processes the top and bottom line values passed in CH
  13716.       and CL relative to an eight-line character matrix. Chapter 3
  13717.       discusses this "cursor emulation" in detail.
  13718.  
  13719.       Use INT 10H function 1 only in alphanumeric video modes.
  13720.  
  13721.       To select a full-height cursor in video mode 3 (80-by-25 16-color
  13722.       alphanumeric mode) on a CGA:
  13723.  
  13724.          mov  cx,0007h   ; CH := 0 (top line)
  13725.                          ; CL := 7 (bottom line of the 8x8 character matrix)
  13726.          mov  ah,1       ; AH := 1 (INT 10H function number)
  13727.          int  10h
  13728.  
  13729.       On an EGA with a 350-line monitor, video mode 3 is a 350-line
  13730.       alphanumeric mode with an 8-by-14 character matrix. Nevertheless, the
  13731.       above code normally runs unchanged in this situation, because the BIOS
  13732.       "emulates" the corresponding 200-line CGA mode and programs the Cursor
  13733.       Start and End registers accordingly.
  13734.  
  13735.  
  13736.  
  13737.  ───────────────────────────────────────────────────────────────────────────
  13738.  Function 2: Set Cursor Location
  13739.  
  13740.  Caller registers:
  13741.  
  13742.       AH   =    2
  13743.       BH   =    video page
  13744.       DH   =    character row
  13745.       DL   =    character column
  13746.  
  13747.  Returned values:
  13748.  
  13749.       (none)
  13750.  
  13751.  Video Display Data Area update:
  13752.  
  13753.       0040:0050  CURSOR_POSN
  13754.  
  13755.       INT 10H function 2 updates the BIOS Video Display Data Area, giving a
  13756.       new cursor position. If the value in BH references the currently
  13757.       displayed video page, this routine also programs the CRT Controller to
  13758.       update the displayed cursor position.
  13759.  
  13760.       To set the cursor position to column 10, row 5, in 80-by-25 16-color
  13761.       mode:
  13762.  
  13763.          mov  ah,2           ; AH := 2 (INT 10H function number)
  13764.          mov  bh,1           ; BH := video page
  13765.          mov  dh,5           ; DH := row
  13766.          mov  dl,10          ; DL := column
  13767.          int  10h
  13768.  
  13769.  
  13770.  
  13771.  ───────────────────────────────────────────────────────────────────────────
  13772.  Function 3: Return Cursor Status
  13773.  
  13774.  Caller registers:
  13775.  
  13776.       AH   =    3
  13777.       BH   =    video page number
  13778.  
  13779.  Returned values:
  13780.  
  13781.       CH   =    top line of cursor
  13782.       CL   =    bottom line of cursor
  13783.       DH   =    character row
  13784.       DL   =    character column
  13785.  
  13786.  Video Display Data Area updates:
  13787.  
  13788.       (none)
  13789.  
  13790.       INT 10H function 3 returns the character cursor location for the
  13791.       specified video page. The character row and column values are copied
  13792.       from CURSOR_POSN in the Video Display Data Area.
  13793.  
  13794.       The values returned in CH and CL are copied from CURSOR_MODE, also in
  13795.       the Video Display Data Area. They are meaningful only in alphanumeric
  13796.       modes.
  13797.  
  13798.       To determine the current cursor location (and size in an alphanumeric
  13799.       mode) in video page 0:
  13800.  
  13801.          mov  ah,3           ; AH := 3 (INT 10H function number)
  13802.          mov  bh,0           ; BH := 0 (video page)
  13803.          int  10h
  13804.  
  13805.  
  13806.  
  13807.  ───────────────────────────────────────────────────────────────────────────
  13808.  Function 4: Return Light Pen Position
  13809.  
  13810.  Caller registers:
  13811.  
  13812.       AH   =    4
  13813.  
  13814.  Returned values:
  13815.  
  13816.       AH   =    1 if valid light pen position returned
  13817.            =    0 if no light pen position returned
  13818.       BX   =    pixel x-coordinate
  13819.       CH   =    pixel y-coordinate (CGA and EGA video modes 4, 5, and 6)
  13820.       CX   =    pixel y-coordinate (EGA except modes 4, 5, and 6)
  13821.       DH   =    character row
  13822.       DL   =    character column
  13823.  
  13824.  Video Display Data Area updates:
  13825.  
  13826.       (none)
  13827.  
  13828.       INT 10H function 4 gets the current position of the light pen from the
  13829.       CRT Controller's Light Pen High and Light Pen Low registers.
  13830.  
  13831.       If the light pen switch is not set, or if the light pen latch has not
  13832.       been triggered (that is, if the CRTC's Light Pen High and Light Pen
  13833.       Low registers do not contain a valid light pen address), function 4
  13834.       returns 0 in register AH. Otherwise, function 4 sets AH to 1, leaves
  13835.       the light pen position in registers BX, CX, and DX, and resets the
  13836.       light pen trigger.
  13837.  
  13838.       When function 4 returns, BX contains the calculated pixel x-coordinate
  13839.       at which the light pen was triggered. Since the CRTC returns the light
  13840.       pen position as a byte address, the value in BX is only as
  13841.       accurate as the number of pixels in each byte of the video buffer. (In
  13842.       640-by-200 2-color mode, each byte of the video buffer represents
  13843.       eight pixels; function 4 thus returns the pixel x-coordinates
  13844.       of every eighth pixel.) The light pen position is calculated relative
  13845.       to the start of the displayed portion of the video buffer (CRT_START).
  13846.  
  13847.       INT 10H function 4 returns the pixel y-coordinate in either CH (in the
  13848.       motherboard BIOS) or CX (in all video modes in the EGA BIOS except
  13849.       modes 4, 5, and 6). For example, in 320-by-200 4-color graphics mode,
  13850.       the pixel y-coordinate is always returned in CH, but in 80-by-25
  13851.       16-color alphanumeric mode, the value is returned in CH on a CGA but
  13852.       in CX on an EGA.
  13853.  
  13854.       The values that function 4 returns in DH and DL represent the
  13855.       character row and column at which the light pen was triggered.
  13856.  
  13857.       INT 10H function 4 always returns AH = 0 on the MCGA and the VGA,
  13858.       which do not support light pens.
  13859.  
  13860.       To determine the light pen status in any video mode, call INT 10H
  13861.       function 4:
  13862.  
  13863.          mov  ah,4           ; AH := 4 (INT 10H function number)
  13864.          int  10h
  13865.  
  13866.       For example, if you trigger the light pen near the center of the
  13867.       display in 640-by-350 16-color mode, the values returned by this
  13868.       function might be:
  13869.  
  13870.       AH   =    1     (valid light pen results were returned)
  13871.       BX   =  320     (x-coordinate of first pixel at the byte
  13872.                       address where the pen was triggered)
  13873.       CX   =  175     (pixel y-coordinate)
  13874.       DH   =   12     (character row)
  13875.       DL   =   40     (character column)
  13876.  
  13877.  
  13878.  
  13879.  ───────────────────────────────────────────────────────────────────────────
  13880.  Function 5: Select Video Page
  13881.  
  13882.  Caller registers:
  13883.  
  13884.       AH   =    5
  13885.       AL   =    video page number
  13886.  
  13887.  Returned values:
  13888.  
  13889.       (none)
  13890.  
  13891.  Video Display Data Area updates:
  13892.  
  13893.       0040:004E  CRT_START
  13894.       0040:0062  ACTIVE_PAGE
  13895.  
  13896.       INT 10H function 5 selects which portion of the video buffer is
  13897.       displayed on the CGA, the EGA, the MCGA, and the VGA. It works by
  13898.       programming the CRTC Start Address registers. You can use the function
  13899.       in 40-by-25 or 80-by-25 alphanumeric video modes (BIOS modes 0, 1, 2,
  13900.       and 3) in any of these subsystems.
  13901.  
  13902.       On the CGA, the entire 16 KB video buffer is used in both 320-by-200
  13903.       and 640-by-200 graphics modes, so no video paging is possible. Calls
  13904.       to function 5 are ignored in these modes.
  13905.  
  13906.       On the MCGA, the EGA, and the VGA, video pages are available in both
  13907.       alphanumeric and graphics modes up to the limits of video RAM.
  13908.       However, the BIOS routine does not check whether video RAM is
  13909.       sufficient to support a requested video page; if the requested video
  13910.       page lies outside the video buffer, the resulting display is unusable.
  13911.  
  13912.       The BIOS maintains a current cursor location for as many as eight
  13913.       video pages in CURSOR_POSN. When you invoke Function 5, the BIOS moves
  13914.       the cursor to where it was located the last time the requested video
  13915.       page was displayed.
  13916.  
  13917.       The following routine sets the displayed portion of the CGA's video
  13918.       buffer to start at B800:1000 (video page 1) in 80-by-25 alphanumeric
  13919.       mode:
  13920.  
  13921.          mov  ax,0501h       ; AH := 5 (INT 10H function number)
  13922.                              ; AL := 1 (video page number)
  13923.          int  10h
  13924.  
  13925.  
  13926.  
  13927.  ───────────────────────────────────────────────────────────────────────────
  13928.  Function 6: Scroll Up
  13929.  
  13930.  Caller registers:
  13931.       AH   =    6
  13932.       AL   =    number of lines to scroll
  13933.       BH   =    attribute
  13934.       CH   =    upper left corner row
  13935.       CL   =    upper left corner column
  13936.       DH   =    lower right corner row
  13937.       DL   =    lower right corner column
  13938.  
  13939.  Returned values:
  13940.  
  13941.       (none)
  13942.  
  13943.  Video Display Data Area updates:
  13944.  
  13945.       (none)
  13946.  
  13947.       INT 10H function 6 performs a row-by-row upward scroll of characters
  13948.       in a designated area of the active video page. You specify the number
  13949.       of rows of characters to scroll in AL. The rectangular area
  13950.       in which the scroll is to be performed is defined by its upper left
  13951.       corner, specified in CH and CL, and its lower right corner, specified
  13952.       in DH and DL.
  13953.  
  13954.       The attribute you specify in BH is used for all blank lines inserted
  13955.       in the bottom of the scrolled area. In alphanumeric modes, this
  13956.       attribute is formatted in the usual manner, with the background
  13957.       attribute in the high nibble and the foreground attribute in the low
  13958.       nibble. In graphics modes, the format of the attribute in BH depends
  13959.       on the mode.
  13960.  
  13961.       In 640-by-200 2-color and 320-by-200 4-color modes, the value in BH
  13962.       represents a 1-byte pixel pattern. The byte represents eight 1-bit
  13963.       pixels in 640-by-200 2-color mode or four 2-bit pixels in 320-by-200
  13964.       4-color mode. The pixel pattern is replicated throughout all lines
  13965.       that function 6 blanks in the scroll area. In all other EGA, MCGA, and
  13966.       VGA graphics modes, the value in BH determines the value of all pixels
  13967.       in the blanked lines.
  13968.  
  13969.       In 320-by-200 4-color mode on the EGA, the MCGA, and the VGA, function
  13970.       6 always scrolls video page 0, regardless of which video page is
  13971.       currently displayed.
  13972.  
  13973.       Specifying 0 as the number of rows to scroll in AL causes the entire
  13974.       scroll area to be blanked.
  13975.  
  13976.       In 80-by-25 16-color alphanumeric mode, you can scroll the entire
  13977.       screen up one line with the following sequence:
  13978.  
  13979.          mov  ax,601h        ; AH := 6 (INT 10H function number)
  13980.                              ; AL := 1 (number of lines to scroll up)
  13981.          mov  bh,7           ; BH := 7 (attribute)
  13982.          mov  cx,0           ; CH := upper left corner:  row 0
  13983.                              ; CL := upper left corner:  column 0
  13984.          mov  dx,184Fh       ; DH := lower right corner:  row 24 (18H)
  13985.                              ; DL := lower right corner:  column 79 (4FH)
  13986.          int  10h
  13987.  
  13988.       In the same video mode, you could clear only the top three lines of
  13989.       the display with a background attribute of 1 (blue on a CGA) and a
  13990.       foreground attribute of 7 (white) using this routine:
  13991.  
  13992.          mov  ax,600h        ; AH := INT 10H function number
  13993.                              ; AL := 0 (clear the scroll area)
  13994.          mov  bh,17h         ; BH := attribute (background 1, foreground 7)
  13995.          mov  cx,0           ; CH,CL := upper left corner at (0,0)
  13996.          mov  dx,024Fh       ; DH,DL := lower right corner at (2,79)
  13997.          int  10h
  13998.  
  13999.       To get the same result in 640-by-350 16-color graphics mode on the
  14000.       EGA, you set the value in BH to indicate a pixel value instead of an
  14001.       alphanumeric attribute:
  14002.  
  14003.          mov  ax,600h
  14004.          mov  bh,1           ; BH := pixel value
  14005.          mov  cx,0
  14006.          mov  dx,024Fh
  14007.          int  10h
  14008.  
  14009.       In 640-by-200 2-color mode, the following call to INT 10H function 6
  14010.       fills the display with vertical stripes of alternating pixel values:
  14011.  
  14012.          mov  ax,600h
  14013.          mov  bh,10101010b   ; BH := pixel pattern
  14014.          mov  cx,0
  14015.          mov  dx,184Fh
  14016.          int  10h
  14017.  
  14018.  
  14019.  
  14020.  ───────────────────────────────────────────────────────────────────────────
  14021.  Function 7: Scroll Down
  14022.  
  14023.  Caller registers:
  14024.  
  14025.       AH   =    7
  14026.       AL   =    number of lines to scroll
  14027.       BH   =    attribute
  14028.       CH   =    upper left corner row
  14029.       CL   =    upper left corner column
  14030.       DH   =    lower right corner row
  14031.       DL   =    lower right corner column
  14032.  
  14033.  Returned values:
  14034.  
  14035.       (none)
  14036.  
  14037.  Video Display Data Area updates:
  14038.  
  14039.       (none)
  14040.  
  14041.       INT 10H function 7 performs a row-by-row downward scroll of characters
  14042.       in a designated area of the active video page. Except for the direc-
  14043.       tion of the scroll, this BIOS function is identical to function 6.
  14044.  
  14045.  
  14046.  
  14047.  ───────────────────────────────────────────────────────────────────────────
  14048.  Function 8: Return Character Code and Attribute at Cursor
  14049.  
  14050.  Caller registers:
  14051.  
  14052.       AH   =    8
  14053.       BH   =    video page
  14054.  
  14055.  Returned values:
  14056.  
  14057.       AH   =    attribute (alphanumeric modes only)
  14058.       AL   =    ASCII code
  14059.  
  14060.  Video Display Data Area updates:
  14061.  
  14062.       (none)
  14063.  
  14064.       INT 10H function 8 returns the ASCII code of the character at the
  14065.       current cursor position in the video page that BH specifies. In
  14066.       alphanumeric modes, this is done by reading a single word from the
  14067.       video buffer. In graphics modes, the routine compares the character
  14068.       matrix at the cursor position to the bit patterns in the current
  14069.       graphics character definition table.
  14070.  
  14071.       In graphics modes, the PC/XT and PC/AT BIOS uses the ROM character
  14072.       definitions at F000:FA6E; the EGA, MCGA, and VGA BIOS uses the
  14073.       definitions designated by the interrupt 43H vector. For ASCII codes
  14074.       80-0FFH in CGA-compatible graphics modes 4, 5, and 6, the BIOS uses
  14075.       the characters defined in the table indicated by the interrupt 1FH
  14076.       vector.
  14077.  
  14078.       To determine the character code for a character in a graphics mode,
  14079.       the BIOS routine regards nonzero pixels as foreground pixels. It is
  14080.       the pattern of foreground (nonzero) and background (zero) pixels that
  14081.       is compared to the bit patterns in the table. If the pixel pattern in
  14082.       the video buffer matches a bit pattern in the character definition
  14083.       table, the BIOS determines the character's ASCII code from the bit
  14084.       pattern's location in the table. If the pixel pattern in the video
  14085.       buffer does not match any bit pattern in the table, the BIOS routine
  14086.       returns 0 in AL.
  14087.  
  14088.       In 320-by-200 4-color mode on the EGA, the MCGA, and the VGA, this
  14089.       function works properly only in video page 0.
  14090.  
  14091.       The following code fragment reads the character in the screen's upper
  14092.       left corner:
  14093.  
  14094.          mov  ah,0Fh         ; AH := 0FH (INT 10H function number)
  14095.          int  10h            ; leaves BH = active video page
  14096.          mov  ah,2           ; AH := 2 (INT 10H function number)
  14097.          mov  dx,0           ; DH,DL := row 0, column 0
  14098.          int  10h            ; sets cursor position to (0,0)
  14099.          mov  ah,8           ; AH := 8 (INT 10H function number)
  14100.          int  10h            ; leaves AL = ASCII code
  14101.  
  14102.  
  14103.  
  14104.  ───────────────────────────────────────────────────────────────────────────
  14105.  Function 9: Write Character and Attribute at Cursor
  14106.  
  14107.  Caller registers:
  14108.  
  14109.       AH   =    9
  14110.       AL   =    ASCII code
  14111.       BH   =    background pixel value (320-by-200 256-color mode) or video
  14112.                 page (all other modes)
  14113.       BL   =    foreground pixel value (graphics modes) or attribute value
  14114.                 (alphanumeric modes)
  14115.       CX   =    repetition factor
  14116.  
  14117.  Returned values:
  14118.  
  14119.       (none)
  14120.  
  14121.  Video Display Data Area updates:
  14122.  
  14123.       (none)
  14124.  
  14125.       INT 10H function 9 writes a character one or more times into the video
  14126.       buffer without moving the cursor. You must specify a repetition factor
  14127.       of 1 or greater in CX. The BIOS writes a string composed of the
  14128.       character in AL into the buffer. The length of the string is
  14129.       determined by the repetition factor in CX.
  14130.  
  14131.       In alphanumeric modes, both the ASCII code and the corresponding
  14132.       attribute byte are updated for each character written into the video
  14133.       buffer. In graphics modes, each character is written into the buffer
  14134.       in a rectangular area the size of the character matrix. The value in
  14135.       BL is used for the character's foreground pixels. In 320-by-200 256-
  14136.       color graphics mode, the value in BH specifies the character's
  14137.       background pixel value; in all other graphics modes, BH designates a
  14138.       video page, so the character's background pixels are 0. In all
  14139.       graphics modes except 320-by-200 256-color mode, the character is
  14140.       XORed into the buffer if bit 7 of BL is set to 1.
  14141.  
  14142.       INT 10H function 9 does not compare the repetition factor with the
  14143.       number of displayed character columns. In alphanumeric modes, this may
  14144.       not matter; the video buffer map is such that a string too long to be
  14145.       displayed in one row of characters wraps to the next row. In graphics
  14146.       modes, however, a string should be no longer than the remainder of the
  14147.       current character row.
  14148.  
  14149.       You must specify a video page in register BH in alphanumeric modes as
  14150.       well as in native EGA graphics modes, but the value in BH is ignored
  14151.       by the EGA, the MCGA, and the VGA BIOS in 320-by-200 4-color graphics
  14152.       mode.
  14153.  
  14154.       The following routine writes a string of 20 asterisks to the upper
  14155.       left corner of the display in 80-by-25 16-color mode. The foreground
  14156.       value in each character's attribute byte is set to 7, and the
  14157.       background value is set to 1. The cursor is positioned with a call to
  14158.       INT 10H function 2 before the string is written with function 9.
  14159.  
  14160.          mov  ah,2           ; AH := 2 (INT 10H function number)
  14161.          mov  bh,0           ; BH := video page
  14162.          mov  dx,0           ; DH := cursor row
  14163.                              ; DL := cursor column
  14164.          int  10h            ; set cursor position to (0,0)
  14165.          mov  ah,9           ; AH := 9 (INT 10H function number)
  14166.          mov  al,'*'         ; AL := ASCII code
  14167.          mov  bl,17h         ; BL := attribute byte
  14168.          mov  cx,20          ; CX := repetition factor
  14169.          int  10h
  14170.  
  14171.  
  14172.  
  14173.  ───────────────────────────────────────────────────────────────────────────
  14174.  Function 0AH: Write Character(s) at Cursor Position
  14175.  
  14176.  Caller registers:
  14177.  
  14178.       AH   =    0AH
  14179.       AL   =    ASCII code
  14180.       BH   =    background pixel value (320-by-200 256-color mode) or video
  14181.                 page (all other modes)
  14182.       BL   =    foreground pixel value (graphics modes only)
  14183.       CX   =    repetition factor
  14184.  
  14185.  Returned values:
  14186.  
  14187.       (none)
  14188.  
  14189.  Video Display Data Area updates:
  14190.  
  14191.       (none)
  14192.  
  14193.       INT 10H function 0AH is the same as INT 10H function 9, with this
  14194.       exception: In alphanumeric video modes, only the character code is
  14195.       written into the video buffer. The character's attribute remains
  14196.       unchanged in the buffer.
  14197.  
  14198.       This example clears one character row from the cursor position to its
  14199.       end. Before calling function 0AH, the example determines the active
  14200.       video page and the number of displayed character columns with a call
  14201.       to INT 10H function 0FH, and the cursor position using INT 10H
  14202.       function 3.
  14203.  
  14204.          mov  ah,0Fh         ; AH := 0FH (INT 10H function number)
  14205.          int  10h            ; leaves AH = number of columns,
  14206.                              ;        BH = active video page
  14207.          mov  al,ah
  14208.          xor  ah,ah          ; AX := number of columns
  14209.          push ax
  14210.          mov  ah,3           ; AH := 3 (INT 10H function number)
  14211.          int  10h            ; leaves DH,DL = cursor position
  14212.          pop  cx             ; CX := displayed character columns
  14213.          sub  cl,dl          ; CX := number of remaining chars in line
  14214.          xor  bl,bl          ; BL := foreground pixel value
  14215.          mov  ax,0A20h       ; AH := 0AH (INT 10H function number)
  14216.                              ; AL := 20H (ASCII blank character)
  14217.          int  10h
  14218.  
  14219.  
  14220.  
  14221.  ───────────────────────────────────────────────────────────────────────────
  14222.  Function 0BH: Set Overscan Color, Select 4-Color Palette
  14223.  
  14224.  Caller registers:
  14225.  
  14226.       AH   =    0BH
  14227.       BH   =    0 to set border or background color
  14228.            =    1 to select 4-color palette
  14229.       BL   =    color value (if BH = 0)
  14230.                 palette value (if BH = 1)
  14231.  
  14232.  Returned values:
  14233.  
  14234.       (none)
  14235.  
  14236.  Video Display Data Area update:
  14237.  
  14238.       0040:0066  CRT_PALETTE
  14239.  
  14240.       INT 10H function 0BH comprises two subfunctions selected according to
  14241.       the value in BH. Function 0BH is intended for use only in 320-by-200
  14242.       4-color mode and in CGA alphanumeric modes, but you can use it with
  14243.       caution in other video modes.
  14244.  
  14245.  
  14246.       BH = 0
  14247.       When BH = 0 on the CGA and the MCGA, the BIOS loads the low-order five
  14248.       bits of the value in BL into the Color Select register (3D9H). In 320-
  14249.       by-200 4-color graphics mode, bits 0-3 determine the background color
  14250.       (the color displayed for pixels of value 0) as well as the border
  14251.       color. In 640-by-200 and 640-by-480 2-color modes, bits 0-3 specify
  14252.       the color of foreground (nonzero) pixels. On the CGA, these same four
  14253.       bits also determine the border color in alphanumeric modes.
  14254.  
  14255.       Bit 4 of the Color Select register selects between normal and high-
  14256.       intensity colors in CGA and MCGA graphics modes (see Chapter 4). For
  14257.       compatibility, the BIOS for the EGA and the VGA emulates this effect
  14258.       by using a palette of high-intensity colors when bit 4 of BL is set.
  14259.  
  14260.       In 200-line modes on the EGA and VGA, the value in BL is placed in
  14261.       the Attribute Controller's Overscan Color register (11H). This sets
  14262.       the border color. If either subsystem is in a graphics mode, the same
  14263.       value is also stored in palette register 0. This establishes the same
  14264.       color for all pixels of value 0.
  14265.  
  14266.       Don't use function 0BH with BL = 0 in other EGA and VGA video modes.
  14267.       In some modes, the BIOS routine stores incorrect color values in the
  14268.       Palette and Overscan registers, while in others it does nothing at
  14269.       all. You should use INT 10H function 10H to program the Attribute
  14270.       Controller on the EGA and VGA.
  14271.  
  14272.       Once the color register or Attribute Controller has been programmed,
  14273.       the BIOS routine copies bit 5 of CRT_PALETTE in the Video Display Area
  14274.       to bit 0 of register BL, and transfers control to the routine for
  14275.       BH = 1.
  14276.  
  14277.  
  14278.       BH = 1
  14279.       When BH = 1, the low-order bit of the value in BL determines which of
  14280.       two 4-color palettes is used for 320-by-200 4-color mode (see Figure
  14281.       A-12). On the CGA and the MCGA, this bit is copied into bit 5 of the
  14282.       Color Select register (3D9H). On the EGA and the VGA, the bit
  14283.       determines which set of color values is loaded into the Attribute
  14284.       Controller's Palette registers. The colors correspond to the CGA's
  14285.       320-by-200 4-color palettes. (See Chapter 4 for more details.)
  14286.  
  14287.  
  14288. ╓┌──────────────────────┌────────────────────────────────────────────────────╖
  14289.  Pixel Value            Color Displayed
  14290.  ──────────────────────────────────────────────────────────────────────────
  14291.  (bit 0 of BL = 0)
  14292.  1                      Green
  14293.  2                      Red
  14294.  3                      Yellow
  14295.  
  14296.  (bit 0 of BL = 1)
  14297.  1                      Cyan
  14298.  2                      Violet
  14299.  3                      White
  14300.  
  14301.       Figure A-12.  Function 0BH 4-color palettes.
  14302.  
  14303.  
  14304.       Function 0BH with BH = 1 has no effect in alphanumeric modes. In
  14305.       graphics modes other than 320-by-200 4-color mode, however, the Color
  14306.       Select register (on the CGA and the MCGA) is loaded or the palette
  14307.       registers (on the EGA and the VGA) are updated as if 320-by-200 4-
  14308.       color mode were in effect. For this reason, you should use this
  14309.       subfunction cautiously in graphics modes other than 320-by-200 4-color
  14310.       mode.
  14311.  
  14312.       The following example has three different effects, depending on the
  14313.       current video mode. In 200-line alphanumeric modes, it sets the border
  14314.       color; in 320-by-200 4-color mode it sets both border and background
  14315.       colors; and in CGA or MCGA 2-color graphics modes, it sets the
  14316.       foreground color.
  14317.  
  14318.          mov  ah,0BH         ; AH := 0BH (INT 10H function number)
  14319.          mov  bh,0           ; BH := subfunction number
  14320.          mov  bl,BorderColor ; BL := color value
  14321.          int  10h
  14322.  
  14323.       To select a 4-color palette in 320-by-200 4-color mode, call function
  14324.       0BH with  BH = 1:
  14325.  
  14326.          mov  ah,0Bh
  14327.          mov  bh,1           ; BH := subfunction number
  14328.          mov  bl,0           ; bit 0 of BL := 0 (red-green-yellow palette)
  14329.          int  10h
  14330.  
  14331.       In 320-by-200 4-color mode, select a high-intensity set of colors by
  14332.       calling function 0BH with BH = 0 and with bit 4 of BL set to 1:
  14333.  
  14334.          mov  ah,0Bh
  14335.          mov  bh,0
  14336.          mov  bl,10h         ; bit 4 selects high-intensity palette
  14337.                              ; bits 3-0 select border/background color
  14338.          int  10h
  14339.  
  14340.  
  14341.  
  14342.  ───────────────────────────────────────────────────────────────────────────
  14343.  Function 0CH: Store Pixel Value
  14344.  
  14345.  Caller registers:
  14346.  
  14347.       AH   =    0CH
  14348.       AL   =    pixel value
  14349.       BH   =    video page
  14350.       CX   =    x-coordinate
  14351.       DX   =    y-coordinate
  14352.  
  14353.  Returned values:
  14354.  
  14355.       (none)
  14356.  
  14357.  Video Display Data Area updates:
  14358.  
  14359.       (none)
  14360.  
  14361.       INT 10H function 0CH updates the value of a pixel at a specified
  14362.       location in the video buffer. In all graphics modes except 320-by-200
  14363.       256-color mode, if the high-order bit of the value in AL is set to 1,
  14364.       the value in AL is XORed into the video buffer. Otherwise, the value
  14365.       in AL becomes the pixel's new value.
  14366.  
  14367.       On the EGA, the MCGA, and the VGA, the value in BH is used to select
  14368.       among available video pages in the current video mode. However, the
  14369.       value in BH is ignored in 320-by-200 4-color mode.
  14370.  
  14371.       To set the value of a pixel in a 350-line graphics mode on an EGA with
  14372.       only 64 KB of video RAM, you must account for the chaining of memory
  14373.       maps to bit planes (as discussed in Chapter 4). In this situation,
  14374.       the BIOS routine expects you to specify the pixel value in AL using
  14375.       only its odd-numbered bits. Thus, the four possible pixel values
  14376.       should be specified as 0 (0000B), 1 (0001B), 4 (0100B), and 5 (0101B)
  14377.       instead of 0, 1, 2, and 3.
  14378.  
  14379.       The following routine shows how you would set the value of the pixel
  14380.       at (200,100) to 1 in any graphics mode:
  14381.  
  14382.          mov  ah,0Ch         ; AH := 0CH (INT 10H function number)
  14383.          mov  al,1           ; AL := pixel value
  14384.          mov  cx,200         ; CX := x-coordinate
  14385.          mov  dx,100         ; DX := y-coordinate
  14386.          int  10h
  14387.  
  14388.       To XOR a pixel value into the video buffer, set bit 7 of AL to 1
  14389.       before executing interrupt 10H, as in the following procedure:
  14390.  
  14391.          mov  ah,0Ch
  14392.          mov  al,1
  14393.          mov  cx,200
  14394.          mov  dx,100
  14395.          or   al,10000000b   ; set bit 7 to indicate XOR
  14396.          int  10h
  14397.  
  14398.       This code fragment illustrates the special situation that arises in a
  14399.       350-line video mode on an IBM EGA with only 64 KB of video RAM. The
  14400.       code sets the value of the pixel at (75,50) to 3.
  14401.  
  14402.          mov  ah,0Ch
  14403.          mov  al,0101b       ; AL := pixel value of 3 (11B)
  14404.                              ; represented in odd bits only
  14405.          mov  cx,75
  14406.          mov  dx,50
  14407.          int  10h
  14408.  
  14409.  
  14410.  
  14411.  ───────────────────────────────────────────────────────────────────────────
  14412.  Function 0DH: Return Pixel Value
  14413.  
  14414.  Caller registers:
  14415.  
  14416.       AH   =    0DH
  14417.       BH   =    video page
  14418.       CX   =    x-coordinate
  14419.       DX   =    y-coordinate
  14420.  
  14421.  Returned values:
  14422.  
  14423.       AL   =    pixel value
  14424.  
  14425.  Video Display Data Area updates:
  14426.  
  14427.       (none)
  14428.  
  14429.       INT 10H function 0DH returns the value of a pixel at a specified
  14430.       location in the video buffer.
  14431.  
  14432.       On an EGA in 320-by-200 4-color mode, the function ignores the video
  14433.       page value specified in BH.
  14434.  
  14435.       IBM's EGA BIOS (9/13/84 version) contains a bug in INT 10H function
  14436.       0DH. In 350-line graphics modes on an IBM EGA with only 64 KB of video
  14437.       RAM, the value returned in AL is incorrect. Apparently, the BIOS
  14438.       routine calculates the pixel's byte offset in the video buffer without
  14439.       properly accounting for the mapping of even addresses to even bit
  14440.       planes and odd addresses to odd bit planes.
  14441.  
  14442.       To determine the value of the pixel at (100,100), you could execute
  14443.       the following sequence of instructions:
  14444.  
  14445.          mov  ah,0Dh         ; AH := 0DH (INT 10H function number)
  14446.          mov  bh,0           ; BH := video page (0 in this example)
  14447.          mov  cx,100         ; CX := x-coordinate
  14448.          mov  dx,100         ; DX := y-coordinate
  14449.          int  10h            ; leaves AL = pixel value
  14450.  
  14451.  
  14452.  
  14453.  ───────────────────────────────────────────────────────────────────────────
  14454.  Function 0EH: Display Character in Teletype Mode
  14455.  
  14456.  Caller registers:
  14457.  
  14458.       AH   =    0EH
  14459.       AL   =    ASCII code
  14460.       BH   =    video page (PC BIOS versions dated 10/19/81 and earlier)
  14461.       BL   =    foreground pixel value (graphics modes only)
  14462.  
  14463.  Returned values:
  14464.  
  14465.       (none)
  14466.  
  14467.  Video Display Data Area update:
  14468.  
  14469.       0040:0050  CURSOR_POSN
  14470.  
  14471.       INT 10H function 0EH calls INT 10H function 0AH to display the
  14472.       character you pass in register AL. Unlike function 0AH, however,
  14473.       function 0EH moves the cursor, and ASCII codes 7 (bell), 8
  14474.       (backspace), 0DH (carriage return), and 0AH (linefeed) are treated as
  14475.       cursor control commands instead of displayable characters. Function
  14476.       0EH always updates the active (currently displayed) video page except
  14477.       as noted above.
  14478.  
  14479.       If the character is displayed in the rightmost character column,
  14480.       function 0EH advances the cursor to the start of the next character
  14481.       row. If necessary, function 0EH calls INT 10H function 06H to scroll
  14482.       the screen. In alphanumeric modes, the attribute of the displayed
  14483.       character is used for the scroll. In graphics modes, the scroll
  14484.       attribute is always 0.
  14485.  
  14486.       In alphanumeric modes, the attribute byte at the position where the
  14487.       character is written determines the character's foreground and
  14488.       background attributes. For this reason, you should probably fill the
  14489.       video buffer with the desired alphanumeric attributes before using
  14490.       function 0EH.
  14491.  
  14492.       In graphics modes, the character is written into the video buffer in a
  14493.       rectangular area the size of the character matrix. The character's
  14494.       pixels have the value BL specifies, and the remaining background
  14495.       pixels have a value of 0. Because the value in BL is passed through to
  14496.       INT 10H function 0AH, you can set bit 7 so that the character is XORed
  14497.       into the video buffer.
  14498.  
  14499.       NOTE: Unfortunately, function 0EH does not expand tab characters
  14500.       (ASCII code 9) into blanks.
  14501.  
  14502.       The following routine shows how you might use function 0EH to display
  14503.       a string of characters.
  14504.  
  14505.          mov  cx,StringLength        ; CX := number of bytes in string
  14506.          jcxz L02                    ; do nothing if null string
  14507.          mov  si,StringAddr          ; DS:SI := address of string
  14508.          mov  bl,GraphicsAttribute   ; BL := attribute (graphics modes only)
  14509.  L01:    lodsb                       ; AL := next character in string
  14510.          mov  ah,0Eh                 ; AH := 0EH (INT 10H function number)
  14511.          int  10h
  14512.          loop L01
  14513.  L02:    .
  14514.          .
  14515.          .
  14516.  
  14517.  
  14518.  
  14519.  ───────────────────────────────────────────────────────────────────────────
  14520.  Function 0FH: Return Current Video Status
  14521.  
  14522.  Caller register:
  14523.  
  14524.       AH   =    0FH
  14525.  
  14526.  Returned values:
  14527.  
  14528.       AH   =    number of displayed character columns
  14529.       AL   =    video mode number
  14530.       BH   =    active video page
  14531.  
  14532.  Video Display Data Area updates:
  14533.  
  14534.       (none)
  14535.  
  14536.       INT 10H function 0FH returns information about the current video mode
  14537.       and the width of the displayed portion of the video buffer. The number
  14538.       of character columns (returned in AH) and the number of the current
  14539.       video page (returned in BH) are copied from CRT_COLS and ACTIVE_PAGE
  14540.       in the Video Display Data Area.
  14541.  
  14542.       The value returned in AL is copied from CRT_MODE in the Video Display
  14543.       Data Area. It corresponds to the video display modes tabulated for
  14544.       function 0. On the EGA and the VGA, bit 7 of the value in AL is
  14545.       derived from bit 7 of the INFO byte. (INT 10H function 0 sets bit 7 of
  14546.       the INFO byte whenever you use function 0 to select a video mode
  14547.       without clearing the video buffer.)
  14548.  
  14549.       This example shows how to determine the current position of the
  14550.       displayed cursor. Before calling INT 10H function 3 to find out the
  14551.       cursor position, the example uses function 0FH to determine the
  14552.       currently displayed video page.
  14553.  
  14554.          mov  ah,0Fh         ; AH := 0FH (INT 10H function number)
  14555.          int  10h            ; leaves BH = active video page
  14556.          mov  ah,3           ; AH := 3 (INT 10H function number)
  14557.          int  10h            ; leaves DH,DL = cursor position
  14558.  
  14559.  
  14560.  
  14561.  ───────────────────────────────────────────────────────────────────────────
  14562.  Function 10H: Set Palette Registers, Set Intensity/Blink Attribute
  14563.  
  14564.  Caller registers:
  14565.  
  14566.       AH   =    10H
  14567.  
  14568.  Update a specified palette register:
  14569.  
  14570.       AL   =    0
  14571.       BH   =    color value
  14572.       BL   =    palette register number
  14573.  
  14574.  Specify the overscan (border) color:
  14575.  
  14576.       AL   =    1
  14577.       BH   =    color value
  14578.  
  14579.  Update all 16 palette registers plus the Overscan register:
  14580.  
  14581.       AL    =    2
  14582.       ES:DX =    address of 17-byte table
  14583.  
  14584.  Select Background Intensity or Blink attribute:
  14585.  
  14586.       AL   =    3
  14587.       BL   =    0 for background intensity (blink disabled)
  14588.            =    1 for blink
  14589.  
  14590.  Read a specified palette register:
  14591.  
  14592.       AL   =    7
  14593.       BL   =    palette register number
  14594.  
  14595.       Returned value:
  14596.       BH   =    contents of specified palette register
  14597.  
  14598.  Read the contents of the Overscan register:
  14599.  
  14600.       AL   =    8
  14601.  
  14602.       Returned value:
  14603.       BH   =    contents of Overscan register
  14604.  
  14605.  Read all 16 palette registers plus the Overscan register:
  14606.  
  14607.       AL    =    9
  14608.       ES:DX =    address of 17-byte table
  14609.  
  14610.       Returned values:
  14611.       Bytes 00H through 0FH of table contain palette register values.
  14612.       Byte 10H of table contains Overscan register value.
  14613.  
  14614.  Update the specified video DAC Color register:
  14615.  
  14616.       AL   =    10H
  14617.       BX   =    color register number
  14618.       CH   =    green value
  14619.       CL   =    blue value
  14620.       DH   =    red value
  14621.  
  14622.  Update a block of video DAC color registers:
  14623.  
  14624.       AL    =    12H
  14625.       BX    =    first register to update
  14626.       CX    =    number of registers to update
  14627.       ES:DX =    address of table of red-green-blue values
  14628.  
  14629.  Set Attribute Controller Color Select State:
  14630.  
  14631.       AL   =    13H
  14632.       BL   =    0 to set Mode Control register bit 7, 1 to set Color Select
  14633.                   register
  14634.       BH   =    value for bit 7 (if BL = 0) or value for Color Select
  14635.                   register (if BL = 1)
  14636.  
  14637.  Read specified video DAC Color register:
  14638.  
  14639.       AL   =    15H
  14640.       BX   =    color register number
  14641.  
  14642.       Returned values:
  14643.       CH   =    green
  14644.       CL   =    blue
  14645.       DH   =    red
  14646.  
  14647.  Read a block of video DAC color registers:
  14648.  
  14649.       AL    =    17H
  14650.       BX    =    first register to read
  14651.       CX    =    number of registers to read
  14652.       ES:DX =    address of table of red-green-blue values
  14653.  
  14654.       Returned values:
  14655.       Bytes 0 through 3n - 1 (where n is the number of registers passed in
  14656.       CX) contain the red-green-blue values read from the specified block of
  14657.       color registers.
  14658.  
  14659.  Update video DAC Mask register:
  14660.  
  14661.       AL   =    18H
  14662.       BL   =    new mask value
  14663.  
  14664.  Read video DAC Mask register:
  14665.  
  14666.       AL   =    19H
  14667.  
  14668.       Returned value:
  14669.       BL   =    value read from video DAC Mask register
  14670.  
  14671.  Read Attribute Controller Color Select register:
  14672.  
  14673.       AL   =    1AH
  14674.  
  14675.       Returned values:
  14676.       BL   =    bit 7 of Mode Control register
  14677.       BH   =    bits 2 through 3 of Color Select register (if BL = 0)
  14678.                 bits 0 through 3 of Color Select register (if BL = 1)
  14679.  
  14680.  Perform gray-scaling on a block of video DAC color registers:
  14681.  
  14682.       AL   =    1BH
  14683.       BX   =    first color register in block
  14684.       CX   =    number of color registers
  14685.  
  14686.  Video Display Data Area updates:
  14687.  
  14688.       0040:0065  CRT_MODE_SET
  14689.       0040:0066  CRT_PALETTE
  14690.  
  14691.       INT 10H function 10H exists only in the EGA, MCGA, and VGA BIOS. The
  14692.       function comprises 16 subfunctions that are selected according to the
  14693.       value in AL. Figure A-13 shows the support that the various
  14694.       subsystems provide for these subfunctions. All subfunctions work in
  14695.       both alphanumeric and graphics modes.
  14696.  
  14697.       Subfunctions 0 through 9 support attribute and palette programming.
  14698.       Subfunctions 10H through 1BH support the video DAC on the MCGA and the
  14699.       VGA.
  14700.  
  14701.  
  14702.       AL = 0
  14703.       When AL = 0 on the EGA and the VGA, function 10H updates the value in
  14704.       one of the palette registers in the Attribute Controller. The routine
  14705.       loads the value in BH into the register that BL specifies.
  14706.  
  14707.       Although this subfunction's intended purpose is to load a color value
  14708.       into a palette register, the BIOS routine does not validate the
  14709.       register number in BL. Thus, you can also use it to update the
  14710.       Attribute Controller's Mode Control, Overscan, Color Plane Enable, and
  14711.       Horizontal Pel Panning registers.
  14712.  
  14713.       On the MCGA, when BH = 7 and BL = 12H, the BIOS routine sets bit 3 of
  14714.       the Video DAC Mask register (3C6H) to 0. This causes the BIOS to
  14715.       regard bit 3 of all 4-bit pixel values or alphanumeric attributes as a
  14716.       "don't care" bit in reference to the Video DAC color registers, so
  14717.       only the first eight registers can be referenced. This is useful in
  14718.       displaying two 256-character sets in an alphanumeric mode (see Chapter
  14719.       10). The MCGA BIOS ignores all other values in BH or BL.
  14720.  
  14721.  
  14722. ╓┌─────────────────────┌─────────────┌────────────┌──────────────────────────╖
  14723.  Subfunction           EGA           MCGA         VGA
  14724.  ──────────────────────────────────────────────────────────────────────────
  14725.  0                     x             x            x
  14726.  1                     x                          x
  14727.  2                     x                          x
  14728.  3                     x             x            x
  14729.  4 (reserved)
  14730.  5 (reserved)
  14731.  6 (reserved)
  14732.  7                                                x
  14733.  8                                                x
  14734.  9                                                x
  14735.  10H                                 x            x
  14736.  11H (reserved)
  14737.  12H                                 x            x
  14738.  13H                                              x
  14739.  14H (reserved)
  14740.  15H                                 x            x
  14741.  16H (reserved)
  14742.  17H                                 x            x
  14743.  18H                                 x            x
  14744.  Subfunction           EGA           MCGA         VGA
  14745. 18H                                 x            x
  14746.  19H                                 x            x
  14747.  1AH                                              x
  14748.  1BH                                 x            x
  14749.  
  14750.       Figure A-13.  INT 10H Function 10H support in EGA, MCGA, and VGA BIOS.
  14751.  
  14752.  
  14753.       AL = 1
  14754.       When AL = 1 on the EGA and the VGA, the BIOS copies the value in BH
  14755.       into the Attribute Controller's Overscan register (11H).
  14756.  
  14757.  
  14758.       AL = 2
  14759.       When AL = 2 on the EGA and the VGA, the BIOS expects ES:DX to contain
  14760.       the address of a 17-byte table of values for the 16 Palette registers
  14761.       (bytes 0 through 15) and for the Overscan register (byte 16). The
  14762.       routine copies these values into the corresponding registers in the
  14763.       Attribute Controller.
  14764.  
  14765.  
  14766.       AL = 3
  14767.       When AL = 3 on the EGA and the VGA, the value in BL determines the
  14768.       value of bit 3 of the Attribute Controller's Mode Control register
  14769.       (10H). If BL = 0, bit 3 of the Mode Control register value is set to
  14770.       0, disabling the blinking attribute. If BL is 1, bit 3 is set to 1 to
  14771.       enable blinking.
  14772.  
  14773.       When AL = 3 on the MCGA, bit 5 of the Color Control register (3D8H) is
  14774.       set to reflect the value in BL. If BL = 0, bit 5 is set to 0 to
  14775.       disable blinking. If BL is 1,  bit 5 is set to 1.
  14776.  
  14777.  
  14778.       AL = 7
  14779.       When AL = 7 on the VGA, the value in the Attribute Controller Palette
  14780.       register that BL specified is returned in BH. Because the BIOS does
  14781.       not check the specified register number, this subfunction may be used
  14782.       to return the contents of any VGA Attribute Controller register.
  14783.  
  14784.  
  14785.       AL = 8
  14786.       When AL = 8 on the VGA, the contents of the Attribute Controller's
  14787.       Overscan register are returned in BH.
  14788.  
  14789.  
  14790.       AL = 9
  14791.       When AL = 9 on the VGA, the contents of all 16 palette registers and
  14792.       the Overscan register are returned to a 17-byte table whose address
  14793.       was passed to the BIOS in the register pair ES:DX.
  14794.  
  14795.  
  14796.       AL = 10H
  14797.       When AL = 10H on the MCGA and the VGA, the video DAC color register
  14798.       that BX specifies is updated with the red, green, and blue values
  14799.       specified in DH, CH, and CL. Only the low-order six bits of each of
  14800.       the three color values are significant.
  14801.  
  14802.       If gray-scale summing is enabled, the value stored in the color
  14803.       register is the gray-scale value that corresponds to the specified
  14804.       color values (see INT 10H function 12H with BL = 33H).
  14805.  
  14806.  
  14807.       AL = 12H
  14808.       When AL = 12H on the MCGA and the VGA, a block of consecutive video
  14809.       DAC color registers is updated from the table whose address is passed
  14810.       in ES:DX. The value in BX (00H through 0FFH) indicates the first color
  14811.       register to update, and CX contains the number of registers affected.
  14812.       The BIOS routine performs no error checking; if the sum of the values
  14813.       in BX and CX is greater than 256 (100H), the routine wraps around and
  14814.       updates the first color register(s) in the video DAC.
  14815.  
  14816.       If gray-scale summing is enabled, the values stored in the color
  14817.       registers are the gray-scale values that correspond to the color
  14818.       values in the table (see INT 10H function 12H with BL = 33H).
  14819.  
  14820.       You must format the table in three-byte groups. Each group must
  14821.       contain a red color value in the first byte, a green value in the
  14822.       second byte, and a blue value in the third byte. Only the low-order
  14823.       six bits of each color value are significant.
  14824.  
  14825.  
  14826.       AL = 13H
  14827.       On the VGA, when AL = 13H, the ROM BIOS updates the Attribute
  14828.       Controller's Mode Control register (10H) and the Color Select register
  14829.       (14H) to enable grouping of the 256 video DAC color registers into
  14830.       blocks of 16 or 64 registers each, as discussed in Chapter 3.
  14831.  
  14832.       When BL = 0, the BIOS uses the value passed in BH to update bit 7 of
  14833.       the Mode Control register. When BH = 1, bit 7 is set to 1. This causes
  14834.       the BIOS to use bits 0 and 1 of the Color Select register in
  14835.       place of bits 4 and 5 of the palette register values. When BH = 0,
  14836.       bit 7 is set to 0, and all six low-order bits of the values in the
  14837.       palette registers are significant.
  14838.  
  14839.       When BL = 1, the value in BH is stored in the appropriate bit fields
  14840.       in the Color Select register. If bit 7 of the Mode Control register is
  14841.       1, bits 0 through 3 of the value in BH are copied into bits 0 through
  14842.       3 of the Color Select register. If bit 7 of the Mode Control register
  14843.       is 0, bits 0 through 1 of BH are copied into bits 2 through 3 of the
  14844.       Color Select register.
  14845.  
  14846.  
  14847.       AL = 15H
  14848.       When AL = 15H on the MCGA and the VGA, the contents of the video DAC
  14849.       color register specified in BX are returned in registers DH (red), CH
  14850.       (green), and CL (blue). Only the low-order six bits of each of the
  14851.       color values are significant.
  14852.  
  14853.  
  14854.       AL = 17H
  14855.       When AL = 17H on the MCGA and the VGA, the values from a block of
  14856.       adjacent video DAC color registers are copied to the table whose
  14857.       address is passed in ES:DX. The value in BX (00H through 0FFH)
  14858.       indicates the first color register to be read, and CX contains the
  14859.       number of registers affected. The BIOS routine performs no error
  14860.       checking; the sum of the values in BX and CX should not exceed 256
  14861.       (100H).
  14862.  
  14863.       The table must contain three bytes for every color register read.
  14864.       Color values for each register are stored sequentially in the table in
  14865.       three-byte groups. The first byte of each group contains the color
  14866.       register's red value, the second its green value, and the third its
  14867.       blue value.
  14868.  
  14869.  
  14870.       AL = 18H
  14871.       On the MCGA and the VGA, when AL = 18H, the value in BL is copied into
  14872.       the video DAC Mask register (3C6H).
  14873.  
  14874.  
  14875.       AL = 19H
  14876.       On the MCGA and the VGA, when AL = 19H, the value in the video DAC
  14877.       Mask register (3C6H) is returned in BL.
  14878.  
  14879.       NOTE: The BIOS on the VGA Adapter does not support subfunctions 18H
  14880.       and 19H. Also, IBM's BIOS Interface Technical Reference does not
  14881.       document these subfunctions, so they might not be supported in future
  14882.       BIOS releases.
  14883.  
  14884.  
  14885.       AL = 1AH
  14886.       On the VGA, when AL = 1AH, the current values of bit 7 of the
  14887.       Attribute Controller's Mode Control register (10H) and bits 0 through
  14888.       3 of the Color Select register (14H) are returned in BL and BH
  14889.       respectively. If bit 7 of the Mode Control register is 1, the value in
  14890.       BH represents bits 0 through 3 of the Color Select register. If bit 7
  14891.       of the Mode Control register is 0, only bits 2 through 3 are returned
  14892.       as bits 0 through 1 of BH.
  14893.  
  14894.  
  14895.       AL = 1BH
  14896.       On the MCGA and the VGA, when AL = 1BH, gray-scale summing is
  14897.       performed on a block of consecutive video DAC color registers. BX
  14898.       indicates the first color register affected. CX specifies the number
  14899.       of registers to update.
  14900.  
  14901.       The following example uses INT 10H function 10H to update the color
  14902.       value in a single palette register:
  14903.  
  14904.          mov  ax,1000h       ; AH := 10H (INT 10H function number)
  14905.                              ; AL := 0
  14906.          mov  bh,6           ; BH := new color value (yellow)
  14907.          mov  bl,7           ; BL := palette register number
  14908.          int  10h
  14909.  
  14910.       To update the Overscan register and change the displayed border color,
  14911.       call function 10H with AL = 1:
  14912.  
  14913.          mov  ax,1001h       ; AH := 10H
  14914.                              ; AL := 1
  14915.          mov  bh,1           ; BH := color value for overscan
  14916.          int  10h
  14917.  
  14918.       To load all 16 palette registers and the Overscan register from a
  14919.       table, call function 10H with AL = 2:
  14920.  
  14921.          mov  ax,1002h               ; AH := 10H
  14922.                                      ; AL := 2
  14923.          mov  dx,seg PaletteTable
  14924.          mov  es,dx
  14925.          mov  dx,offset PaletteTable ; ES:DX -> table of palette register
  14926.                                      ;           values
  14927.          int  10h
  14928.          .
  14929.          .
  14930.          .
  14931.  PaletteTable db    00h,01h,02h,03h,04h,05h,06h,07h ; palette registers 0-7
  14932.               db    38h,39h,3Ah,3Bh,3Ch,3Dh,3Eh,3Fh ; palette regs 8-0FH
  14933.               db    00h                             ; Overscan reg
  14934.  
  14935.       To disable the blinking attribute, call function 10H with AL = 3 and
  14936.       BL = 0:
  14937.  
  14938.          mov  ax,1003h       ; AH := 10H
  14939.                              ; AL := 3
  14940.          mov  bl,0           ; BL := 0 (disable blinking)
  14941.          int  10h
  14942.  
  14943.       The following fragment performs gray-scale summing on the first 16
  14944.       video DAC color registers. The remaining 240 registers are unaffected.
  14945.  
  14946.          mov  ax,101Bh       ; AH := 10H
  14947.                              ; AL := 1BH
  14948.          mov  bx,0           ; BX := first color register affected
  14949.          mov  cx,16          ; CX := number of color registers
  14950.          int  10h
  14951.  
  14952.  
  14953.  
  14954.  ───────────────────────────────────────────────────────────────────────────
  14955.  Function 11H: Character Generator Interface
  14956.  
  14957.  Caller registers:
  14958.  
  14959.       AH    =    11H
  14960.  
  14961.  Load alphanumeric character definitions.
  14962.  
  14963.       User-specified character definition table:
  14964.       AL    =    0
  14965.       BH    =    points (bytes per character definition)
  14966.       BL    =    table in character generator RAM
  14967.       CX    =    number of characters defined in table
  14968.       DX    =    ASCII code of first character defined
  14969.       ES:BP =    address of user-specified table
  14970.  
  14971.       ROM BIOS 8-by-14 character definitions:
  14972.       AL    =    1
  14973.       BL    =    table in character generator RAM
  14974.  
  14975.       ROM BIOS 8-by-8 character definitions:
  14976.       AL    =    2
  14977.       BL    =    table in character generator RAM
  14978.  
  14979.       ROM BIOS 8-by-16 character definitions:
  14980.       AL    =    4
  14981.       BL    =    table in character generator RAM
  14982.  
  14983.  Select displayed character definition tables.
  14984.  
  14985.       AL    =    3
  14986.       BL    =    value for Character Map Select register (EGA, VGA)
  14987.             =    character generator RAM table numbers (MCGA)
  14988.  
  14989.  Load alphanumeric character definitions and program the CRT Controller.
  14990.  
  14991.       User-specified character definition table:
  14992.       AL    =    10H
  14993.       BH    =    points
  14994.       BL    =    table in character generator RAM
  14995.       CX    =    number of characters defined in table
  14996.       DX    =    ASCII code of first character defined
  14997.       ES:BP =    address of user-specified table
  14998.  
  14999.       ROM BIOS 8-by-14 character definitions:
  15000.       AL    =    11H
  15001.       BL    =    table in character generator RAM
  15002.  
  15003.       ROM BIOS 8-by-8 character definitions:
  15004.       AL    =    12H
  15005.       BL    =    table in character generator RAM
  15006.  
  15007.       ROM BIOS 8-by-16 character definitions:
  15008.       AL    =    14H
  15009.       BL    =    table in character generator RAM
  15010.  
  15011.  Load graphics character definitions.
  15012.  
  15013.       User-specified 8-by-8 character definition table for interrupt 1FH
  15014.       vector:
  15015.       AL    =    20H
  15016.       ES:BP =    address of user-specified character definition table
  15017.  
  15018.       User-specified character definition table:
  15019.       AL    =    21H
  15020.       BL    =    0  (character rows per screen specified in DL)
  15021.             =    1  14 character rows per screen
  15022.             =    2  25 character rows per screen
  15023.             =    3  43 character rows per screen
  15024.       CX    =    points (bytes per character definition)
  15025.       DL    =    character rows per screen (when BL = 0)
  15026.       ES:BP =    address of user-specified character definition table
  15027.  
  15028.       ROM BIOS 8-by-14 character definitions:
  15029.       AL    =    22H
  15030.       BL    =    character rows per screen (as above)
  15031.       DL    =    (as above)
  15032.  
  15033.       ROM BIOS 8-by-8 character definitions:
  15034.       AL    =    23H
  15035.       BL    =    character rows per screen (as above)
  15036.       DL    =    (as above)
  15037.  
  15038.       ROM BIOS 8-by-16 character definitions:
  15039.       AL    =    24H
  15040.       BL    =    character rows per screen (as above)
  15041.       DL    =    (as above)
  15042.  
  15043.  Get current character generator information.
  15044.  
  15045.       AL    =    30H
  15046.       BH    =    0  Contents of interrupt 1FH vector
  15047.             =    1  Contents of interrupt 43H vector
  15048.             =    2  Address of ROM 8-by-14 character table
  15049.             =    3  Address of ROM 8-by-8 character table
  15050.             =    4  Address of second half of ROM 8-by-8 character table
  15051.             =    5  Address of ROM 9-by-14 alternate character table
  15052.             =    6  Address of ROM 8-by-16 character table
  15053.             =    7  Address of ROM 9-by-16 alternate character table
  15054.  
  15055.       Returned values:
  15056.       CX    =    POINTS (height of character matrix)
  15057.       DL    =    ROWS (displayed character rows - 1)
  15058.       ES:BP =    address of character definition table
  15059.  
  15060.  Video Display Data Area updates:
  15061.  
  15062.       0040:004C  CRT_LEN
  15063.       0040:0060  CURSOR_MODE
  15064.       0040:0084  ROWS
  15065.       0040:0085  POINTS
  15066.  
  15067.       INT 10H function 11H comprises a gamut of subfunctions that support
  15068.       both the alphanumeric and the graphics character generators on the
  15069.       EGA, the MCGA, and the VGA. You choose a subfunction with the value
  15070.       you specify in AL. The contents of the other registers depend on the
  15071.       subfunction.
  15072.  
  15073.  
  15074.       AL = 0, 1, 2, or 4
  15075.       You can use subfunctions 0, 1, 2, and 4 to load a table of character
  15076.       definitions into video RAM for use by the character generator.
  15077.       (Chapter 10 describes this in detail.) All four subfunctions are
  15078.       available on the VGA. On the EGA, the BIOS ignores subfunction 4. The
  15079.       MCGA BIOS does not contain an 8-by-14 character definition table, so
  15080.       calls with AL = 1 are treated as calls with AL = 4.
  15081.  
  15082.       On the MCGA, character definitions in character generator RAM are not
  15083.       displayed until they are loaded into the character generator's
  15084.       internal font pages (see Chapter 10). To accomplish this through the
  15085.       video BIOS, follow each call to function 11H performed with AL = 0, 1,
  15086.       2, or 4 with a call to function 11H with AL = 3.
  15087.  
  15088.       The MCGA's CRTC can only display characters that are 2, 4, 6, 8, 10,
  15089.       12, 14, or 16 lines high. Thus, BH should specify one of these values.
  15090.       Also, for compatibility with the VGA BIOS, the MCGA BIOS routine
  15091.       extends character definitions for 14-line characters into definitions
  15092.       for 16-line characters by duplicating the 14th line of each character
  15093.       definition.
  15094.  
  15095.  
  15096.       AL = 3
  15097.       On the EGA and the VGA, when AL = 3, function 11H loads the value
  15098.       passed in BL into the Sequencer's Character Map Select register. On
  15099.       the EGA and the MCGA, bits 0 and 1 of BL indicate which of four 256-
  15100.       character tables is used when bit 3 of a character's attribute byte is
  15101.       0. Bits 2 and 3 of BL indicate which table is used when bit 3 of a
  15102.       character's attribute is 1. On the VGA, bits 0, 1, and 4 specify one
  15103.       of eight tables to be used when a character's attribute bit 3 is 0,
  15104.       and bits 2, 3, and 5 specify the table used when attribute bit 3 is 1.
  15105.  
  15106.       If both bit fields in BL specify the same character definition table,
  15107.       only that table is loaded and displayed.
  15108.  
  15109.       AL = 10H, 11H, 12H, or 14H
  15110.       Subfunctions 10H, 11H, 12H, and 14H are analogous to subfunctions 0,
  15111.       1, 2, and 4 in that they load an alphanumeric character definition
  15112.       table into video RAM. The difference is that, for these subfunctions
  15113.       on the EGA and the VGA, the BIOS reprograms the CRT Controller to
  15114.       accommodate the height of the character matrix. On the MCGA, calls to
  15115.       function 11H with AL = 10H, 11H, 12H, and 14H are treated as calls to
  15116.       functions 0, 1, 2, and 4 respectively.
  15117.  
  15118.       NOTE: Disable alphanumeric cursor emulation before using these
  15119.       subfunctions on the EGA. The EGA BIOS cursor emulation routine does
  15120.       not always produce a satisfactory alphanumeric cursor. (Chapter 3
  15121.       discusses this in detail.)
  15122.  
  15123.  
  15124.       AL = 20H
  15125.       If AL = 20H, the address in ES:BP is copied into the interrupt 1FH
  15126.       vector at 0000:007C. This vector points to a table of 8-by-8 character
  15127.       definitions for ASCII codes 80H through FFH. This character definition
  15128.       table is used by the BIOS in CGA-compatible 320-by-200 4-color and
  15129.       640-by-200 2-color graphics modes.
  15130.  
  15131.  
  15132.       AL = 21H, 22H, 23H, or 24H
  15133.       Subfunctions 21H, 22H, 23H, and 24H are analogous to subfunctions 0,
  15134.       1, 2, and 4 respectively. The BIOS updates the interrupt 43H vector
  15135.       and the Video Display Data Area variables POINTS and ROWS with values
  15136.       that describe the specified graphics character definitions.
  15137.  
  15138.       The BIOS does not reprogram the CRT Controller when it loads graphics-
  15139.       mode character definition tables.
  15140.  
  15141.  
  15142.       AL = 30H
  15143.       If AL = 30H, INT 10H function 11H returns information about the
  15144.       character generator's current status. The value in POINTS in the Video
  15145.       Display Data Area is copied into register CX, the value of ROWS is
  15146.       returned in DL, and the address of one of eight character definition
  15147.       tables is returned in ES:BP. The value in BH indicates which table's
  15148.       address is returned.
  15149.  
  15150.       NOTE: If you call this subfunction on the EGA with BH equal to 6 or 7,
  15151.       or on the MCGA with BH equal to 5 or 7, the address returned in ES:BP
  15152.       is undefined.
  15153.  
  15154.       To select an 80-by-43 alphanumeric mode on a 350-line display, invoke
  15155.       INT 10H function 11H to load the ROM 8-by-8 character set and
  15156.       reprogram the CRTC to display 43 character rows. (Dividing 350 lines
  15157.       by 8 lines per character gives 43 character rows.) The following
  15158.       example assumes that the EGA is already in an 80-by-25 alphanumeric
  15159.       mode (BIOS mode number 3 or 7).
  15160.  
  15161.          mov  ax,40h
  15162.          mov  es,ax
  15163.  
  15164.          push es:[87h]               ; preserve INFO
  15165.          or   byte ptr es:[87h],1    ; disable cursor emulation
  15166.          mov  ax,1112h               ; AH := 11H (INT 10H function number)
  15167.                                      ; AL := 12H (subfunction:  load 8x8
  15168.                                      ;  alphanumeric characters, reprogram
  15169.                                      ;  CRTC)
  15170.          mov  bl,0                   ; BL := table 0 in character generator
  15171.                                      ;  RAM
  15172.          int  10h
  15173.          pop  es:[87h]               ; restore INFO
  15174.  
  15175.  
  15176.  
  15177.  ───────────────────────────────────────────────────────────────────────────
  15178.  Function 12H: Video Subsystem Configuration (Alternate Select)
  15179.  
  15180.  Caller registers:
  15181.  
  15182.       AH    =    12H
  15183.  
  15184.  Return video configuration information:
  15185.  
  15186.       BL    =    10H
  15187.  
  15188.       Returned values:
  15189.       BH    =    default BIOS video mode
  15190.                  0  Color
  15191.                  1  Monochrome
  15192.       BL    =    amount of EGA video RAM
  15193.                  0  64 KB
  15194.                  1  128 KB
  15195.                  2  192 KB
  15196.                  3  256 KB
  15197.       CH    =    feature bits
  15198.       CL    =    configuration switch setting
  15199.  
  15200.  Select alternate Print Screen routine:
  15201.  
  15202.       BL    =    20H
  15203.  
  15204.  Select scan lines for alphanumeric modes:
  15205.  
  15206.       BL    =    30H
  15207.       AL    =    0  200 scan lines
  15208.                  1  350 scan lines
  15209.                  2  400 scan lines
  15210.  
  15211.       Returned value:
  15212.       AL    =    12H
  15213.  
  15214.  Select default palette loading:
  15215.  
  15216.       BL    =    31H
  15217.       AL    =    0  Enable default palette loading
  15218.             =    1  Disable default palette loading
  15219.  
  15220.       Returned value:
  15221.       AL    =    12H
  15222.  
  15223.  CPU access to video RAM:
  15224.  
  15225.       BL    =    32H
  15226.       AL    =    0  Enable CPU access to video RAM and I/O ports
  15227.             =    1  Disable CPU access to video RAM and I/O ports
  15228.  
  15229.       Returned value:
  15230.       AL    =    12H
  15231.  
  15232.  Gray-scale summing:
  15233.  
  15234.       BL    =    33H
  15235.       AL    =    0  Enable gray-scale summing
  15236.             =    1  Disable gray-scale summing
  15237.  
  15238.       Returned value:
  15239.       AL    =    12H
  15240.  
  15241.  Cursor emulation:
  15242.  
  15243.       BL    =    34H
  15244.       AL    =    0  Enable cursor emulation
  15245.             =    1  Disable cursor emulation
  15246.  
  15247.       Returned value:
  15248.       AL    =    12H
  15249.  
  15250.  PS/2 video display switching:
  15251.  
  15252.       BL    =    35H
  15253.       AL    =    0  Initial adapter video off
  15254.                  1  Initial planar video on
  15255.                  2  Switch active video off
  15256.                  3  Switch inactive video on
  15257.       ES:DX =    address of 128-byte save area (for AL = 0, 2, or 3)
  15258.  
  15259.       Returned value:
  15260.       AL    =    12H
  15261.  
  15262.  Video refresh control:
  15263.  
  15264.       BL    =    36H
  15265.       AL    =    0  Enable refresh
  15266.                  1  Disable refresh
  15267.  
  15268.       Returned value:
  15269.       AL    =    12H
  15270.  
  15271.  Video Display Data Area updates:
  15272.  
  15273.       (see below)
  15274.  
  15275.       INT 10H function 12H comprises nine subfunctions selected using the
  15276.       value in BL.
  15277.  
  15278.  
  15279.       BL = 10H
  15280.       When BL = 10H on the EGA and the VGA, this BIOS routine returns
  15281.       information about the configuration of the video subsystem. This
  15282.       information is copied from INFO and INFO_3 in the Video Display Data
  15283.       Area. These variables are initialized in the BIOS power-on startup
  15284.       code.
  15285.  
  15286.       The value returned in BH reflects whether the video subsystem is
  15287.       configured for a color (BH = 0) or monochrome (BH = 1) video mode.
  15288.       Bits 0 and 1 in BL indicate how much video RAM is present. The values
  15289.       returned in CH and CL are derived from the INFO_3 byte. Bits 4 through
  15290.       7 of INFO_3 (input from the EGA feature connector) are copied to bits
  15291.       0 through 3 of CH. Bits 0 through 3 of INFO_3 (configuration switch
  15292.       settings) are copied to bits 0 through 3 of CL.
  15293.  
  15294.  
  15295.       BL = 20H
  15296.       When BL = 20H on the MCGA, the EGA, and the VGA, the BIOS points the
  15297.       interrupt 5 vector at 0000:0014 to an alternate Print Screen routine
  15298.       contained in the video ROM BIOS. The difference between this routine
  15299.       and the default planar BIOS routine is that the video ROM version uses
  15300.       the Video Display Data Area variable ROWS to determine the number of
  15301.       character rows to print. The PC/XT and PC/AT planar BIOS versions
  15302.       always print 25 rows.
  15303.  
  15304.  
  15305.       BL = 30H
  15306.       When BL = 30 on the VGA, the BIOS routine updates bits 0-3 of the
  15307.       INFO_3 byte (0040:0088) and bits 7 and 4 of the Flags byte at
  15308.       0040:0089. INT 10H function 0 refers to INFO_3 and the Flags byte to
  15309.       determine whether to configure the video subsystem for a 200-line,
  15310.       350-line, or 400-line mode when it establishes an alphanumeric video
  15311.       mode. You can thus select among 200-line, 350-line, and 400-line
  15312.       alphanumeric modes by first executing INT 10H function 12H with BL =
  15313.       30H and AL = 0, 1, or 2, and then calling INT 10H function 0 to set
  15314.       the video mode.
  15315.  
  15316.       This function normally returns the value 12H in AL. If the VGA is
  15317.       inactive (bit 3 of INFO is set to 1), the function returns with
  15318.       AL = 0.
  15319.  
  15320.  
  15321.       BL = 31H
  15322.       When BL = 31H on the MCGA or VGA, the BIOS routine updates bit 3 of
  15323.       the Flags byte at 0040:0089 to indicate whether ROM BIOS default
  15324.       palette values should be loaded when a video mode is selected using
  15325.       INT 10H function 0. If the value 0 is passed in AL, bit 3 of the Flags
  15326.       byte is set to 0 to enable default palette setting. If AL = 1, bit 3
  15327.       is set to 1 to disable default palette setting.
  15328.  
  15329.       When a valid value is passed in AL, the function returns with AL =
  15330.       12H.
  15331.  
  15332.       BL = 32H
  15333.       When BL = 32H on the MCGA or the VGA, the value in AL specifies
  15334.       whether CPU access to the video buffer and I/O ports is enabled (AL =
  15335.       0) or disabled (AL = 1). Although the hardware interface for control
  15336.       of video addressing differs on the MCGA, the VGA, and the VGA Adapter,
  15337.       this BIOS function is the same in all three subsystems (see Chapter
  15338.       2).
  15339.  
  15340.       When a valid value is passed in AL, the function returns with AL =
  15341.       12H.
  15342.  
  15343.       NOTE: Although the EGA video BIOS does not support this function, you
  15344.       can control CPU addressing of video RAM on the EGA by updating bit 1
  15345.       of the Miscellaneous Output register (3C2H).
  15346.  
  15347.  
  15348.       BL = 33H
  15349.       When BL = 33H on the MCGA or the VGA, the BIOS routine updates bit 1
  15350.       of the Flags byte at 0040:0089 to indicate whether red-green-blue
  15351.       color values should be averaged to gray-scale values when INT 10H
  15352.       functions 0 and 10H update the video DAC color registers. If the value
  15353.       0 is passed in AL, bit 1 of the Flags byte is set to 1 to enable gray-
  15354.       scale summing. If AL = 1, bit 1 is set to 0 to disable gray-scale
  15355.       summing.
  15356.  
  15357.       When a valid value is passed in AL, the function returns with
  15358.       AL = 12H.
  15359.  
  15360.  
  15361.       BL = 34H
  15362.       When BL = 34H on the VGA, the BIOS routine updates bit 0 of INFO
  15363.       (0040:0087) to indicate whether BIOS cursor emulation is in effect. If
  15364.       the value 0 is passed in AL, bit 0 of INFO is set to 0 to enable
  15365.       cursor emulation. If AL = 1, bit 0 is set to 1 to disable cursor
  15366.       emulation.
  15367.  
  15368.       When a valid value is passed in AL, the function returns with
  15369.       AL = 12H.
  15370.  
  15371.  
  15372.       BL = 35H
  15373.       INT 10H function 1AH with BL = 35H provides a set of routines that
  15374.       support switching between two PS/2 video subsystems in the same
  15375.       computer. In a computer that contains two different PS/2-compatible
  15376.       video subsystems, calls to this function let a program separately
  15377.       access the video BIOS on a video adapter and the video BIOS on a PS/2
  15378.       motherboard.
  15379.  
  15380.       When you boot a PS/2 that contains a PS/2-compatible video adapter,
  15381.       the adapter subsystem is always the active subsystem by default. To
  15382.       use the PS/2's planar (motherboard) subsystem, you must use the
  15383.       display switch interface to disable the adapter subsystem and enable
  15384.       the planar subsystem.
  15385.  
  15386.       You can specify four related subfunctions for function 12H with
  15387.       BL = 35H, using the value passed in register AL. The four subfunctions
  15388.       are designed to be called in pairs. Subfunctions 0 and 1 should be
  15389.       called once each to initialize the BIOS display switch interface and
  15390.       to establish a default video mode for the planar video subsystem.
  15391.       Subsequent calls to subfunctions 2 and 3 then let you switch between
  15392.       the two video subsystems.
  15393.  
  15394.       When AL = 0, the adapter BIOS initializes the display switch
  15395.       interface. First, the adapter BIOS calls the motherboard BIOS to set
  15396.       bit 6 of the Flags byte at 0040:0089 to 1 to indicate that the
  15397.       interface is supported. Next, the current Video Display Data Area and
  15398.       video interrupt vectors are preserved in the 128-byte buffer whose
  15399.       address is passed in ES:DX, and the video interrupt vectors are
  15400.       redirected to the motherboard BIOS. Finally, the adapter's video
  15401.       buffer and control port addressing are disabled (see INT 10H function
  15402.       12H, BL = 32H).
  15403.  
  15404.       When AL = 1, the motherboard BIOS establishes a default 80-by-25
  15405.       alphanumeric mode on the planar video subsystem.
  15406.  
  15407.       When AL = 2 and bit 6 of the Flags byte is 1, the contents of the
  15408.       Video Display Data Area and video interrupt vectors are copied to the
  15409.       128-byte buffer whose address is passed in ES:DX, and the video
  15410.       interrupt vectors are redirected to the currently inactive BIOS. Then
  15411.       video buffer and control port addressing are disabled for the
  15412.       currently active subsystem. A call to this subfunction should normally
  15413.       be followed by a call with AL = 3.
  15414.  
  15415.       When AL = 3 and bit 6 of the Flags byte is 1, the contents of the
  15416.       Video Display Data Area and interrupt vectors are restored from the
  15417.       buffer whose address is in ES:DX. (This buffer should contain
  15418.       information previously saved by a call with AL = 0 or AL = 2.) Then
  15419.       video buffer and control port addressing are enabled, using the
  15420.       restored video information.
  15421.  
  15422.       When a valid value is passed in AL, and when both the adapter BIOS and
  15423.       the planar BIOS support the display switch interface, each of the four
  15424.       subfunctions returns with AL = 12H.
  15425.  
  15426.       NOTE: The PS/2 Model 30 BIOS (dated 12/12/86 and earlier) and the PS/2
  15427.       Model 25 BIOS (dated 6/26/87) contain a bug that makes the display
  15428.       switch interface unusable. The problem should be corrected in later
  15429.       BIOS versions.
  15430.  
  15431.  
  15432.       BL = 36H
  15433.       When BL = 36H on the VGA, the value in AL specifies whether the BIOS
  15434.       routine enables (AL = 0) or disables (AL = 1) video refresh.
  15435.       (Temporarily disabling video refresh can speed software that performs
  15436.       repeated video memory accesses.) Bit 5 of the VGA's Sequencer Clocking
  15437.       Mode register (01H) controls whether video refresh is enabled or
  15438.       disabled. When the value 0 is passed in AL, bit 5 is set to 0 to
  15439.       enable video refresh; when AL is 1, bit 5 is set to 1 to disable video
  15440.       refresh.
  15441.  
  15442.       The function always returns with AL = 12H.
  15443.  
  15444.       To obtain EGA configuration information, call INT 10H function 12H
  15445.       with BL = 10H:
  15446.  
  15447.          mov  ah,12h
  15448.          mov  bl,10h
  15449.          int  10h
  15450.  
  15451.       To vector the EGA BIOS alternate Print Screen routine, call INT 10H
  15452.       function 12H with BL = 20H:
  15453.  
  15454.          mov  ah,12h
  15455.          mov  bl,20h
  15456.          int  10h
  15457.  
  15458.       To implement display switching between a VGA Adapter and the MCGA in a
  15459.       PS/2 Model 30:
  15460.  
  15461.  ; save areas for video BIOS display switch interface
  15462.  
  15463.  VGAsave   db   128 dup(?)     ; save area for VGA
  15464.  MCGAsave  db   128 dup(?)     ; save area for MCGA
  15465.  
  15466.  ; initialize display switching (execute this code only once)
  15467.  
  15468.       mov  ax,1200h            ; AH := 12H (INT 10H function number)
  15469.                                ; AL := 0
  15470.       mov  bl,35h              ; BL := 35H (display switch interface)
  15471.       mov  dx,seg VGAsave
  15472.       mov  es,dx
  15473.       mov  dx,offset VGAsave   ; ES:DX -> save area for VGA BIOS info
  15474.       int  10h
  15475.       cmp  al,12h
  15476.       jne  Error               ; exit if display switching not supported
  15477.  
  15478.       mov  ax,1201h
  15479.       mov  bl,35h
  15480.       int  10h                 ; disable adapter, enable planar video
  15481.  
  15482.  ; switch from planar (MCGA) to adapter (VGA) subsystem
  15483.  
  15484.       mov  ax,1202h            ; AL := 2 (switch active
  15485.                                ;  video off)
  15486.       mov  bl,35h
  15487.       mov  dx,seg VGAsave
  15488.       mov  es,dx
  15489.       mov  dx,offset VGAsave   ; ES:DX -> save area for
  15490.                                ;  currently active subsystem
  15491.       int  10h
  15492.  
  15493.       mov  ax,1203h            ; AL := 3 (switch inactive
  15494.                                ;  video on)
  15495.       mov  bl,35h
  15496.       mov  dx,offset MCGAsave  ; ES:DX -> save area for
  15497.                                ;  subsystem to be made active
  15498.       int  10h
  15499.  
  15500.  ; (to switch from adapter to planar, interchange VGAsave and
  15501.  ;  MCGAsave in the calls with AL = 2 and AL = 3)
  15502.  
  15503.  
  15504.  
  15505.  
  15506.  ───────────────────────────────────────────────────────────────────────────
  15507.  Function 13H: Display Character String
  15508.  
  15509.  Caller registers:
  15510.  
  15511.       AH    =    13H
  15512.       AL    =    0  BL contains attribute for string. Cursor position
  15513.                     not updated.
  15514.             =    1  BL contains attribute for string. Cursor position
  15515.                     updated.
  15516.             =    2  String contains embedded attribute bytes. Cursor
  15517.                     position not updated.
  15518.             =    3  String contains embedded attribute bytes. Cursor
  15519.                     position updated.
  15520.       BH    =    video page
  15521.       BL    =    attribute
  15522.       CX    =    string length
  15523.       DH    =    character row
  15524.       DL    =    character column
  15525.       ES:BP =    address of start of string
  15526.  
  15527.  Returned values:
  15528.  
  15529.       (none)
  15530.  
  15531.  Video Display Data Area updates:
  15532.  
  15533.       0040:0050  CURSOR_POSN
  15534.  
  15535.       INT 10H function 13H writes a character string into the video buffer.
  15536.       Bell, backspace, linefeed, and carriage-return characters embedded in
  15537.       the string are treated as commands rather than displayable characters.
  15538.       If the string cannot be displayed in one row of characters, function
  15539.       13H wraps the string around to the start of the next line. Function
  15540.       13H also scrolls the screen upward as necessary.
  15541.  
  15542.       The string is copied from the address you specify in ES:BP to the
  15543.       location in the video buffer indicated by registers DH and DL
  15544.       (character row and column) and register BH (video page). You must also
  15545.       specify the number of characters in the string in register CX.
  15546.  
  15547.       Function 13H comprises four subfunctions that are selected according
  15548.       to the value in AL. These four subfunctions allow you to select the
  15549.       method of specifying display attributes for characters in the string
  15550.       and to control the cursor's final position after the string is
  15551.       displayed.
  15552.  
  15553.       You can specify the attribute used for each character either in BL
  15554.       (AL = 0 or 1) or by pairing each character code with its attribute in
  15555.       the string itself (AL = 2 or 3). Also, you can indicate whether the
  15556.       cursor will stay in place after the string is written (AL = 0 or 2) or
  15557.       will move to the character position just past the end of the string
  15558.       (AL = 1 or 3).
  15559.  
  15560.       In all graphics modes except 320-by-200 256-color mode, setting bit 7
  15561.       of the attribute value in BL to 1 causes the BIOS to XOR the string
  15562.       into the video buffer.
  15563.  
  15564.       The video page specified in BH must be 0 in 320-by-200 4-color mode.
  15565.  
  15566.       NOTE: On the PC/AT, the EGA, and the MCGA, linefeed and carriage-
  15567.       return characters are always written to the currently displayed video
  15568.       page, regardless of the value you specify in BH. If you write a string
  15569.       containing any of these control characters to a video page not
  15570.       currently displayed, function 13H writes them to the wrong video page.
  15571.  
  15572.       The following routine writes the string "Hello, World" into the video
  15573.       buffer in video page 0 at row 12, column 34. An attribute value of 7
  15574.       is used for all characters in the string.
  15575.  
  15576.          mov  ax,1300h               ; AH := 13H (INT 10H function number)
  15577.                                      ; AL := 0 (attribute specified in BL,
  15578.                                      ;  don't move the cursor)
  15579.          mov  bh,0                   ; BH := video page
  15580.          mov  bl,7                   ; BL := attribute
  15581.          mov  cx,12                  ; CX := number of characters to display
  15582.          mov  dh,12                  ; DH := row 12
  15583.          mov  dl,34                  ; DL := column 34
  15584.          mov  bp,seg HelloString
  15585.          mov  es,bp
  15586.          mov  bp,offset HelloString  ; ES:BP := string address
  15587.          int  10h
  15588.          .
  15589.          .
  15590.          .
  15591.  HelloString  db      'Hello, World'
  15592.  
  15593.       This example displays the digits 1 through 7 in the upper left corner
  15594.       of video page 0. The attribute used for each digit corresponds to the
  15595.       digit:
  15596.  
  15597.          mov  ax,1303h               ; AH := 13H (INT 10H function number)
  15598.                                      ; AL := 3 (string contains embedded
  15599.                                      ;  attribute bytes, move cursor to end
  15600.                                      ;  of string)
  15601.          mov  bh,0                   ; BH := video page
  15602.          mov  cx,7                   ; CX := number of characters to display
  15603.          mov  dx,0                   ; DH := row 0
  15604.                                      ; DL := column 0
  15605.          mov  bp,seg StringData
  15606.          mov  es,bp
  15607.          mov  bp,offset StringData   ; ES:BP := address of string
  15608.          int  10h
  15609.          .
  15610.          .
  15611.          .
  15612.  StringData   db      '1',1,'2',2,'3',3,'4',4,'5',5,'6',6,'7',7
  15613.  
  15614.  
  15615.  
  15616.  ───────────────────────────────────────────────────────────────────────────
  15617.  Function 14H: (PC Convertible only)
  15618.  
  15619.  
  15620.  Function 15H: (PC Convertible only)
  15621.  
  15622.  
  15623.  Function 16H: (reserved)
  15624.  
  15625.  
  15626.  Function 17H: (reserved)
  15627.  
  15628.  
  15629.  Function 18H: (reserved)
  15630.  
  15631.  
  15632.  
  15633.  ───────────────────────────────────────────────────────────────────────────
  15634.  Function 19H: (reserved)
  15635.  
  15636.  
  15637.  Function 1AH: Video Display Combination
  15638.  
  15639.  Caller registers:
  15640.  
  15641.       AH    =    1AH
  15642.  
  15643.  Return video display combination:
  15644.  
  15645.       AL    =    0
  15646.  
  15647.       Returned values:
  15648.       AL    =    1AH
  15649.       BL    =    active display
  15650.       BH    =    inactive display
  15651.  
  15652.  Set video display combination:
  15653.  
  15654.       AL    =    1
  15655.       BL    =    active display
  15656.       BH    =    inactive display
  15657.  
  15658.       Returned value:
  15659.       AL    =    1AH
  15660.  
  15661.  Video Display Data Area update:
  15662.  
  15663.       0040:008A  DCC byte
  15664.  
  15665.       INT 10H function 1AH returns or updates the video BIOS video display
  15666.       combination status. This status is represented in the DCC byte at
  15667.       0040:008A in the Video Display Data Area. This byte contains an index
  15668.       into the ROM BIOS Display Combination Code table, which contains a
  15669.       list of byte pairs that specify valid combinations of one or two video
  15670.       subsystems. Video subsystems are designated by the following values.
  15671.  
  15672.       FFH       Unrecognized video subsystem
  15673.         0        No display
  15674.         1       MDA with monochrome display
  15675.         2       CGA with color display
  15676.         3       (reserved)
  15677.         4       EGA with color display
  15678.         5       EGA with monochrome display
  15679.         6       Professional Graphics Controller
  15680.         7       VGA with analog monochrome display
  15681.         8       VGA with analog color display
  15682.         9       (reserved)
  15683.       0AH       MCGA with digital color display
  15684.       0BH       MCGA with analog monochrome display
  15685.       0CH       MCGA with analog color display
  15686.  
  15687.       AL = 0
  15688.       When AL = 0 on the MCGA or the VGA, the video BIOS routine uses the
  15689.       value in the DCC byte as an index into its Display Combination Code
  15690.       table and copies the 2-byte table entry into BH and BL. If two video
  15691.       subsystems are present, one subsystem must be monochrome and the other
  15692.       color; the BIOS routine determines which is active by examining bits 4
  15693.       through 5 of EQUIP_FLAG (0040:0010).
  15694.  
  15695.  
  15696.       AL = 1
  15697.       When AL = 1 on the MCGA or the VGA, the BIOS routine scans the Display
  15698.       Combination Code table for the combination specified in BH and BL. If
  15699.       the specified combination is found in the table, the DCC byte is
  15700.       updated with the appropriate index into the table. If the specified
  15701.       combination is not found, 0FFH is stored in the DCC byte.
  15702.  
  15703.       When a valid value (0 or 1) is passed in AL, INT 10H function 1AH
  15704.       returns with AL = 1AH.
  15705.  
  15706.       The following sequence returns the display combination in registers BH
  15707.       and BL.
  15708.  
  15709.          mov  ax,1A00h               ; AH := 1AH (INT 10H function number)
  15710.                                      ; AL := 0
  15711.          int  10h
  15712.          cmp  al,1AH
  15713.          jne  ErrorExit              ; jump if function not supported
  15714.                                      ; at this point BL = active display
  15715.                                      ; BH = inactive display
  15716.  
  15717.       If this sequence is executed on a PS/2 Model 30 with an analog
  15718.       monochrome display attached to the MCGA and a monochrome display
  15719.       attached to an MDA, the values returned are:
  15720.  
  15721.        AL    =    1AH
  15722.        BL    =    0BH  (active display = MCGA with analog monochrome)
  15723.        BH    =      1  (inactive display = MDA with digital monochrome)
  15724.  
  15725.  
  15726.  
  15727.  ───────────────────────────────────────────────────────────────────────────
  15728.  Function 1BH: Video BIOS Functionality/State Information
  15729.  
  15730.  Caller registers:
  15731.  
  15732.       AH    =    1BH
  15733.       BX    =    implementation type (must be 0)
  15734.       ES:DI =    address of 64-byte buffer
  15735.  
  15736.  Returned values:
  15737.  
  15738.  
  15739.       ES:DI =    buffer updated with function and state information
  15740.       AL    =    1BH
  15741.  
  15742.  Video Display Data Area updates:
  15743.  
  15744.      (none)
  15745.  
  15746.       INT 10H function 1BH returns a table of video BIOS state information
  15747.       on the MCGA and the VGA. The table contains dynamic information (shown
  15748.       in Figure A-14) that is determined when function 1BH is invoked, as
  15749.       well as static information (shown in Figure A-15) describing the
  15750.       capabilities of the video BIOS itself.
  15751.  
  15752.       The dynamic information is copied into the 64-byte buffer whose
  15753.       address is passed to the BIOS routine in ES:DI. The 32-bit address of
  15754.       the static information table is returned as bytes 0 through 3 of the
  15755.       dynamic information table.
  15756.  
  15757.       When called with BX = 0, INT 10H function 1BH always returns with
  15758.       AL = 1BH.
  15759.  
  15760.  
  15761. ╓┌─────────┌────────────────┌────────────────────────────────────────────────╖
  15762.  Offset    Data Type        Description
  15763.  ───────────────────────────────────────────────────────────────────────────
  15764.  0         Dword            Address of static functionality table
  15765.  4         Byte             Video mode
  15766.  5         Word             Number of displayed character columns
  15767.  7         Word             Length of displayed portion of video buffer
  15768.                               in bytes
  15769.  9         Word             Start address of upper left corner of video
  15770.                               buffer
  15771.  0BH       16-byte array    Table of cursor locations (column, row) for
  15772.                               eight video pages
  15773.  Offset    Data Type        Description
  15774.                              eight video pages
  15775.  1BH       Byte             Cursor end line
  15776.  1CH       Byte             Cursor start line
  15777.  1DH       Byte             Active video page
  15778.  1EH       Word             I/O port for CRTC Address register
  15779.  20H       Byte             CRT_MODE_SET (current value of 3x8H register)
  15780.  21H       Byte             CRT_PALETTE (current value of 3x9H register)
  15781.  22H       Byte             Number of displayed character rows
  15782.  23H       Word             POINTS (height of displayed character matrix)
  15783.  25H       Byte             Active display combination code
  15784.  26H       Byte             Inactive display combination code
  15785.  27H       Word             Number of displayed colors (0 for monochrome)
  15786.  29H       Byte             Number of video pages supported
  15787.  2AH       Byte             Raster scan lines:
  15788.                               0: 200 lines
  15789.                               1: 350 lines
  15790.                               2: 400 lines
  15791.                               3: 480 lines
  15792.  2BH       Byte             Alphanumeric character table used when attribute
  15793.                                bit 3 is 0 (VGA only)
  15794.  Offset    Data Type        Description
  15795.                               bit 3 is 0 (VGA only)
  15796.  2CH       Byte             Alphanumeric character table used when attribute
  15797.                               bit 3 is 1 (VGA only)
  15798.  2DH       Byte             Miscellaneous state information (bits are set
  15799.                               to 1 if state is true)
  15800.                             Bit 0: all modes active on all video subsystems
  15801.                               (always 0 on MCGA)
  15802.                             Bit 1: gray-scale summing enabled
  15803.                             Bit 2: monochrome display attached
  15804.                             Bit 3: default palette loading disabled
  15805.                             Bit 4: cursor emulation enabled
  15806.                             Bit 5: blinking attribute enabled
  15807.                             (bits 6-7 reserved)
  15808.  2EH       Byte             (reserved)
  15809.  2FH       Byte             (reserved)
  15810.  30H       Byte             (reserved)
  15811.  31H       Byte             Video RAM available
  15812.                               0: 64K
  15813.                               1: 128K
  15814.                               2: 192K
  15815.  Offset    Data Type        Description
  15816.                              2: 192K
  15817.                               3: 256K
  15818.  32H       Byte             Save area status (bits are set to 1 if state
  15819.                               is true)
  15820.                             Bit 0: two alphanumeric character sets are
  15821.                               active (VGA only)
  15822.                             Bit 1: dynamic save area is active
  15823.                             Bit 2: alphanumeric character set override is
  15824.                               active
  15825.                             Bit 3: graphics character set override is active
  15826.                             Bit 4: palette override is active
  15827.                             Bit 5: display combination code extension is
  15828.                               active
  15829.                             (bits 6-7 reserved)
  15830.  33H through 3FH            (reserved)
  15831.  
  15832.       Figure A-14.  Dynamic video state table returned by INT 10H
  15833.       function 1BH.
  15834.  
  15835.  
  15836. ╓┌─────────┌──────────────┌──────────────────────────────────────────────────╖
  15837.  Offset    Data Type      Description
  15838.  ──────────────────────────────────────────────────────────────────────────
  15839.  0         Byte           Video modes supported (bits = 1 if a mode is
  15840.                             supported)
  15841.                             Bit 0: mode 0
  15842.                             Bit 1: mode 1
  15843.                             Bit 2: mode 2
  15844.                             Bit 3: mode 3
  15845.                             Bit 4: mode 4
  15846.                             Bit 5: mode 5
  15847.                             Bit 6: mode 6
  15848.                             Bit 7: mode 7
  15849.  1         Byte           Video modes supported (bits = 1 if a mode is
  15850.                             supported)
  15851.                             Bit 0: mode 8
  15852.                             Bit 1: mode 9
  15853.                             Bit 2: mode 0AH
  15854.                             Bit 3: mode 0BH
  15855.                             Bit 4: mode 0CH
  15856.                             Bit 5: mode 0DH
  15857.  Offset    Data Type      Description
  15858.                            Bit 5: mode 0DH
  15859.                             Bit 6: mode 0EH
  15860.                             Bit 7: mode 0FH
  15861.  2         Byte           Video modes supported (bits = 1 if a mode is
  15862.                             supported)
  15863.                             Bit 0: mode 10H
  15864.                             Bit 1: mode 11H
  15865.                             Bit 2: mode 12H
  15866.                             Bit 3: mode 13H
  15867.                             Bit 4: (reserved)
  15868.                             Bit 5: (reserved)
  15869.                             Bit 6: (reserved)
  15870.                             Bit 7: (reserved)
  15871.  3         Byte           (reserved)
  15872.  4         Byte           (reserved)
  15873.  5         Byte           (reserved)
  15874.  6         Byte           (reserved)
  15875.  7         Byte           Scan lines available in alphanumeric modes
  15876.                             (bits = 1 if supported)
  15877.                             Bit 0: 200 lines
  15878.  Offset    Data Type      Description
  15879.                            Bit 0: 200 lines
  15880.                             Bit 1: 350 lines
  15881.                             Bit 2: 400 lines
  15882.  8         Byte           Maximum number of displayable alphanumeric
  15883.                             character sets
  15884.  9         Byte           Number of available alphanumeric character
  15885.                             definition tables in character generator RAM
  15886.  0AH       Byte           Miscellaneous video BIOS capabilities (bits = 1
  15887.                             if available)
  15888.                             Bit 0: all modes on all monitors (INT 10H
  15889.                               function 0) (Note: This bit is always 0 on
  15890.                               MCGA)
  15891.                             Bit 1: gray-scale summing (INT 10H function 10H
  15892.                               and 12H)
  15893.                             Bit 2: character set loading (INT 10H function
  15894.                               11H)
  15895.                             Bit 3: default palette loading (INT 10H
  15896.                               function 0)
  15897.                             Bit 4: cursor emulation (INT 10H function 1)
  15898.                             Bit 5: 64-color palette (INT 10H function 10H)
  15899.  Offset    Data Type      Description
  15900.                            Bit 5: 64-color palette (INT 10H function 10H)
  15901.                             Bit 6: video DAC loading (INT 10H function 10H)
  15902.                             Bit 7: control of video DAC via Attribute
  15903.                               Controller Color Select (INT 10H function 10H)
  15904.  0BH       Byte           Miscellaneous video BIOS capabilities (bits = 1
  15905.                             if available)
  15906.                             Bit 0: light pen support (INT 10H function 4)
  15907.                             Bit 1: save/restore video state (INT 10H
  15908.                               function 1CH)
  15909.                             Bit 2: blinking/background intensity (INT 10H
  15910.                               function 10H)
  15911.                             Bit 3: Display Combination Code (INT 10H
  15912.                               function 1AH)
  15913.                             (bits 4-7 reserved)
  15914.  0CH       Byte           (reserved)
  15915.  0DH       Byte           (reserved)
  15916.  0EH       Byte           Save area capabilities
  15917.                             Bit 0: multiple alphanumeric character sets
  15918.                             Bit 1: dynamic save area
  15919.                             Bit 2: alphanumeric character set override
  15920.  Offset    Data Type      Description
  15921.                            Bit 2: alphanumeric character set override
  15922.                             Bit 3: graphics character set override
  15923.                             Bit 4: palette override
  15924.                             Bit 5: Display Combination Code extension
  15925.                             (bits 6-7 reserved)
  15926.  0FH       Byte           (reserved)
  15927.  
  15928.       Figure A-15.  Static functionality table. This table's address is
  15929.       returned by INT 10H function 1BH. The table describes the capabilities
  15930.       of the ROM BIOS in the video subsystem.
  15931.  
  15932.  
  15933.       The following sequence returns video BIOS state information in the
  15934.       buffer whose address is passed in ES:DI.
  15935.  
  15936.          mov     ax,1B00h                ; AH := 1BH (INT 10H function
  15937.                                          ;         number)
  15938.                                          ; AL := 0
  15939.          mov     bx,0                    ; BX := 0 (Implementation type)
  15940.          mov     di,seg StateTable
  15941.          mov     es,di
  15942.          mov     di,offset StateTable    ; ES:DI -> buffer
  15943.          int     10h
  15944.          cmp     al,1BH
  15945.          jne     ErrorExit               ; jump if function not supported
  15946.          .
  15947.          .                               ; at this point StateTable contains
  15948.          .                               ;  the dynamic information table
  15949.  StateTable db 64 dup(?)
  15950.  
  15951.  
  15952.  
  15953.  ───────────────────────────────────────────────────────────────────────────
  15954.  Function 1CH: Save or Restore Video State
  15955.  
  15956.  Caller registers:
  15957.  
  15958.       AH    =    1CH
  15959.  
  15960.  Return save/restore buffer size:
  15961.  
  15962.       AL    =    0
  15963.       CX    =    requested states
  15964.                  Bit 0:       video hardware state
  15965.                  Bit 1:       video BIOS data areas
  15966.                  Bit 2:       video DAC state
  15967.                  Bits 3-0FH:  reserved
  15968.  
  15969.       Returned values:
  15970.       AL    =    1CH
  15971.       BX    =    buffer size in 64-byte blocks
  15972.  
  15973.  Save requested state(s):
  15974.  
  15975.       AL    =    1
  15976.       CX    =    requested states (as above)
  15977.       ES:BX =    buffer address
  15978.  
  15979.  Restore requested state(s):
  15980.  
  15981.       AL    =    2
  15982.       CX    =    requested states (as above)
  15983.       ES:BX =    buffer address
  15984.  
  15985.  Video Display Data Area updates:
  15986.  
  15987.       (see below)
  15988.  
  15989.       INT 10H function 1CH, supported only on the VGA, lets you save and
  15990.       restore the state of the video hardware and video ROM BIOS. INT 10H
  15991.       function 1CH comprises three subfunctions selected by the value passed
  15992.       in AL. For each subfunction, you must set the low-order three bits in
  15993.       CX to indicate the combination of video subsystem states you wish to
  15994.       save or restore. You must also pass the address of a save/restore
  15995.       buffer in ES:BX whenever you use function 1CH to save or restore the
  15996.       video state.
  15997.  
  15998.  
  15999.       AL = 0
  16000.       When AL = 0, function 1CH returns the size of the buffer required to
  16001.       store the state information for states requested in CX. The value
  16002.       returned in BX is in 64-byte blocks.
  16003.  
  16004.       Function 1CH returns AL = 1CH when called with AL = 0 and at least one
  16005.       of the low-order three bits in CX set to 1.
  16006.  
  16007.  
  16008.       AL = 1
  16009.       When AL = 1, function 1CH copies the state information requested in CX
  16010.       into the buffer whose address is passed in ES:BX.
  16011.  
  16012.  
  16013.       AL = 2
  16014.       When AL = 2, function 1CH restores the video hardware state, the BIOS
  16015.       state, or both using information saved in the buffer whose address is
  16016.       passed in ES:BX.
  16017.  
  16018.       NOTE: The BIOS routine may modify the current video state as it
  16019.       executes function 1CH. If you plan not to change the video state after
  16020.       saving it with function 1CH, restore the video state immediately
  16021.       afterward (using function 1CH with  AL = 2) to ensure that it isn't
  16022.       inadvertently modified.
  16023.  
  16024.       The following sequence runs under MS-DOS version 2.0 or later. It
  16025.       calls MS-DOS INT 21H function 48H to allocate RAM for a save/restore
  16026.       buffer. It then calls INT 10H function 1CH to save the current video
  16027.       state.
  16028.  
  16029.          mov  ax,1C00h               ; AH := 1CH (INT 10H function number)
  16030.                                      ; AL := 0
  16031.          mov  cx,111b                ; CX := 111b (all three video states)
  16032.          int  10h
  16033.          cmp  al,1Ch
  16034.          jne  ErrorExit              ; jump if function not supported
  16035.          shl  bx,1                   ; convert number of 64-byte blocks
  16036.          shl  bx,1                   ;  to number of 16-byte blocks
  16037.          mov  ah,48h                 ; AH := 48H (MS-DOS INT 21H function
  16038.                                      ;        number)
  16039.          int  21h                    ; AX := segment of allocated buffer
  16040.          jc   ErrorExit              ; jump if error
  16041.          mov  es,ax
  16042.          xor  bx,bx                  ; ES:BX -> buffer
  16043.          mov  cx,111b                ; CX := 111b (all three video states)
  16044.          mov  ax,1C01h               ; AH := INT 10H function number
  16045.                                      ; AL := 1
  16046.          int  10h                    ; save video state in buffer
  16047.  
  16048.  
  16049.  
  16050.                      Appendix B  Printing the Screen
  16051.  
  16052.  
  16053.  
  16054.       Many computer users find it convenient to "snapshot" the current
  16055.       contents of the video display. Although all members of the IBM PC and
  16056.       PS/2 series come with a short ROM BIOS routine that dumps the contents
  16057.       of the video buffer to a printer, you may need to write your own video
  16058.       snapshot program to supplement the ROM routine. This appendix
  16059.       discusses how to use the BIOS screen dump utility, as well as why and
  16060.       how to write your own.
  16061.  
  16062.  
  16063.  Alphanumeric Modes
  16064.  
  16065.  
  16066.       You invoke the motherboard ROM's alphanumeric screen dump routine by
  16067.       executing software interrupt 5. (The ROM BIOS keyboard handler issues
  16068.       this interrupt when you press Shift-PrtSc.) This routine copies the
  16069.       contents of the currently displayed video page to the printer in
  16070.       80-by-25 or 40-by-25 alphanumeric mode. The routine prints only the
  16071.       ASCII character codes, ignoring the attribute bytes in the video
  16072.       buffer.
  16073.  
  16074.  
  16075.  EGA, MCGA, VGA
  16076.  
  16077.       The EGA, the MCGA, and the VGA ROM BIOS contain a more flexible
  16078.       version of the INT 5 screen dump routine. That version uses the Video
  16079.       Display Data Area value ROWS (0040:0084) to determine how many rows of
  16080.       characters to print. (The motherboard ROM version always prints 25
  16081.       rows.) An IBM PC/XT or PC/AT uses the motherboard version by default.
  16082.       To make the EGA or VGA ROM BIOS routine accessible through interrupt
  16083.       5, call INT 10H function 12H with BL = 20H. This points the interrupt
  16084.       5 vector to the more flexible routine.
  16085.  
  16086.  
  16087.  Block Graphics Characters
  16088.  
  16089.       Because most printers are designed to work with many different
  16090.       computers, not just IBM PCs, manufacturers do not always design their
  16091.       printers to print the same 256 ASCII characters that the video
  16092.       hardware displays in alphanumeric modes. In particular, the characters
  16093.       used for block graphics are not always available on PC-compatible
  16094.       printers. These characters may print differently than they are
  16095.       displayed or they may not print at all.
  16096.  
  16097.  
  16098.  Graphics Modes
  16099.  
  16100.  
  16101.       The ROM BIOS does not support screen dumps in graphics modes, so in
  16102.       these modes you must use some other program to print the video
  16103.       buffer's contents.
  16104.  
  16105.  
  16106.  GRAPHICS
  16107.  
  16108.       GRAPHICS is a RAM-resident graphics-mode screen dump program that
  16109.       Microsoft supplies as part of MS-DOS under the name GRAPHICS.COM or
  16110.       GRAPHICS.EXE. This program establishes a memory-resident screen dump
  16111.       program for CGA graphics modes (320-by-200 4-color and 640-by-200
  16112.       2-color) when executed. The program uses an IBM- or Epson-compatible
  16113.       dot-matrix printer for output.
  16114.  
  16115.       The RAM-resident portion of GRAPHICS traps interrupt 5 and tests the
  16116.       current video mode. If a graphics mode is active, it performs the
  16117.       screen dump. Otherwise, the BIOS interrupt 5 routine gets control and
  16118.       performs the alphanumeric-mode screen dump. Thus, once GRAPHICS.COM or
  16119.       GRAPHICS.EXE has been executed, you can obtain a graphics-mode screen
  16120.       dump by pressing Shift-PrtSc, just as you would in alphanumeric video
  16121.       modes.
  16122.  
  16123.  
  16124.  Writing a Screen Dump Routine
  16125.  
  16126.       If you want screen snapshots in native EGA, VGA, or MCGA graphics
  16127.       modes or on a Hercules adapter, or if GRAPHICS produces unsatisfactory
  16128.       output on your printer, you can write your own screen dump routine.
  16129.       Listing B-1 is an example of a simple routine for CGA graphics modes.
  16130.       ScreenDumpCGA can be incorporated into an assembly-language program or
  16131.       a high-level-language program by calling it with the appropriate
  16132.       register values and memory model. (See Chapter 13 for more on this
  16133.       topic.) You might also build ScreenDumpCGA into a Terminate-but-Stay-
  16134.       Resident program that, like GRAPHICS, chains into the interrupt 5
  16135.       vector and executes whenever Shift-PrtSc is pressed.
  16136.  
  16137.  
  16138.  ───────────────────────────────────────────────────────────────────────────
  16139.  
  16140.       Listing B-1.  A simple screen dump routine for the
  16141.       CGA.
  16142.  
  16143.  ───────────────────────────────────────────────────────────────────────────
  16144.  
  16145.  
  16146.       ScreenDumpCGA copies pixels from the video buffer into an inter-
  16147.       mediate print buffer. It formats the print buffer so that its contents
  16148.       can be sent directly to the printer (an Epson MX-80 in this example).
  16149.       Since the video buffer can be accessed randomly, ScreenDumpCGA reads
  16150.       pixels from it in an order that is conveniently transmitted to the
  16151.       printer.
  16152.  
  16153.       The heart of ScreenDumpCGA is the subroutine TranslatePixels. This
  16154.       routine maps pixels from the video buffer into the print buffer. In
  16155.       this example, the routine is short and fast, because it uses a simple
  16156.       transformation to convert video buffer pixels to printer pixels.
  16157.       Because the Epson MX-80 prints vertically oriented groups of pixels
  16158.       (see Figure B-1), the easiest way to print an image from  the
  16159.       horizontally mapped video buffer is to rotate it by 90 degrees.
  16160.  
  16161.       To customize ScreenDumpCGA, concentrate on how best to map pixels from
  16162.       the video buffer to your printer. Change the TranslatePixels routine
  16163.       to scale or rotate the pixels differently, or modify ScreenDumpCGA to
  16164.       change the order in which the contents of the video buffer are copied
  16165.       to the printer.
  16166.  
  16167.  
  16168.                * ────────────────────────────┐
  16169.                * ─────────────────────────┐  │
  16170.                * ──────────────────────┐  │  │
  16171.       Printed  * ───────────────────┐  │  │  │
  16172.       pixels   * ────────────────┐  │  │  │  │
  16173.                * ─────────────┐  │  │  │  │  │
  16174.                * ──────────┐  │  │  │  │  │  │
  16175.                * ───────┐  │  │  │  │  │  │  │
  16176.                          │  │  │  │  │  │  │  │
  16177.                          │  │  │  │  │  │  │  │
  16178.                          0  1  2  3  4  5  6  7 ────────Bit number
  16179.  
  16180.       Figure B-1.  Pixel mapping for a typical dot-matrix graphics printer.
  16181.       As the print head moves across the page, it prints eight rows of
  16182.       pixels at a time. Each byte of data transmitted to the printer
  16183.       controls 8 vertical pixels as shown.
  16184.  
  16185.  
  16186.       For example, you could modify ScreenDumpCGA and TranslatePixels  to
  16187.       dump the contents of the EGA or VGA video buffer in 640-by-350 16-
  16188.       color mode as in Listing B-2. The modified routine prints all nonzero
  16189.       pixels in the video buffer as black dots. Note how the Graphics
  16190.       Controller's read mode 1 simplifies this task in TranslatePixels.
  16191.  
  16192.  
  16193.  ───────────────────────────────────────────────────────────────────────────
  16194.  
  16195.       Listing B-2.  An EGA screen printing routine.
  16196.  
  16197.  ───────────────────────────────────────────────────────────────────────────
  16198.  
  16199.  
  16200.  RAM-Based Alphanumeric Character Definitions
  16201.  
  16202.       You can also modify the graphics-mode screen dump routine to print
  16203.       RAM-based characters used in alphanumeric modes on the EGA, MCGA, VGA,
  16204.       HGC+, and InColor Card. The technique is to use the character codes
  16205.       stored in the displayed portion of the video buffer to index the bit
  16206.       patterns in character definition RAM. The bit pattern that defines
  16207.       each character can then be used as a dot pattern for the printer.
  16208.  
  16209.       As an example, Listing B-3 shows how this can be done for the
  16210.       characters defined in the default character definition table in memory
  16211.       map 2 on the EGA or VGA. The routine prints each column of characters
  16212.       in the video buffer by filling the buffer (PrintBuf) with the bit
  16213.       patterns that define each of the characters. Memory map 0 (containing
  16214.       the character codes) and map 2 (containing the character definitions)
  16215.       are addressed separately in the subroutine TranslatePixels by
  16216.       programming the Sequencer and Graphics Controller as discussed in
  16217.       Chapter 10.
  16218.  
  16219.  
  16220.  ───────────────────────────────────────────────────────────────────────────
  16221.  
  16222.       Listing B-3.  Using RAM-based character definition tables to print
  16223.       the character set.
  16224.  
  16225.  ───────────────────────────────────────────────────────────────────────────
  16226.  
  16227.  
  16228.  
  16229.                  Appendix C  Identifying Video Subsystems
  16230.  
  16231.  
  16232.  
  16233.       Programs need to determine the configuration of the video hardware on
  16234.       which they are run for two reasons. One is to maintain portability. A
  16235.       program that recognizes the video subsystems in the computer in which
  16236.       it runs can adapt itself to specific hardware configurations. Imagine,
  16237.       for example, a program that displays both text and graphics images.
  16238.       This program could display text and graphics on a single screen in a
  16239.       computer with only one video subsystem, but it could also take full
  16240.       advantage of a dual-display configuration by placing text on one
  16241.       screen and graphics on the other.
  16242.  
  16243.       Another reason to enable a program to examine its video hardware
  16244.       environment is to allow use of the fastest possible video output
  16245.       routines. For example, if your program runs in an alphanumeric mode on
  16246.       a CGA, you may need to avoid snow by synchronizing with the CRT
  16247.       Controller's timing signals. However, this overhead can be avoided if
  16248.       the program is running on some other video subsystem. If your program
  16249.       "knows" that it's not running on a CGA, it can use faster video output
  16250.       routines that omit the overhead of avoiding snow.
  16251.  
  16252.  
  16253.  CGA and Clones
  16254.  
  16255.  
  16256.       Unfortunately, for Color Graphics Adapters and clones, no reliable way
  16257.       exists to determine whether the hardware manages conflicts over video
  16258.       buffer memory access without display interference (see Chapter 3). If
  16259.       your program must run on  a CGA, you might wish to ask the user to
  16260.       configure your alphanumeric output routines by testing whether or not
  16261.       they produce snow.
  16262.  
  16263.       You can also detect whether your program is running on a CGA work-
  16264.       alike that does not have the alphanumeric snow problem. If you know
  16265.       that your program may run on a CGA work-alike such as the video
  16266.       hardware built into a COMPAQ or an AT&T 6300, you can search the ROM
  16267.       BIOS for a string indicating the name of the computer, for example,
  16268.       "COMPAQ". You might also inspect the ROM BIOS ID byte at F000:FFFE to
  16269.       determine whether your program is running on a member of the IBM PC
  16270.       family that does not have the snow problem (such as the PCjr).
  16271.  
  16272.  
  16273.  Other Video Adapters
  16274.  
  16275.  
  16276.       Although determining whether a particular CGA or clone has a problem
  16277.       with alphanumeric snow can be hard, distinguishing among the various
  16278.       common IBM video adapters is relatively easy. Some of the techniques
  16279.       described in this appendix rely on serendipitous peculiarities of
  16280.       different adapters' firmware or hardware, but all are based on IBM and
  16281.       Hercules recommendations.
  16282.  
  16283.  
  16284.  PS/2s
  16285.  
  16286.  
  16287.       On the PS/2s, INT 10H function 1AH lets you determine which video
  16288.       subsystems are present and active in the computer (see Appendix A). Of
  16289.       course, the PS/2 video BIOS does not recognize non-IBM video adapters.
  16290.       For example, if you use a Hercules adapter in a PS/2 Model 30, a
  16291.       call to INT 10H function 1AH returns only the information that
  16292.       an MDA-compatible adapter is present in the system. Identifying
  16293.       the adapter is then up to you.
  16294.  
  16295.       VideoID, the routine in Listing C-1, detects the presence of either
  16296.       one or two adapters. If two adapters are present, VideoID indicates
  16297.       which is active (that is, which one the BIOS is currently using for
  16298.       output). The techniques used to identify each adapter are described in
  16299.       the listing.
  16300.  
  16301.  
  16302.  ───────────────────────────────────────────────────────────────────────────
  16303.  
  16304.       Listing C-1.  A routine to identify PC and PS/2 video
  16305.       subsystems.
  16306.  
  16307.  ───────────────────────────────────────────────────────────────────────────
  16308.  
  16309.  
  16310.       The VideoID routine checks for adapters by a process of elimination.
  16311.       For example, if the routine is run on a PS/2, the INT 10H call returns
  16312.       the desired information. On PC/XTs and PC/ATs, if an EGA with a
  16313.       monochrome display is detected, there is no reason to look for an MDA
  16314.       or a Hercules card in the same system. If a monochrome adapter is
  16315.       present, the routine differentiates between the MDA and the various
  16316.       Hercules adapters.
  16317.  
  16318.         ╔═══╗     INT 10H function 1AH on the VGA adapter fails to report
  16319.         ║ T ║     the presence of the MCGA when the adapter is installed in
  16320.         ║ I ║     a PS/2 Model 30. Also, function 1AH in the MCGA ignores
  16321.         ║ P ║     the presence of an EGA if one is installed in a Model 30.
  16322.         ╚═══╝     If you are concerned about these combinations, you must
  16323.                   test for them explicitly after you call INT 10H function
  16324.                   1AH. (In the first situation, inspect the motherboard BIOS
  16325.                   identfication byte at F000:FFFE to detect the presence of
  16326.                   a Model 30. In the second situation, execute INT 10H
  16327.                   function 12H with BL = 10H to detect the presence of an
  16328.                   EGA.)
  16329.  
  16330.       The C program in Listing C-2 demonstrates how you might use VideoID.
  16331.  
  16332.  
  16333.  ───────────────────────────────────────────────────────────────────────────
  16334.  
  16335.       Listing C-2.  Calling VideoID from a C program.
  16336.  
  16337.  ───────────────────────────────────────────────────────────────────────────
  16338.  
  16339.  
  16340.  
  16341.  Glossary
  16342.  
  16343.  
  16344.       This glossary includes some of the acronyms, abbreviations, buzzwords,
  16345.       engineering terms, and programming jargon that appear frequently
  16346.       throughout this book.
  16347.  
  16348.       80x86: Refers to all the processors in the Intel 8086 family. The IBM
  16349.       PCs and PS/2s all use one of these processors: 8086, 8088, 80286, or
  16350.       80386.
  16351.  
  16352.       active display: In a computer that contains two video subsystems and
  16353.       displays, the display to which a program sends its output.
  16354.  
  16355.       adapter: A modular, plug-in circuit that performs a specialized task
  16356.       such as generating video output. Well-known IBM PC video adapters
  16357.       include the MDA, CGA, HGC, EGA, and VGA Adapter.
  16358.  
  16359.       ANSI: American National Standards Institute. One of ANSI's many
  16360.       activities is to certify the standardization of programming tools,
  16361.       including languages (such as C and FORTRAN) and software interfaces
  16362.       (such as GKS).
  16363.  
  16364.       APA: All Points Addressable; describes graphics modes on the CGA, EGA,
  16365.       and Hercules graphics cards.
  16366.  
  16367.       API: Application Program Interface; a set of system-level routines
  16368.       that can be used in an application program for basic input and output,
  16369.       file management, and so on. In a graphics-oriented operating
  16370.       environment like Microsoft Windows, high-level support for video
  16371.       graphics output is part of the API.
  16372.  
  16373.       ASCII: American Standard Code for Information Interchange. The ASCII
  16374.       standard specifies the basic character set used in IBM PCs and PS/2s.
  16375.  
  16376.       aspect ratio: The ratio of a video screen's width to its height. A
  16377.       typical IBM PC display has an aspect ratio of about 4:3. This term is
  16378.       also frequently used to describe pixels: If you think of a pixel as
  16379.       being rectangular, its aspect ratio would be the ratio of its width to
  16380.       height.
  16381.  
  16382.       attributes: Color, intensity, blinking, and other displayed
  16383.       characteristics of characters or pixels.
  16384.  
  16385.       BIOS: Basic Input/Output System; a low-level programming interface to
  16386.       the system's major I/O devices.
  16387.  
  16388.       bit plane: Video RAM containing formatted graphics data. In IBM video
  16389.       subsystems up to four bit planes can be addressed in parallel, with
  16390.       pixel values represented by the bits at corresponding locations in the
  16391.       bit planes.
  16392.  
  16393.       CGA: IBM's Color Graphics Adapter.
  16394.  
  16395.       character code: A numeric code associated with a character. The
  16396.       default ASCII character set used in all PCs and PS/2s comprises 256
  16397.       8-bit character codes.
  16398.  
  16399.       character matrix: The rectangular array of pixels in which characters
  16400.       are displayed on the screen. On IBM's Monochrome Display Adapter, each
  16401.       character is displayed in a character matrix that is 9 dots wide and
  16402.       14 dots high. On the Color Graphics Adapter, the character matrix is 8
  16403.       by 8.
  16404.  
  16405.       character set: A set of alphabetic and numeric characters and symbols.
  16406.  
  16407.       clipping: The process of determining which portions of a graphics
  16408.       image lie within a specified boundary.
  16409.  
  16410.       code page: A character set designed for use with computers. Each
  16411.       character in a code page is associated with a numeric code (such as an
  16412.       ASCII or EBCDIC code).
  16413.  
  16414.       CPU: Central Processing Unit, or the main processor in a computer. For
  16415.       example, the CPU is an Intel 8088 in PCs and an 80286 in PC/ATs.
  16416.  
  16417.       CRT: Cathode Ray Tube, or the picture tube you see when you look at
  16418.       your computer monitor. Some people refer to the entire monitor (the
  16419.       tube and its associated circuitry) as a CRT.
  16420.  
  16421.       CRTC: CRT Controller; a chip that controls a video display's timing
  16422.       signals.
  16423.  
  16424.       DGIS: Direct Graphics Interface Specification; a firmware graphics
  16425.       interface designed for video subsystems based on hardware graphics
  16426.       coprocessors.
  16427.  
  16428.       display: A video monitor.
  16429.  
  16430.       driver: Software or firmware that directly programs a specific
  16431.       hardware unit such as a video adapter or a printer.
  16432.  
  16433.       EBCDIC: Extended Binary Coded Decimal Interchange Code; the character-
  16434.       set implementation used on IBM mainframe computers.
  16435.  
  16436.       EGA: Enhanced Graphics Adapter.
  16437.  
  16438.       font: A description of the style and shapes of the characters in a
  16439.       character set.
  16440.  
  16441.       gate array: An integrated circuit that is partly prefabricated in its
  16442.       manufacture. An application-specific integrated circuit based on gate
  16443.       array technology can be less expensive and manufactured more rapidly
  16444.       than a custom integrated circuit.
  16445.  
  16446.       GKS: Graphical Kernel System; a standard high-level graphics
  16447.       interface.
  16448.  
  16449.       HGC: Hercules monochrome Graphics Card.
  16450.  
  16451.       HGC+ (HGC Plus): Hercules Graphics Card Plus; a monochrome video
  16452.       adapter like the HGC, but with a hardware character generator that
  16453.       can use RAM-based character sets.
  16454.  
  16455.       InColor: Hercules InColor Card; a 16-color version of the HGC+.
  16456.  
  16457.       latch: A hardware register external to the CPU and used for transient
  16458.       storage of data. For example, the EGA Graphics Controller uses four
  16459.       internal 8-bit latches to mediate data transfers between the bit
  16460.       planes and the CPU.
  16461.  
  16462.       LSI: Large Scale Integration.
  16463.  
  16464.       MCGA: Multi-Color Graphics Array; the video subsystem integrated into
  16465.       the PS/2 Model 30. Also, Memory Controller Gate Array, one of the
  16466.       components of the Model 30's video subsystem.
  16467.  
  16468.       MDA: IBM's Monochrome Display Adapter.
  16469.  
  16470.       MDPA: Monochrome Display and Printer Adapter; same as an MDA.
  16471.  
  16472.       monitor: The hardware that displays your computer's video output;
  16473.       comprises a CRT (cathode ray tube) and associated circuitry.
  16474.  
  16475.       MPA: Monochrome/Printer Adapter; same as an MDA.
  16476.  
  16477.       palette: A range of colors that can be displayed by a video subsystem.
  16478.  
  16479.       pel: A pixel.
  16480.  
  16481.       PGA: Professional Graphics Adapter; another name for IBM's PGC.
  16482.  
  16483.       PGC: IBM's Professional Graphics Controller.
  16484.  
  16485.       pixel: One dot or point in an image that is composed of a matrix of
  16486.       dots or points. The image on the video screen or on a page printed by
  16487.       a dot-matrix printer is composed of a large number of pixels. (The
  16488.       word "pixel" is a rough acronym for "picture element.")
  16489.  
  16490.       planar BIOS: BIOS routines found in ROM on the IBM PC or PS/2
  16491.       motherboard.
  16492.  
  16493.       PS/2: Personal System/2.
  16494.  
  16495.       PS/2 Display Adapter: A VGA-compatible IBM video adapter that may be
  16496.       used in a PC/XT, PC/AT, or PS/2 Model 30; commonly called "VGA
  16497.       Adapter."
  16498.  
  16499.       raster: The group of closely spaced horizontal scan lines that makes
  16500.       up a displayed video image.
  16501.  
  16502.       RGB: Red, Green, Blue; the three primary colors displayed by the
  16503.       monitors used in PC and PS/2 video subsystems. All other colors are
  16504.       blends of these three primaries. Video displays that are driven by
  16505.       separate red, green, and blue signals are often called RGB displays.
  16506.  
  16507.       scan line: One horizontal line traced across the screen by a CRT's
  16508.       electron beam.
  16509.  
  16510.       VDI: Computer Graphics Virtual Device Interface; a proposed ANSI
  16511.       standard high-level graphics interface. The Graphics Development
  16512.       Toolkit (GDT) sold by IBM and Graphics Software Systems is a
  16513.       commercial implementation of VDI.
  16514.  
  16515.       VGA: Video Graphics Array. People refer to the video subsystem
  16516.       integrated into the PS/2 Models 50, 60, and 80, as well as the IBM
  16517.       PS/2 Display Adapter, as the "VGA." Strictly speaking, however, the
  16518.       VGA is the circuitry in the video subsystem that performs the tasks of
  16519.       the CRT Controller, the Sequencer, the Graphics Controller, and the
  16520.       Attribute Controller. Most of this circuitry is contained in a single
  16521.       VLSI chip.
  16522.  
  16523.       VGA Adapter: The IBM PS/2 Display Adapter.
  16524.  
  16525.       video buffer: A buffer that contains the data that appears on the
  16526.       video display; variously known as a "display buffer," "frame buffer,"
  16527.       "refresh buffer," or "regenerative buffer."
  16528.  
  16529.       Video Control Data Area: Part of the Video Display Data Area. The
  16530.       block of RAM from 0040:0049 through 0040:0066 is Video Control Data
  16531.       Area 1; the block between 0040:0084 and 0040:008A is Video Control
  16532.       Data Area 2.
  16533.  
  16534.       Video Display Data Area: A global data area maintained by the ROM BIOS
  16535.       for storage of parameters related to its INT 10H video I/O routines.
  16536.  
  16537.       VLSI: Very Large Scale Integration.
  16538.  
  16539.  
  16540.  
  16541.       Richard Wilton
  16542.  
  16543.  
  16544.       Richard Wilton has been programming computers since the late
  16545.       1960s. He has written systems software and graphics applications in
  16546.       FORTRAN, Pascal, C, Forth, and assembly language. His articles and
  16547.       reviews have appeared in several computer publications, including
  16548.       BYTE, Computer Language, and The Seybold Outlook on Professional
  16549.       Computing. Wilton lives in Los Angeles, California.
  16550.  
  16551.  
  16552.  
  16553.  ───────────────────────────────────────────────────────────────────────────
  16554.  Listing 1-1.  SetVmode().
  16555.  ───────────────────────────────────────────────────────────────────────────
  16556.  
  16557.                  TITLE   'Listing 1-1'
  16558.                  NAME    SetVmode
  16559.                  PAGE    55,132
  16560.  
  16561.  ;
  16562.  ; Name:         SetVmode
  16563.  ;
  16564.  ; Function:     Call IBM ROM BIOS to set a video display mode.
  16565.  ;
  16566.  ; Caller:       Microsoft C:
  16567.  ;
  16568.  ;                       void SetVmode(n);
  16569.  ;
  16570.  ;                       int n;                  /* video mode */
  16571.  ;
  16572.  
  16573.  ARGn            EQU     byte ptr [bp+4] ; stack frame addressing
  16574.  
  16575.  EQUIP_FLAG      EQU     byte ptr ds:[10h]
  16576.  
  16577.  CGAbits         EQU     00100000b       ; bits for EQUIP_FLAG
  16578.  MDAbits         EQU     00110000b
  16579.  
  16580.  _TEXT           SEGMENT byte public 'CODE'
  16581.                  ASSUME  cs:_TEXT
  16582.  
  16583.                  PUBLIC  _SetVmode
  16584.  _SetVmode       PROC    near
  16585.  
  16586.                  push    bp              ; preserve caller registers
  16587.                  mov     bp,sp
  16588.                  push    ds
  16589.  
  16590.                  mov     ax,40h
  16591.                  mov     ds,ax           ; DS -> Video Display Data Area
  16592.  
  16593.                  mov     bl,CGAbits      ; BL := bits indicating presence
  16594.                                          ;  of CGA
  16595.  
  16596.                  mov     al,ARGn         ; AL := desired video mode number
  16597.  
  16598.                  mov     ah,al          ; test if desired mode is monochrome
  16599.                  and     ah,7
  16600.                  cmp     ah,7
  16601.                  jne     L01             ; jump if desired mode not 7 or 0Fh
  16602.  
  16603.                  mov     bl,MDAbits      ; BL := bits indicating presence
  16604.                                          ;  of MDA
  16605.  
  16606.  L01:            and     EQUIP_FLAG,11001111b
  16607.                  or      EQUIP_FLAG,bl   ; set bits in EQUIP_FLAG
  16608.  
  16609.                  xor     ah,ah           ; AH := 0 (INT 10h function number)
  16610.  
  16611.                  push    bp
  16612.                  int     10h           ; call ROM BIOS to set the video mode
  16613.                  pop     bp
  16614.  
  16615.                  pop     ds              ; restore caller registers & return
  16616.                  mov     sp,bp
  16617.                  pop     bp
  16618.                  ret
  16619.  
  16620.  _SetVmode       ENDP
  16621.  
  16622.  _TEXT           ENDS
  16623.  
  16624.                  END
  16625.  
  16626.  
  16627.  
  16628.  ───────────────────────────────────────────────────────────────────────────
  16629.  Listing 1-2.  GetVmode().
  16630.  ───────────────────────────────────────────────────────────────────────────
  16631.  
  16632.                  TITLE   'Listing 1-2'
  16633.                  NAME    GetVmode
  16634.                  PAGE    55,132
  16635.  
  16636.  ;
  16637.  ; Name:         GetVmode
  16638.  ;
  16639.  ; Function:     Call IBM ROM BIOS to set a video display mode.
  16640.  ;
  16641.  ; Caller:       Microsoft C:
  16642.  ;
  16643.  ;                       int     GetVmode();
  16644.  ;
  16645.  
  16646.  _TEXT           SEGMENT byte public 'CODE'
  16647.                  ASSUME  cs:_TEXT
  16648.  
  16649.                  PUBLIC  _GetVmode
  16650.  _GetVmode       PROC    near
  16651.  
  16652.                  push    bp         ; preserve caller registers
  16653.                  mov     bp,sp
  16654.  
  16655.                  mov     ah,0Fh     ; AH := 0Fh (INT 10h function number)
  16656.  
  16657.                  push    bp
  16658.                  int     10h        ; call ROM BIOS to get video mode number
  16659.                  pop     bp
  16660.  
  16661.                  xor     ah,ah      ; AX := video mode number
  16662.  
  16663.                  mov     sp,bp
  16664.                  pop     bp
  16665.                  ret
  16666.  
  16667.  _GetVmode       ENDP
  16668.  
  16669.  _TEXT           ENDS
  16670.  
  16671.                  END
  16672.  
  16673.  
  16674.  
  16675.  ───────────────────────────────────────────────────────────────────────────
  16676.  Listing 1-3.  A C program based on SetVmode().
  16677.  ───────────────────────────────────────────────────────────────────────────
  16678.  
  16679.  /* Listing 1-3 */
  16680.  
  16681.  main( argc, argv )
  16682.  int     argc;
  16683.  char    **argv;
  16684.  {
  16685.        int     ModeNumber;
  16686.        void    SetVmode();
  16687.  
  16688.  
  16689.        if (argc != 2)                  /* verify command line syntax */
  16690.        {
  16691.          printf( "\nSyntax:  SETVMODE n\n" );
  16692.          exit( 1 );
  16693.        }
  16694.  
  16695.        sscanf( argv[1], "%x", &ModeNumber ); /* get desired mode number */
  16696.  
  16697.        SetVmode( ModeNumber );               /* call ROM BIOS via INT 10h */
  16698.  }
  16699.  
  16700.  
  16701.  
  16702.  ───────────────────────────────────────────────────────────────────────────
  16703.  Listing 1-4.  A C program based on GetVmode().
  16704.  ───────────────────────────────────────────────────────────────────────────
  16705.  
  16706.  /* Listing 1-4 */
  16707.  
  16708.  main()
  16709.  {
  16710.          int     GetVmode();
  16711.          return( GetVmode() );
  16712.  }
  16713.  
  16714.  
  16715.  
  16716.  ───────────────────────────────────────────────────────────────────────────
  16717.  Listing 1-5.  Microsoft C's int86() function.
  16718.  ───────────────────────────────────────────────────────────────────────────
  16719.  
  16720.  /* Listing 1-5 */
  16721.  
  16722.  #include        "dos.h"
  16723.  
  16724.  main()
  16725.  {
  16726.          struct  BYTEREGS regs;     /* BYTEREGS defined in dos.h */
  16727.  
  16728.  
  16729.          regs.ah = 0x0F;            /* AH=0x0F (ROM BIOS function number) */
  16730.  
  16731.          int86( 0x10, ®s, ®s );  /* perform interrupt 10h */
  16732.  
  16733.          return( (int)regs.al );
  16734.  }
  16735.  
  16736.  
  16737.  
  16738.  ───────────────────────────────────────────────────────────────────────────
  16739.  Listing 2-1.  Reading the 6845 Cursor Location registers.
  16740.  ───────────────────────────────────────────────────────────────────────────
  16741.  
  16742.          mov     ax,40h
  16743.          mov     es,ax                ; ES := video BIOS data segment
  16744.          mov     dx,es:[63h]          ; DX := 3x4h (3B4h or 3D4h)
  16745.  
  16746.          mov     al,0Eh
  16747.          out     dx,al                ; select 6845 Cursor Location
  16748.                                       ;  High register
  16749.          inc     dx
  16750.          in      al,dx                ; read selected register at 3x5h
  16751.          mov     ah,al                ; AH := high byte of cursor
  16752.                                       ;  location
  16753.          dec     dx
  16754.          mov     al,0Fh
  16755.          out     dx,al                ; select Cursor Location Low register
  16756.  
  16757.          inc     dx
  16758.          in      al,dx                ; AX := offset of cursor relative
  16759.                                       ;  to start of video buffer
  16760.  
  16761.  ; convert to character row and column
  16762.  
  16763.          mov     dx,es:[4Eh]          ; DX := CRT_START (buffer start offset
  16764.                                       ;  in bytes)
  16765.          shr     dx,1                 ; convert to words
  16766.          sub     ax,dx                ; subtract from cursor offset
  16767.          div     byte ptr es:[4Ah]    ; divide by CRT_COLS
  16768.          xchg    ah,al                ; AH := row, AL := column
  16769.  
  16770.  
  16771.  
  16772.  ───────────────────────────────────────────────────────────────────────────
  16773.  Listing 2-2.  Timing the horizontal blanking interval on the CGA
  16774.  ───────────────────────────────────────────────────────────────────────────
  16775.  
  16776.                  TITLE   'Listing 2-2'
  16777.                  NAME    HRTimeout
  16778.                  PAGE    55,132
  16779.  
  16780.  ;
  16781.  ; Name:         HRTimeout
  16782.  ;
  16783.  ; Function:     Determine a timeout value for horizontal blanking interval
  16784.  ;
  16785.  ; Caller:       Microsoft C:
  16786.  ;
  16787.  ;                       int HRTimeout();
  16788.  ;
  16789.  
  16790.  _TEXT           SEGMENT byte public 'CODE'
  16791.                  ASSUME  cs:_TEXT
  16792.  
  16793.                  PUBLIC  _HRTimeout
  16794.  _HRTimeout      PROC    near
  16795.  
  16796.                  push    bp           ; usual C prologue to establish
  16797.                  mov     bp,sp        ;  stack frame
  16798.  
  16799.                  mov     ax,40h
  16800.                  mov     es,ax        ; ES := video BIOS data segment
  16801.  
  16802.                  mov     dx,es:[63h]  ; DX := port for CRTC Address register
  16803.                  add     dl,6         ; DX := port for CRTC Status register
  16804.  
  16805.  ; synchronize with start of refresh cycle
  16806.  
  16807.  L01:            in      al,dx        ; AL := CRTC status
  16808.                  test    al,8         ; test bit 3
  16809.                  jz      L01          ; loop while NOT in vertical retrace
  16810.  
  16811.  L02:            in      al,dx
  16812.                  test    al,8
  16813.                  jnz     L02          ; loop during vertical retrace
  16814.  
  16815.  ; synchronize with a horizontal scan and time the horizontal blanking
  16816.  ;  interval
  16817.  
  16818.                  mov     cx,0FFFFh    ; CX := loop counter
  16819.  
  16820.                  cli                  ; disable interrupts
  16821.  
  16822.  L03:            in      al,dx
  16823.                  test    al,1
  16824.                  jnz     L03          ; loop while Display Enable is
  16825.                                       ;  inactive
  16826.  
  16827.  L04:            in      al,dx
  16828.                  test    al,1
  16829.                  jz      L04          ; loop while Display Enable is active
  16830.  
  16831.  L05:            in      al,dx
  16832.                  test    al,1
  16833.                  loopnz  L05          ; decrement CX and loop while Display
  16834.                                       ;  Enable is inactive
  16835.  
  16836.                  sti                  ; enable interrupts again
  16837.  
  16838.                  mov     ax,cx        ; AX := loop counter
  16839.                  neg     ax
  16840.                  shl     ax,1         ; AX := timeout value
  16841.  
  16842.                  mov     sp,bp        ; discard stack frame and return to C
  16843.                  pop     bp
  16844.                  ret
  16845.  
  16846.  _HRTimeout      ENDP
  16847.  
  16848.  _TEXT           ENDS
  16849.  
  16850.                  END
  16851.  
  16852.  
  16853.  
  16854.  ───────────────────────────────────────────────────────────────────────────
  16855.  Listing 2-3.  Updating the EGA or VGA Attribute Controller
  16856.  ───────────────────────────────────────────────────────────────────────────
  16857.  
  16858.  ; program the Attribute Controller directly
  16859.  
  16860.          mov     ax,40h
  16861.          mov     es,ax                ; ES := video BIOS data segment
  16862.          mov     dx,es:[63h]          ; DX := 3x4h (3B4h or 3D4h)
  16863.          add     dl,6                 ; DX := 3xAh (CRT Status Register)
  16864.  
  16865.          cli                          ; clear the interrupts
  16866.          in      al,dx                ; reset Attribute Controller flip-flop
  16867.          push    dx                   ; preserve Status Reg port
  16868.  
  16869.          mov     dl,0C0h              ; DX := 3C0h
  16870.          mov     al,RegNumber
  16871.          out     dx,al                ; write to Address Register
  16872.          jmp     $+2                  ; waste a few cycles so that Attribute
  16873.                                       ;  Controller can respond
  16874.          mov     al,DataValue
  16875.          out     dx,al                ; write to data register
  16876.  
  16877.          pop     dx                   ; DX := 3xAh
  16878.          in      al,dx                ; reset that flip-flop
  16879.          mov     dl,0C0h
  16880.          mov     al,20h               ; restore palette
  16881.          out     dx,al
  16882.          sti                          ; enable interrupts
  16883.  
  16884.  
  16885.  ; using the video BIOS
  16886.  
  16887.          mov     ax,1000h             ; AH := 10h (INT 10h function number)
  16888.                                       ; AL := 0 (Set individual Attribute
  16889.                                       ;  Controller register)
  16890.          mov     bl,RegNumber
  16891.          mov     bh,DataValue
  16892.          int     10h
  16893.  
  16894.  
  16895.  
  16896.  ───────────────────────────────────────────────────────────────────────────
  16897.  Listing 2-4.  Configuring a Hercules adapter for 720-by-348 graphics mode
  16898.  ───────────────────────────────────────────────────────────────────────────
  16899.  
  16900.                  TITLE   'Listing 2-4'
  16901.                  NAME    HercGraphMode
  16902.                  PAGE    55,132
  16903.  
  16904.  ;
  16905.  ; Name:         HercGraphMode
  16906.  ;
  16907.  ; Function:     Establish Hercules 720x348 graphics mode on HGC, HGC+,
  16908.  ;                InColor
  16909.  ;
  16910.  ; Caller:       Microsoft C:
  16911.  ;
  16912.  ;                               void HercGraphMode();
  16913.  ;
  16914.  
  16915.  DGROUP          GROUP   _DATA
  16916.  
  16917.  _TEXT           SEGMENT byte public 'CODE'
  16918.                  ASSUME  cs:_TEXT,ds:DGROUP
  16919.  
  16920.                  PUBLIC  _HercGraphMode
  16921.  _HercGraphMode  PROC    near
  16922.  
  16923.                  push    bp              ; preserve caller registers
  16924.                  mov     bp,sp
  16925.                  push    si
  16926.                  push    di
  16927.  
  16928.  ; Update Video BIOS Data Area with reasonable values
  16929.  
  16930.                  mov     ax,40h
  16931.                  mov     es,ax
  16932.                  mov     di,49h          ; ES:DI := 0040:0049 (BIOS data
  16933.                                          ;  area)
  16934.  
  16935.                  mov     si,offset DGROUP:BIOSData
  16936.                  mov     cx,BIOSDataLen
  16937.                  rep     movsb           ; update BIOS data area
  16938.  
  16939.  ; Set Configuration Switch
  16940.  
  16941.                  mov     dx,3BFh         ; DX := Configuration Switch port
  16942.                  mov     al,1            ; AL bit 1 := 0 (exclude 2nd 32K of
  16943.                                          ;                 video buffer)
  16944.                                         ; AL bit 0 := 1 (allow graphics mode
  16945.                  out     dx,al          ;                setting via 3B8h)
  16946.  
  16947.  ; Blank the screen to avoid interference during CRTC programming
  16948.  
  16949.                  mov     dx,3B8h     ; DX := CRTC Mode Control register port
  16950.                  xor     al,al       ; AL bit 3 := 0 (disable video signal)
  16951.                  out     dx,al       ; blank the screen
  16952.  
  16953.  ; Program the CRTC
  16954.  
  16955.                  sub     dl,4        ; DX := CRTC Address reg port 3B4h
  16956.  
  16957.                  mov     si,offset DGROUP:CRTCParms
  16958.                  mov     cx,CRTCParmsLen
  16959.  
  16960.  L01:            lodsw               ; AL := CRTC register number
  16961.                                      ; AH := data for this register
  16962.                  out     dx,ax
  16963.                  loop    L01
  16964.  
  16965.  ; Set graphics mode
  16966.  
  16967.                  add     dl,4        ; DX := 3B8h (CRTC Mode Control reg)
  16968.                  mov     al,CRTMode  ; AL bit 1 = 1 (enable graphics mode)
  16969.                                      ;    bit 3 = 1 (enable video)
  16970.                  out     dx,al
  16971.  
  16972.                  pop     di          ; restore registers and exit
  16973.                  pop     si
  16974.                  mov     sp,bp
  16975.                  pop     bp
  16976.                  ret
  16977.  
  16978.  _HercGraphMode  ENDP
  16979.  
  16980.  _TEXT           ENDS
  16981.  
  16982.  
  16983.  _DATA           SEGMENT word public 'DATA'
  16984.  
  16985.                                  ; These are the parameters recommended by
  16986.                                  ;  Hercules.
  16987.                                  ; They are based on 16 pixels/character and
  16988.                                  ;  4 scan lines per character.
  16989.  
  16990.  CRTCParms       DB      00h,35h ; Horizontal Total:  54 characters
  16991.                  DB      01h,2Dh ; Horizontal Displayed:  45 characters
  16992.                  DB      02h,2Eh ; Horizontal Sync Position:  at 46th
  16993.                                  ;  character
  16994.                  DB      03h,07h ; Horizontal Sync Width:  7 character
  16995.                                  ;  clocks
  16996.  
  16997.                  DB      04h,5Bh ; Vertical Total:  92 characters
  16998.                                  ;  (368 lines)
  16999.                  DB      05h,02h ; Vertical Adjust:  2 scan lines
  17000.                  DB      06h,57h ; Vertical Displayed:  87 character rows
  17001.                                  ;  (348 lines)
  17002.                  DB      07h,57h ; Vertical Sync Position:  after 87th char
  17003.                                  ;  row
  17004.                  DB      09h,03h ; Max Scan Line:  4 scan lines per char
  17005.  
  17006.  CRTCParmsLen    EQU     ($-CRTCParms)/2
  17007.  
  17008.  BIOSData        DB      7       ; CRT_MODE
  17009.                  DW      80      ; CRT_COLS
  17010.                  DW      8000h   ; CRT_LEN
  17011.                  DW      0       ; CRT_START
  17012.                  DW      8 dup(0) ; CURSOR_POSN
  17013.                  DW      0       ; CURSOR_MODE
  17014.                  DB      0       ; ACTIVE_PAGE
  17015.  CRTCAddr        DW      3B4h    ; ADDR_6845
  17016.  CRTMode         DB      0Ah     ; CRT_MODE_SET (value for port 3B8h)
  17017.                  DB      0       ; CRT_PALETTE (unused)
  17018.  
  17019.  BIOSDataLen     EQU     $-BIOSData
  17020.  
  17021.  _DATA           ENDS
  17022.  
  17023.                  END
  17024.  
  17025.  
  17026.  
  17027.  ───────────────────────────────────────────────────────────────────────────
  17028.  Listing 2-5.  Enable or disable video I/O port and buffer addressing
  17029.                on an MCGA or VGA.
  17030.  ───────────────────────────────────────────────────────────────────────────
  17031.  
  17032.          mov     ah,12h                ; AH := 12h (INT 10h function number)
  17033.          mov     al,1                  ; AL := 1 (disable addressing)
  17034.                                        ; (use AL = 0 to enable addressing)
  17035.          mov     bl,32h                ; INT 10H subfunction number
  17036.          int     10h
  17037.  
  17038.          cmp     al,12h
  17039.          jne     ErrorExit             ; jump if BIOS does not support this
  17040.                                        ;  function
  17041.  
  17042.  
  17043.  
  17044.  ───────────────────────────────────────────────────────────────────────────
  17045.  Listing 3-1.  Resetting the Enable Blink bit on the MDA or CGA.
  17046.  ───────────────────────────────────────────────────────────────────────────
  17047.  
  17048.          mov     ax,40h
  17049.          mov     es,ax           ; ES := video BIOS data segment
  17050.          mov     dx,es:[63h]     ; DX := 3B4h (MDA) or
  17051.                                  ;  3D4h (CGA) from ADDR_6845
  17052.          add     dl,4            ; DX := 3x8h (CRT Mode Control reg)
  17053.          mov     al,es:[65h]     ; AL := current value of reg (CRT_MODE_SET)
  17054.          and     al,11011111b    ; zero bit 5
  17055.          out     dx,al           ; update the register
  17056.          mov     es:[65h],al     ; update the BIOS data area
  17057.  
  17058.  
  17059.  
  17060.  ───────────────────────────────────────────────────────────────────────────
  17061.  Listing 3-2.  Setting and resetting the Enable Blink bit on
  17062.                the MCGA, EGA, or VGA.
  17063.  ───────────────────────────────────────────────────────────────────────────
  17064.  
  17065.          mov     bl,0                    ; BL := value for Enable Blink bit
  17066.          mov     ax,1003h                ; AH := INT 10H function number
  17067.                                          ; AL := subfunction number
  17068.          int     10h
  17069.  
  17070.  
  17071.  
  17072.  ───────────────────────────────────────────────────────────────────────────
  17073.  Listing 3-3.  Palette register programming on the EGA or VGA.
  17074.  ───────────────────────────────────────────────────────────────────────────
  17075.  
  17076.  ; updating apalette register directly:
  17077.  
  17078.          mov     ax,40h
  17079.          mov     es,ax                  ; ES := video BIOS data segment
  17080.          mov     dx,es:[63h]            ; DX := CRTC address reg (3x4h)
  17081.          add     dl,6                   ; DX := Status reg (3xAh)
  17082.          push    dx                     ;preserve this value
  17083.          cli
  17084.          in      al,dx                  ; reset Attribute Controller address
  17085.                                         ;  flip-flop
  17086.          mov     dl,0C0h                ; DX := 3C0h
  17087.          mov     al,PaletteRegNumber
  17088.          out     dx,al                  ; update one palette register
  17089.          mov     al,PaletteRegValue
  17090.          out     dx,al
  17091.          pop     dx                     ; DX := Status register port
  17092.          in      al,dx                  ; reset the flip-flop
  17093.          mov     dl,0C0h
  17094.          mov     al,20h
  17095.          out     dx,al                  ; set bit 5 of
  17096.                                         ;  Attribute Controller address reg
  17097.          sti
  17098.  
  17099.  
  17100.  ; updating a palette register using the video BIOS
  17101.  
  17102.          mov     bl,PaletteRegNumber
  17103.          mov     bh,PaletteRegValue
  17104.          mov     ax,1000h               ; AH := INT 10H function number
  17105.                                         ; AL := subfunction number
  17106.          int     10h
  17107.  
  17108.  
  17109.  
  17110.  ───────────────────────────────────────────────────────────────────────────
  17111.  Listing 3-4.  InColor Exception register programming.
  17112.  ───────────────────────────────────────────────────────────────────────────
  17113.  
  17114.          mov     ax,0017h          ; AH bit 5 := 0 (disable
  17115.                                    ;  monochrome attributes)
  17116.                                    ; AH bit 4 := 0 (disable palette)
  17117.                                    ; AH bits 0-3 := 0 (default cursor color)
  17118.                                    ; AL := 17h (Exception Register number)
  17119.          mov     dx,3B4h           ; DX := I/O port
  17120.          out     dx,ax
  17121.  
  17122.  
  17123.  
  17124.  ───────────────────────────────────────────────────────────────────────────
  17125.  Listing 3-5.  InColor palette register programming.
  17126.  ───────────────────────────────────────────────────────────────────────────
  17127.  
  17128.          mov     dx,3B4h               ; DX := CRTC address register
  17129.          mov     al,1Ch                ; AL := 1Ch (Palette Register number)
  17130.          out     dx,al
  17131.          inc     dx                    ; DX := 3B5h
  17132.          in      al,dx                 ; reset palette register index
  17133.  
  17134.          mov     si,offset PaletteTable ; DS:SI ->Palette Table
  17135.          mov     cx,16                 ; CX := number of palette registers
  17136.  L01:    lodsb                         ; AL := next byte from table
  17137.          out     dx,al                 ; update next palette reg
  17138.          loop    L01
  17139.          .
  17140.          .
  17141.          .
  17142.  PaletteTable    db     00h,01h,02h,03h,04h,05h,06h,07h ;palette regs 0-7
  17143.                  db     38h,39h,3Ah,3Bh,3Ch,3Dh,3Eh,3Fh ;palette regs 8-0Fh
  17144.  
  17145.  
  17146.  
  17147.  ───────────────────────────────────────────────────────────────────────────
  17148.  Listing 3-6.  Loading an alternative MCGA monochrome gray-scale palette.
  17149.  ───────────────────────────────────────────────────────────────────────────
  17150.  
  17151.          mov     bx,0Fh                 ; BX := first video DAC
  17152.                                         ;  Color register number
  17153.          mov     di,offset VDACTable    ; DS:DI ->table
  17154.  
  17155.  L01:    mov     dh,[bx+di]             ; DH := red value
  17156.          mov     ch,dh
  17157.          mov     cl,dh                  ; green and blue values are the same
  17158.          mov     ax,1010h               ; AH := INT 10h function number
  17159.                                         ; AL := subfunction number
  17160.          int     10h
  17161.          dec     bx
  17162.          jns     L01                    ; loop from register 0FH through
  17163.                                         ;  register 0
  17164.          .
  17165.          .
  17166.          .
  17167.  VDACTable       db      00h,05h,08h,0Bh,0Eh,11h,14h,18h
  17168.                  db      1Ch,20h,24h,28h,2Dh,32h,38h,3Fh
  17169.  
  17170.  
  17171.  
  17172.  ───────────────────────────────────────────────────────────────────────────
  17173.  Listing 3-7.  Loading an alternative VGA monochrome gray-scale palette.
  17174.  ───────────────────────────────────────────────────────────────────────────
  17175.  
  17176.          mov     bx,0Fh                ; BX := first Palette register number
  17177.          mov     di,offset VDACTable   ; DS:DI -> table
  17178.  
  17179.  L01:    mov     dh,[bx+di]            ; DH := red value
  17180.          mov     ch,dh
  17181.          mov     cl,dh                 ; green and blue values are the same
  17182.  
  17183.          push    bx                    ; preserve Palette register number
  17184.          mov     ax,1007h              ; AH := INT 10h function number
  17185.                                        ; AL := subfunction number
  17186.                                        ;  (read Palette register)
  17187.          int     10h                   ; BH := Palette register value
  17188.          mov     bl,bh
  17189.          xor     bh,bh                 ; BX := desired video DAC
  17190.                                        ; Color register number
  17191.  
  17192.          mov     ax,1010h              ; AH := INT 10h function number
  17193.                                        ; AL := subfunction number
  17194.          int     10h
  17195.          pop     bx
  17196.          dec     bx                    ; BX := next Palette register number
  17197.          jns     L01                   ; loop from Palette registers
  17198.                                        ;  0FH through 0
  17199.          .
  17200.          .
  17201.          .
  17202.  VDACTable       db      00h,05h,08h,0Bh,0Eh,11h,14h,18h
  17203.                  db      1Ch,20h,24h,28h,2Dh,32h,38h,3Fh
  17204.  
  17205.  
  17206.  
  17207.  ───────────────────────────────────────────────────────────────────────────
  17208.  Listing 3-8.  Setting a border color.
  17209.  ───────────────────────────────────────────────────────────────────────────
  17210.  
  17211.  ; updating the CRT Color Register directly (CGA only)
  17212.  
  17213.          mov     ax,40h
  17214.          mov     es,ax                   ; ES := video BIOS data segment
  17215.          mov     dx,es:[63h]             ; DX := 3D4H (ADDR_6845)
  17216.          add     dl,5                    ; DX := 3D9H (CRT Color Select reg)
  17217.          mov     al,es:[66h]             ; AL := current value of reg
  17218.                                          ;  (CRT_PALETTE)
  17219.          and     al,11110000b            ; zero bits 0-3
  17220.          or      al,BorderValue          ; update bits 0-3
  17221.          out     dx,al                   ; update the register
  17222.          mov     es:[66h],al             ; update the BIOS data area
  17223.  
  17224.  
  17225.  ; using the video BIOS interface (CGA, EGA, VGA)
  17226.  
  17227.          mov     bl,BorderValue
  17228.          mov     bh,0                    ; BH := subfunction number
  17229.          mov     ah,0Bh                  ; AH := INT 10h function number
  17230.          int     10h
  17231.  
  17232.  
  17233.  
  17234.  ───────────────────────────────────────────────────────────────────────────
  17235.  Listing 3-9.  Display alphanumeric text on the CGA by blanking the display.
  17236.  ───────────────────────────────────────────────────────────────────────────
  17237.  
  17238.                  TITLE   'Listing 3-9'
  17239.                  NAME    DisplayText
  17240.                  PAGE    55,132
  17241.  
  17242.  ;
  17243.  ; Name:         DisplayText
  17244.  ;
  17245.  ; Function:     Display an alphanumeric string without interference on the
  17246.  ;                CGA
  17247.  ;
  17248.  ; Caller:       Microsoft C:
  17249.  ;
  17250.  ;                       int DisplayText1(buf,n,offset);
  17251.  ;
  17252.  ;                       char *buf;           /* buffer containing text in
  17253.  ;                                               CGA alphanumeric format
  17254.  ;                                               (alternating character
  17255.  ;                                               codes and attribute
  17256.  ;                                               bytes) */
  17257.  ;
  17258.  ;                       int n;               /* buffer length in bytes */
  17259.  ;
  17260.  ;                       unsigned int offset; /* offset into video buffer */
  17261.  ;
  17262.  
  17263.  Set80X25        EQU     (1 SHL 0)       ; bit masks for Mode Control
  17264.                                          ;  Register
  17265.  Set320X200      EQU     (1 SHL 1)
  17266.  BlackAndWhite   EQU     (1 SHL 2)
  17267.  EnableVideo     EQU     (1 SHL 3)
  17268.  Set640X200      EQU     (1 SHL 4)
  17269.  EnableBlink     EQU     (1 SHL 5)
  17270.  
  17271.  ARGbuf          EQU     word ptr [bp+4] ; stack frame addressing
  17272.  ARGn            EQU     word ptr [bp+6]
  17273.  ARGoffset       EQU     word ptr [bp+8]
  17274.  TIMEOUT         EQU     6               ; Horizontal timeout loop limit
  17275.  
  17276.  _TEXT           SEGMENT byte public 'CODE'
  17277.                  ASSUME  cs:_TEXT
  17278.  
  17279.                  PUBLIC  _DisplayText
  17280.  _DisplayText    PROC    near
  17281.  
  17282.                  push    bp              ; usual C prologue to establish
  17283.                  mov     bp,sp           ;  stack frame and preserve
  17284.                                          ;  registers
  17285.                  push    di
  17286.                  push    si
  17287.  
  17288.                  mov     ax,0B800h
  17289.                  mov     es,ax
  17290.                  mov     di,ARGoffset    ; ES:DI -> destination in video
  17291.                                          ;  buffer
  17292.                  mov     si,ARGbuf       ; DS:SI -> source buffer
  17293.                  mov     bx,ARGn
  17294.                  shr     bx,1            ; BX := buffer length in words
  17295.  
  17296.                  mov     dx,3DAh         ; DX := CGA Status Port
  17297.  
  17298.  ; wait for start of vertical blanking interval
  17299.  
  17300.  L01:            mov     cx,TIMEOUT      ; CX := loop counter (timeout
  17301.                                          ;  value)
  17302.  
  17303.  L02:            in      al,dx           ; AL := video status
  17304.                  test    al,8
  17305.                  jnz     L02             ; loop if vertical sync active
  17306.                  test    al,1
  17307.                  jz      L02             ; loop if Display Enable active
  17308.  
  17309.                  cli                     ; disable interrupts
  17310.  
  17311.  L03:            in      al,dx
  17312.                  test    al,1
  17313.                  loopnz  L03             ; loop until end of horizontal
  17314.                                          ;  blanking or timeout
  17315.  
  17316.                  sti                     ; reenable interrupts
  17317.  
  17318.                  jz      L01             ; loop if no timeout
  17319.  
  17320.  ; blank the display
  17321.  
  17322.                  mov     dl,0D8h         ; DX := 3D8h (Mode Control
  17323.                                          ;  register)
  17324.                  mov     al,(Set80X25 OR EnableBlink)
  17325.                  out     dx,al           ; turn video off
  17326.  
  17327.  ; copy the data to the video buffer
  17328.  
  17329.                  mov     cx,bx           ; CX := buffer length in words
  17330.                  rep     movsw
  17331.  
  17332.  ; reenable the display
  17333.  
  17334.                  or      al,EnableVideo
  17335.                  out     dx,al
  17336.  
  17337.                  pop     si              ; usual C epilogue to restore
  17338.                  pop     di              ;  registers and discard
  17339.                                          ;  stack frame
  17340.                  mov     sp,bp
  17341.                  pop     bp
  17342.                  ret
  17343.  
  17344.  _DisplayText    ENDP
  17345.  
  17346.  _TEXT           ENDS
  17347.  
  17348.                  END
  17349.  
  17350.  
  17351.  
  17352.  ───────────────────────────────────────────────────────────────────────────
  17353.  Listing 3-10.  Display alphanumeric text on the CGA during horizontal
  17354.                 and vertical blanking intervals.
  17355.  ───────────────────────────────────────────────────────────────────────────
  17356.  
  17357.                  TITLE   'Listing 3-10'
  17358.                  NAME    DisplayText
  17359.                  PAGE    55,132
  17360.  
  17361.  ;
  17362.  ; Name:         DisplayText
  17363.  ;
  17364.  ; Function:     Display an alphanumeric string without interference on the
  17365.  ;                CGA
  17366.  ;
  17367.  ; Caller:       Microsoft C:
  17368.  ;
  17369.  ;                       int DisplayText(buf,n,offset);
  17370.  ;
  17371.  ;                       char *buf;            /* buffer containing text
  17372.  ;                                                in CGA alphanumeric
  17373.  ;                                                format (alternating
  17374.  ;                                                character codes and
  17375.  ;                                                attribute bytes) */
  17376.  ;
  17377.  ;                       int n;                /* buffer length in bytes */
  17378.  ;
  17379.  ;                       unsigned int offset; /* offset into video buffer */
  17380.  ;
  17381.  ARGbuf          EQU     word ptr [bp+4]
  17382.  ARGn            EQU     word ptr [bp+6]
  17383.  
  17384.  ARGoffset       EQU     word ptr [bp+8]
  17385.  TIMEOUT         EQU     6               ; horizontal timeout loop limit
  17386.  VBcount         EQU     250             ; number of words to write during
  17387.                                          ;  vertical blanking interval
  17388.  
  17389.  _TEXT           SEGMENT byte public 'CODE'
  17390.                  ASSUME  cs:_TEXT
  17391.  
  17392.                  PUBLIC  _DisplayText
  17393.  _DisplayText    PROC    near
  17394.  
  17395.                  push    bp              ; usual C prologue to establish
  17396.                  mov     bp,sp           ; stack frame and preserve
  17397.                                          ;  registers
  17398.                  push    di
  17399.                  push    si
  17400.  
  17401.                  mov     ax,0B800h
  17402.                  mov     es,ax
  17403.                  mov     di,ARGoffset    ; ES:DI -> destination in video
  17404.                                          ;  buffer
  17405.                  mov     si,ARGbuf       ; DS:SI -> source buffer
  17406.                  mov     cx,ARGn
  17407.                  shr     cx,1            ; CX := buffer length in words
  17408.  
  17409.                  mov     dx,3DAh         ; DX := CGA Status Port
  17410.  
  17411.  ; write during remaining vertical blanking interval
  17412.  
  17413.  L01:            mov     bx,cx           ; preserve buffer length in BX
  17414.                  mov     cx,TIMEOUT      ; CX := horizontal timeout
  17415.                  cli                     ; disable interrupts during loop
  17416.  
  17417.  L02:            in      al,dx           ; AL := video status
  17418.                  test    al,1
  17419.                  loopnz  L02             ; loop while Display Enable
  17420.                                          ;  inactive
  17421.                  jz      L03             ; jump if loop did not time out
  17422.  
  17423.                  movsw                   ; copy one word
  17424.                  sti
  17425.                  mov     cx,bx           ; CX := buffer length
  17426.                  loop    L01
  17427.  
  17428.                  jmp     short L10       ; exit (entire string copied)
  17429.  
  17430.  ; write during horizontal blanking intervals
  17431.  
  17432.  L03:            sti
  17433.                  mov     cx,bx           ; restore CX
  17434.  
  17435.  L04:            lodsw                   ; AL := character code
  17436.                                          ; AH := attribute
  17437.                  mov     bx,ax           ; BX := character and attribute
  17438.  
  17439.                  push    cx              ; preserve word loop counter
  17440.                  mov     cx,TIMEOUT      ; CX := timeout loop limit
  17441.  
  17442.                  cli                     ; clear interrupts during one
  17443.                                          ;  scan line
  17444.  
  17445.  L05:            in      al,dx
  17446.                  test    al,1
  17447.                  loopnz  L05             ; loop during horizontal blanking
  17448.                                          ;  until timeout occurs
  17449.                  jnz     L07             ; jump if timed out (vertical
  17450.                                          ;  blanking has started)
  17451.  L06:            in      al,dx
  17452.                  test    al,1
  17453.                  jz      L06             ; loop while Display Enable is
  17454.                                          ;  active
  17455.  
  17456.                  xchg    ax,bx           ; AX := character & attribute
  17457.                  stosw                   ; copy 2 bytes to display buffer
  17458.  
  17459.                  sti                     ; restore interrupts
  17460.                  pop     cx              ; CX := word loop counter
  17461.                  loop    L04
  17462.  
  17463.                  jmp     short L10       ; exit (entire string copied)
  17464.  
  17465.  ; write during entire vertical blanking interval
  17466.  
  17467.  L07:            pop     bx              ; BX := word loop counter
  17468.                  dec     si
  17469.                  dec     si              ; DS:SI -> word to copy from buffer
  17470.  
  17471.                  mov     cx,VBcount      ; CX := # of words to copy
  17472.                  cmp     bx,cx
  17473.                  jnb     L08             ; jump if more than VBcount words
  17474.                                          ;  remain
  17475.                                          ;  in buffer
  17476.                  mov     cx,bx           ; CX := # of remaining words in
  17477.                                          ;  buffer
  17478.                  xor     bx,bx           ; BX := 0
  17479.                  jmp     short L09
  17480.  
  17481.  L08:            sub     bx,cx           ; BX := (# of remaining words) -
  17482.                                          ;  VBcount
  17483.  
  17484.  L09:            rep     movsw           ; copy to video buffer
  17485.  
  17486.                  mov     cx,bx           ; CX := # of remaining words
  17487.                  test    cx,cx
  17488.                  jnz     L01             ; loop until buffer is displayed
  17489.  
  17490.  L10:            pop     si              ; usual C epilogue to restore
  17491.                                          ;  registers
  17492.                  pop     di              ;  and discard stack frame
  17493.                  mov     sp,bp
  17494.                  pop     bp
  17495.                  ret
  17496.  
  17497.  _DisplayText    ENDP
  17498.  
  17499.  _TEXT           ENDS
  17500.  
  17501.                  END
  17502.  
  17503.  
  17504.  
  17505.  ───────────────────────────────────────────────────────────────────────────
  17506.  Listing 3-11.  Setting the CRTC Start Address registers.
  17507.  ───────────────────────────────────────────────────────────────────────────
  17508.  
  17509.          mov     ax,40h
  17510.          mov     es,ax               ; ES := video BIOS data segment
  17511.          mov     dx,es:[63h]         ; DX := ADDR_6845
  17512.  
  17513.          mov     al,0Ch              ; AL := reg number (Start Address High)
  17514.          out     dx,al
  17515.          inc     dx                  ; DX := 3x5h
  17516.          mov     al,HiByte           ; AL := high-order byte of start offset
  17517.          out     dx,al
  17518.          dec     dx                  ; DX := 3x4h
  17519.  
  17520.          mov     al,0Dh              ; AL := reg number (Start Address Low)
  17521.          out     dx,al
  17522.          inc     dx                  ; DX := 3x5h
  17523.          mov     al,LoByte           ; AL := low-order byte of start offset
  17524.          out     dx,al
  17525.          mov     ah,HiByte           ; AX := start offset in words
  17526.  
  17527.          shl     ax,1                ; AX := offset in bytes
  17528.          mov     es:[4Eh],ax         ; update CRT_START
  17529.  
  17530.  
  17531.  
  17532.  ───────────────────────────────────────────────────────────────────────────
  17533.  Listing 3-12.  Video page selection using the ROM BIOS.
  17534.  ───────────────────────────────────────────────────────────────────────────
  17535.  
  17536.          mov     al,Vpage                ; AL := video page number
  17537.          mov     ah,5                    ; AH := INT 10h function number
  17538.          int     10h
  17539.  
  17540.  
  17541.  
  17542.  ───────────────────────────────────────────────────────────────────────────
  17543.  Listing 3-13.  Setting the cursor size.
  17544.  ───────────────────────────────────────────────────────────────────────────
  17545.  
  17546.  ; updating the CRTC registers directly
  17547.  
  17548.          mov     ax,40h
  17549.          mov     es,ax                   ; ES := video BIOS data segment
  17550.          mov     dx,es:[63h]             ; DX := ADDR_6845
  17551.  
  17552.          mov     al,0Ah                  ; AL := reg number (Cursor Start)
  17553.          out     dx,al
  17554.          inc     dx                      ; DX := 3x5h
  17555.          mov     al,TopLine              ; AL := top scan line for cursor
  17556.          out     dx,al
  17557.          dec     dx                      ; DX := 3x4h
  17558.  
  17559.          mov     al,0Bh                  ; AL := reg number (Cursor End)
  17560.          out     dx,al
  17561.          inc     dx                      ; DX := 3x5h
  17562.          mov     al,BottomLine           ; AL := bottom scan line for cursor
  17563.          out     dx,al
  17564.  
  17565.          mov     ah,TopLine              ; AX := top and bottom lines
  17566.          mov     es:[60h],ax             ; update CURSOR_MODE
  17567.  
  17568.  
  17569.  ; using the video BIOS interface
  17570.  
  17571.          mov     ch,TopLine
  17572.          mov     cl,BottomLine
  17573.          mov     ah,1                    ; AH := INT 10h function number
  17574.          int     10h
  17575.  
  17576.  
  17577.  
  17578.  ───────────────────────────────────────────────────────────────────────────
  17579.  Listing 3-14.  Setting the cursor location.
  17580.  ───────────────────────────────────────────────────────────────────────────
  17581.  
  17582.  ; updating the CRTC registers directly
  17583.  
  17584.          mov     ax,40h
  17585.          mov     es,ax             ; ES := video BIOS data segment
  17586.          mov     dx,es:[63h]       ; DX := ADDR_6845
  17587.  
  17588.          mov     al,0Eh            ; AL := reg number (Cursor Location High)
  17589.          out     dx,al
  17590.          inc     dx                ; DX := 3x5h
  17591.          mov     al,HiByte         ; AL := high-order byte of cursor offset
  17592.          out     dx,al
  17593.          dec     dx                ; DX := 3x4h
  17594.  
  17595.          mov     al,0Fh            ; AL := reg number (Cursor Location Low)
  17596.          out     dx,al
  17597.          inc     dx                ; DX := 3x5h
  17598.          mov     al,LoByte         ; AL := low-order byte of cursor offset
  17599.          out     dx,al
  17600.  
  17601.  
  17602.  ; using the video BIOS interface
  17603.  
  17604.          mov     dh,CursorRow
  17605.          mov     dl,CursorColumn
  17606.          mov     bh,VideoPage
  17607.          mov     ah,2              ; AH := INT 10h function number
  17608.          int     10h
  17609.  
  17610.  
  17611.  
  17612.  ───────────────────────────────────────────────────────────────────────────
  17613.  Listing 3-15.  An invisible alphanumeric cursor for IBM video subsystems.
  17614.  ───────────────────────────────────────────────────────────────────────────
  17615.  
  17616.          mov     cx,2000h                ; CH := top scan line for cursor
  17617.                                          ; CL := bottom scan line for cursor
  17618.          mov     ah,1                    ; AH := INT 10h function number
  17619.          int     10h
  17620.  
  17621.  
  17622.  
  17623.  ───────────────────────────────────────────────────────────────────────────
  17624.  Listing 4-1.  Computing a pixel's address in 320-by-200 4-color mode
  17625.  ───────────────────────────────────────────────────────────────────────────
  17626.  
  17627.                  TITLE   'Listing 4-1'
  17628.                  NAME    PixelAddr04
  17629.                  PAGE    55,132
  17630.  
  17631.  ;
  17632.  ; Name:         PixelAddr04
  17633.  ;
  17634.  ; Function:     Determine buffer address of pixel in 320x200 4-color mode
  17635.  ;
  17636.  ; Caller:       AX = y-coordinate (0-199)
  17637.  ;               BX = x-coordinate (0-319)
  17638.  ;
  17639.  ; Returns:      AH = bit mask
  17640.  ;               BX = byte offset in buffer
  17641.  ;               CL = number of bits to shift left
  17642.  ;               ES = video buffer segment
  17643.  ;
  17644.  
  17645.  
  17646.  OriginOffset    EQU     0               ; byte offset of (0,0)
  17647.  VideoBufferSeg  EQU     0B800h
  17648.  
  17649.  _TEXT           SEGMENT byte public 'CODE'
  17650.                  ASSUME  cs:_TEXT
  17651.                  PUBLIC  PixelAddr04
  17652.  PixelAddr04     PROC    near
  17653.  
  17654.                  mov     cl,bl           ; CL := low-order byte of x
  17655.  
  17656.                  xchg    ah,al           ; AX := 100h * y
  17657.                  shr     ax,1            ; AL := 80h * (y&1)
  17658.                  add     bh,al           ; BX := x + 8000h*(y&1)
  17659.                  xor     al,al           ; AX := 100h*(y/2)
  17660.                  add     bx,ax           ; BX := x + 8000h*(y&1) +
  17661.                                          ;  100h*(y/2)
  17662.                  shr     ax,1
  17663.                  shr     ax,1            ; AX := 40h*(y/2)
  17664.                  add     bx,ax           ; BX := x + 8000h*(y&1) +
  17665.                                          ;  140h*(y/2)
  17666.                  shr     bx,1
  17667.                  shr     bx,1            ; BX := x/4 + 2000h*(y&1) +
  17668.                                          ;  50h*(y/2)
  17669.                  add     bx,OriginOffset ; BX := byte offset in video buffer
  17670.  
  17671.                  mov     ax,VideoBufferSeg
  17672.                  mov     es,ax           ; ES:BX := byte address of pixel
  17673.  
  17674.                  mov     ah,3            ; AH := unshifted bit mask
  17675.                  and     cl,ah           ; CL := x & 3
  17676.                  xor     cl,ah           ; CL := 3 - (x & 3)
  17677.                  shl     cl,1            ; CL := # bits to shift left
  17678.  
  17679.                  ret
  17680.  
  17681.  PixelAddr04     ENDP
  17682.  
  17683.  _TEXT           ENDS
  17684.  
  17685.                  END
  17686.  
  17687.  
  17688.  
  17689.  ───────────────────────────────────────────────────────────────────────────
  17690.  Listing 4-2.  Computing a pixel's address in 640-by-200 2-color mode
  17691.  ───────────────────────────────────────────────────────────────────────────
  17692.  
  17693.                  TITLE   'Listing 4-2'
  17694.                  NAME    PixelAddr06
  17695.                  PAGE    55,132
  17696.  
  17697.  ;
  17698.  ; Name:         PixelAddr06
  17699.  ;
  17700.  ; Function:     Determine buffer address of pixel in 640x200 2-color mode
  17701.  ;
  17702.  ; Caller:       AX = y-coordinate (0-199)
  17703.  ;               BX = x-coordinate (0-639)
  17704.  ;
  17705.  ; Returns:      AH = bit mask
  17706.  ;               BX = byte offset in buffer
  17707.  ;               CL = number of bits to shift left
  17708.  ;               ES = video buffer segment
  17709.  ;
  17710.  
  17711.  OriginOffset    EQU     0               ; byte offset of (0,0)
  17712.  VideoBufferSeg  EQU     0B800h
  17713.  
  17714.  _TEXT           SEGMENT byte public 'CODE'
  17715.                  ASSUME  cs:_TEXT
  17716.  
  17717.                  PUBLIC  PixelAddr06
  17718.  PixelAddr06     PROC    near
  17719.  
  17720.                  mov     cl,bl           ; CL := low-order byte of x
  17721.  
  17722.                  xchg    ah,al           ; AX := 100h * y
  17723.                  shr     bx,1            ; BX := x/2
  17724.                  shr     ax,1            ; AL := 80h*(y&1)
  17725.                  add     bh,al           ; BX := x/2 + 8000h*(y&1)
  17726.                  xor     al,al           ; AX := 100h*(y/2)
  17727.                  add     bx,ax           ; BX := x/2 + 8000h*(y&1) +
  17728.                                          ;  100h*(y/2)
  17729.                  shr     ax,1
  17730.                  shr     ax,1            ; AX := 40h*(y/2)
  17731.                  add     bx,ax           ; BX := x/2 + 8000h*(y&1) +
  17732.                                          ;  140h*(y/2)
  17733.                  shr     bx,1
  17734.                  shr     bx,1            ; BX := x/8 + 2000h*(y&1) +
  17735.                                          ;  50h*(y/2)
  17736.                  add     bx,OriginOffset ; BX := byte offset in video buffer
  17737.  
  17738.                  mov     ax,VideoBufferSeg
  17739.                  mov     es,ax           ; ES:BX := byte address of pixel
  17740.  
  17741.                  and     cl,7            ; CL := x & 7
  17742.                  xor     cl,7            ; CL := number of bits to shift
  17743.                                          ;  left
  17744.                  mov     ah,1            ; AH := unshifted bit mask
  17745.  
  17746.                  ret
  17747.  
  17748.  PixelAddr06     ENDP
  17749.  
  17750.  _TEXT           ENDS
  17751.  
  17752.                  END
  17753.  
  17754.  
  17755.  
  17756.  ───────────────────────────────────────────────────────────────────────────
  17757.  Listing 4-3.  Computing a pixel's address in Hercules graphics mode.
  17758.  ───────────────────────────────────────────────────────────────────────────
  17759.  
  17760.                  TITLE   'Listing 4-3'
  17761.                  NAME    PixelAddrHGC
  17762.                  PAGE    55,132
  17763.  
  17764.  ;
  17765.  ; Name:         PixelAddrHGC
  17766.  ;
  17767.  ; Function:     Determine buffer address of pixel in 720x348 Hercules
  17768.  ;                graphics
  17769.  ;
  17770.  ; Caller:       AX = y-coordinate (0-347)
  17771.  ;               BX = x-coordinate (0-719)
  17772.  ;
  17773.  ; Returns:      AH = bit mask
  17774.  ;               BX = byte offset in buffer
  17775.  ;               CL = number of bits to shift left
  17776.  ;               ES = video buffer segment
  17777.  ;
  17778.  
  17779.  BytesPerLine    EQU     90
  17780.  OriginOffset    EQU     0               ; byte offset of (0,0)
  17781.  VideoBufferSeg  EQU     0B000h
  17782.  
  17783.  _TEXT           SEGMENT byte public 'CODE'
  17784.                  ASSUME  cs:_TEXT
  17785.  
  17786.                  PUBLIC  PixelAddrHGC
  17787.  PixelAddrHGC    PROC    near
  17788.  
  17789.                  mov     cl,bl           ; CL := low-order byte of x
  17790.  
  17791.                  shr     ax,1            ; AX := y/2
  17792.                  rcr     bx,1            ; BX := 8000h*(y&1) + x/2
  17793.                  shr     ax,1            ; AX := y/4
  17794.                  rcr     bx,1            ; BX := 4000h*(y&3) + x/4
  17795.                  shr     bx,1            ; BX := 2000h*(y&3) + x/8
  17796.                  mov     ah,BytesPerLine
  17797.                  mul     ah              ; AX := BytesPerLine*(y/4)
  17798.                  add     bx,ax           ; BX := 2000h*(y&3) + x/8 +
  17799.                                          ; BytesPerLine*(y/4)
  17800.                  add     bx,OriginOffset ; BX := byte offset in video buffer
  17801.  
  17802.                  mov     ax,VideoBufferSeg
  17803.                  mov     es,ax           ; ES:BX := byte address of pixel
  17804.  
  17805.                  and     cl,7            ; CL := x & 7
  17806.                  xor     cl,7            ; CL := number of bits to shift
  17807.                                          ;  left
  17808.                  mov     ah,1            ; AH := unshifted bit mask
  17809.  
  17810.                  ret
  17811.  
  17812.  PixelAddrHGC    ENDP
  17813.  
  17814.  _TEXT           ENDS
  17815.  
  17816.                  END
  17817.  
  17818.  
  17819.  
  17820.  ───────────────────────────────────────────────────────────────────────────
  17821.  Listing 4-4.  Computing a pixel's address in CGA and VGA graphics modes.
  17822.  ───────────────────────────────────────────────────────────────────────────
  17823.  
  17824.                  TITLE   'Listing 4-4'
  17825.                  NAME    PixelAddr10
  17826.                  PAGE    55,132
  17827.  
  17828.  ;
  17829.  ; Name:         PixelAddr10
  17830.  ;
  17831.  ; Function:     Determine buffer address of pixel in native EGA and VGA
  17832.  ;                modes:
  17833.  ;                       320x200 16-color
  17834.  ;                       640x200 16-color
  17835.  ;                       640x350 16-color
  17836.  ;                       640x350 monochrome (4-color)
  17837.  ;                       640x480 2-color
  17838.  ;                       640x480 16-color
  17839.  ;
  17840.  ; Caller:       AX = y-coordinate
  17841.  ;               BX = x-coordinate
  17842.  ;
  17843.  ; Returns:      AH = bit mask
  17844.  ;               BX = byte offset in buffer
  17845.  ;               CL = number of bits to shift left
  17846.  ;               ES = video buffer segment
  17847.  ;
  17848.  
  17849.  BytesPerLine    EQU     80              ; bytes in one horizontal line
  17850.  OriginOffset    EQU     0               ; byte offset of (0,0)
  17851.  VideoBufferSeg  EQU     0A000h
  17852.  
  17853.  _TEXT           SEGMENT byte public 'CODE'
  17854.                  ASSUME  cs:_TEXT
  17855.  
  17856.                  PUBLIC  PixelAddr10
  17857.  PixelAddr10     PROC    near
  17858.  
  17859.                  mov     cl,bl           ; CL := low-order byte of x
  17860.                  push    dx              ; preserve DX
  17861.  
  17862.                  mov     dx,BytesPerLine ; AX := y * BytesPerLine
  17863.                  mul     dx
  17864.  
  17865.                  pop     dx
  17866.                  shr     bx,1
  17867.                  shr     bx,1
  17868.                  shr     bx,1            ; BX := x/8
  17869.                  add     bx,ax           ; BX := y*BytesPerLine + x/8
  17870.                  add     bx,OriginOffset ; BX := byte offset in video buffer
  17871.  
  17872.                  mov     ax,VideoBufferSeg
  17873.                  mov     es,ax           ; ES:BX := byte address of pixel
  17874.  
  17875.                  and     cl,7            ; CL := x & 7
  17876.                  xor     cl,7            ; CL := number of bits to shift
  17877.                                          ;  left
  17878.                  mov     ah,1            ; AH := unshifted bit mask
  17879.                  ret
  17880.  
  17881.  PixelAddr10     ENDP
  17882.  
  17883.  _TEXT           ENDS
  17884.                  END
  17885.  
  17886.  
  17887.  
  17888.  ───────────────────────────────────────────────────────────────────────────
  17889.  Listing 4-5.  Computing a pixel's address in 320-by-200 256-color mode
  17890.  ───────────────────────────────────────────────────────────────────────────
  17891.  
  17892.                  TITLE   'Listing 4-5'
  17893.                  NAME    PixelAddr13
  17894.                  PAGE    55,132
  17895.  
  17896.  ;
  17897.  ; Name:         PixelAddr13
  17898.  ;
  17899.  ; Function:     Determine buffer address of pixel in 320x200 256-color mode
  17900.  ;
  17901.  ; Caller:       AX = y-coordinate (0-199)
  17902.  ;               BX = x-coordinate (0-319)
  17903.  ;
  17904.  ; Returns:      BX = byte offset in buffer
  17905.  ;               ES = video buffer segment
  17906.  ;
  17907.  
  17908.  OriginOffset    EQU     0               ; byte offset of (0,0)
  17909.  VideoBufferSeg  EQU     0A000h
  17910.  
  17911.  _TEXT           SEGMENT byte public 'CODE'
  17912.                  ASSUME  cs:_TEXT
  17913.  
  17914.                  PUBLIC  PixelAddr13
  17915.  PixelAddr13     PROC    near
  17916.  
  17917.                  xchg    ah,al           ; AX := 256*y
  17918.                  add     bx,ax           ; BX := 256*y + x
  17919.                  shr     ax,1
  17920.                  shr     ax,1            ; AX := 64*y
  17921.                  add     bx,ax           ; BX := 320*y + x
  17922.  
  17923.                  add     bx,OriginOffset ; BX := byte offset in video buffer
  17924.  
  17925.                  mov     ax,VideoBufferSeg
  17926.                  mov     es,ax           ; ES:BX := byte address of pixel
  17927.                  ret
  17928.  
  17929.  PixelAddr13     ENDP
  17930.  
  17931.  _TEXT           ENDS
  17932.  
  17933.                  END
  17934.  
  17935.  
  17936.  
  17937.  ───────────────────────────────────────────────────────────────────────────
  17938.  Listing 4-6.  Foreground color in CGA 640-by-200 2-color graphics.
  17939.  ───────────────────────────────────────────────────────────────────────────
  17940.  
  17941.  mov     ah,0Bh          ; AH := 0BH (INT 10H function number)
  17942.  mov     bh,0            ; BH := subfunction number
  17943.  mov     bl,ColorValue   ; BL := desired color (0-0FH)
  17944.  int     10h
  17945.  
  17946.  
  17947.  
  17948.  ───────────────────────────────────────────────────────────────────────────
  17949.  Listing 4-7.  Four-color palettes in CGA 320-by-200 4-color mode.
  17950.  ───────────────────────────────────────────────────────────────────────────
  17951.  
  17952.  ; cyan-red-white
  17953.  
  17954.  mov     ax,40h
  17955.  mov     es,ax           ; ES := Video BIOS data segment
  17956.  mov     al,es:[65h]     ; AL := CRT_MODE_SET
  17957.  or      al,00000100b    ; AL bit 2 := 1
  17958.  mov     dx,3D8h         ; DX := Mode Control I/O port
  17959.  out     dx,al           ; update Mode Control register
  17960.  mov     es:[65h],al     ; update CRT_MODE_SET
  17961.  
  17962.  ; green-red-yellow or cyan-violet-white
  17963.  
  17964.  mov     ax,40h
  17965.  mov     es,ax           ; ES := Video BIOS data segment
  17966.  mov     al,es:[65h]     ; AL := CRT_MODE_SET
  17967.  and     al,11111011b    ; AL bit 2 := 0
  17968.  mov     dx,3D8h         ; DX := Mode Control I/O port
  17969.  out     dx,al           ; update Mode Control register
  17970.  mov     es:[65h],al     ; update CRT_MODE_SET
  17971.  
  17972.  mov     al,es:[66h]     ; AL := CRT_PALETTE
  17973.  and     al,11011111b    ; AL bit 5 := 0
  17974.  or      al,PaletteSelect; 00000000b for green-red-yellow
  17975.                          ; 00100000b for cyan-violet-white
  17976.  inc     dx              ; DX := Color Select I/O port
  17977.  out     dx,al           ; update Color Select register
  17978.  mov     es:[66h],al     ; update CRT_PALETTE
  17979.  
  17980.  
  17981.  
  17982.  ───────────────────────────────────────────────────────────────────────────
  17983.  Listing 4-8.  Four-color palettes in CGA 320-by-200 4-color mode using
  17984.                video BIOS.
  17985.  ───────────────────────────────────────────────────────────────────────────
  17986.  
  17987.  ; cyan-red-white
  17988.  
  17989.          mov     ax,0005         ; AH := 0 (INT 10H function number)
  17990.                                  ; AL := 5 (320x200 4-color mode, color
  17991.                                  ;  burst disabled)
  17992.          int     10h
  17993.  
  17994.  ; green-red-yellow or cyan-violet-white
  17995.  
  17996.          mov     ax,0004         ; AH := 0 (INT 10H function number)
  17997.                                  ; AL := 4 (320x200 4-color mode, color
  17998.                                  ;  burst enabled)
  17999.          int     10h
  18000.  
  18001.          mov     ah,0Bh          ; AH := INT 10H function number
  18002.          mov     bh,1
  18003.          mov     bl,PaletteID    ; 0 for green-red-yellow
  18004.                                  ; 1 for cyan-violet-white
  18005.          int     10h
  18006.  
  18007.  
  18008.  
  18009.  ───────────────────────────────────────────────────────────────────────────
  18010.  Listing 5-1.  How to set Graphics Controller read and write modes. This
  18011.                example sets read mode 0 and write mode 1 in 640-by-350
  18012.                16-color mode.
  18013.  ───────────────────────────────────────────────────────────────────────────
  18014.  
  18015.                  mov     ax,0105h     ; AH := 1 (reg 5 value)
  18016.                                       ;  bit 3 := 0 (reead mode 0)
  18017.                                       ;  bits 0-1 := 1 (write mode 1)
  18018.                                       ; AL := register number
  18019.                  mov     dx,3CEh      ; DX := Graphics Controller port
  18020.                  out     dx,ax
  18021.  
  18022.  
  18023.  
  18024.  ───────────────────────────────────────────────────────────────────────────
  18025.  Listing 5-2.  Determining a pixel value in CGA 640-by-200 2-color mode.
  18026.  ───────────────────────────────────────────────────────────────────────────
  18027.  
  18028.                  TITLE   'Listing 5-2'
  18029.                  NAME    ReadPixel06
  18030.                  PAGE    55,132
  18031.  
  18032.  ;
  18033.  ; Name:         ReadPixel06
  18034.  ;
  18035.  ; Function:     Read the value of a pixel in 640x200 2-color mode
  18036.  ;
  18037.  ; Caller:       Microsoft C:
  18038.  ;
  18039.  ;                       int ReadPixel06(x,y);
  18040.  ;
  18041.  ;                       int x,y;                /* pixel coordinates */
  18042.  ;
  18043.  
  18044.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  18045.  ARGy            EQU     word ptr [bp+6]
  18046.  
  18047.  
  18048.  _TEXT           SEGMENT byte public 'CODE'
  18049.                  ASSUME  cs:_TEXT
  18050.  
  18051.                  EXTRN   PixelAddr06:near
  18052.  
  18053.                  PUBLIC  _ReadPixel06
  18054.  _ReadPixel06    PROC    near
  18055.  
  18056.                  push    bp            ; preserve caller registers
  18057.                  mov     bp,sp
  18058.  
  18059.                  mov     ax,ARGy       ; AX := y
  18060.                  mov     bx,ARGx       ; BX := x
  18061.                  call    PixelAddr06   ; AH := bit mask
  18062.                                        ; ES:BX -> buffer
  18063.                                        ; CL := # bits to shift
  18064.  
  18065.                  mov     al,es:[bx]    ; AL := byte containing pixel
  18066.                  shr     al,cl         ; shift pixel value to low-order bits
  18067.                  and     al,ah         ; AL := pixel value
  18068.                  xor     ah,ah         ; AX := pixel value
  18069.  
  18070.                  mov     sp,bp         ; restore caller registers and return
  18071.                  pop     bp
  18072.                  ret
  18073.  
  18074.  _ReadPixel06    ENDP
  18075.  
  18076.  _TEXT           ENDS
  18077.                  END
  18078.  
  18079.  
  18080.  
  18081.  ───────────────────────────────────────────────────────────────────────────
  18082.  Listing 5-3.  Determining a pixel value in CGA 320-by-200 4-color mode.
  18083.  ───────────────────────────────────────────────────────────────────────────
  18084.  
  18085.                  TITLE   'Listing 5-3'
  18086.                  NAME    ReadPixel04
  18087.                  PAGE    55,132
  18088.  
  18089.  ;
  18090.  ; Name:         ReadPixel04
  18091.  ;
  18092.  ; Function:     Read the value of a pixel in 320x200 4-color mode
  18093.  ;
  18094.  ; Caller:       Microsoft C:
  18095.  ;
  18096.  ;                       int ReadPixel04(x,y);
  18097.  ;
  18098.  ;                       int x,y;                /* pixel coordinates */
  18099.  ;
  18100.  
  18101.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  18102.  ARGy            EQU     word ptr [bp+6]
  18103.  
  18104.  
  18105.  _TEXT           SEGMENT byte public 'CODE'
  18106.                  ASSUME  cs:_TEXT
  18107.  
  18108.                  EXTRN   PixelAddr04:near
  18109.  
  18110.                  PUBLIC  _ReadPixel04
  18111.  _ReadPixel04    PROC    near
  18112.  
  18113.                  push    bp            ; preserve caller registers
  18114.                  mov     bp,sp
  18115.  
  18116.                  mov     ax,ARGy       ; AX := y
  18117.                  mov     bx,ARGx       ; BX := x
  18118.                  call    PixelAddr04   ; AH := bit mask
  18119.                                        ; ES:BX -> buffer
  18120.                                        ; CL := # bits to shift
  18121.  
  18122.                  mov     al,es:[bx]    ; AL := byte containing pixel
  18123.                  shr     al,cl         ; shift pixel value to low-order bits
  18124.                  and     al,ah         ; AL := pixel value
  18125.                  xor     ah,ah         ; AX := pixel value
  18126.  
  18127.                  mov     sp,bp         ; restore caller registers and return
  18128.                  pop     bp
  18129.                  ret
  18130.  
  18131.  _ReadPixel04    ENDP
  18132.  
  18133.  _TEXT           ENDS
  18134.                  END
  18135.  
  18136.  
  18137.  
  18138.  ───────────────────────────────────────────────────────────────────────────
  18139.  Listing 5-4.  Determining a pixel value in native EGA graphics modes.
  18140.  ───────────────────────────────────────────────────────────────────────────
  18141.  
  18142.                  TITLE   'Listing 5-4'
  18143.                  NAME    ReadPixel10
  18144.                  PAGE    55,132
  18145.  
  18146.  ;
  18147.  ; Name:         ReadPixel10
  18148.  ;
  18149.  ; Function:     Read the value of a pixel in native EGA graphics modes
  18150.  ;
  18151.  ; Caller:       Microsoft C:
  18152.  ;
  18153.  ;                       int ReadPixel10(x,y);
  18154.  ;
  18155.  ;                       int x,y;                /* pixel coordinates */
  18156.  ;
  18157.  
  18158.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  18159.  ARGy            EQU     word ptr [bp+6]
  18160.  
  18161.  
  18162.  _TEXT           SEGMENT byte public 'CODE'
  18163.                  ASSUME  cs:_TEXT
  18164.  
  18165.                  EXTRN   PixelAddr10:near
  18166.  
  18167.                  PUBLIC  _ReadPixel10
  18168.  _ReadPixel10    PROC    near
  18169.  
  18170.                  push    bp              ; preserve caller registers
  18171.                  mov     bp,sp
  18172.                  push    si
  18173.  
  18174.                  mov     ax,ARGy         ; AX := y
  18175.                  mov     bx,ARGx         ; BX := x
  18176.                  call    PixelAddr10     ; AH := bit mask
  18177.                                          ; ES:BX -> buffer
  18178.                                          ; CL := # bits to shift
  18179.  
  18180.                  mov     ch,ah
  18181.                  shl     ch,cl           ; CH := bit mask in proper position
  18182.  
  18183.                  mov     si,bx           ; ES:SI -> regen buffer byte
  18184.                  xor     bl,bl           ; BL is used to accumulate the
  18185.                                          ;  pixel value
  18186.  
  18187.                  mov     dx,3CEh         ; DX := Graphics Controller port
  18188.                  mov     ax,304h         ; AH := initial bit plane number
  18189.                                          ; AL := Read Map Select register
  18190.                                          ;  number
  18191.  
  18192.  L01:            out     dx,ax           ; select bit plane
  18193.                  mov     bh,es:[si]      ; BH := byte from current bit plane
  18194.                  and     bh,ch           ; mask one bit
  18195.                  neg     bh              ; bit 7 of BH := 1 (if masked
  18196.                                          ;  bit = 1)
  18197.                                          ; bit 7 of BH := 0 (if masked
  18198.                                          ;  bit = 0)
  18199.                  rol     bx,1            ; bit 0 of BL := next bit from
  18200.                                          ;  pixel value
  18201.                  dec     ah              ; AH := next bit plane number
  18202.                  jge     L01
  18203.  
  18204.                  mov     al,bl           ; AL := pixel value
  18205.                  xor     ah,ah           ; AX := pixel value
  18206.  
  18207.                  pop     si              ; restore caller registers and
  18208.                                          ;  return
  18209.                  mov     sp,bp
  18210.                  pop     bp
  18211.                  ret
  18212.  
  18213.  _ReadPixel10    ENDP
  18214.  
  18215.  _TEXT           ENDS
  18216.                  END
  18217.  
  18218.  
  18219.  
  18220.  ───────────────────────────────────────────────────────────────────────────
  18221.  Listing 5-5.  Determining a pixel value in EGA monochrome graphics mode.
  18222.  ───────────────────────────────────────────────────────────────────────────
  18223.  
  18224.                  TITLE   'Listing 5-5'
  18225.                  NAME    ReadPixel0F
  18226.                  PAGE    55,132
  18227.  
  18228.  ;
  18229.  ; Name:         ReadPixel0F
  18230.  ;
  18231.  ; Function:     Read the value of a pixel in 640x350 monochrome mode
  18232.  ;
  18233.  ; Caller:       Microsoft C:
  18234.  ;
  18235.  ;                       int     ReadPixel0F(x,y);
  18236.  ;
  18237.  ;                       int x,y;                /* pixel coordinates */
  18238.  ;
  18239.  
  18240.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  18241.  ARGy            EQU     word ptr [bp+6]
  18242.  
  18243.  
  18244.  _TEXT           SEGMENT byte public 'CODE'
  18245.                  ASSUME  cs:_TEXT
  18246.  
  18247.                  EXTRN   PixelAddr10:near
  18248.  
  18249.                  PUBLIC  _ReadPixel0F
  18250.  _ReadPixel0F    PROC    near
  18251.  
  18252.                  push    bp              ; preserve caller registers
  18253.                  mov     bp,sp
  18254.                  push    si
  18255.  
  18256.                  mov     ax,ARGy         ; AX := y
  18257.                  mov     bx,ARGx         ; BX := x
  18258.                  call    PixelAddr10     ; AH := bit mask
  18259.                                          ; ES:BX -> buffer
  18260.                                          ; CL := # bits to shift
  18261.  
  18262.  ; concatenate bits from bit planes 2 and 0
  18263.  
  18264.                  mov     ch,ah
  18265.                  shl     ch,cl           ; CH := bit mask in proper position
  18266.                  mov     si,bx           ; ES:SI -> regen buffer byte
  18267.  
  18268.                  mov     dx,3CEh         ; DX := Graphics Controller port
  18269.                  mov     ax,204h         ; AH := initial bit plane number
  18270.                                          ; AL := Read Map Select register
  18271.                                          ;  number
  18272.  
  18273.                  xor     bl,bl           ; BL is used to accumulate the
  18274.                                          ;  pixel value
  18275.  
  18276.  L01:            out     dx,ax           ; (same as before)
  18277.                  mov     bh,es:[si]
  18278.                  and     bh,ch
  18279.                  neg     bh
  18280.  
  18281.                  rol     bx,1
  18282.                  sub     ah,2            ; decrement map number by 2
  18283.                  jge     L01
  18284.  
  18285.                  mov     al,bl
  18286.                  xor     ah,ah
  18287.  
  18288.                  pop     si
  18289.                  mov     sp,bp
  18290.                  pop     bp
  18291.                  ret
  18292.  
  18293.  _ReadPixel0F    ENDP
  18294.  
  18295.  _TEXT           ENDS
  18296.  
  18297.                  END
  18298.  
  18299.  
  18300.  
  18301.  ───────────────────────────────────────────────────────────────────────────
  18302.  Listing 5-6.  Determining a pixel value in 640-by-350 modes on
  18303.                an EGA with 64 KB.
  18304.  ───────────────────────────────────────────────────────────────────────────
  18305.  
  18306.                  TITLE   'Listing 5-6'
  18307.                  NAME    ReadPixel10
  18308.                  PAGE    55,132
  18309.  
  18310.  ;
  18311.  ; Name:         ReadPixel10
  18312.  ;
  18313.  ; Function:     Read the value of a pixel in 640x350 modes on 64K EGA
  18314.  ;
  18315.  ; Caller:       Microsoft C:
  18316.  ;
  18317.  ;                       int     ReadPixel10(x,y);
  18318.  ;
  18319.  ;                       int x,y;                /* pixel coordinates */
  18320.  ;
  18321.  
  18322.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  18323.  ARGy            EQU     word ptr [bp+6]
  18324.  
  18325.  _TEXT           SEGMENT byte public 'CODE'
  18326.                  ASSUME  cs:_TEXT
  18327.  
  18328.                  EXTRN   PixelAddr10:near
  18329.  
  18330.                  PUBLIC  _ReadPixel10
  18331.  _ReadPixel10    PROC    near
  18332.  
  18333.                  push    bp              ; preserve caller registers
  18334.                  mov     bp,sp
  18335.                  push    si
  18336.  
  18337.                  mov     ax,ARGy         ; AX := y
  18338.                  mov     bx,ARGx         ; BX := x
  18339.                  call    PixelAddr10     ; AH := bit mask
  18340.                                          ; ES:BX -> buffer
  18341.                                          ; CL := # bits to shift
  18342.  
  18343.  ; concatenate bits from bit planes 2 and 0 (even byte address)
  18344.  ;  or 3 and 1 (odd byte address)
  18345.  
  18346.                  mov     ch,ah
  18347.                  shl     ch,cl           ; CH := bit mask in proper position
  18348.  
  18349.                  mov     si,bx           ; ES:SI -> regen buffer byte
  18350.  
  18351.                  mov     ah,bl           ; AH := low-order byte of address
  18352.                  and     ax,100h         ; AH := low-order bit of address
  18353.                                          ; AL := 0
  18354.                  add     ax,204h         ; AH := initial bit plane number (2
  18355.                                          ;  or 3)
  18356.                                          ; AL := Read Map Select register
  18357.                                          ;  number
  18358.  
  18359.                  mov     dx,3CEh         ; DX := Graphics Controller port
  18360.                  xor     bl,bl           ; BL is used to accumulate the
  18361.                                          ;  pixel value
  18362.  
  18363.  L01:            out     dx,ax           ; (same as before)
  18364.                  mov     bh,es:[si]
  18365.                  and     bh,ch
  18366.                  neg     bh
  18367.  
  18368.                  rol     bx,1
  18369.                  sub     ah,2
  18370.                  jge     L01
  18371.  
  18372.                  mov     al,bl
  18373.                  xor     ah,ah
  18374.  
  18375.                  pop     si
  18376.                  mov     sp,bp
  18377.                  pop     bp
  18378.                  ret
  18379.  
  18380.  _ReadPixel10    ENDP
  18381.  
  18382.  _TEXT           ENDS
  18383.  
  18384.                  END
  18385.  
  18386.  
  18387.  
  18388.  ───────────────────────────────────────────────────────────────────────────
  18389.  Listing 5-7.  Determining a pixel value in InColor graphics mode.
  18390.  ───────────────────────────────────────────────────────────────────────────
  18391.  
  18392.                  TITLE   'Listing 5-7'
  18393.                  NAME    ReadPixelInC
  18394.                  PAGE    55,132
  18395.  
  18396.  ;
  18397.  ; Name:         ReadPixelInC
  18398.  ;
  18399.  ; Function:     Read the value of a pixel in InColor 720x348 16-color mode
  18400.  ;
  18401.  ; Caller:       Microsoft C:
  18402.  ;
  18403.  ;                       int     ReadPixelInC(x,y);
  18404.  ;
  18405.  ;                       int x,y;
  18406.  ;
  18407.  
  18408.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  18409.  ARGy            EQU     word ptr [bp+6]
  18410.  
  18411.  DefaultRWColor  EQU     0Fh             ; default value for R/W Color
  18412.                                          ;  Register
  18413.  
  18414.  
  18415.  _TEXT           SEGMENT byte public 'CODE'
  18416.                  ASSUME  cs:_TEXT
  18417.  
  18418.                  EXTRN   PixelAddrHGC:near
  18419.  
  18420.                  PUBLIC  _ReadPixelInC
  18421.  _ReadPixelInC   PROC    near
  18422.  
  18423.                  push    bp              ; preserve caller registers
  18424.                  mov     bp,sp
  18425.                  push    si
  18426.  
  18427.                  mov     ax,ARGy         ; AX := y
  18428.                  mov     bx,ARGx         ; BX := x
  18429.                  call    PixelAddrHGC    ; AH := bit mask
  18430.                                          ; ES:BX -> buffer
  18431.                                          ; CL := # bits to shift
  18432.  
  18433.  ; set up to examine each bit plane separately
  18434.  
  18435.                  mov     si,bx           ; ES:SI -> buffer
  18436.  
  18437.                  shl     ah,cl
  18438.                  mov     cl,ah           ; CL := bit mask in proper position
  18439.  
  18440.                  mov     dx,3B4h         ; DX := graphics control port
  18441.  
  18442.                  mov     ax,0F01Ah       ; AH bits 4-7 := 1111b (background
  18443.                                          ;  value)
  18444.                                          ; AL := 1Ah (R/W Color Register)
  18445.                  out     dx,ax           ; set background value
  18446.  
  18447.                  mov     bx,800h         ; BH := 1000b (initial "don't care"
  18448.                                          ;  bits)
  18449.                                          ; BL := 0 (initial value for
  18450.                                          ;  result)
  18451.  
  18452.                  dec     ax              ; AL := 19h (R/W Control Register
  18453.                                          ;  number)
  18454.  
  18455.  ; loop across bit planes by updating "don't care" bits
  18456.  
  18457.  L01:            mov     ah,bh           ; AH bits 0-3 := next "don't care"
  18458.                                          ;  bits
  18459.                                          ; AH bit 6 := 0 (Mask Polarity bit)
  18460.                  xor     ah,1111b        ; invert "don't care" bits
  18461.                  out     dx,ax           ; set R/W Control Register
  18462.  
  18463.                  mov     ch,cl           ; CH := bit mask
  18464.                  and     ch,es:[si]      ; latch bit planes
  18465.                                          ; CH <> 0 if bit in latch is set
  18466.  
  18467.                  neg     ch              ; cf set if CH <> 0
  18468.                  rcl     bl,1            ; accumulate result in BL
  18469.  
  18470.                  shr     bh,1            ; BH := shifted "don't care" bits
  18471.                  jnz     L01             ; loop until shifted out of BH,
  18472.                                          ;  at which point BX = pixel value
  18473.  ; restore default state
  18474.  
  18475.                  mov     ah,40h          ; AH := default R/W Control
  18476.                                          ;  Register value
  18477.                  out     dx,ax
  18478.  
  18479.                  inc     ax              ; AL := 1Ah (R/W Color Register
  18480.                                          ;  number)
  18481.                  mov     ah,DefaultRWColor
  18482.                  out     dx,ax
  18483.  
  18484.                  mov     ax,bx           ; AX := pixel value
  18485.  
  18486.                  pop     si              ; restore caller registers and
  18487.                                          ;  return
  18488.                  mov     sp,bp
  18489.                  pop     bp
  18490.                  ret
  18491.  
  18492.  _ReadPixelInC   ENDP
  18493.  
  18494.  _TEXT           ENDS
  18495.  
  18496.                  END
  18497.  
  18498.  
  18499.  
  18500.  ───────────────────────────────────────────────────────────────────────────
  18501.  Listing 5-8.  Determining a pixel value in MCGA and VGA
  18502.                640-by-480 2-color mode.
  18503.  ───────────────────────────────────────────────────────────────────────────
  18504.  
  18505.                  TITLE   'Listing 5-8'
  18506.                  NAME    ReadPixel11
  18507.                  PAGE    55,132
  18508.  
  18509.  ;
  18510.  ; Name:         ReadPixel11
  18511.  ;
  18512.  ; Function:     Read the value of a pixel in 640x480 2-color mode (MCGA or
  18513.  ;                VGA)
  18514.  ;
  18515.  ; Caller:       Microsoft C:
  18516.  ;
  18517.  ;                       int ReadPixel11(x,y);
  18518.  ;
  18519.  ;                       int x,y;                /* pixel coordinates */
  18520.  ;
  18521.  
  18522.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  18523.  ARGy            EQU     word ptr [bp+6]
  18524.  
  18525.  
  18526.  _TEXT           SEGMENT byte public 'CODE'
  18527.                  ASSUME  cs:_TEXT
  18528.  
  18529.                  EXTRN   PixelAddr10:near
  18530.  
  18531.                  PUBLIC  _ReadPixel11
  18532.  _ReadPixel11    PROC    near
  18533.  
  18534.                  push    bp              ; preserve caller registers
  18535.                  mov     bp,sp
  18536.  
  18537.                  mov     ax,ARGy         ; AX := y
  18538.                  mov     bx,ARGx         ; BX := x
  18539.                  call    PixelAddr10     ; AH := bit mask
  18540.                                          ; ES:BX -> buffer
  18541.                                          ; CL := # bits to shift
  18542.  
  18543.                  mov     al,es:[bx]      ; AL := byte containing pixel
  18544.                  shr     al,cl           ; shift pixel value to low-order
  18545.                                          ;  bits
  18546.                  and     al,ah           ; AL := pixel value
  18547.                  xor     ah,ah           ; AX := pixel value
  18548.  
  18549.                  mov     sp,bp           ; restore caller registers and
  18550.                                          ;  return
  18551.                  pop     bp
  18552.                  ret
  18553.  
  18554.  _ReadPixel11    ENDP
  18555.  
  18556.  _TEXT           ENDS
  18557.  
  18558.                  END
  18559.  
  18560.  
  18561.  
  18562.  ───────────────────────────────────────────────────────────────────────────
  18563.  Listing 5-9.  Determining a pixel value in MCGA and VGA 320-by-200
  18564.                256-color mode.
  18565.  ───────────────────────────────────────────────────────────────────────────
  18566.  
  18567.                  TITLE   'Listing 5-9'
  18568.                  NAME    ReadPixel13
  18569.                  PAGE    55,132
  18570.  
  18571.  ;
  18572.  
  18573.  ; Name:         ReadPixel13
  18574.  ;
  18575.  ; Function:     Read the value of a pixel in 320x200 256-color mode
  18576.  ;                (MCGA and VGA)
  18577.  ;
  18578.  ; Caller:       Microsoft C:
  18579.  ;
  18580.  ;                       int ReadPixel13(x,y);
  18581.  ;
  18582.  ;                       int x,y;                /* pixel coordinates */
  18583.  ;
  18584.  
  18585.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  18586.  ARGy            EQU     word ptr [bp+6]
  18587.  
  18588.  
  18589.  _TEXT           SEGMENT byte public 'CODE'
  18590.                  ASSUME  cs:_TEXT
  18591.  
  18592.                  EXTRN   PixelAddr13:near
  18593.  
  18594.                  PUBLIC  _ReadPixel13
  18595.  _ReadPixel13    PROC    near
  18596.  
  18597.                  push    bp              ; preserve caller registers
  18598.                  mov     bp,sp
  18599.  
  18600.                  mov     ax,ARGy         ; AX := y
  18601.                  mov     bx,ARGx         ; BX := x
  18602.                  call    PixelAddr13     ; ES:BX -> buffer
  18603.  
  18604.                  mov     al,es:[bx]      ; AL := pixel value
  18605.                  xor     ah,ah           ; AX := pixel value
  18606.  
  18607.                  mov     sp,bp
  18608.                  pop     bp
  18609.                  ret
  18610.  
  18611.  _ReadPixel13    ENDP
  18612.  
  18613.  _TEXT           ENDS
  18614.  
  18615.                  END
  18616.  
  18617.  
  18618.  
  18619.  ───────────────────────────────────────────────────────────────────────────
  18620.  Listing 5-10.  Setting a pixel value in CGA and 640-by-200 2-color mode.
  18621.  ───────────────────────────────────────────────────────────────────────────
  18622.  
  18623.                  TITLE   'Listing 5-10'
  18624.                  NAME    SetPixel06
  18625.                  PAGE    55,132
  18626.  
  18627.  ;
  18628.  ; Name:         SetPixel06
  18629.  ;
  18630.  ; Function:     Set the value of a pixel in 640x200 2-color mode
  18631.  ;
  18632.  ; Caller:       Microsoft C:
  18633.  ;
  18634.  ;                       void SetPixel(x,y,n);
  18635.  ;
  18636.  ;                       int x,y;                /* pixel coordinates */
  18637.  ;
  18638.  ;                       int n;                  /* pixel value */
  18639.  ;
  18640.  
  18641.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  18642.  ARGy            EQU     word ptr [bp+6]
  18643.  ARGn            EQU     byte ptr [bp+8]
  18644.  
  18645.  
  18646.  DGROUP          GROUP   _DATA
  18647.  
  18648.  _TEXT           SEGMENT byte public 'CODE'
  18649.                  ASSUME  cs:_TEXT,ds:DGROUP
  18650.  
  18651.                  EXTRN   PixelAddr06:near
  18652.  
  18653.                  PUBLIC  _SetPixel06
  18654.  _SetPixel06     PROC    near
  18655.  
  18656.                  push    bp              ; preserve caller registers
  18657.                  mov     bp,sp
  18658.  
  18659.                  mov     ax,ARGy         ; AX := y
  18660.                  mov     bx,ARGx         ; BX := x
  18661.                  call    PixelAddr06     ; AH := bit mask
  18662.                                          ; ES:BX -> buffer
  18663.                                          ; CL := # bits to shift left
  18664.  
  18665.                  mov     al,ARGn         ; AL := unshifted pixel value
  18666.                  shl     ax,cl           ; AH := bit mask in proper position
  18667.                                          ; AL := pixel value in proper
  18668.                                          ;  position
  18669.  
  18670.                  jmp     word ptr SetPixelOp06   ; jump to Replace, AND,
  18671.                                                  ;  OR or XOR routine
  18672.  
  18673.  
  18674.                                          ; routine to Replace pixel value
  18675.  
  18676.  ReplacePixel06: not     ah              ; AH := inverse bit mask
  18677.                  and     es:[bx],ah      ; zero the pixel value
  18678.                  or      es:[bx],al      ; set the pixel value
  18679.                  jmp     short L02
  18680.  
  18681.  
  18682.                                          ; routine to AND pixel value
  18683.  ANDPixel06:     test    al,al
  18684.                  jnz     L02             ; do nothing if pixel value = 1
  18685.  
  18686.  L01:            not     ah              ; AH := inverse of bit mask
  18687.                  and     es:[bx],ah      ; set bit in video buffer to 0
  18688.                  jmp     short L02
  18689.  
  18690.  
  18691.                                          ; routine to OR pixel value
  18692.  ORPixel06:      test    al,al
  18693.                  jz      L02             ; do nothing if pixel value = 0
  18694.  
  18695.                  or      es:[bx],al      ; set bit in video buffer
  18696.                  jmp     short L02
  18697.  
  18698.  
  18699.                                          ; routine to XOR pixel value
  18700.  XORPixel06:     test    al,al
  18701.                  jz      L02             ; do nothing if pixel value = 0
  18702.  
  18703.                  xor     es:[bx],al      ; XOR bit in video buffer
  18704.  
  18705.  
  18706.  L02:            mov     sp,bp           ; restore caller registers and
  18707.                                          ;  return
  18708.                  pop     bp
  18709.                  ret
  18710.  
  18711.  _SetPixel06     ENDP
  18712.  
  18713.  _TEXT           ENDS
  18714.  
  18715.  _DATA           SEGMENT word public 'DATA'
  18716.  
  18717.  SetPixelOp06    DW      ReplacePixel06  ; contains addr of pixel operation
  18718.  
  18719.  _DATA           ENDS
  18720.  
  18721.                  END
  18722.  
  18723.  
  18724.  
  18725.  ───────────────────────────────────────────────────────────────────────────
  18726.  Listing 5-11.  Setting a pixel value in CGA 320-by-200 2-color mode.
  18727.  ───────────────────────────────────────────────────────────────────────────
  18728.  
  18729.                  TITLE   'Listing 5-11'
  18730.                  NAME    SetPixel04
  18731.                  PAGE    55,132
  18732.  
  18733.  ;
  18734.  ; Name:         SetPixel04
  18735.  ;
  18736.  ; Function:     Set the value of a pixel in 320x200 4-color mode
  18737.  ;
  18738.  ; Caller:       Microsoft C:
  18739.  ;
  18740.  ;                       void SetPixel(x,y,n);
  18741.  ;
  18742.  ;                       int x,y;                /* pixel coordinates */
  18743.  ;
  18744.  ;                       int n;                  /* pixel value */
  18745.  ;
  18746.  
  18747.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  18748.  ARGy            EQU     word ptr [bp+6]
  18749.  ARGn            EQU     byte ptr [bp+8]
  18750.  
  18751.  
  18752.  DGROUP          GROUP   _DATA
  18753.  
  18754.  _TEXT           SEGMENT byte public 'CODE'
  18755.                  ASSUME  cs:_TEXT,ds:DGROUP
  18756.  
  18757.                  EXTRN   PixelAddr04:near
  18758.  
  18759.                  PUBLIC  _SetPixel04
  18760.  _SetPixel04     PROC    near
  18761.  
  18762.                  push    bp              ; preserve caller registers
  18763.                  mov     bp,sp
  18764.  
  18765.                  mov     ax,ARGy         ; AX := y
  18766.                  mov     bx,ARGx         ; BX := x
  18767.  
  18768.                  call    PixelAddr04     ; AH := bit mask
  18769.                                          ; ES:BX -> buffer
  18770.                                          ; CL := # bits to shift left
  18771.  
  18772.                  mov     al,ARGn
  18773.                  shl     ax,cl           ; AH := bit mask in proper position
  18774.                                          ; AL := pixel value in proper
  18775.                                          ;  position
  18776.  
  18777.                  jmp     word ptr SetPixelOp04   ; jump to Replace, AND,
  18778.                                                  ;  OR or XOR routine
  18779.  
  18780.  
  18781.                                          ; routine to Replace pixel value
  18782.  
  18783.  ReplacePixel04: not     ah              ; AH := inverse bit mask
  18784.                  and     es:[bx],ah      ; zero the pixel value
  18785.                  or      es:[bx],al      ; set the pixel value
  18786.                  jmp     short L02
  18787.  
  18788.                                          ; routine to AND pixel value
  18789.  
  18790.  ANDPixel04:     not     ah              ; AH := inverse bit mask
  18791.                  or      al,ah           ; AL := all 1's except pixel value
  18792.                  and     es:[bx],al
  18793.                  jmp     short L02
  18794.  
  18795.  
  18796.  ORPixel04:      or      es:[bx],al      ; routine to OR pixel value
  18797.                  jmp     short L02
  18798.  
  18799.  
  18800.  XORPixel04:     xor     es:[bx],al      ; routine to XOR pixel value
  18801.  
  18802.  
  18803.  L02:            mov     sp,bp           ; restore caller registers and
  18804.                                          ;  return
  18805.                  pop     bp
  18806.                  ret
  18807.  
  18808.  _SetPixel04     ENDP
  18809.  
  18810.  _TEXT           ENDS
  18811.  
  18812.  _DATA           SEGMENT word public 'DATA'
  18813.  
  18814.  SetPixelOp04    DW      ReplacePixel04  ; contains addr of pixel operation
  18815.  
  18816.  _DATA           ENDS
  18817.  
  18818.                  END
  18819.  
  18820.  
  18821.  
  18822.  ───────────────────────────────────────────────────────────────────────────
  18823.  Listing 5-12.  Setting a pixel value in native EGA graphics modes
  18824.                 using write mode 0.
  18825.  ───────────────────────────────────────────────────────────────────────────
  18826.  
  18827.                  TITLE   'Listing 5-12'
  18828.                  NAME    SetPixel10
  18829.                  PAGE    55,132
  18830.  
  18831.  ;
  18832.  ; Name:         SetPixel10
  18833.  ;
  18834.  ; Function:     Set the value of a pixel in native EGA graphics modes.
  18835.  ;
  18836.  ;               *** Write Mode 0, Set/Reset ***
  18837.  ;
  18838.  ; Caller:       Microsoft C:
  18839.  ;
  18840.  ;                       void SetPixel(x,y,n);
  18841.  ;
  18842.  ;                       int x,y;                /* pixel coordinates */
  18843.  ;
  18844.  ;                       int n;                  /* pixel value */
  18845.  ;
  18846.  
  18847.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  18848.  ARGy            EQU     word ptr [bp+6]
  18849.  ARGn            EQU     byte ptr [bp+8]
  18850.  
  18851.  RMWbits         EQU     18h             ; read-modify-write bits
  18852.  
  18853.  
  18854.  _TEXT           SEGMENT byte public 'CODE'
  18855.                  ASSUME  cs:_TEXT
  18856.  
  18857.                  EXTRN   PixelAddr10:near
  18858.                  PUBLIC  _SetPixel10
  18859.  
  18860.  _SetPixel10     PROC    near
  18861.  
  18862.                  push    bp              ; preserve caller registers
  18863.                  mov     bp,sp
  18864.  
  18865.                  mov     ax,ARGy         ; AX := y
  18866.                  mov     bx,ARGx         ; BX := x
  18867.                  call    PixelAddr10     ; AH := bit mask
  18868.                                          ; ES:BX -> buffer
  18869.                                          ; CL := # bits to shift left
  18870.  
  18871.  ; set Graphics Controller Bit Mask register
  18872.  
  18873.                  shl     ah,cl           ; AH := bit mask in proper position
  18874.                  mov     dx,3CEh         ; GC address register port
  18875.                  mov     al,8            ; AL := Bit Mask register number
  18876.                  out     dx,ax
  18877.  
  18878.  ; set Graphics Controller Mode register
  18879.  
  18880.                  mov     ax,0005h        ; AL :=  Mode register number
  18881.                                          ; AH :=  Write Mode 0 (bits 0,1)
  18882.                                          ;        Read Mode 0 (bit 3)
  18883.                  out     dx,ax
  18884.  
  18885.  ; set Data Rotate/Function Select register
  18886.  
  18887.                  mov     ah,RMWbits      ; AH := Read-Modify-Write bits
  18888.                  mov     al,3            ; AL := Data Rotate/Function Select
  18889.                                          ;  reg
  18890.                  out     dx,ax
  18891.  
  18892.  ; set Set/Reset and Enable Set/Reset registers
  18893.  
  18894.                  mov     ah,ARGn         ; AH := pixel value
  18895.                  mov     al,0            ; AL := Set/Reset reg number
  18896.                  out     dx,ax
  18897.  
  18898.                  mov     ax,0F01h        ; AH := value for Enable Set/Reset
  18899.                                          ;  (all bit planes enabled)
  18900.                                          ; AL := Enable Set/Reset reg number
  18901.                  out     dx,ax
  18902.  
  18903.  ; set the pixel value
  18904.  
  18905.                  or      es:[bx],al      ; load latches during CPU read
  18906.                                          ; update latches and bit planes
  18907.                                          ;  during CPU write
  18908.  
  18909.  ; restore default Graphics Controller registers
  18910.  
  18911.                  mov     ax,0FF08h       ; default Bit Mask
  18912.                  out     dx,ax
  18913.  
  18914.                  mov     ax,0005         ; default Mode register
  18915.                  out     dx,ax
  18916.  
  18917.                  mov     ax,0003         ; default Function Select
  18918.                  out     dx,ax
  18919.  
  18920.                  mov     ax,0001         ; default Enable Set/Reset
  18921.                  out     dx,ax
  18922.  
  18923.                  mov     sp,bp           ; restore caller registers and
  18924.                                          ;  return
  18925.                  pop     bp
  18926.                  ret
  18927.  
  18928.  _SetPixel10     ENDP
  18929.  
  18930.  _TEXT           ENDS
  18931.                  END
  18932.  
  18933.  
  18934.  
  18935.  ───────────────────────────────────────────────────────────────────────────
  18936.  Listing 5-13.  Setting a pixel value in native EGA graphics modes
  18937.                 using the Sequencer Map Mask.
  18938.  ───────────────────────────────────────────────────────────────────────────
  18939.  
  18940.                  TITLE   'Listing 5-13'
  18941.                  NAME    SetPixel10
  18942.                  PAGE    55,132
  18943.  ;
  18944.  ; Name:         SetPixel10
  18945.  ;
  18946.  ; Function:     Set the value of a pixel in native EGA graphics modes.
  18947.  ;
  18948.  ;               *** Write Mode 0, Sequencer Map Mask ***
  18949.  
  18950.  ;
  18951.  ; Caller:       Microsoft C:
  18952.  ;
  18953.  ;                       void SetPixel(x,y,n);
  18954.  ;
  18955.  ;                       int x,y;                /* pixel coordinates */
  18956.  ;                       int n;                  /* pixel value */
  18957.  
  18958.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  18959.  ARGy            EQU     word ptr [bp+6]
  18960.  ARGn            EQU     byte ptr [bp+8]
  18961.  
  18962.  _TEXT           SEGMENT byte public 'CODE'
  18963.                  ASSUME  cs:_TEXT
  18964.  
  18965.                  EXTRN   PixelAddr10:near
  18966.  
  18967.                  PUBLIC  _SetPixel10
  18968.  _SetPixel10     PROC    near
  18969.  
  18970.                  push    bp              ; preserve caller registers
  18971.                  mov     bp,sp
  18972.  
  18973.                  mov     ax,ARGy         ; AX := y
  18974.                  mov     bx,ARGx         ; BX := x
  18975.                  call    PixelAddr10     ; AH := bit mask
  18976.                                          ; ES:BX -> buffer
  18977.                                          ; CL := # bits to shift left
  18978.  
  18979.  ; set Graphics Controller Bit Mask register
  18980.  
  18981.                  shl     ah,cl           ; AH := bit mask in
  18982.                                          ;  proper position
  18983.                  mov     dx,3CEh         ; Graphics Controller address
  18984.                                          ;  reg port
  18985.                  mov     al,8            ; AL := Bit Mask register number
  18986.                  out     dx,ax
  18987.  
  18988.  ; zero the pixel value
  18989.  
  18990.                  mov     al,es:[bx]      ; latch one byte from each
  18991.                                          ;  bit plane
  18992.                  mov     byte ptr es:[bx],0  ; zero masked bits in
  18993.                                          ;  all planes
  18994.  
  18995.  ; set Sequencer Map Mask register
  18996.  
  18997.                  mov     dl,0C4h         ; DX := 3C4h (Sequencer addr
  18998.                                          ;  reg port)
  18999.                  mov     ah,ARGn         ; AH := value for Map Mask
  19000.                                          ;  register
  19001.                                          ;  (nonzero bits in pixel
  19002.                                          ;  value select
  19003.                                          ;  enabled bit planes for
  19004.                                          ;  Sequencer)
  19005.  
  19006.                  mov     al,2            ; AL := Map Mask register number
  19007.                  out     dx,ax
  19008.  
  19009.  ; set the nonzero bits in the pixel value
  19010.                  mov     byte ptr es:[bx],0FFh ; set bits in enabled
  19011.                                                ;  bit planes
  19012.  
  19013.  ; restore default Sequencer registers
  19014.  
  19015.                  mov     ah,0Fh          ; AH := value for Map Mask reg
  19016.                                          ;  (all bit
  19017.                                          ;  planes enabled)
  19018.                  out     dx,ax
  19019.  
  19020.  ; restore default Graphics Controller registers
  19021.  
  19022.                  mov     dl,0CEh         ; DX := 3CEh (Graphics
  19023.                                          ;  Controller port)
  19024.                  mov     ax,0FF08h       ; default Bit Mask
  19025.                  out     dx,ax
  19026.  
  19027.                  mov     sp,bp           ; restore caller registers
  19028.                                          ;  and return
  19029.                  pop     bp
  19030.                  ret
  19031.  
  19032.  _SetPixel10     ENDP
  19033.  
  19034.  _TEXT           ENDS
  19035.  
  19036.                  END
  19037.  
  19038.  
  19039.  
  19040.  ───────────────────────────────────────────────────────────────────────────
  19041.  Listing 5-14.  Setting a pixel value in native EGA graphics modes
  19042.                 using write mode 2.
  19043.  ───────────────────────────────────────────────────────────────────────────
  19044.  
  19045.                  TITLE   'Listing 5-14'
  19046.                  NAME    SetPixel10
  19047.                  PAGE    55,132
  19048.  
  19049.  ;
  19050.  ; Name:         SetPixel10
  19051.  ;
  19052.  ; Function:     Set the value of a pixel in native EGA graphics modes.
  19053.  ;
  19054.  ;               *** Write Mode 2 ***
  19055.  
  19056.  ;
  19057.  ; Caller:       Microsoft C:
  19058.  ;
  19059.  ;                       void SetPixel(x,y,n);
  19060.  ;
  19061.  ;                       int x,y;                /* pixel coordinates */
  19062.  ;                       int n;                  /* pixel value */
  19063.  ;
  19064.  
  19065.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  19066.  ARGy            EQU     word ptr [bp+6]
  19067.  ARGn            EQU     byte ptr [bp+8]
  19068.  
  19069.  RMWbits         EQU     18h             ; read-modify-write bits
  19070.  
  19071.  
  19072.  _TEXT           SEGMENT byte public 'CODE'
  19073.                  ASSUME  cs:_TEXT
  19074.  
  19075.                  EXTRN   PixelAddr10:near
  19076.  
  19077.                  PUBLIC  _SetPixel10
  19078.  _SetPixel10     PROC    near
  19079.  
  19080.                  push    bp              ; preserve stack frame
  19081.                  mov     bp,sp
  19082.  
  19083.                  mov     ax,ARGy         ; AX := y
  19084.                  mov     bx,ARGx         ; BX := x
  19085.                  call    PixelAddr10     ; AH := bit mask
  19086.                                          ; ES:BX -> buffer
  19087.                                          ; CL := # bits to shift left
  19088.  
  19089.  ; set Graphics Controller Bit Mask register
  19090.  
  19091.                  shl     ah,cl           ; AH := bit mask in proper position
  19092.                  mov     dx,3CEh         ; GC address register port
  19093.                  mov     al,8            ; AL := Bit Mask register number
  19094.                  out     dx,ax
  19095.  
  19096.  ; set Graphics Controller Mode register
  19097.  
  19098.                  mov     ax,205h         ; AL :=  Mode register number
  19099.                                          ; AH :=  Write Mode 2 (bits 0,1)
  19100.                                          ;        Read Mode 0 (bit 3)
  19101.                  out     dx,ax
  19102.  
  19103.  ; set Data Rotate/Function Select register
  19104.  
  19105.                  mov     ah,RMWbits      ; AH := Read-Modify-Write bits
  19106.                  mov     al,3            ; AL := Data Rotate/Function Select
  19107.                                          ;  reg
  19108.                  out     dx,ax
  19109.  
  19110.  ; set the pixel value
  19111.  
  19112.                  mov     al,es:[bx]      ; latch one byte from each bit
  19113.                                          ;  plane
  19114.                  mov     al,ARGn         ; AL := pixel value
  19115.                  mov     es:[bx],al      ; update all bit planes
  19116.  
  19117.  ; restore default Graphics Controller registers
  19118.  
  19119.                  mov     ax,0FF08h       ; default Bit Mask
  19120.                  out     dx,ax
  19121.  
  19122.                  mov     ax,0005         ; default Mode register
  19123.                  out     dx,ax
  19124.  
  19125.                  mov     ax,0003         ; default Function Select
  19126.                  out     dx,ax
  19127.  
  19128.                  mov     sp,bp           ; restore stack frame and return
  19129.                  pop     bp
  19130.                  ret
  19131.  
  19132.  _SetPixel10     ENDP
  19133.  
  19134.  _TEXT           ENDS
  19135.  
  19136.                  END
  19137.  
  19138.  
  19139.  
  19140.  ───────────────────────────────────────────────────────────────────────────
  19141.  Listing 5-15.  Setting a pixel value in InColor graphics mode.
  19142.  ───────────────────────────────────────────────────────────────────────────
  19143.  
  19144.                  TITLE   'Listing 5-15'
  19145.                  NAME    SetPixelInC
  19146.                  PAGE    55,132
  19147.  
  19148.  ;
  19149.  ; Name:         SetPixelInC
  19150.  ;
  19151.  ; Function:     Set the value of a pixel in 720x348 16-color mode
  19152.  ;
  19153.  ; Caller:       Microsoft C:
  19154.  ;
  19155.  ;                       void SetPixel(x,y,n);
  19156.  ;
  19157.  
  19158.  ;                       int x,y;                /* pixel coordinates */
  19159.  ;
  19160.  ;                       int n;                  /* pixel value */
  19161.  ;
  19162.  
  19163.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  19164.  ARGy            EQU     word ptr [bp+6]
  19165.  ARGn            EQU     byte ptr [bp+8]
  19166.  
  19167.  DefaultRWColor  EQU     0Fh             ; default value for R/W Color
  19168.                                          ;  Register
  19169.  
  19170.  
  19171.  DGROUP          GROUP   _DATA
  19172.  
  19173.  _TEXT           SEGMENT byte public 'CODE'
  19174.                  ASSUME  cs:_TEXT,ds:DGROUP
  19175.  
  19176.                  EXTRN   PixelAddrHGC:near
  19177.  
  19178.                  PUBLIC  _SetPixelInC
  19179.  _SetPixelInC    PROC    near
  19180.  
  19181.                  push    bp              ; preserve caller registers
  19182.                  mov     bp,sp
  19183.  
  19184.                  mov     ax,ARGy         ; AX := y
  19185.                  mov     bx,ARGx         ; BX := x
  19186.                  call    PixelAddrHGC    ; AH := bit mask
  19187.                                          ; ES:BX -> buffer
  19188.                                          ; CL := # bits to shift left
  19189.  
  19190.                  shl     ah,cl           ; AH := bit mask in proper position
  19191.  
  19192.                  mov     dx,3B4h         ; DX := CRTC port
  19193.  
  19194.                  jmp     word ptr SetPixelOpInC  ; jump to Replace, AND,
  19195.                                                  ;  OR or XOR routine
  19196.  
  19197.  
  19198.  ReplacePixelInC:                        ; routine to Replace pixel value
  19199.  
  19200.                  mov     ch,ah           ; CH := bit mask for pixel
  19201.                  mov     ax,1F19h        ; AH bit 6 := 0 (Mask Polarity)
  19202.                                          ; AH bits 5-4 := 1 (Write Mode)
  19203.                                          ; AH bits 3-0 := "don't care" bits
  19204.                                          ; AL := R/W Control Register number
  19205.                  out     dx,ax           ; set R/W Control Register
  19206.  
  19207.                  inc     ax              ; AL := 1Ah (R/W Color Reg number)
  19208.                  mov     ah,ARGn         ; AH := foreground value
  19209.                  out     dx,ax           ; set R/W color register
  19210.  
  19211.                  and     es:[bx],ch      ; update bit planes
  19212.                  jmp     short L01
  19213.  
  19214.  ANDPixelInC:                            ; routine to AND pixel value
  19215.  
  19216.                  mov     ch,ah           ; CH := bit mask for pixel
  19217.                  mov     ax,1F19h        ; AH bit 6 := 0 (Mask Polarity)
  19218.                                          ; AH bits 5-4 := 1 (Write Mode)
  19219.                                          ; AH bits 3-0 := "don't care" bits
  19220.                                          ; AL := R/W Control Register number
  19221.                  out     dx,ax           ; set R/W Control Register
  19222.  
  19223.                  dec     ax              ; AL := 18h (Plane Mask Register
  19224.                                          ;  number)
  19225.                  mov     ah,ARGn         ; AH := pixel value
  19226.                  mov     cl,4
  19227.                  shl     ah,cl           ; AH bits 7-4 := writeable plane
  19228.                                          ;  mask
  19229.                  or      ah,0Fh          ; AH bits 3-0 := visible plane mask
  19230.                  out     dx,ax           ; set Plane Mask Register
  19231.  
  19232.                  mov     ax,001Ah        ; AH := 0 (foreground value)
  19233.                                          ; AL := 1Ah (R/W Color reg)
  19234.                  out     dx,ax           ; set R/W Color Register
  19235.  
  19236.                  and     es:[bx],ch      ; update bit planes
  19237.                  jmp     short L01
  19238.  
  19239.                                          ; routine to OR pixel value
  19240.  ORPixelInC:
  19241.                  mov     ch,ah           ; CH := bit mask for pixel
  19242.                  mov     ax,1F19h        ; AH bit 6 := 0 (Mask Polarity)
  19243.                                          ; AH bits 5-4 := 1 (Write Mode)
  19244.                                          ; AH bits 3-0 := "don't care" bits
  19245.                                          ; AL := R/W Control Register number
  19246.                  out     dx,ax           ; set R/W Control Register
  19247.  
  19248.                  dec     ax              ; AL := 18h (Plane Mask Register
  19249.                                          ;  number)
  19250.                  mov     ah,ARGn         ; AH := pixel value
  19251.                  not     ah              ; AH := complement of pixel value
  19252.                  mov     cl,4
  19253.                  shl     ah,cl           ; AH bits 7-4 := writeable plane
  19254.                                          ;  mask
  19255.                  or      ah,0Fh          ; AH bits 3-0 := visible plane mask
  19256.                  out     dx,ax           ; set Plane Mask Register
  19257.  
  19258.                  mov     ax,0F1Ah        ; AH := 0 (foreground value)
  19259.                                          ; AL := 1Ah (R/W Color reg)
  19260.                  out     dx,ax           ; set R/W Color Register
  19261.  
  19262.                  and     es:[bx],ch      ; update bit planes
  19263.                  jmp     short L01
  19264.  
  19265.  XORPixelInC:                            ; routine to XOR pixel value
  19266.                  mov     ch,ah           ; CH := bit mask for pixel
  19267.                  mov     ax,3F19h        ; AH bit 6 := 0 (Mask Polarity)
  19268.                                          ; AH bits 5-4 := 3 (Write Mode)
  19269.                                          ; AH bits 3-0 := "don't care" bits
  19270.                                          ; AL := R/W Control Register number
  19271.                  out     dx,ax           ; set R/W Control Register
  19272.  
  19273.                  dec     ax              ; AL := 18h (Plane Mask Register
  19274.                                          ;  number)
  19275.                  mov     ah,ARGn         ; AH := pixel value
  19276.                  not     ah              ; AH := complement of pixel value
  19277.                  mov     cl,4
  19278.                  shl     ah,cl           ; AH bits 7-4 := writeable plane
  19279.                                          ;  mask
  19280.                  or      ah,0Fh          ; AH bits 3-0 := visible plane mask
  19281.                  out     dx,ax           ; set Plane Mask Register
  19282.  
  19283.                  xor     es:[bx],ch      ; update bit planes
  19284.                  jmp     short L01
  19285.  
  19286.  L01:            mov     ax,0F18h
  19287.                  out     dx,ax           ; restore default Plane Mask value
  19288.  
  19289.                  mov     ax,4019h        ; restore default R/W Control value
  19290.                  out     dx,ax
  19291.  
  19292.                  inc     ax              ; restore default R/W Color value
  19293.                  mov     ah,DefaultRWColor
  19294.                  out     dx,ax
  19295.  
  19296.                  mov     sp,bp           ; restore caller registers and
  19297.                                          ;  return
  19298.                  pop     bp
  19299.                  ret
  19300.  
  19301.  _SetPixelInC    ENDP
  19302.  
  19303.  _TEXT           ENDS
  19304.  
  19305.  
  19306.  _DATA           SEGMENT word public 'DATA'
  19307.  
  19308.  SetPixelOpInC   DW      ReplacePixelInc ; contains addr of pixel operation
  19309.  
  19310.  _DATA           ENDS
  19311.  
  19312.                  END
  19313.  
  19314.  
  19315.  
  19316.  ───────────────────────────────────────────────────────────────────────────
  19317.  Listing 5-16.  Setting a pixel value in MCGA or VGA 640-by-480
  19318.                 2-color mode.
  19319.  ───────────────────────────────────────────────────────────────────────────
  19320.  
  19321.                  TITLE   'Listing 5-16'
  19322.                  NAME    SetPixel11
  19323.                  PAGE    55,132
  19324.  
  19325.  ;
  19326.  ; Name:         SetPixel11
  19327.  ;
  19328.  ; Function:     Set the value of a pixel in 640x480 2-color mode (MCGA or
  19329.  ;                VGA)
  19330.  ;
  19331.  ; Caller:       Microsoft C:
  19332.  ;
  19333.  ;                       void SetPixel(x,y,n);
  19334.  ;
  19335.  ;                       int x,y;                /* pixel coordinates */
  19336.  ;
  19337.  ;                       int n;                  /* pixel value */
  19338.  ;
  19339.  
  19340.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  19341.  ARGy            EQU     word ptr [bp+6]
  19342.  ARGn            EQU     byte ptr [bp+8]
  19343.  
  19344.  
  19345.  DGROUP          GROUP   _DATA
  19346.  
  19347.  _TEXT           SEGMENT byte public 'CODE'
  19348.                  ASSUME  cs:_TEXT,ds:DGROUP
  19349.  
  19350.                  EXTRN   PixelAddr10:near
  19351.  
  19352.                  PUBLIC  _SetPixel11
  19353.  _SetPixel11     PROC    near
  19354.  
  19355.                  push    bp              ; preserve caller registers
  19356.                  mov     bp,sp
  19357.  
  19358.                  mov     ax,ARGy         ; AX := y
  19359.                  mov     bx,ARGx         ; BX := x
  19360.                  call    PixelAddr10     ; AH := bit mask
  19361.                                          ; ES:BX -> buffer
  19362.                                          ; CL := # bits to shift left
  19363.  
  19364.                  mov     al,ARGn         ; AL := unshifted pixel value
  19365.                  shl     ax,cl           ; AH := bit mask in proper position
  19366.                                          ; AL := pixel value in proper
  19367.                                          ;  position
  19368.  
  19369.                  jmp     word ptr SetPixelOp11   ; jump to Replace, AND,
  19370.                                                  ;  OR or XOR routine
  19371.  
  19372.  
  19373.                                          ; routine to Replace pixel value
  19374.  
  19375.  ReplacePixel11: not     ah              ; AH := inverse bit mask
  19376.                  and     es:[bx],ah      ; zero the pixel value
  19377.                  or      es:[bx],al      ; set the pixel value
  19378.                  jmp     short L02
  19379.  
  19380.  
  19381.                                          ; routine to AND pixel value
  19382.  ANDPixel11:     test    al,al
  19383.                  jnz     L02             ; do nothing if pixel value = 1
  19384.  
  19385.  L01:            not     ah              ; AH := inverse of bit mask
  19386.                  and     es:[bx],ah      ; set bit in video buffer to 0
  19387.                  jmp     short L02
  19388.  
  19389.  
  19390.                                          ; routine to OR pixel value
  19391.  ORPixel11:      test    al,al
  19392.                  jz      L02             ; do nothing if pixel value = 0
  19393.  
  19394.                  or      es:[bx],al      ; set bit in video buffer
  19395.                  jmp     short L02
  19396.  
  19397.  
  19398.                                          ; routine to XOR pixel value
  19399.  XORPixel11:     test    al,al
  19400.                  jz      L02             ; do nothing if pixel value = 0
  19401.  
  19402.                  xor     es:[bx],al      ; XOR bit in video buffer
  19403.  
  19404.  
  19405.  L02:            mov     sp,bp           ; restore caller registers and
  19406.                                          ;  return
  19407.                  pop     bp
  19408.                  ret
  19409.  
  19410.  _SetPixel11     ENDP
  19411.  
  19412.  _TEXT           ENDS
  19413.  
  19414.  
  19415.  _DATA           SEGMENT word public 'DATA'
  19416.  
  19417.  SetPixelOp11    DW      ReplacePixel11  ; contains addr of pixel operation
  19418.  
  19419.  _DATA           ENDS
  19420.  
  19421.                  END
  19422.  
  19423.  
  19424.  
  19425.  ───────────────────────────────────────────────────────────────────────────
  19426.  Listing 5-17.  Setting a pixel value in MCGA or VGA 320-by-200
  19427.                 256-color mode.
  19428.  ───────────────────────────────────────────────────────────────────────────
  19429.  
  19430.                  TITLE   'Listing 5-17'
  19431.                  NAME    SetPixel13
  19432.                  PAGE    55,132
  19433.  
  19434.  ;
  19435.  ; Name:         SetPixel13
  19436.  ;
  19437.  ; Function:     Set the value of a pixel in 320x200 256-color mode (MCGA or
  19438.  ;                VGA)
  19439.  ;
  19440.  ; Caller:       Microsoft C:
  19441.  ;
  19442.  ;                       void SetPixel(x,y,n);
  19443.  ;
  19444.  ;                       int x,y;                /* pixel coordinates */
  19445.  ;
  19446.  ;                       int n;                  /* pixel value */
  19447.  ;
  19448.  
  19449.  ARGx            EQU     word ptr [bp+4] ; stack frame addressing
  19450.  ARGy            EQU     word ptr [bp+6]
  19451.  ARGn            EQU     byte ptr [bp+8]
  19452.  
  19453.  DGROUP          GROUP   _DATA
  19454.  
  19455.  _TEXT           SEGMENT byte public 'CODE'
  19456.                  ASSUME  cs:_TEXT,ds:DGROUP
  19457.  
  19458.                  EXTRN   PixelAddr13:near
  19459.  
  19460.                  PUBLIC  _SetPixel13
  19461.  _SetPixel13     PROC    near
  19462.  
  19463.                  push    bp              ; preserve caller registers
  19464.                  mov     bp,sp
  19465.  
  19466.                  mov     ax,ARGy         ; AX := y
  19467.                  mov     bx,ARGx         ; BX := x
  19468.                  call    PixelAddr13     ; ES:BX -> buffer
  19469.  
  19470.                  mov     al,ARGn         ; AL := pixel value
  19471.  
  19472.                  jmp     word ptr SetPixelOp13   ; jump to Replace, AND,
  19473.                                                  ;  OR or XOR routine
  19474.  
  19475.  
  19476.  ReplacePixel13: mov     es:[bx],al
  19477.                  jmp     short L01
  19478.  
  19479.  ANDPixel13:     and     es:[bx],al
  19480.                  jmp     short L01
  19481.  
  19482.  ORPixel13:      or      es:[bx],al
  19483.                  jmp     short L01
  19484.  
  19485.  XORPixel13:     xor     es:[bx],al
  19486.  
  19487.  
  19488.  L01:            mov     sp,bp           ; restore caller registers and
  19489.                                          ;  return
  19490.                  pop     bp
  19491.                  ret
  19492.  
  19493.  _SetPixel13     ENDP
  19494.  
  19495.  _TEXT           ENDS
  19496.  
  19497.  
  19498.  _DATA           SEGMENT word public 'DATA'
  19499.  
  19500.  SetPixelOp13    DW      ReplacePixel13
  19501.  
  19502.  _DATA           ENDS
  19503.  
  19504.                  END
  19505.  
  19506.  
  19507.  
  19508.  ───────────────────────────────────────────────────────────────────────────
  19509.  Listing 5-18.  Simple CGA graphics buffer fill.
  19510.  ───────────────────────────────────────────────────────────────────────────
  19511.  
  19512.          mov     di,0B800h
  19513.          mov     es,di
  19514.          xor     di,di           ; ES:DI -> start of video buffer
  19515.          mov     al,11110000b    ; AL := pixel pattern
  19516.          mov     ah,al           ; AX := replicated pixel pattern
  19517.          mov     cx,2000h        ; CX := number of words in video buffer
  19518.          rep     stosw           ; fill buffer with pixel pattern
  19519.  
  19520.  ; this may also be accomplished using the video BIOS
  19521.  
  19522.          mov     ah,0Fh          ; AH := 0Fh (INT 10H function number)
  19523.          int     10h             ; get current video state; AH = number of
  19524.                                  ;  character columns
  19525.          mov     dl,ah           ; DL := number of character columns
  19526.  
  19527.          mov     ax,600h         ; AH := 6 (INT 10H function number)
  19528.                                  ; AL := 0 (number of rows to scroll)
  19529.          mov     bh,11110000b    ; BH := pixel pattern
  19530.          mov     cx,0            ; CH := 0 (upper left character column)
  19531.                                  ; CL := 0 (upper left character row)
  19532.          mov     dh,18h          ; DH := 18h (lower right character row)
  19533.          dec     dl              ; DL := lower right character column
  19534.          int     10h
  19535.  
  19536.  
  19537.  
  19538.  ───────────────────────────────────────────────────────────────────────────
  19539.  Listing 5-19.  CGA graphics buffer fill using two-way interleave.
  19540.  ───────────────────────────────────────────────────────────────────────────
  19541.  
  19542.          mov     di,0B800h
  19543.          mov     es,di
  19544.          xor     di,di           ; ES:DI -> start of video buffer
  19545.  
  19546.          mov     al,11001100b    ; AL pixel pattern
  19547.          mov     ah,al           ; AX := replicated pixel pattern
  19548.  
  19549.          mov     bx,100          ; BX := number of pairs of rows
  19550.  
  19551.  L01:    mov     cx,40           ; CX := number of words in each row
  19552.          rep     stosw           ; fill even row
  19553.  
  19554.          add     di,2000h-80     ; ES:DI -> odd row
  19555.          mov     cx,40
  19556.          rep     stosw           ; fill odd row
  19557.  
  19558.          sub     di,2000h        ; ES:DI -> next even row
  19559.          dec     bx
  19560.          jnz     L01
  19561.  
  19562.  
  19563.  
  19564.  ───────────────────────────────────────────────────────────────────────────
  19565.  Listing 5-20.  CGA graphics buffer fill with different pixel pattern
  19566.                 in odd and even rows.
  19567.  ───────────────────────────────────────────────────────────────────────────
  19568.  
  19569.          mov     di,0B800h
  19570.          mov     es,di
  19571.          xor     di,di           ; ES:DI -> start of pixel row 0
  19572.  
  19573.          mov     al,10101010b    ; AL := pixel pattern for even rows
  19574.          mov     ah,al           ; AX := replicated pixel pattern
  19575.          mov     cx,1000h        ; CX := number of words in video buffer
  19576.          rep     stosw           ; fill even pixel rows
  19577.  
  19578.          mov     di,2000h        ; ES:DI -> start of pixel row 1
  19579.          mov     al,01010101b    ; AL := pixel pattern for odd rows
  19580.          mov     ah,al
  19581.          mov     cx,1000h
  19582.          rep     stosw           ; fill odd pixel rows
  19583.  
  19584.  
  19585.  
  19586.  ───────────────────────────────────────────────────────────────────────────
  19587.  Listing 5-21.  HGC graphics buffer fill using four-way interleave.
  19588.  ───────────────────────────────────────────────────────────────────────────
  19589.  
  19590.          mov     es,BufferSeg    ; ES := 0B000h for first video page
  19591.                                  ;    or 0B800h for second video page
  19592.          xor     di,di           ; ES:DI -> first byte to fill
  19593.  
  19594.          mov     al,10101010b    ; AL pixel pattern
  19595.          mov     ah,al           ; AX := replicated pixel pattern
  19596.  
  19597.  L01:    mov     cx,1000h        ; CX := number of words in
  19598.                                  ;  each 8 KB buffer interleave
  19599.          rep     stosw           ; fill interleave; increment DI by 2000h
  19600.  
  19601.          ror     ax,1            ; shift pixel pattern between rows
  19602.          or      di,di
  19603.          jns     L01             ; jump if DI < 8000h
  19604.  
  19605.  
  19606.  
  19607.  ───────────────────────────────────────────────────────────────────────────
  19608.  Listing 5-22.  MCGA and VGA 640-by-480 2-color graphics buffer fill.
  19609.  ───────────────────────────────────────────────────────────────────────────
  19610.  
  19611.          mov     di,0A000h
  19612.          mov     es,di
  19613.          xor     di,di           ; ES:DI -> start of video buffer
  19614.          mov     al,01010101b    ; AL := pixel pattern
  19615.          mov     ah,al           ; AX := replicated pixel pattern
  19616.          mov     cx,480*40       ; CX := (pixel rows) * (words per row)
  19617.          rep     stosw           ; fill buffer with pixel pattern
  19618.  
  19619.  
  19620.  ; this may also be accomplished using the video BIOS
  19621.  
  19622.          mov     ax,1130h        ; AH := 11h (INT 10H function number)
  19623.                                  ; AL := 30h (character generator info)
  19624.          int     10h             ; get info; DL = number of
  19625.                                  ;  character rows - 1
  19626.  
  19627.          mov     ax,600h         ; AH := 6 (INT 10H function number)
  19628.                                  ; AL := 0 (number of rows to scroll)
  19629.          mov     bh,01010101b    ; BH := pixel pattern
  19630.          mov     cx,0            ; CH := 0 (upper left character column)
  19631.                                  ; CL := 0 (upper left character row)
  19632.          mov     dh,dl           ; DH := lower right character row
  19633.          mov     dl,4Fh          ; DL := 4Fh (lower right character column)
  19634.          int     10h
  19635.  
  19636.  
  19637.  
  19638.  ───────────────────────────────────────────────────────────────────────────
  19639.  Listing 5-23.  MCGA and VGA 320-by-200 256-color graphics buffer fill.
  19640.                 This routine fills alternate pixel rows separately
  19641.                 to allow dithered pixel patterns.
  19642.  ───────────────────────────────────────────────────────────────────────────
  19643.  
  19644.          mov     di,0A000h
  19645.          mov     es,di
  19646.          xor     di,di           ; ES:DI -> start of video buffer
  19647.          mov     ah,PixelValue1  ; AX := 2-pixel pattern
  19648.          mov     al,PixelValue2
  19649.          mov     bx,100          ; BX := number of pairs of rows
  19650.  
  19651.  L01:    mov     cx,160          ; CX := number of words per row
  19652.          rep     stosw           ; fill even-numbered row
  19653.          xchg    ah,al           ; exchange pixels in pattern
  19654.  
  19655.          mov     cx,160
  19656.          rep     stosw           ; fill odd-numbered row
  19657.          xchg    ah,al           ; exchange pixels in pattern
  19658.  
  19659.          dec     bx
  19660.          jnz     L01
  19661.  
  19662.  
  19663.  
  19664.  ───────────────────────────────────────────────────────────────────────────
  19665.  Listing 5-24.  Solid buffer fill for EGA and VGA native graphics
  19666.                 modes. The code assumes that the Graphics Controller
  19667.                 is already in write mode 0 (the BIOS default).
  19668.  ───────────────────────────────────────────────────────────────────────────
  19669.  
  19670.          mov     di,0A000h
  19671.          mov     es,di
  19672.          xor     di,di           ; ES:DI -> start of video buffer
  19673.  
  19674.          mov     dx,3CEh         ; DX := Graphics Controller I/O port
  19675.  
  19676.          mov     ah,PixelValue   ; AH := pixel value for fill
  19677.          mov     al,0            ; AL := 0 (Set/Reset register number)
  19678.          out     dx,ax           ; load Set/Reset register
  19679.  
  19680.          mov     ax,0F01         ; AH := 1111b (mask for Enable Set/Reset)
  19681.                                  ; AL := 1 (Enable Set/Reset reg number)
  19682.          out     dx,ax           ; load Enable Set/Reset register
  19683.  
  19684.          mov     cx,PixelRows*40 ; CX := (pixel rows) * (words per row)
  19685.          rep     stosw           ; fill the buffer
  19686.  
  19687.          mov     ax,0001         ; AH := 0 (default Enable Set/Reset value)
  19688.                                  ; AL := 1 (Enable Set/Reset reg number)
  19689.          out     dx,ax           ; restore default Enable Set/Reset
  19690.  
  19691.  
  19692.  
  19693.  ───────────────────────────────────────────────────────────────────────────
  19694.  Listing 5-25.  Patterned buffer fill for EGA and VGA native graphics
  19695.                 modes. The code assumes that the desired pixel
  19696.                 pattern is already stored in the first eight pixels
  19697.                 of the first two rows of the video buffer (that is, at
  19698.                 A000:0000 and A000:0050).
  19699.  ───────────────────────────────────────────────────────────────────────────
  19700.  
  19701.          mov     di,0A000h
  19702.          mov     es,di
  19703.          xor     di,di           ; ES:DI -> start of video buffer
  19704.  
  19705.          mov     dx,3CEh         ; DX := Graphics Controller I/O port
  19706.  
  19707.          mov     ax,105h         ; AH bits 0-1 := 01b (write mode 1)
  19708.                                  ; AL := 5 (Graphics Mode register)
  19709.          out     dx,ax           ; establish write mode 1
  19710.  
  19711.          mov     ax,PixelRows/2  ; AX := number of pairs of rows to fill
  19712.  
  19713.  L01:    mov     cl,es:[0]       ; latch pixel pattern for even rows
  19714.          mov     cx,40           ; CX := words per row of pixels
  19715.          rep     stosw           ; copy latches across even-numbered row
  19716.  
  19717.          mov     cl,es:[50h]     ; latch pixel pattern for odd rows
  19718.          mov     cx,40
  19719.          rep     stosw           ; fill odd-numbered row
  19720.  
  19721.          dec     ax
  19722.          jnz     L01             ; loop down the buffer
  19723.  
  19724.          mov     ax,0005
  19725.          out     dx,ax           ; restore write mode 0 (default)
  19726.  
  19727.  
  19728.  
  19729.  ───────────────────────────────────────────────────────────────────────────
  19730.  Listing 5-26.  Solid buffer fill for Hercules InColor graphics mode.
  19731.  ───────────────────────────────────────────────────────────────────────────
  19732.  
  19733.          mov     es,BufferSeg    ; ES := 0B000h for first video page
  19734.                                  ;    or 0B800h for second video page
  19735.          xor     di,di           ; ES:DI -> first byte to fill
  19736.  
  19737.          mov     dx,3B4h         ; DX := control register I/O port
  19738.  
  19739.          mov     ah,PixelValue   ; AH := pixel value for fill
  19740.          mov     al,1Ah          ; AL := 1AH (Read/Write Color register
  19741.                                  ;  number)
  19742.          out     dx,ax           ; load Read/Write Color register
  19743.  
  19744.          mov     ax,4019h        ; AH bits 5-6 := 00b (write mode 0)
  19745.                                  ; AL := 19H (Read/Write Control register)
  19746.          out     dx,ax           ; load Read/Write Control reg
  19747.  
  19748.          mov     ax,0FFFFh       ; AX := pixel bit mask
  19749.          mov     cx,4000h        ; CX := number of words in buffer (32K / 2)
  19750.          rep     stosw           ; fill the buffer
  19751.  
  19752.  
  19753.  
  19754.  ───────────────────────────────────────────────────────────────────────────
  19755.  Listing 5-27.  Patterned buffer fill for InColor Card. The code assumes
  19756.                 that the desired pixel pattern is already stored in the
  19757.                 first eight pixels of the first two rows of the video
  19758.                 buffer (that is, at offsets 0 and 2000H in BufferSeg).
  19759.  ───────────────────────────────────────────────────────────────────────────
  19760.  
  19761.          mov     es,BufferSeg    ; ES := 0B000h for first video page
  19762.                                  ;    or 0B800h for second video page
  19763.          xor     di,di           ; ES:DI -> first byte to fill
  19764.  
  19765.          mov     dx,3B4h         ; DX := control register I/O port
  19766.  
  19767.          mov     ax,6019h        ; AH bits 5-6 := 10b (write mode 2)
  19768.                                  ; AL := 19H (Read/Write Control register)
  19769.          out     dx,ax           ; load Read/Write Control reg
  19770.  
  19771.          mov     ax,0FFFFh       ; AX := pixel bit mask
  19772.  
  19773.  L01:    mov     cl,es:[0]       ; latch pixel pattern for even rows
  19774.          mov     cx,1000h        ; CX := number of words in
  19775.                                  ;  each 8 KB buffer interleave
  19776.          rep     stosw           ; fill even-numbered interleave;
  19777.                                  ;  increment DI by 2000h
  19778.  
  19779.          mov     cl,es:[2000h]   ; latch pixel pattern for odd rows
  19780.          mov     cx,1000h
  19781.          rep     stosw           ; fill odd-numbered interleave
  19782.  
  19783.          or      di,di
  19784.          jns     L01             ; loop while DI < 8000H
  19785.  
  19786.          mov     ax,4019h        ; restore default value of
  19787.          out     dx,ax           ;  Read/Write Control register
  19788.  
  19789.  
  19790.  
  19791.  ───────────────────────────────────────────────────────────────────────────
  19792.  Listing 6-1.  Drawing a line using the equation of the line.
  19793.  ───────────────────────────────────────────────────────────────────────────
  19794.  
  19795.  /* Listing 6-1 */
  19796.  
  19797.  Line( x1, y1, x2, y2, n )
  19798.  int     x1,y1;                  /* endpoint */
  19799.  int     x2,y2;                  /* endpoint */
  19800.  int     n;                      /* pixel value */
  19801.  {
  19802.          int     x,y;
  19803.          float   m;              /* slope */
  19804.          float   b;              /* y-intercept */
  19805.  
  19806.  
  19807.          if (x2 == x1)                           /* vertical line */
  19808.          {
  19809.            if (y1 > y2)
  19810.             Swap( &y1, &y2 );                    /* force y1 < y2 */
  19811.  
  19812.            for (y=y1; y<=y2; y++)                /* draw from y1 to y2 */
  19813.             SetPixel( x1, y, n );
  19814.  
  19815.            return;
  19816.          }
  19817.  
  19818.          if (x1 > x2)                            /* force x1 < x2 */
  19819.          {
  19820.            Swap( &x1, &x2 );
  19821.            Swap( &y1, &y2 );
  19822.          }
  19823.  
  19824.          m = (float)(y2-y1) / (float)(x2-x1);    /* compute m and b */
  19825.          b = y1 - (m*x1);
  19826.  
  19827.          for (x=x1; x<=x2; x++)                  /* draw from x1 to x2 */
  19828.          {
  19829.            y = m*x + b;
  19830.            SetPixel( x, y, n );
  19831.          }
  19832.  }
  19833.  
  19834.  Swap( a, b )                                    /* exchange values of a */
  19835.                                                   /* and b */
  19836.  int     *a,*b;
  19837.  {
  19838.          int     t;
  19839.  
  19840.          t = *a;
  19841.          *a = *b;
  19842.          *b = t;
  19843.  }
  19844.  
  19845.  
  19846.  
  19847.  ───────────────────────────────────────────────────────────────────────────
  19848.  Listing 6-2.  A high-level implementation of Bresenham's algorithm.
  19849.  ───────────────────────────────────────────────────────────────────────────
  19850.  
  19851.  /* Listing 6-2 */
  19852.  
  19853.  Line( x1, y1, x2, y2, n )       /* for lines with slope between -1 and 1 */
  19854.  int     x1,y1;
  19855.  int     x2,y2;                  /* endpoints */
  19856.  int     n;                      /* pixel value */
  19857.  {
  19858.          int     d,dx,dy;
  19859.          int     Aincr,Bincr,yincr;
  19860.          int     x,y;
  19861.  
  19862.          if (x1 > x2)                        /* force x1 < x2 */
  19863.          {
  19864.            Swap( &x1, &x2 );
  19865.            Swap( &y1, &y2 );
  19866.          }
  19867.  
  19868.          if (y2 > y1)                        /* determine increment for y */
  19869.            yincr = 1;
  19870.          else
  19871.            yincr = -1;
  19872.  
  19873.          dx = x2 - x1;                       /* initialize constants */
  19874.          dy = abs( y2-y1 );
  19875.          d = 2 * dy - dx;
  19876.  
  19877.          Aincr = 2 * (dy - dx);
  19878.          Bincr = 2 * dy;
  19879.  
  19880.          x = x1;                             /* initial x and y */
  19881.          y = y1;
  19882.  
  19883.          SetPixel( x, y, n );                /* set pixel at (x1,y1) */
  19884.  
  19885.          for (x=x1+1; x<=x2; x++)            /* do from x1+1 to x2 */
  19886.          {
  19887.            if (d >= 0)
  19888.            {
  19889.              y += yincr;                     /* set pixel A */
  19890.              d += Aincr;
  19891.            }
  19892.            else                              /* set pixel B */
  19893.              d += Bincr;
  19894.  
  19895.            SetPixel( x, y, n );
  19896.          }
  19897.  }
  19898.  
  19899.  
  19900.  Swap( pa, pb )
  19901.  int     *pa,*pb;
  19902.  {
  19903.          int     t;
  19904.  
  19905.          t = *pa;
  19906.          *pa = *pb;
  19907.          *pb = t;
  19908.  }
  19909.  
  19910.  
  19911.  
  19912.  ───────────────────────────────────────────────────────────────────────────
  19913.  Listing 6-3.  A routine that draws horizontal lines.
  19914.  ───────────────────────────────────────────────────────────────────────────
  19915.  
  19916.  /* Listing 6-3 */
  19917.  
  19918.  FilledRectangle( x1, y1, x2, y2, n )
  19919.  int     x1,y1;                  /* upper left corner */
  19920.  int     x2,y2;                  /* lower right corner */
  19921.  int     n;                      /* pixel value */
  19922.  {
  19923.          int     y;
  19924.  
  19925.          for (y=y1; y<=y2; y++)          /* draw rectangle as a set of */
  19926.            Line( x1, y, x2, y, n );      /*  adjacent horizontal lines */
  19927.  }
  19928.  
  19929.  
  19930.  
  19931.  ───────────────────────────────────────────────────────────────────────────
  19932.  Listing 6-4.  A line-drawing routine for CGA 640-by-200 2-color mode.
  19933.  ───────────────────────────────────────────────────────────────────────────
  19934.  
  19935.                  TITLE   'Listing 6-4'
  19936.                  NAME    Line06
  19937.                  PAGE    55,132
  19938.  
  19939.  ;
  19940.  ; Name:         Line06
  19941.  ;
  19942.  ; Function:     Draw a line in 640x200 2-color mode
  19943.  ;
  19944.  ; Caller:       Microsoft C:
  19945.  
  19946.  ;
  19947.  ;                       void Line06(x1,y1,x2,y2,n);
  19948.  ;
  19949.  ;                       int x1,y1,x2,y2;        /* pixel coordinates */
  19950.  ;
  19951.  ;                       int n;                  /* pixel value */
  19952.  ;
  19953.  
  19954.  ARGx1           EQU     word ptr [bp+4] ; stack frame addressing
  19955.  ARGy1           EQU     word ptr [bp+6]
  19956.  ARGx2           EQU     word ptr [bp+8]
  19957.  ARGy2           EQU     word ptr [bp+10]
  19958.  ARGn            EQU     byte ptr [bp+12]
  19959.  VARleafincr     EQU     word ptr [bp-6]
  19960.  VARincr1        EQU     word ptr [bp-8]
  19961.  VARincr2        EQU     word ptr [bp-10]
  19962.  VARroutine      EQU     word ptr [bp-12]
  19963.  
  19964.  ByteOffsetShift EQU     3               ; used to convert pixels to byte
  19965.                                          ;  offset
  19966.  
  19967.  DGROUP          GROUP   _DATA
  19968.  
  19969.  _TEXT           SEGMENT byte public 'CODE'
  19970.                  ASSUME  cs:_TEXT,ds:DGROUP
  19971.  
  19972.                  EXTRN   PixelAddr06:near
  19973.  
  19974.                  PUBLIC  _Line06
  19975.  _Line06         PROC    near
  19976.  
  19977.                  push    bp              ; preserve caller registers
  19978.                  mov     bp,sp
  19979.                  sub     sp,8            ; stack space for local variables
  19980.                  push    si
  19981.                  push    di
  19982.  
  19983.                  mov     si,2000h        ; increment for video buffer
  19984.                                          ;  interleave
  19985.                  mov     di,80-2000h     ; increment from last to first
  19986.                                          ;  interleave
  19987.  
  19988.                  mov     cx,ARGx2
  19989.                  sub     cx,ARGx1        ; CX := x2 - x1
  19990.                  jz      VertLine06      ; jump if vertical line
  19991.  
  19992.  ; force x1 < x2
  19993.  
  19994.                  jns     L01             ; jump if x2 > x1
  19995.  
  19996.                  neg     cx              ; CX := x1 - x2
  19997.  
  19998.                  mov     bx,ARGx2        ; exchange x1 and x2
  19999.                  xchg    bx,ARGx1
  20000.                  mov     ARGx2,bx
  20001.  
  20002.                  mov     bx,ARGy2        ; exchange y1 and y2
  20003.                  xchg    bx,ARGy1
  20004.                  mov     ARGy2,bx
  20005.  
  20006.  ; calculate dy = ABS(y2-y1)
  20007.  
  20008.  L01:            mov     bx,ARGy2
  20009.                  sub     bx,ARGy1        ; BX := y2 - y1
  20010.                  jnz     L02
  20011.  
  20012.                  jmp     HorizLine06     ; jump if horizontal line
  20013.  
  20014.  L02:            jns     L03
  20015.  
  20016.                  neg     bx              ; BX := y1 - y2
  20017.                  neg     si              ; negate increments for buffer
  20018.                                          ;  interleave
  20019.                  neg     di
  20020.                  xchg    si,di           ; exchange increments
  20021.  
  20022.  ; select appropriate routine for slope of line
  20023.  
  20024.  L03:            mov     VARleafincr,di  ; save increment for buffer
  20025.                                          ;  interleave
  20026.  
  20027.                  mov     VARroutine,offset LoSlopeLine06
  20028.                  cmp     bx,cx
  20029.                  jle     L04             ; jump if dy <= dx (slope <= 1)
  20030.                  mov     VARroutine,offset HiSlopeLine06
  20031.                  xchg    bx,cx           ; exchange dy and dx
  20032.  
  20033.  ; calculate initial decision variable and increments
  20034.  
  20035.  L04:            shl     bx,1            ; BX := 2 * dy
  20036.                  mov     VARincr1,bx     ; incr1 := 2 * dy
  20037.                  sub     bx,cx
  20038.                  mov     di,bx           ; DI := d = 2 * dy - dx
  20039.                  sub     bx,cx
  20040.                  mov     VARincr2,bx     ; incr2 := 2 * (dy - dx)
  20041.  
  20042.  ; calculate first pixel address
  20043.  
  20044.                  push    cx              ; preserve this register
  20045.                  mov     ax,ARGy1        ; AX := y
  20046.                  mov     bx,ARGx1        ; BX := x
  20047.                  call    PixelAddr06     ; AH := bit mask
  20048.                                          ; ES:BX -> buffer
  20049.                                          ; CL := # bits to shift left
  20050.  
  20051.                  mov     al,ARGn         ; AL := unshifted pixel value
  20052.                  shl     ax,cl           ; AH := bit mask in proper position
  20053.                                          ; AL := pixel value in proper
  20054.                                          ;        position
  20055.  
  20056.                  mov     dx,ax           ; DH := bit mask
  20057.                                          ; DL := pixel value
  20058.                  not     dh              ; DH := inverse bit mask
  20059.  
  20060.                  pop     cx              ; restore this register
  20061.                  inc     cx              ; CX := # of pixels to draw
  20062.  
  20063.                  test    bx,2000h        ; set zero flag if BX in 1st
  20064.                                          ;  interleave
  20065.                  jz      L05
  20066.                  xchg    si,VARleafincr  ; exchange increment values if 1st
  20067.                                          ;  pixel lies in 1st interleave
  20068.  
  20069.  L05:            jmp     VARroutine      ; jump to appropriate routine for
  20070.                                          ;  slope
  20071.  
  20072.  
  20073.  ; routine for vertical lines
  20074.  
  20075.  VertLine06:     mov     ax,ARGy1        ; AX := y1
  20076.                  mov     bx,ARGy2        ; BX := y2
  20077.                  mov     cx,bx
  20078.                  sub     cx,ax           ; CX := dy
  20079.                  jge     L31             ; jump if dy >= 0
  20080.  
  20081.                  neg     cx              ; force dy >= 0
  20082.                  mov     ax,bx           ; AX := y2
  20083.  
  20084.  L31:            inc     cx              ; CX := # of pixels to draw
  20085.                  mov     bx,ARGx1        ; BX := x
  20086.                  push    cx              ; preserve this register
  20087.                  call    PixelAddr06     ; AH := bit mask
  20088.                                          ; ES:BX -> video buffer
  20089.                                          ; CL := # bits to shift left
  20090.                  mov     al,ARGn         ; AL := pixel value
  20091.                  shl     ax,cl           ; AH := bit mask in proper position
  20092.                                          ; AL := pixel value in proper
  20093.                                          ;        position
  20094.                  not     ah              ; AH := inverse bit mask
  20095.                  pop     cx              ; restore this register
  20096.  
  20097.                  test    bx,si           ; set zero flag if BX in 1st
  20098.                                          ;  interleave
  20099.                  jz      L32
  20100.  
  20101.                  xchg    si,di           ; exchange increment values if 1st
  20102.                                          ;  pixel lies in 1st interleave
  20103.  
  20104.  L32:            test    al,al
  20105.                  jz      L34             ; jump if pixel value = 0
  20106.  
  20107.  L33:            or      es:[bx],al      ; set pixel values in buffer
  20108.                  add     bx,si           ; increment to next portion of
  20109.                                          ;  interleave
  20110.                  xchg    si,di           ; toggle between increment values
  20111.                  loop    L33             ; loop down the line
  20112.                  jmp     short L35
  20113.  
  20114.  L34:            and     es:[bx],ah      ; reset pixel values in buffer
  20115.                  add     bx,si           ; increment to next portion of
  20116.                                          ;  interleave
  20117.                  xchg    si,di           ; toggle between increment values
  20118.                  loop    L34
  20119.  
  20120.  L35:            jmp     Lexit
  20121.  
  20122.  
  20123.  ; routine for horizontal lines (slope = 0)
  20124.  
  20125.  HorizLine06:    mov     ax,ARGy1
  20126.                  mov     bx,ARGx1
  20127.                  call    PixelAddr06     ; AH := bit mask
  20128.                                          ; ES:BX -> video buffer
  20129.                                          ; CL := # bits to shift left
  20130.                  mov     di,bx           ; ES:DI -> buffer
  20131.  
  20132.                  mov     dh,ah
  20133.                  not     dh              ; DH := unshifted bit mask for
  20134.                                          ;        leftmost byte
  20135.                  mov     dl,0FFh         ; DL := unshifted bit mask for
  20136.                                          ;        rightmost byte
  20137.  
  20138.                  shl     dh,cl           ; DH := reverse bit mask for first
  20139.                                          ;        byte
  20140.                  not     dh              ; DH := bit mask for first byte
  20141.  
  20142.                  mov     cx,ARGx2
  20143.                  and     cl,7
  20144.                  xor     cl,7            ; CL := number of bits to shift
  20145.                                          ;        left
  20146.                  shl     dl,cl           ; DL := bit mask for last byte
  20147.  
  20148.  ; determine byte offset of first and last pixel in the line
  20149.  
  20150.                  mov     ax,ARGx2        ; AX := x2
  20151.                  mov     bx,ARGx1        ; BX := x1
  20152.  
  20153.                  mov     cl,ByteOffsetShift     ; number of bits to shift to
  20154.                                                 ;  convert pixels to bytes
  20155.  
  20156.                  shr     ax,cl           ; AX := byte offset of x2
  20157.                  shr     bx,cl           ; BX := byte offset of x1
  20158.                  mov     cx,ax
  20159.                  sub     cx,bx           ; CX := (# bytes in line) - 1
  20160.  
  20161.  ; propagate pixel value throughout one byte
  20162.  
  20163.                  mov     bx,offset DGROUP:PropagatedPixel
  20164.                  mov     al,ARGn         ; AL := pixel value
  20165.                  xlat
  20166.  
  20167.  ; set pixels in leftmost byte of the line
  20168.  
  20169.                  or      dh,dh
  20170.                  js      L43             ; jump if byte-aligned (x1 is
  20171.                                          ;  leftmost pixel in byte)
  20172.                  or      cx,cx
  20173.                  jnz     L42             ; jump if more than one byte in the
  20174.                                          ;  line
  20175.  
  20176.                  and     dl,dh           ; bit mask for the line
  20177.                  jmp     short L44
  20178.  
  20179.  L42:            mov     ah,al
  20180.                  and     ah,dh           ; AH := masked pixel bits
  20181.                  not     dh              ; DH := reverse bit mask for 1st
  20182.                                          ;        byte
  20183.                  and     es:[di],dh      ; zero masked pixels in buffer
  20184.                  or      es:[di],ah      ; update masked pixels in buffer
  20185.                  inc     di
  20186.                  dec     cx
  20187.  
  20188.  ; use a fast 8086 machine instruction to draw the remainder of the line
  20189.  
  20190.  L43:            rep     stosb           ; update all pixels in the line
  20191.  
  20192.  ; set pixels in the rightmost byte of the line
  20193.  
  20194.  L44:            and     al,dl           ; AL := masked pixels for last byte
  20195.                  not     dl
  20196.                  and     es:[di],dl      ; zero masked pixels in buffer
  20197.                  or      es:[di],al      ; update masked pixels in buffer
  20198.  
  20199.                  jmp     Lexit
  20200.  
  20201.  
  20202.  ; routine for dy <= dx (slope <= 1)     ; ES:BX -> video buffer
  20203.                                          ; CX = # pixels to draw
  20204.                                          ; DH = inverse bit mask
  20205.                                          ; DL = pixel value in proper
  20206.                                          ;       position
  20207.                                          ; SI = buffer interleave increment
  20208.                                          ; DI = decision variable
  20209.  LoSlopeLine06:
  20210.  
  20211.  L10:            mov     ah,es:[bx]      ; AH := byte from video buffer
  20212.  
  20213.  L11:            and     ah,dh           ; zero pixel value at current bit
  20214.                                          ;  offset
  20215.                  or      ah,dl           ; set pixel value in byte
  20216.  
  20217.                  ror     dl,1            ; rotate pixel value
  20218.                  ror     dh,1            ; rotate bit mask
  20219.                  jnc     L14             ; jump if bit mask rotated to
  20220.                                          ;  leftmost pixel position
  20221.  
  20222.  ; bit mask not shifted out
  20223.  
  20224.                  or      di,di           ; test sign of d
  20225.                  jns     L12             ; jump if d >= 0
  20226.  
  20227.                  add     di,VARincr1     ; d := d + incr1
  20228.                  loop    L11
  20229.  
  20230.                  mov     es:[bx],ah      ; store remaining pixels in buffer
  20231.                  jmp     short Lexit
  20232.  
  20233.  L12:            add     di,VARincr2     ; d := d + incr2
  20234.                  mov     es:[bx],ah      ; update buffer
  20235.  
  20236.                  add     bx,si           ; increment y
  20237.                  xchg    si,VARleafincr  ; exchange interleave increment
  20238.                                          ;  values
  20239.                  loop    L10
  20240.                  jmp     short Lexit
  20241.  
  20242.  ; bit mask shifted out
  20243.  
  20244.  L14:            mov     es:[bx],ah      ; update buffer
  20245.                  inc     bx              ; BX := offset of next byte
  20246.  
  20247.                  or      di,di           ; test sign of d
  20248.                  jns     L15             ; jump if non-negative
  20249.  
  20250.                  add     di,VARincr1     ; d := d + incr1
  20251.                  loop    L10
  20252.                  jmp     short Lexit
  20253.  
  20254.  L15:            add     di,VARincr2     ; d := d + incr2
  20255.  
  20256.                  add     bx,si           ; increment y
  20257.                  xchg    si,VARleafincr
  20258.  
  20259.                  loop    L10
  20260.                  jmp     short Lexit
  20261.  
  20262.  
  20263.  ; routine for dy > dx (slope > 1)       ; ES:BX -> video buffer
  20264.                                          ; CX = # pixels to draw
  20265.                                          ; DH = inverse bit mask
  20266.                                          ; DL = pixel value in proper
  20267.                                          ;       position
  20268.                                          ; SI = buffer interleave increment
  20269.                                          ; DI = decision variable
  20270.  HiSlopeLine06:
  20271.  
  20272.  L21:            and     es:[bx],dh      ; zero pixel value in video buffer
  20273.                  or      es:[bx],dl      ; set pixel value in byte
  20274.  
  20275.                  add     bx,si           ; increment y
  20276.                  xchg    si,VARleafincr  ; exchange interleave increment
  20277.                                          ;  values
  20278.  
  20279.  L22:            or      di,di           ; test sign of d
  20280.                  jns     L23             ; jump if d >= 0
  20281.  
  20282.                  add     di,VARincr1     ; d := d + incr1
  20283.                  loop    L21
  20284.  
  20285.                  jmp     short Lexit
  20286.  
  20287.  
  20288.  L23:            add     di,VARincr2     ; d := d + incr2
  20289.  
  20290.                  ror     dl,1            ; rotate pixel value
  20291.                  ror     dh,1            ; rotate bit mask
  20292.                  cmc                     ; cf set if bit mask not rotated to
  20293.                                          ;  leftmost pixel position
  20294.  
  20295.                  adc     bx,0            ; BX := offset of next byte
  20296.  
  20297.                  loop    L21
  20298.  
  20299.  
  20300.  Lexit:          pop     di              ; restore registers and return
  20301.                  pop     si
  20302.                  mov     sp,bp
  20303.                  pop     bp
  20304.                  ret
  20305.  
  20306.  _Line06         ENDP
  20307.  
  20308.  _TEXT           ENDS
  20309.  
  20310.  
  20311.  _DATA           SEGMENT word public 'DATA'
  20312.  
  20313.  PropagatedPixel DB      00000000b       ; 0
  20314.                  DB      11111111b       ; 1
  20315.  
  20316.  _DATA           ENDS
  20317.  
  20318.                  END
  20319.  
  20320.  
  20321.  
  20322.  ───────────────────────────────────────────────────────────────────────────
  20323.  Listing 6-5.  A line-drawing routine for CGA 320-by-200 4-color mode.
  20324.  ───────────────────────────────────────────────────────────────────────────
  20325.  
  20326.                  TITLE   'Listing 6-5'
  20327.                  NAME    Line04
  20328.                  PAGE    55,132
  20329.  
  20330.  ;
  20331.  ; Name:         Line04
  20332.  ;
  20333.  ; Function:     Draw a line in 320x200 4-color mode
  20334.  ;
  20335.  ; Caller:       Microsoft C:
  20336.  ;
  20337.  ;                       void Line04(x1,y1,x2,y2,n);
  20338.  ;
  20339.  ;                       int x1,y1,x2,y2;        /* pixel coordinates */
  20340.  ;
  20341.  ;                       int n;                  /* pixel value */
  20342.  ;
  20343.  
  20344.  ARGx1           EQU     word ptr [bp+4] ; stack frame addressing
  20345.  ARGy1           EQU     word ptr [bp+6]
  20346.  ARGx2           EQU     word ptr [bp+8]
  20347.  ARGy2           EQU     word ptr [bp+10]
  20348.  ARGn            EQU     byte ptr [bp+12]
  20349.  VARleafincr     EQU     word ptr [bp-6]
  20350.  VARincr1        EQU     word ptr [bp-8]
  20351.  VARincr2        EQU     word ptr [bp-10]
  20352.  VARroutine      EQU     word ptr [bp-12]
  20353.  
  20354.  ByteOffsetShift EQU     2               ; used to convert pixels to byte
  20355.                                          ;  offset
  20356.  
  20357.  DGROUP          GROUP   _DATA
  20358.  
  20359.  _TEXT           SEGMENT byte public 'CODE'
  20360.                  ASSUME  cs:_TEXT,ds:DGROUP
  20361.  
  20362.                  EXTRN   PixelAddr04:near
  20363.  
  20364.                  PUBLIC  _Line04
  20365.  _Line04         PROC    near
  20366.  
  20367.                  push    bp              ; preserve caller registers
  20368.                  mov     bp,sp
  20369.                  sub     sp,8            ; stack space for local variables
  20370.                  push    si
  20371.                  push    di
  20372.  
  20373.                  mov     si,2000h        ; increment for video buffer
  20374.                                          ;  interleave
  20375.                  mov     di,80-2000h     ; increment from last to first
  20376.                                          ;  interleave
  20377.  
  20378.                  mov     cx,ARGx2
  20379.                  sub     cx,ARGx1        ; CX := x2 - x1
  20380.                  jz      VertLine04      ; jump if vertical line
  20381.  
  20382.  ; force x1 < x2
  20383.  
  20384.                  jns     L01             ; jump if x2 > x1
  20385.  
  20386.                  neg     cx              ; CX := x1 - x2
  20387.  
  20388.                  mov     bx,ARGx2        ; exchange x1 and x2
  20389.                  xchg    bx,ARGx1
  20390.                  mov     ARGx2,bx
  20391.  
  20392.                  mov     bx,ARGy2        ; exchange y1 and y2
  20393.                  xchg    bx,ARGy1
  20394.                  mov     ARGy2,bx
  20395.  
  20396.  ; calculate dy = ABS(y2-y1)
  20397.  
  20398.  L01:            mov     bx,ARGy2
  20399.                  sub     bx,ARGy1        ; BX := y2 - y1
  20400.                  jnz     L02
  20401.  
  20402.                  jmp     HorizLine04     ; jump if horizontal line
  20403.  
  20404.  L02:            jns     L03
  20405.  
  20406.                  neg     bx              ; BX := y1 - y2
  20407.                  neg     si              ; negate increments for buffer
  20408.                                          ;  interleave
  20409.                  neg     di
  20410.                  xchg    si,di           ; exchange increments
  20411.  
  20412.  ; select appropriate routine for slope of line
  20413.  
  20414.  L03:            mov     VARleafincr,di  ; save increment for buffer
  20415.                                          ;  interleave
  20416.  
  20417.                  mov     VARroutine,offset LoSlopeLine04
  20418.                  cmp     bx,cx
  20419.                  jle     L04             ; jump if dy <= dx (slope <= 1)
  20420.                  mov     VARroutine,offset HiSlopeLine04
  20421.                  xchg    bx,cx           ; exchange dy and dx
  20422.  
  20423.  ; calculate initial decision variable and increments
  20424.  
  20425.  L04:            shl     bx,1            ; BX := 2 * dy
  20426.                  mov     VARincr1,bx     ; incr1 := 2 * dy
  20427.                  sub     bx,cx
  20428.                  mov     di,bx           ; DI := d = 2 * dy - dx
  20429.                  sub     bx,cx
  20430.                  mov     VARincr2,bx     ; incr2 := 2 * (dy - dx)
  20431.  
  20432.  ; calculate first pixel address
  20433.  
  20434.                  push    cx              ; preserve this register
  20435.                  mov     ax,ARGy1        ; AX := y
  20436.                  mov     bx,ARGx1        ; BX := x
  20437.                  call    PixelAddr04     ; AH := bit mask
  20438.                                          ; ES:BX -> buffer
  20439.                                          ; CL := # bits to shift left
  20440.  
  20441.                  mov     al,ARGn         ; AL := unshifted pixel value
  20442.                  shl     ax,cl           ; AH := bit mask in proper position
  20443.                                          ; AL := pixel value in proper
  20444.                                          ;        position
  20445.  
  20446.                  mov     dx,ax           ; DH := bit mask
  20447.  
  20448.                                          ; DL := pixel value
  20449.                  not     dh              ; DH := inverse bit mask
  20450.  
  20451.                  pop     cx              ; restore this register
  20452.                  inc     cx              ; CX := # of pixels to draw
  20453.  
  20454.                  test    bx,2000h        ; set zero flag if BX in 1st
  20455.                                          ;  interleave
  20456.                  jz      L05
  20457.  
  20458.                  xchg    si,VARleafincr  ; exchange increment values if 1st
  20459.                                          ;  pixel
  20460.                                          ;  lies in 1st interleave
  20461.  
  20462.  L05:            jmp     VARroutine      ; jump to appropriate routine for
  20463.                                          ;  slope
  20464.  
  20465.  ; routine for vertical lines
  20466.  
  20467.  VertLine04:     mov     ax,ARGy1        ; AX := y1
  20468.                  mov     bx,ARGy2        ; BX := y2
  20469.                  mov     cx,bx
  20470.                  sub     cx,ax           ; CX := dy
  20471.                  jge     L31             ; jump if dy >= 0
  20472.  
  20473.                  neg     cx              ; force dy >= 0
  20474.                  mov     ax,bx           ; AX := y2
  20475.  
  20476.  L31:            inc     cx              ; CX := # of pixels to draw
  20477.                  mov     bx,ARGx1        ; BX := x
  20478.                  push    cx              ; preserve this register
  20479.                  call    PixelAddr04     ; AH := bit mask
  20480.                                          ; ES:BX -> video buffer
  20481.                                          ; CL := # bits to shift left
  20482.                  mov     al,ARGn         ; AL := pixel value
  20483.                  shl     ax,cl           ; AH := bit mask in proper position
  20484.                                          ; AL := pixel value in proper
  20485.                                          ;        position
  20486.                  not     ah              ; AH := inverse bit mask
  20487.                  pop     cx              ; restore this register
  20488.  
  20489.                  test    bx,si           ; set zero flag if BX in 1st
  20490.                                          ;  interleave
  20491.                  jz      L32
  20492.  
  20493.                  xchg    si,di           ; exchange increment values if 1st
  20494.                                          ;  pixel lies
  20495.                                          ;  in 1st interleave
  20496.  
  20497.  L32:            and     es:[bx],ah      ; zero pixel in buffer
  20498.                  or      es:[bx],al      ; set pixel value in buffer
  20499.  
  20500.                  add     bx,si           ; increment to next portion of
  20501.                                          ;  interleave
  20502.                  xchg    si,di           ; toggle between increment values
  20503.  
  20504.                  loop    L32
  20505.  
  20506.                  jmp     Lexit
  20507.  
  20508.  
  20509.  ; routine for horizontal lines (slope = 0)
  20510.  
  20511.  HorizLine04:    mov     ax,ARGy1
  20512.                  mov     bx,ARGx1
  20513.                  call    PixelAddr04     ; AH := bit mask
  20514.                                          ; ES:BX -> video buffer
  20515.                                          ; CL := # bits to shift left
  20516.                  mov     di,bx           ; ES:DI -> buffer
  20517.  
  20518.                  mov     dh,ah
  20519.                  not     dh              ; DH := unshifted bit mask for
  20520.                                          ;        leftmost byte
  20521.                  mov     dl,0FFh         ; DL := unshifted bit mask for
  20522.                                          ;        rightmost byte
  20523.  
  20524.                  shl     dh,cl           ; DH := reverse bit mask for first
  20525.                                          ;        byte
  20526.                  not     dh              ; DH := bit mask for first byte
  20527.  
  20528.                  mov     cx,ARGx2
  20529.                  and     cl,3
  20530.                  xor     cl,3
  20531.                  shl     cl,1            ; CL := number of bits to shift
  20532.                                          ;        left
  20533.                  shl     dl,cl           ; DL := bit mask for last byte
  20534.  
  20535.  ; determine byte offset of first and last pixel in the line
  20536.  
  20537.                  mov     ax,ARGx2        ; AX := x2
  20538.                  mov     bx,ARGx1        ; BX := x1
  20539.  
  20540.                  mov     cl,ByteOffsetShift      ; number of bits to shift
  20541.                                                  ;  to convert pixels to
  20542.                                                  ;  bytes
  20543.  
  20544.                  shr     ax,cl           ; AX := byte offset of x2
  20545.                  shr     bx,cl           ; BX := byte offset of x1
  20546.                  mov     cx,ax
  20547.                  sub     cx,bx           ; CX := (# bytes in line) - 1
  20548.  
  20549.  ; propagate pixel value throughout one byte
  20550.  
  20551.                  mov     bx,offset DGROUP:PropagatedPixel
  20552.                  mov     al,ARGn         ; AL := pixel value
  20553.                  xlat                    ; AL := propagated pixel value
  20554.  
  20555.  ; set pixels in leftmost byte of the line
  20556.  
  20557.                  or      dh,dh
  20558.                  js      L43             ; jump if byte-aligned (x1 is
  20559.                                          ;  leftmost pixel in byte)
  20560.                  or      cx,cx
  20561.                  jnz     L42             ; jump if more than one byte in
  20562.                                          ;  the line
  20563.  
  20564.                  and     dl,dh           ; bit mask for the line
  20565.                  jmp     short L44
  20566.  
  20567.  L42:            mov     ah,al
  20568.                  and     ah,dh           ; AH := masked pixel bits
  20569.                  not     dh              ; DH := reverse bit mask for
  20570.                                          ;        1st byte
  20571.                  and     es:[di],dh      ; zero masked pixels in buffer
  20572.                  or      es:[di],ah      ; update masked pixels in buffer
  20573.                  inc     di
  20574.                  dec     cx
  20575.  
  20576.  ; use a fast 8086 machine instruction to draw the remainder of the line
  20577.  
  20578.  L43:            rep     stosb           ; update all pixels in the line
  20579.  
  20580.  ; set pixels in the rightmost byte of the line
  20581.  
  20582.  L44:            and     al,dl           ; AL := masked pixels for last byte
  20583.                  not     dl
  20584.                  and     es:[di],dl      ; zero masked pixels in buffer
  20585.                  or      es:[di],al      ; update masked pixels in buffer
  20586.  
  20587.                  jmp     Lexit
  20588.  
  20589.  ; routine for dy <= dx (slope <= 1)     ; ES:BX -> video buffer
  20590.                                          ; CX = # pixels to draw
  20591.                                          ; DH = inverse bit mask
  20592.                                          ; DL = pixel value in proper
  20593.                                          ;       position
  20594.                                          ; SI = buffer interleave increment
  20595.                                          ; DI = decision variable
  20596.  LoSlopeLine04:
  20597.  
  20598.  L10:            mov     ah,es:[bx]      ; AH := byte from video buffer
  20599.  
  20600.  L11:            and     ah,dh           ; zero pixel value at current bit
  20601.                                          ;  offset
  20602.                  or      ah,dl           ; set pixel value in byte
  20603.  
  20604.                  ror     dl,1            ; rotate pixel value
  20605.                  ror     dl,1
  20606.                  ror     dh,1            ; rotate bit mask
  20607.                  ror     dh,1
  20608.                  jnc     L14             ; jump if bit mask rotated to
  20609.                                          ;  leftmost pixel position
  20610.  
  20611.  ; bit mask not shifted out
  20612.  
  20613.                  or      di,di           ; test sign of d
  20614.                  jns     L12             ; jump if d >= 0
  20615.  
  20616.                  add     di,VARincr1     ; d := d + incr1
  20617.                  loop    L11
  20618.  
  20619.                  mov     es:[bx],ah      ; store remaining pixels in buffer
  20620.                  jmp     short Lexit
  20621.  
  20622.  L12:            add     di,VARincr2     ; d := d + incr2
  20623.                  mov     es:[bx],ah      ; update buffer
  20624.  
  20625.                  add     bx,si           ; increment y
  20626.                  xchg    si,VARleafincr  ; exchange interleave increment
  20627.                                          ;  values
  20628.  
  20629.                  loop    L10
  20630.                  jmp     short Lexit
  20631.  
  20632.  ; bit mask shifted out
  20633.  
  20634.  L14:            mov     es:[bx],ah      ; update buffer
  20635.                  inc     bx              ; BX := offset of next byte
  20636.  
  20637.                  or      di,di           ; test sign of d
  20638.                  jns     L15             ; jump if non-negative
  20639.  
  20640.                  add     di,VARincr1     ; d := d + incr1
  20641.                  loop    L10
  20642.                  jmp     short Lexit
  20643.  
  20644.  
  20645.  L15:            add     di,VARincr2     ; d := d + incr2
  20646.  
  20647.                  add     bx,si           ; increment y
  20648.                  xchg    si,VARleafincr
  20649.  
  20650.                  loop    L10
  20651.                  jmp     short Lexit
  20652.  
  20653.  
  20654.  ; routine for dy > dx (slope > 1)       ; ES:BX -> video buffer
  20655.                                          ; CX = # pixels to draw
  20656.                                          ; DH = inverse bit mask
  20657.                                          ; DL = pixel value in proper
  20658.                                          ;       position
  20659.                                          ; SI = buffer interleave increment
  20660.                                          ; DI = decision variable
  20661.  HiSlopeLine04:
  20662.  
  20663.  L21:            and     es:[bx],dh      ; zero pixel value in video buffer
  20664.                  or      es:[bx],dl      ; set pixel value in byte
  20665.  
  20666.                  add     bx,si           ; increment y
  20667.                  xchg    si,VARleafincr  ; exchange interleave increment
  20668.                                          ;  values
  20669.  
  20670.  L22:            or      di,di           ; test sign of d
  20671.                  jns     L23             ; jump if d >= 0
  20672.  
  20673.                  add     di,VARincr1     ; d := d + incr1
  20674.                  loop    L21
  20675.  
  20676.                  jmp     short Lexit
  20677.  
  20678.  
  20679.  L23:            add     di,VARincr2     ; d := d + incr2
  20680.  
  20681.                  ror     dl,1            ; rotate pixel value
  20682.                  ror     dl,1
  20683.                  ror     dh,1            ; rotate bit mask
  20684.                  ror     dh,1
  20685.                  cmc                     ; cf set if bit mask not rotated to
  20686.                                          ;  leftmost pixel position
  20687.  
  20688.                  adc     bx,0            ; BX := offset of next byte
  20689.  
  20690.                  loop    L21
  20691.  
  20692.  
  20693.  Lexit:          pop     di              ; restore registers and return
  20694.                  pop     si
  20695.                  mov     sp,bp
  20696.                  pop     bp
  20697.                  ret
  20698.  
  20699.  _Line04         ENDP
  20700.  
  20701.  _TEXT           ENDS
  20702.  
  20703.  
  20704.  _DATA           SEGMENT word public 'DATA'
  20705.  
  20706.  PropagatedPixel DB      00000000b       ; 0
  20707.                  DB      01010101b       ; 1
  20708.                  DB      10101010b       ; 2
  20709.                  DB      11111111b       ; 3
  20710.  
  20711.  _DATA           ENDS
  20712.  
  20713.                  END
  20714.  
  20715.  
  20716.  
  20717.  ───────────────────────────────────────────────────────────────────────────
  20718.  Listing 6-6.  A line-drawing routine for Hercules monochrome graphics mode.
  20719.  ───────────────────────────────────────────────────────────────────────────
  20720.  
  20721.                  TITLE   'Listing 6-6'
  20722.                  NAME    LineHGC
  20723.                  PAGE    55,132
  20724.  
  20725.  ;
  20726.  ; Name:         LineHGC
  20727.  ;
  20728.  ; Function:     Draw a line in HGC or HGC+ 720x348 graphics
  20729.  ;
  20730.  ; Caller:       Microsoft C:
  20731.  ;
  20732.  ;                       void LineHGC(x1,y1,x2,y2,n);
  20733.  ;
  20734.  ;                       int x1,y1,x2,y2;        /* pixel coordinates */
  20735.  ;
  20736.  ;                       int n;                  /* pixel value */
  20737.  ;
  20738.  
  20739.  ARGx1           EQU     word ptr [bp+4] ; stack frame addressing
  20740.  ARGy1           EQU     word ptr [bp+6]
  20741.  ARGx2           EQU     word ptr [bp+8]
  20742.  ARGy2           EQU     word ptr [bp+10]
  20743.  ARGn            EQU     byte ptr [bp+12]
  20744.  VARleafincr     EQU     word ptr [bp-6]
  20745.  VARincr1        EQU     word ptr [bp-8]
  20746.  VARincr2        EQU     word ptr [bp-10]
  20747.  VARroutine      EQU     word ptr [bp-12]
  20748.  
  20749.  ByteOffsetShift EQU     3               ; used to convert pixels to byte
  20750.                                          ;  offset
  20751.  
  20752.  DGROUP          GROUP   _DATA
  20753.  
  20754.  _TEXT           SEGMENT byte public 'CODE'
  20755.                  ASSUME  cs:_TEXT,ds:DGROUP
  20756.  
  20757.                  EXTRN   PixelAddrHGC:near
  20758.  
  20759.                  PUBLIC  _LineHGC
  20760.  _LineHGC        PROC    near
  20761.  
  20762.                  push    bp              ; preserve caller registers
  20763.                  mov     bp,sp
  20764.                  sub     sp,8            ; stack space for local variables
  20765.                  push    si
  20766.                  push    di
  20767.  
  20768.                  mov     si,2000h        ; increment for video buffer
  20769.                                          ;  interleave
  20770.                  mov     di,90-8000h     ; increment from last to first
  20771.                                          ;  interleave
  20772.  
  20773.                  mov     cx,ARGx2
  20774.                  sub     cx,ARGx1        ; CX := x2 - x1
  20775.                  jz      VertLineHGC     ; jump if vertical line
  20776.  
  20777.  ; force x1 < x2
  20778.  
  20779.                  jns     L01             ; jump if x2 > x1
  20780.  
  20781.                  neg     cx              ; CX := x1 - x2
  20782.  
  20783.                  mov     bx,ARGx2        ; exchange x1 and x2
  20784.                  xchg    bx,ARGx1
  20785.                  mov     ARGx2,bx
  20786.  
  20787.                  mov     bx,ARGy2        ; exchange y1 and y2
  20788.                  xchg    bx,ARGy1
  20789.                  mov     ARGy2,bx
  20790.  
  20791.  ; calculate dy = ABS(y2-y1)
  20792.  
  20793.  L01:            mov     bx,ARGy2
  20794.                  sub     bx,ARGy1        ; BX := y2 - y1
  20795.                  jz      HorizLineHGC    ; jump if horizontal line
  20796.  
  20797.                  jns     L03
  20798.  
  20799.                  neg     bx              ; BX := y1 - y2
  20800.                  neg     si              ; negate increments for buffer
  20801.                                          ;  interleave
  20802.                  neg     di
  20803.  
  20804.  ; select appropriate routine for slope of line
  20805.  
  20806.  L03:            mov     VARleafincr,di  ; save increment for buffer
  20807.                                          ;  interleave
  20808.  
  20809.                  mov     VARroutine,offset LoSlopeLineHGC
  20810.                  cmp     bx,cx
  20811.                  jle     L04             ; jump if dy <= dx (slope <= 1)
  20812.                  mov     VARroutine,offset HiSlopeLineHGC
  20813.                  xchg    bx,cx           ; exchange dy and dx
  20814.  
  20815.  ; calculate initial decision variable and increments
  20816.  
  20817.  L04:            shl     bx,1            ; BX := 2 * dy
  20818.                  mov     VARincr1,bx     ; incr1 := 2 * dy
  20819.                  sub     bx,cx
  20820.                  mov     di,bx           ; DI := d = 2 * dy - dx
  20821.                  sub     bx,cx
  20822.                  mov     VARincr2,bx     ; incr2 := 2 * (dy - dx)
  20823.  
  20824.  ; calculate first pixel address
  20825.  
  20826.                  push    cx              ; preserve this register
  20827.                  mov     ax,ARGy1        ; AX := y
  20828.                  mov     bx,ARGx1        ; BX := x
  20829.                  call    PixelAddrHGC    ; AH := bit mask
  20830.                                          ; ES:BX -> buffer
  20831.                                          ; CL := # bits to shift left
  20832.  
  20833.                  mov     al,ARGn         ; AL := unshifted pixel value
  20834.                  shl     ax,cl           ; AH := bit mask in proper position
  20835.                                          ; AL := pixel value in proper
  20836.                                          ;        position
  20837.  
  20838.                  mov     dx,ax           ; DH := bit mask
  20839.                                          ; DL := pixel value
  20840.                  not     dh              ; DH := inverse bit mask
  20841.  
  20842.                  pop     cx              ; restore this register
  20843.                  inc     cx              ; CX := # of pixels to draw
  20844.  
  20845.                  jmp     VARroutine      ; jump to appropriate routine for
  20846.                                          ;  slope
  20847.  
  20848.  ; routine for vertical lines
  20849.  
  20850.  VertLineHGC:    mov     ax,ARGy1        ; AX := y1
  20851.                  mov     bx,ARGy2        ; BX := y2
  20852.                  mov     cx,bx
  20853.                  sub     cx,ax           ; CX := dy
  20854.                  jge     L31             ; jump if dy >= 0
  20855.  
  20856.                  neg     cx              ; force dy >= 0
  20857.                  mov     ax,bx           ; AX := y2
  20858.  
  20859.  L31:            inc     cx              ; CX := # of pixels to draw
  20860.                  mov     bx,ARGx1        ; BX := x
  20861.                  push    cx              ; preserve this register
  20862.                  call    PixelAddrHGC    ; AH := bit mask
  20863.                                          ; ES:BX -> video buffer
  20864.                                          ; CL := # bits to shift left
  20865.                  mov     al,ARGn         ; AL := pixel value
  20866.                  shl     ax,cl           ; AH := bit mask in proper position
  20867.                                          ; AL := pixel value in proper
  20868.                                          ;        position
  20869.                  not     ah              ; AH := inverse bit mask
  20870.                  pop     cx              ; restore this register
  20871.  
  20872.  ; draw the line
  20873.                  test    al,al
  20874.                  jz      L34             ; jump if pixel value is zero
  20875.  
  20876.  L32:            or      es:[bx],al      ; set pixel values in buffer
  20877.  
  20878.                  add     bx,si           ; increment to next portion of
  20879.                                          ;  interleave
  20880.                  jns     L33
  20881.                  add     bx,di           ; increment to first portion of
  20882.                                          ;  interleave
  20883.  L33:            loop    L32
  20884.                  jmp     short L36
  20885.  
  20886.  L34:            and     es:[bx],ah      ; reset pixel values in buffer
  20887.  
  20888.                  add     bx,si           ; increment to next portion of
  20889.                                          ;  interleave
  20890.                  jns     L35
  20891.                  add     bx,di           ; increment to first portion of
  20892.                                          ;  interleave
  20893.  L35:            loop    L34
  20894.  
  20895.  L36:            jmp     Lexit
  20896.  
  20897.  
  20898.  ; routine for horizontal lines (slope = 0)
  20899.  
  20900.  HorizLineHGC:   mov     ax,ARGy1
  20901.                  mov     bx,ARGx1
  20902.                  call    PixelAddrHGC    ; AH := bit mask
  20903.                                          ; ES:BX -> video buffer
  20904.                                          ; CL := # bits to shift left
  20905.                  mov     di,bx           ; ES:DI -> buffer
  20906.                  mov     dh,ah
  20907.                  not     dh              ; DH := unshifted bit mask for
  20908.                                          ;        leftmost byte
  20909.                  mov     dl,0FFh         ; DL := unshifted bit mask for
  20910.                                          ;        rightmost byte
  20911.                  shl     dh,cl           ; DH := reverse bit mask for first
  20912.                                          ;        byte
  20913.                  not     dh              ; DH := bit mask for first byte
  20914.  
  20915.                  mov     cx,ARGx2
  20916.                  and     cl,7
  20917.                  xor     cl,7            ; CL := number of bits to shift
  20918.                                          ;        left
  20919.                  shl     dl,cl           ; DL := bit mask for last byte
  20920.  
  20921.  ; determine byte offset of first and last pixel in the line
  20922.  
  20923.                  mov     ax,ARGx2        ; AX := x2
  20924.                  mov     bx,ARGx1        ; BX := x1
  20925.  
  20926.                  mov     cl,ByteOffsetShift      ; number of bits to shift
  20927.                                                  ;  to convert pixels to
  20928.                                                  ;  bytes
  20929.  
  20930.                  shr     ax,cl           ; AX := byte offset of x2
  20931.                  shr     bx,cl           ; BX := byte offset of x1
  20932.                  mov     cx,ax
  20933.                  sub     cx,bx           ; CX := (# bytes in line) - 1
  20934.  
  20935.  ; propagate pixel value throughout one byte
  20936.  
  20937.                  mov     bx,offset DGROUP:PropagatedPixel
  20938.                  mov     al,ARGn         ; AL := pixel value
  20939.                  xlat                    ; AL := propagated pixel value
  20940.  
  20941.  ; set pixels in leftmost byte of the line
  20942.  
  20943.                  or      dh,dh
  20944.                  js      L43             ; jump if byte-aligned (x1 is
  20945.                                          ;  leftmost
  20946.                                          ;  pixel in byte)
  20947.                  or      cx,cx
  20948.                  jnz     L42             ; jump if more than one byte in the
  20949.                                          ;  line
  20950.  
  20951.                  and     dl,dh           ; bit mask for the line
  20952.                  jmp     short L44
  20953.  
  20954.  L42:            mov     ah,al
  20955.                  and     ah,dh           ; AH := masked pixel bits
  20956.                  not     dh              ; DH := reverse bit mask for 1st
  20957.                                          ;        byte
  20958.                  and     es:[di],dh      ; zero masked pixels in buffer
  20959.                  or      es:[di],ah      ; update masked pixels in buffer
  20960.                  inc     di
  20961.                  dec     cx
  20962.  
  20963.  ; use a fast 8086 machine instruction to draw the remainder of the line
  20964.  
  20965.  L43:            rep     stosb           ; update all pixels in the line
  20966.  
  20967.  ; set pixels in the rightmost byte of the line
  20968.  
  20969.  L44:            and     al,dl           ; AL := masked pixels for last byte
  20970.                  not     dl
  20971.                  and     es:[di],dl      ; zero masked pixels in buffer
  20972.                  or      es:[di],al      ; update masked pixels in buffer
  20973.  
  20974.                  jmp     Lexit
  20975.  
  20976.  ; routine for dy <= dx (slope <= 1)     ; ES:BX -> video buffer
  20977.                                          ; CX = # pixels to draw
  20978.                                          ; DH = inverse bit mask
  20979.                                          ; DL = pixel value in proper
  20980.                                          ;       position
  20981.                                          ; SI = buffer interleave increment
  20982.                                          ; DI = decision variable
  20983.  LoSlopeLineHGC:
  20984.  
  20985.  L10:            mov     ah,es:[bx]      ; AH := byte from video buffer
  20986.  
  20987.  L11:            and     ah,dh           ; zero pixel value at current bit
  20988.                                          ;  offset
  20989.                  or      ah,dl           ; set pixel value in byte
  20990.  
  20991.                  ror     dl,1            ; rotate pixel value
  20992.                  ror     dh,1            ; rotate bit mask
  20993.                  jnc     L14             ; jump if bit mask rotated to
  20994.                                          ;  leftmost pixel position
  20995.  
  20996.  ; bit mask not shifted out
  20997.  
  20998.                  or      di,di           ; test sign of d
  20999.                  jns     L12             ; jump if d >= 0
  21000.  
  21001.                  add     di,VARincr1     ; d := d + incr1
  21002.                  loop    L11
  21003.  
  21004.                  mov     es:[bx],ah      ; store remaining pixels in buffer
  21005.                  jmp     short Lexit
  21006.  
  21007.  L12:            add     di,VARincr2     ; d := d + incr2
  21008.                  mov     es:[bx],ah      ; update buffer
  21009.  
  21010.                  add     bx,si           ; increment y
  21011.                  jns     L13             ; jump if not in last interleave
  21012.  
  21013.                  add     bx,VARleafincr  ; increment into next interleave
  21014.  
  21015.  L13:            loop    L10
  21016.                  jmp     short Lexit
  21017.  
  21018.  ; bit mask shifted out
  21019.  
  21020.  L14:            mov     es:[bx],ah      ; update buffer
  21021.                  inc     bx              ; BX := offset of next byte
  21022.  
  21023.                  or      di,di           ; test sign of d
  21024.                  jns     L15             ; jump if non-negative
  21025.  
  21026.                  add     di,VARincr1     ; d := d + incr1
  21027.                  loop    L10
  21028.                  jmp     short Lexit
  21029.  
  21030.  L15:            add     di,VARincr2     ; d := d + incr2
  21031.  
  21032.                  add     bx,si           ; increment y
  21033.                  jns     L16             ; jump if not in last interleave
  21034.  
  21035.                  add     bx,VARleafincr  ; increment into next interleave
  21036.  
  21037.  L16:            loop    L10             ; loop until all pixels are set
  21038.                  jmp     short Lexit
  21039.  
  21040.  
  21041.  ; routine for dy > dx (slope > 1)       ; ES:BX -> video buffer
  21042.                                          ; CX = # pixels to draw
  21043.                                          ; DH = inverse bit mask
  21044.                                          ; DL = pixel value in proper
  21045.                                          ;       position
  21046.                                          ; SI = buffer interleave increment
  21047.                                          ; DI = decision variable
  21048.  HiSlopeLineHGC:
  21049.  
  21050.  L21:            and     es:[bx],dh      ; zero pixel value in video buffer
  21051.                  or      es:[bx],dl      ; set pixel value in byte
  21052.  
  21053.                  add     bx,si           ; increment y
  21054.                  jns     L22             ; jump if not in last interleave
  21055.  
  21056.                  add     bx,VARleafincr  ; increment into next interleave
  21057.  
  21058.  L22:            or      di,di
  21059.                  jns     L23             ; jump if d >= 0
  21060.  
  21061.                  add     di,VARincr1     ; d := d + incr1
  21062.                  loop    L21
  21063.                  jmp     short Lexit
  21064.  
  21065.  
  21066.  L23:            add     di,VARincr2     ; d := d + incr2
  21067.  
  21068.                  ror     dl,1            ; rotate pixel value
  21069.                  ror     dh,1            ; rotate bit mask
  21070.                  cmc                     ; cf set if bit mask not rotated to
  21071.                                          ;  leftmost pixel position
  21072.                  adc     bx,0            ; BX := offset of next byte
  21073.  
  21074.                  loop    L21
  21075.  
  21076.  
  21077.  Lexit:          pop     di              ; restore registers and return
  21078.                  pop     si
  21079.                  mov     sp,bp
  21080.                  pop     bp
  21081.                  ret
  21082.  
  21083.  _LineHGC        ENDP
  21084.  
  21085.  _TEXT           ENDS
  21086.  
  21087.  _DATA           SEGMENT word public 'DATA'
  21088.  
  21089.  PropagatedPixel DB      00000000b       ; 0
  21090.                  DB      11111111b       ; 1
  21091.  
  21092.  _DATA           ENDS
  21093.  
  21094.                  END
  21095.  
  21096.  
  21097.  
  21098.  ───────────────────────────────────────────────────────────────────────────
  21099.  Listing 6-7.  A line-drawing routine for native EGA graphics modes.
  21100.  ───────────────────────────────────────────────────────────────────────────
  21101.  
  21102.                  TITLE   'Listing 6-7'
  21103.                  NAME    Line10
  21104.                  PAGE    55,132
  21105.  
  21106.  ;
  21107.  ; Name:         Line10
  21108.  ;
  21109.  ; Function:     Draw a line in the following EGA and VGA graphics modes:
  21110.  ;                       200-line 16-color modes
  21111.  ;                       350-line modes
  21112.  ;                       640x480 16-color
  21113.  ;
  21114.  ; Caller:       Microsoft C:
  21115.  ;
  21116.  ;                       void Line10(x1,y1,x2,y2,n);
  21117.  ;
  21118.  ;                       int x1,y1,x2,y2;        /* pixel coordinates */
  21119.  ;
  21120.  ;                       int n;                  /* pixel value */
  21121.  ;
  21122.  
  21123.  ARGx1           EQU     word ptr [bp+4] ; stack frame addressing
  21124.  ARGy1           EQU     word ptr [bp+6]
  21125.  ARGx2           EQU     word ptr [bp+8]
  21126.  ARGy2           EQU     word ptr [bp+10]
  21127.  ARGn            EQU     byte ptr [bp+12]
  21128.  VARvertincr     EQU     word ptr [bp-6]
  21129.  VARincr1        EQU     word ptr [bp-8]
  21130.  VARincr2        EQU     word ptr [bp-10]
  21131.  VARroutine      EQU     word ptr [bp-12]
  21132.  
  21133.  ByteOffsetShift EQU     3               ; used to convert pixels to byte
  21134.                                          ;  offset
  21135.  BytesPerLine    EQU     80
  21136.  RMWbits         EQU     0               ; value for Data Rotate/Func Select
  21137.                                          ;  reg
  21138.  
  21139.  
  21140.  _TEXT           SEGMENT byte public 'CODE'
  21141.                  ASSUME  cs:_TEXT
  21142.  
  21143.                  EXTRN   PixelAddr10:near
  21144.  
  21145.                  PUBLIC  _Line10
  21146.  _Line10         PROC    near
  21147.  
  21148.                  push    bp              ; preserve caller registers
  21149.                  mov     bp,sp
  21150.                  sub     sp,8            ; stack space for local variables
  21151.                  push    si
  21152.                  push    di
  21153.  
  21154.  ; configure the Graphics Controller
  21155.  
  21156.                  mov     dx,3CEh         ; DX := Graphics Controller port
  21157.                                          ;        addr
  21158.  
  21159.                  mov     ah,ARGn         ; AH := pixel value
  21160.                  xor     al,al           ; AL := Set/Reset Register number
  21161.                  out     dx,ax
  21162.  
  21163.                  mov     ax,0F01h        ; AH := 1111b (bit plane mask for
  21164.                                          ;  Enable Set/Reset)
  21165.                  out     dx,ax           ; AL := Enable Set/Reset Register #
  21166.  
  21167.                  mov     ah,RMWbits      ; bits 3 and 4 of AH := function
  21168.                  mov     al,3            ; AL := Data Rotate/Func Select
  21169.                                          ;        reg #
  21170.                  out     dx,ax
  21171.  
  21172.  ; check for vertical line
  21173.  
  21174.                  mov     si,BytesPerLine ; increment for video buffer
  21175.  
  21176.                  mov     cx,ARGx2
  21177.                  sub     cx,ARGx1        ; CX := x2 - x1
  21178.                  jz      VertLine10      ; jump if vertical line
  21179.  
  21180.  ; force x1 < x2
  21181.  
  21182.                  jns     L01             ; jump if x2 > x1
  21183.  
  21184.                  neg     cx              ; CX := x1 - x2
  21185.  
  21186.                  mov     bx,ARGx2        ; exchange x1 and x2
  21187.                  xchg    bx,ARGx1
  21188.                  mov     ARGx2,bx
  21189.  
  21190.                  mov     bx,ARGy2        ; exchange y1 and y2
  21191.                  xchg    bx,ARGy1
  21192.                  mov     ARGy2,bx
  21193.  
  21194.  ; calculate dy = ABS(y2-y1)
  21195.  
  21196.  L01:            mov     bx,ARGy2
  21197.                  sub     bx,ARGy1        ; BX := y2 - y1
  21198.                  jz      HorizLine10     ; jump if horizontal line
  21199.  
  21200.                  jns     L03             ; jump if slope is positive
  21201.  
  21202.                  neg     bx              ; BX := y1 - y2
  21203.                  neg     si              ; negate increment for buffer
  21204.                                          ;  interleave
  21205.  
  21206.  ; select appropriate routine for slope of line
  21207.  
  21208.  L03:            mov     VARvertincr,si  ; save vertical increment
  21209.  
  21210.                  mov     VARroutine,offset LoSlopeLine10
  21211.                  cmp     bx,cx
  21212.                  jle     L04             ; jump if dy <= dx (slope <= 1)
  21213.                  mov     VARroutine,offset HiSlopeLine10
  21214.                  xchg    bx,cx           ; exchange dy and dx
  21215.  
  21216.  ; calculate initial decision variable and increments
  21217.  
  21218.  L04:            shl     bx,1            ; BX := 2 * dy
  21219.                  mov     VARincr1,bx     ; incr1 := 2 * dy
  21220.                  sub     bx,cx
  21221.                  mov     si,bx           ; SI := d = 2 * dy - dx
  21222.                  sub     bx,cx
  21223.                  mov     VARincr2,bx     ; incr2 := 2 * (dy - dx)
  21224.  
  21225.  ; calculate first pixel address
  21226.  
  21227.                  push    cx              ; preserve this register
  21228.                  mov     ax,ARGy1        ; AX := y
  21229.                  mov     bx,ARGx1        ; BX := x
  21230.                  call    PixelAddr10     ; AH := bit mask
  21231.                                          ; ES:BX -> buffer
  21232.                                          ; CL := # bits to shift left
  21233.  
  21234.                  mov     di,bx           ; ES:DI -> buffer
  21235.                  shl     ah,cl           ; AH := bit mask in proper position
  21236.                  mov     bl,ah           ; AH,BL := bit mask
  21237.                  mov     al,8            ; AL := Bit Mask Register number
  21238.  
  21239.                  pop     cx              ; restore this register
  21240.                  inc     cx              ; CX := # of pixels to draw
  21241.  
  21242.                  jmp     VARroutine      ; jump to appropriate routine for
  21243.                                          ;  slope
  21244.  
  21245.  ; routine for vertical lines
  21246.  
  21247.  VertLine10:     mov     ax,ARGy1        ; AX := y1
  21248.                  mov     bx,ARGy2        ; BX := y2
  21249.                  mov     cx,bx
  21250.                  sub     cx,ax           ; CX := dy
  21251.                  jge     L31             ; jump if dy >= 0
  21252.  
  21253.                  neg     cx              ; force dy >= 0
  21254.                  mov     ax,bx           ; AX := y2
  21255.  
  21256.  L31:            inc     cx              ; CX := # of pixels to draw
  21257.                  mov     bx,ARGx1        ; BX := x
  21258.                  push    cx              ; preserve this register
  21259.                  call    PixelAddr10     ; AH := bit mask
  21260.                                          ; ES:BX -> video buffer
  21261.                                          ; CL := # bits to shift left
  21262.  ; set up Graphics Controller
  21263.  
  21264.                  shl     ah,cl           ; AH := bit mask in proper position
  21265.                  mov     al,8            ; AL := Bit Mask reg number
  21266.                  out     dx,ax
  21267.  
  21268.                  pop     cx              ; restore this register
  21269.  
  21270.  ; draw the line
  21271.  
  21272.  L32:            or      es:[bx],al      ; set pixel
  21273.                  add     bx,si           ; increment to next line
  21274.                  loop    L32
  21275.  
  21276.                  jmp     Lexit
  21277.  
  21278.  
  21279.  ; routine for horizontal lines (slope = 0)
  21280.  
  21281.  HorizLine10:
  21282.                  push    ds              ; preserve DS
  21283.  
  21284.                  mov     ax,ARGy1
  21285.                  mov     bx,ARGx1
  21286.                  call    PixelAddr10     ; AH := bit mask
  21287.                                          ; ES:BX -> video buffer
  21288.                                          ; CL := # bits to shift left
  21289.                  mov     di,bx           ; ES:DI -> buffer
  21290.                  mov     dh,ah           ; DH := unshifted bit mask for
  21291.                                          ;        leftmost byte
  21292.                  not     dh
  21293.                  shl     dh,cl           ; DH := reverse bit mask for first
  21294.                                                   byte
  21295.                  not     dh              ; DH := bit mask for first byte
  21296.  
  21297.                  mov     cx,ARGx2
  21298.                  and     cl,7
  21299.                  xor     cl,7            ; CL := number of bits to shift
  21300.                                          ;        left
  21301.                  mov     dl,0FFh         ; DL := unshifted bit mask for
  21302.                                          ;        rightmost byte
  21303.                  shl     dl,cl           ; DL := bit mask for last byte
  21304.  
  21305.  ; determine byte offset of first and last pixel in the line
  21306.  
  21307.                  mov     ax,ARGx2        ; AX := x2
  21308.                  mov     bx,ARGx1        ; BX := x1
  21309.  
  21310.                  mov     cl,ByteOffsetShift      ; number of bits to shift
  21311.                                                  ;  to convert pixels to
  21312.                                                  ;  bytes
  21313.  
  21314.                  shr     ax,cl           ; AX := byte offset of x2
  21315.                  shr     bx,cl           ; BX := byte offset of x1
  21316.                  mov     cx,ax
  21317.                  sub     cx,bx           ; CX := (# bytes in line) - 1
  21318.  
  21319.  ; get Graphics Controller port address into DX
  21320.  
  21321.                  mov     bx,dx           ; BH := bit mask for first byte
  21322.                                          ; BL := bit mask for last byte
  21323.                  mov     dx,3CEh         ; DX := Graphics Controller port
  21324.                  mov     al,8            ; AL := Bit Mask Register number
  21325.  
  21326.  ; make video buffer addressable through DS:SI
  21327.  
  21328.                  push    es
  21329.                  pop     ds
  21330.                  mov     si,di           ; DS:SI -> video buffer
  21331.  
  21332.  ; set pixels in leftmost byte of the line
  21333.  
  21334.                  or      bh,bh
  21335.                  js      L43             ; jump if byte-aligned (x1 is
  21336.                                          ;  leftmost pixel in byte)
  21337.                  or      cx,cx
  21338.                  jnz     L42             ; jump if more than one byte in the
  21339.                                          ;  line
  21340.  
  21341.                  and     bl,bh           ; BL := bit mask for the line
  21342.                  jmp     short L44
  21343.  
  21344.  L42:            mov     ah,bh           ; AH := bit mask for 1st byte
  21345.                  out     dx,ax           ; update Graphics Controller
  21346.  
  21347.                  movsb                   ; update bit planes
  21348.                  dec     cx
  21349.  
  21350.  ; use a fast 8086 machine instruction to draw the remainder of the line
  21351.  
  21352.  L43:            mov     ah,11111111b    ; AH := bit mask
  21353.                  out     dx,ax           ; update Bit Mask Register
  21354.                  rep     movsb           ; update all pixels in the line
  21355.  
  21356.  ; set pixels in the rightmost byte of the line
  21357.  
  21358.  L44:            mov     ah,bl           ; AH := bit mask for last byte
  21359.                  out     dx,ax           ; update Graphics Controller
  21360.  
  21361.                  movsb                   ; update bit planes
  21362.  
  21363.                  pop     ds              ; restore DS
  21364.                  jmp     short Lexit
  21365.  
  21366.  ; routine for dy >= dx (slope <= 1)     ; ES:DI -> video buffer
  21367.                                          ; AL = Bit Mask Register number
  21368.                                          ; BL = bit mask for 1st pixel
  21369.                                          ; CX = # pixels to draw
  21370.                                          ; DX = Graphics Controller port
  21371.                                          ;       addr
  21372.                                          ; SI = decision variable
  21373.  LoSlopeLine10:
  21374.  
  21375.  L10:            mov     ah,bl           ; AH := bit mask for next pixel
  21376.  
  21377.  L11:            or      ah,bl           ; mask current pixel position
  21378.                  ror     bl,1            ; rotate pixel value
  21379.                  jc      L14             ; jump if bit mask rotated to
  21380.                                          ;  leftmost pixel position
  21381.  
  21382.  ; bit mask not shifted out
  21383.  
  21384.                  or      si,si           ; test sign of d
  21385.                  jns     L12             ; jump if d >= 0
  21386.  
  21387.                  add     si,VARincr1     ; d := d + incr1
  21388.                  loop    L11
  21389.  
  21390.                  out     dx,ax           ; update Bit Mask Register
  21391.                  or      es:[di],al      ; set remaining pixel(s)
  21392.                  jmp     short Lexit
  21393.  
  21394.  L12:            add     si,VARincr2     ; d := d + incr2
  21395.                  out     dx,ax           ; update Bit Mask Register
  21396.  
  21397.                  or      es:[di],al      ; update bit planes
  21398.  
  21399.                  add     di,VARvertincr  ; increment y
  21400.                  loop    L10
  21401.                  jmp     short Lexit
  21402.  
  21403.  ; bit mask shifted out
  21404.  
  21405.  L14:            out     dx,ax           ; update Bit Mask Register ...
  21406.  
  21407.                  or      es:[di],al      ; update bit planes
  21408.                  inc     di              ; increment x
  21409.  
  21410.                  or      si,si           ; test sign of d
  21411.                  jns     L15             ; jump if non-negative
  21412.  
  21413.                  add     si,VARincr1     ; d := d + incr1
  21414.                  loop    L10
  21415.                  jmp     short Lexit
  21416.  
  21417.  L15:            add     si,VARincr2     ; d := d + incr2
  21418.                  add     di,VARvertincr  ; vertical increment
  21419.                  loop    L10
  21420.                  jmp     short Lexit
  21421.  
  21422.  ; routine for dy > dx (slope > 1)       ; ES:DI -> video buffer
  21423.                                          ; AH = bit mask for 1st pixel
  21424.                                          ; AL = Bit Mask Register number
  21425.                                          ; CX = # pixels to draw
  21426.                                          ; DX = Graphics Controller port
  21427.                                          ;       addr
  21428.                                          ; SI = decision variable
  21429.  HiSlopeLine10:
  21430.                  mov     bx,VARvertincr  ; BX := y-increment
  21431.  
  21432.  L21:            out     dx,ax           ; update Bit Mask Register
  21433.                  or      es:[di],al      ; update bit planes
  21434.  
  21435.                  add     di,bx           ; increment y
  21436.  
  21437.  L22:            or      si,si           ; test sign of d
  21438.                  jns     L23             ; jump if d >= 0
  21439.  
  21440.                  add     si,VARincr1     ; d := d + incr1
  21441.                  loop    L21
  21442.                  jmp     short Lexit
  21443.  
  21444.  
  21445.  L23:            add     si,VARincr2     ; d := d + incr2
  21446.  
  21447.                  ror     ah,1            ; rotate bit mask
  21448.                  adc     di,0            ; increment DI if when mask rotated
  21449.                                          ;  to leftmost pixel position
  21450.  
  21451.                  loop    L21
  21452.  
  21453.  
  21454.  ; restore default Graphics Controller state and return to caller
  21455.  
  21456.  Lexit:          xor     ax,ax           ; AH := 0, AL := 0
  21457.                  out     dx,ax           ; restore Set/Reset Register
  21458.  
  21459.                  inc     ax              ; AH := 0, AL := 1
  21460.                  out     dx,ax           ; restore Enable Set/Reset Register
  21461.  
  21462.                  mov     al,3            ; AH := 0, AL := 3
  21463.                  out     dx,ax           ; AL := Data Rotate/Func Select
  21464.                                          ;        reg #
  21465.  
  21466.                  mov     ax,0FF08h       ; AH := 11111111b, AL := 8
  21467.                  out     dx,ax           ; restore Bit Mask Register
  21468.  
  21469.                  pop     di              ; restore registers and return
  21470.                  pop     si
  21471.                  mov     sp,bp
  21472.                  pop     bp
  21473.                  ret
  21474.  
  21475.  _Line10         ENDP
  21476.  
  21477.  _TEXT           ENDS
  21478.  
  21479.                  END
  21480.  
  21481.  
  21482.  
  21483.  ───────────────────────────────────────────────────────────────────────────
  21484.  Listing 6-8.  A line-drawing routine for MCGA and VGA 640-by-480
  21485.                2-color mode.
  21486.  ───────────────────────────────────────────────────────────────────────────
  21487.  
  21488.                  TITLE   'Listing 6-8'
  21489.                  NAME    Line11
  21490.                  PAGE    55,132
  21491.  
  21492.  ;
  21493.  ; Name:         Line11
  21494.  ;
  21495.  ; Function:     Draw a line in 640x480 2-color mode (MCGA, VGA)
  21496.  ;
  21497.  ; Caller:       Microsoft C:
  21498.  ;
  21499.  ;                       void Line11(x1,y1,x2,y2,n);
  21500.  ;
  21501.  
  21502.  ;                       int x1,y1,x2,y2;        /* pixel coordinates */
  21503.  ;
  21504.  ;                       int n;                  /* pixel value */
  21505.  ;
  21506.  
  21507.  ARGx1           EQU     word ptr [bp+4] ; stack frame addressing
  21508.  ARGy1           EQU     word ptr [bp+6]
  21509.  ARGx2           EQU     word ptr [bp+8]
  21510.  ARGy2           EQU     word ptr [bp+10]
  21511.  ARGn            EQU     byte ptr [bp+12]
  21512.  VARincr1        EQU     word ptr [bp-6]
  21513.  VARincr2        EQU     word ptr [bp-8]
  21514.  VARroutine      EQU     word ptr [bp-10]
  21515.  
  21516.  BytesPerLine    EQU     80              ; bytes in one row of pixels
  21517.  ByteOffsetShift EQU     3               ; used to convert pixels to byte
  21518.                                          ;  offset
  21519.  
  21520.  DGROUP          GROUP   _DATA
  21521.  
  21522.  _TEXT           SEGMENT byte public 'CODE'
  21523.                  ASSUME  cs:_TEXT,ds:DGROUP
  21524.  
  21525.                  EXTRN   PixelAddr10:near
  21526.  
  21527.                  PUBLIC  _Line11
  21528.  _Line11         PROC    near
  21529.  
  21530.                  push    bp              ; preserve caller registers
  21531.                  mov     bp,sp
  21532.                  sub     sp,6            ; stack space for local variables
  21533.                  push    si
  21534.                  push    di
  21535.  
  21536.  ; check for vertical line
  21537.  
  21538.                  mov     si,BytesPerLine ; SI := initial y-increment
  21539.  
  21540.                  mov     cx,ARGx2
  21541.                  sub     cx,ARGx1        ; CX := x2 - x1
  21542.                  jz      VertLine11      ; jump if vertical line
  21543.  
  21544.  ; force x1 < x2
  21545.  
  21546.                  jns     L01             ; jump if x2 > x1
  21547.  
  21548.                  neg     cx              ; CX := x1 - x2
  21549.  
  21550.                  mov     bx,ARGx2        ; exchange x1 and x2
  21551.                  xchg    bx,ARGx1
  21552.                  mov     ARGx2,bx
  21553.  
  21554.                  mov     bx,ARGy2        ; exchange y1 and y2
  21555.                  xchg    bx,ARGy1
  21556.                  mov     ARGy2,bx
  21557.  
  21558.  ; calculate dy = ABS(y2-y1)
  21559.  
  21560.  L01:            mov     bx,ARGy2
  21561.                  sub     bx,ARGy1        ; BX := y2 - y1
  21562.                  jnz     L02
  21563.  
  21564.                  jmp     HorizLine11     ; jump if horizontal line
  21565.  
  21566.  L02:            jns     L03
  21567.                  neg     bx              ; BX := y1 - y2
  21568.                  neg     si              ; negate y-increment
  21569.  
  21570.  ; select appropriate routine for slope of line
  21571.  
  21572.  L03:            mov     VARroutine,offset LoSlopeLine11
  21573.                  cmp     bx,cx
  21574.                  jle     L04             ; jump if dy <= dx (slope <= 1)
  21575.                  mov     VARroutine,offset HiSlopeLine11
  21576.                  xchg    bx,cx           ; exchange dy and dx
  21577.  
  21578.  ; calculate initial decision variable and increments
  21579.  
  21580.  L04:            shl     bx,1            ; BX := 2 * dy
  21581.                  mov     VARincr1,bx     ; incr1 := 2 * dy
  21582.                  sub     bx,cx
  21583.                  mov     di,bx           ; DI := d = 2 * dy - dx
  21584.                  sub     bx,cx
  21585.                  mov     VARincr2,bx     ; incr2 := 2 * (dy - dx)
  21586.  
  21587.  ; calculate first pixel address
  21588.  
  21589.                  push    cx              ; preserve this register
  21590.                  mov     ax,ARGy1        ; AX := y
  21591.                  mov     bx,ARGx1        ; BX := x
  21592.                  call    PixelAddr10     ; AH := bit mask
  21593.                                          ; ES:BX -> buffer
  21594.                                          ; CL := # bits to shift left
  21595.  
  21596.                  mov     al,ARGn         ; AL := unshifted pixel value
  21597.                  shl     ax,cl           ; AH := bit mask in proper position
  21598.                                          ; AL := pixel value in proper
  21599.                                          ;        position
  21600.  
  21601.                  mov     dx,ax           ; DH := bit mask
  21602.                                          ; DL := pixel value
  21603.                  not     dh              ; DH := inverse bit mask
  21604.  
  21605.                  pop     cx              ; restore this register
  21606.                  inc     cx              ; CX := # of pixels to draw
  21607.  
  21608.                  jmp     VARroutine      ; jump to appropriate routine for
  21609.                                          ;  slope
  21610.  
  21611.  ; routine for vertical lines
  21612.  
  21613.  VertLine11:     mov     ax,ARGy1        ; AX := y1
  21614.                  mov     bx,ARGy2        ; BX := y2
  21615.                  mov     cx,bx
  21616.                  sub     cx,ax           ; CX := dy
  21617.                  jge     L31             ; jump if dy >= 0
  21618.  
  21619.                  neg     cx              ; force dy >= 0
  21620.                  mov     ax,bx           ; AX := y2
  21621.  
  21622.  L31:            inc     cx              ; CX := # of pixels to draw
  21623.                  mov     bx,ARGx1        ; BX := x
  21624.                  push    cx              ; preserve this register
  21625.                  call    PixelAddr10     ; AH := bit mask
  21626.                                          ; ES:BX -> video buffer
  21627.                                          ; CL := # bits to shift left
  21628.                  mov     al,ARGn         ; AL := pixel value
  21629.                  shl     ax,cl           ; AH := bit mask in proper position
  21630.                                          ; AL := pixel value in proper
  21631.                                          ;        position
  21632.                  not     ah              ; AH := inverse bit mask
  21633.                  pop     cx              ; restore this register
  21634.  
  21635.  ; draw the line
  21636.  
  21637.                  test    al,al
  21638.                  jz      L33             ; jump if pixel value = 0
  21639.  
  21640.  L32:            or      es:[bx],al      ; set pixel values in buffer
  21641.                  add     bx,si
  21642.                  loop    L32
  21643.                  jmp     short L34
  21644.  
  21645.  L33:            and     es:[bx],ah      ; reset pixel values in buffer
  21646.                  add     bx,si
  21647.                  loop    L33
  21648.  
  21649.  L34:            jmp     Lexit
  21650.  
  21651.  
  21652.  ; routine for horizontal lines (slope = 0)
  21653.  
  21654.  HorizLine11:    mov     ax,ARGy1
  21655.                  mov     bx,ARGx1
  21656.                  call    PixelAddr10     ; AH := bit mask
  21657.                                          ; ES:BX -> video buffer
  21658.                                          ; CL := # bits to shift left
  21659.                  mov     di,bx           ; ES:DI -> buffer
  21660.  
  21661.                  mov     dh,ah
  21662.                  not     dh              ; DH := unshifted bit mask for
  21663.                                          ;        leftmost byte
  21664.                  mov     dl,0FFh         ; DL := unshifted bit mask for
  21665.                                          ;        rightmost byte
  21666.  
  21667.                  shl     dh,cl           ; DH := reverse bit mask for first
  21668.                                          ;        byte
  21669.                  not     dh              ; DH := bit mask for first byte
  21670.  
  21671.                  mov     cx,ARGx2
  21672.                  and     cl,7
  21673.                  xor     cl,7            ; CL := number of bits to shift
  21674.                                          ;        left
  21675.                  shl     dl,cl           ; DL := bit mask for last byte
  21676.  
  21677.  ; determine byte offset of first and last pixel in the line
  21678.  
  21679.                  mov     ax,ARGx2        ; AX := x2
  21680.                  mov     bx,ARGx1        ; BX := x1
  21681.  
  21682.                  mov     cl,ByteOffsetShift      ; number of bits to shift
  21683.                                                  ;  to convert pixels to
  21684.                                                  ;  bytes
  21685.  
  21686.                  shr     ax,cl           ; AX := byte offset of x2
  21687.                  shr     bx,cl           ; BX := byte offset of x1
  21688.                  mov     cx,ax
  21689.                  sub     cx,bx           ; CX := (# bytes in line) - 1
  21690.  
  21691.  ; propagate pixel value throughout one byte
  21692.  
  21693.                  mov     bx,offset DGROUP:PropagatedPixel
  21694.                  mov     al,ARGn         ; AL := pixel value
  21695.                  xlat
  21696.  
  21697.  ; set pixels in leftmost byte of the line
  21698.  
  21699.                  or      dh,dh
  21700.                  js      L43             ; jump if byte-aligned (x1 is
  21701.                                          ;  leftmost
  21702.                                          ;  pixel in byte)
  21703.                  or      cx,cx
  21704.                  jnz     L42             ; jump if more than one byte in the
  21705.                                          ;  line
  21706.  
  21707.                  and     dl,dh           ; bit mask for the line
  21708.                  jmp     short L44
  21709.  
  21710.  L42:            mov     ah,al
  21711.                  and     ah,dh           ; AH := masked pixel bits
  21712.                  not     dh              ; DH := reverse bit mask for 1st
  21713.                                          ;        byte
  21714.                  and     es:[di],dh      ; zero masked pixels in buffer
  21715.                  or      es:[di],ah      ; update masked pixels in buffer
  21716.                  inc     di
  21717.                  dec     cx
  21718.  
  21719.  ; use a fast 8086 machine instruction to draw the remainder of the line
  21720.  
  21721.  L43:            rep     stosb           ; update all pixels in the line
  21722.  
  21723.  ; set pixels in the rightmost byte of the line
  21724.  
  21725.  L44:            and     al,dl           ; AL := masked pixels for last byte
  21726.                  not     dl
  21727.                  and     es:[di],dl      ; zero masked pixels in buffer
  21728.                  or      es:[di],al      ; update masked pixels in buffer
  21729.  
  21730.                  jmp     Lexit
  21731.  
  21732.  
  21733.  ; routine for dy <= dx (slope <= 1)     ; ES:BX -> video buffer
  21734.                                          ; CX = # pixels to draw
  21735.                                          ; DH = inverse bit mask
  21736.                                          ; DL = pixel value in proper
  21737.                                          ;       position
  21738.                                          ; SI = bytes per pixel row
  21739.                                          ; DI = decision variable
  21740.  
  21741.  LoSlopeLine11:
  21742.  
  21743.  L10:            mov     ah,es:[bx]      ; AH := byte from video buffer
  21744.  
  21745.  L11:            and     ah,dh           ; zero pixel value at current bit
  21746.                                          ;  offset
  21747.                  or      ah,dl           ; set pixel value in byte
  21748.  
  21749.                  ror     dl,1            ; rotate pixel value
  21750.                  ror     dh,1            ; rotate bit mask
  21751.                  jnc     L14             ; jump if bit mask rotated to
  21752.                                          ;  leftmost pixel position
  21753.  
  21754.  ; bit mask not shifted out
  21755.  
  21756.                  or      di,di           ; test sign of d
  21757.                  jns     L12             ; jump if d >= 0
  21758.  
  21759.                  add     di,VARincr1     ; d := d + incr1
  21760.                  loop    L11
  21761.  
  21762.                  mov     es:[bx],ah      ; store remaining pixels in buffer
  21763.                  jmp     short Lexit
  21764.  
  21765.  L12:            add     di,VARincr2     ; d := d + incr2
  21766.                  mov     es:[bx],ah      ; update buffer
  21767.  
  21768.                  add     bx,si           ; increment y
  21769.                  loop    L10
  21770.                  jmp     short Lexit
  21771.  
  21772.  ; bit mask shifted out
  21773.  
  21774.  L14:            mov     es:[bx],ah      ; update buffer
  21775.                  inc     bx              ; BX := offset of next byte
  21776.  
  21777.                  or      di,di           ; test sign of d
  21778.                  jns     L15             ; jump if non-negative
  21779.  
  21780.                  add     di,VARincr1     ; d := d + incr1
  21781.                  loop    L10
  21782.                  jmp     short Lexit
  21783.  
  21784.  L15:            add     di,VARincr2     ; d := d + incr2
  21785.  
  21786.                  add     bx,si           ; increment y
  21787.                  loop    L10
  21788.                  jmp     short Lexit
  21789.  
  21790.  ; routine for dy > dx (slope > 1)       ; ES:BX -> video buffer
  21791.                                          ; CX = # pixels to draw
  21792.                                          ; DH = inverse bit mask
  21793.                                          ; DL = pixel value in proper
  21794.                                          ;       position
  21795.                                          ; SI = bytes per pixel row
  21796.                                          ; DI = decision variable
  21797.  HiSlopeLine11:
  21798.  
  21799.  L21:            and     es:[bx],dh      ; zero pixel value in video buffer
  21800.                  or      es:[bx],dl      ; set pixel value in byte
  21801.  
  21802.                  add     bx,si           ; increment y
  21803.  
  21804.  L22:            or      di,di           ; test sign of d
  21805.                  jns     L23             ; jump if d >= 0
  21806.  
  21807.                  add     di,VARincr1     ; d := d + incr1
  21808.                  loop    L21
  21809.  
  21810.                  jmp     short Lexit
  21811.  
  21812.  
  21813.  L23:            add     di,VARincr2     ; d := d + incr2
  21814.  
  21815.                  ror     dl,1            ; rotate pixel value
  21816.                  ror     dh,1            ; rotate bit mask
  21817.                  cmc                     ; cf set if bit mask not rotated to
  21818.                                          ;  leftmost pixel position
  21819.  
  21820.                  adc     bx,0            ; BX := offset of next byte
  21821.  
  21822.                  loop    L21
  21823.  
  21824.  
  21825.  Lexit:          pop     di              ; restore caller registers and
  21826.                                          ;  return
  21827.                  pop     si
  21828.                  mov     sp,bp
  21829.                  pop     bp
  21830.                  ret
  21831.  
  21832.  _Line11         ENDP
  21833.  
  21834.  _TEXT           ENDS
  21835.  
  21836.  
  21837.  _DATA           SEGMENT word public 'DATA'
  21838.  
  21839.  PropagatedPixel DB      00000000b       ; 0
  21840.                  DB      11111111b       ; 1
  21841.  
  21842.  _DATA           ENDS
  21843.  
  21844.                  END
  21845.  
  21846.  
  21847.  
  21848.  ───────────────────────────────────────────────────────────────────────────
  21849.  Listing 6-9.  A Line-drawing routine for MCGA and VGA 320-by-200
  21850.                256-color mode.
  21851.  ───────────────────────────────────────────────────────────────────────────
  21852.  
  21853.                  TITLE   'Listing 6-9'
  21854.                  NAME    Line13
  21855.                  PAGE    55,132
  21856.  
  21857.  ;
  21858.  ; Name:         Line13
  21859.  ;
  21860.  ; Function:     Draw a line in MCGA/VGA 320x200 256-color mode
  21861.  ;
  21862.  
  21863.  ; Caller:       Microsoft C:
  21864.  ;
  21865.  ;                       void Line13(x1,y1,x2,y2,n);
  21866.  ;
  21867.  ;                       int x1,y1,x2,y2;        /* pixel coordinates */
  21868.  ;
  21869.  ;                       int n;                  /* pixel value */
  21870.  ;
  21871.  
  21872.  ARGx1           EQU     word ptr [bp+4] ; stack frame addressing
  21873.  ARGy1           EQU     word ptr [bp+6]
  21874.  ARGx2           EQU     word ptr [bp+8]
  21875.  ARGy2           EQU     word ptr [bp+10]
  21876.  ARGn            EQU     byte ptr [bp+12]
  21877.  VARincr1        EQU     word ptr [bp-6]
  21878.  VARincr2        EQU     word ptr [bp-8]
  21879.  VARroutine      EQU     word ptr [bp-10]
  21880.  
  21881.  BytesPerLine    EQU     320
  21882.  
  21883.  
  21884.  _TEXT           SEGMENT byte public 'CODE'
  21885.                  ASSUME  cs:_TEXT
  21886.  
  21887.                  EXTRN   PixelAddr13:near
  21888.  
  21889.                  PUBLIC  _Line13
  21890.  _Line13         PROC    near
  21891.  
  21892.                  push    bp              ; preserve caller registers
  21893.                  mov     bp,sp
  21894.                  sub     sp,6            ; stack space for local variables
  21895.                  push    si
  21896.                  push    di
  21897.  
  21898.  ; check for vertical line
  21899.  
  21900.                  mov     si,BytesPerLine ; initial y-increment
  21901.  
  21902.                  mov     cx,ARGx2
  21903.                  sub     cx,ARGx1        ; CX := x2 - x1
  21904.                  jz      VertLine13      ; jump if vertical line
  21905.  
  21906.  ; force x1 < x2
  21907.  
  21908.                  jns     L01             ; jump if x2 > x1
  21909.  
  21910.                  neg     cx              ; CX := x1 - x2
  21911.  
  21912.                  mov     bx,ARGx2        ; exchange x1 and x2
  21913.                  xchg    bx,ARGx1
  21914.                  mov     ARGx2,bx
  21915.  
  21916.                  mov     bx,ARGy2        ; exchange y1 and y2
  21917.  
  21918.                  xchg    bx,ARGy1
  21919.                  mov     ARGy2,bx
  21920.  
  21921.  
  21922.  ; calculate dy = ABS(y2-y1)
  21923.  
  21924.  L01:            mov     bx,ARGy2
  21925.                  sub     bx,ARGy1        ; BX := y2 - y1
  21926.                  jz      HorizLine13     ; jump if horizontal line
  21927.  
  21928.                  jns     L03             ; jump if slope is positive
  21929.  
  21930.                  neg     bx              ; BX := y1 - y2
  21931.                  neg     si              ; negate y-increment
  21932.  
  21933.  ; select appropriate routine for slope of line
  21934.  
  21935.  L03:            push    si              ; preserve y-increment
  21936.  
  21937.                  mov     VARroutine,offset LoSlopeLine13
  21938.                  cmp     bx,cx
  21939.                  jle     L04             ; jump if dy <= dx (slope <= 1)
  21940.                  mov     VARroutine,offset HiSlopeLine13
  21941.                  xchg    bx,cx           ; exchange dy and dx
  21942.  
  21943.  ; calculate initial decision variable and increments
  21944.  
  21945.  L04:            shl     bx,1            ; BX := 2 * dy
  21946.                  mov     VARincr1,bx     ; incr1 := 2 * dy
  21947.                  sub     bx,cx
  21948.                  mov     si,bx           ; SI := d = 2 * dy - dx
  21949.  
  21950.                  sub     bx,cx
  21951.                  mov     VARincr2,bx     ; incr2 := 2 * (dy - dx)
  21952.  
  21953.  ; calculate first pixel address
  21954.  
  21955.                  push    cx              ; preserve this register
  21956.                  mov     ax,ARGy1        ; AX := y
  21957.                  mov     bx,ARGx1        ; BX := x
  21958.                  call    PixelAddr13     ; ES:BX -> buffer
  21959.  
  21960.                  mov     di,bx           ; ES:DI -> buffer
  21961.  
  21962.                  pop     cx              ; restore this register
  21963.                  inc     cx              ; CX := # of pixels to draw
  21964.  
  21965.                  pop     bx              ; BX := y-increment
  21966.                  jmp     VARroutine      ; jump to appropriate routine for
  21967.                                          ;  slope
  21968.  
  21969.  
  21970.  ; routine for vertical lines
  21971.  
  21972.  VertLine13:     mov     ax,ARGy1        ; AX := y1
  21973.                  mov     bx,ARGy2        ; BX := y2
  21974.                  mov     cx,bx
  21975.                  sub     cx,ax           ; CX := dy
  21976.                  jge     L31             ; jump if dy >= 0
  21977.  
  21978.                  neg     cx              ; force dy >= 0
  21979.                  mov     ax,bx           ; AX := y2
  21980.  
  21981.  L31:            inc     cx              ; CX := # of pixels to draw
  21982.                  mov     bx,ARGx1        ; BX := x
  21983.                  push    cx              ; preserve this register
  21984.                  call    PixelAddr13     ; ES:BX -> video buffer
  21985.                  pop     cx
  21986.  
  21987.                  mov     di,bx           ; ES:DI -> video buffer
  21988.                  dec     si              ; SI := bytes/line - 1
  21989.  
  21990.                  mov     al,ARGn         ; AL := pixel value
  21991.  
  21992.  L32:            stosb                   ; set pixel value in buffer
  21993.                  add     di,si           ; increment to next line
  21994.                  loop    L32
  21995.  
  21996.                  jmp     Lexit
  21997.  
  21998.  
  21999.  ; routine for horizontal lines (slope = 0)
  22000.  
  22001.  HorizLine13:
  22002.                  push    cx              ; preserve CX
  22003.                  mov     ax,ARGy1
  22004.                  mov     bx,ARGx1
  22005.                  call    PixelAddr13     ; ES:BX -> video buffer
  22006.                  mov     di,bx           ; ES:DI -> buffer
  22007.  
  22008.                  pop     cx
  22009.                  inc     cx              ; CX := number of pixels to draw
  22010.  
  22011.                  mov     al,ARGn         ; AL := pixel value
  22012.  
  22013.                  rep     stosb           ; update the video buffer
  22014.  
  22015.                  jmp     short Lexit
  22016.  
  22017.  
  22018.  ; routine for dy <= dx (slope <= 1)     ; ES:DI -> video buffer
  22019.                                          ; BX = y-increment
  22020.                                          ; CX = # pixels to draw
  22021.                                          ; SI = decision variable
  22022.  LoSlopeLine13:
  22023.  
  22024.                  mov     al,ARGn         ; AL := pixel value
  22025.  
  22026.  L11:            stosb                   ; store pixel, increment x
  22027.  
  22028.                  or      si,si           ; test sign of d
  22029.                  jns     L12             ; jump if d >= 0
  22030.  
  22031.                  add     si,VARincr1     ; d := d + incr1
  22032.                  loop    L11
  22033.                  jmp     short Lexit
  22034.  
  22035.  L12:            add     si,VARincr2     ; d := d + incr2
  22036.                  add     di,bx           ; increment y
  22037.                  loop    L11
  22038.                  jmp     short Lexit
  22039.  
  22040.  
  22041.  ; routine for dy > dx (slope > 1)       ; ES:DI -> video buffer
  22042.                                          ; BX = y-increment
  22043.                                          ; CX = # pixels to draw
  22044.                                          ; SI = decision variable
  22045.  HiSlopeLine13:
  22046.                  mov     al,ARGn         ; AL := pixel value
  22047.  
  22048.  L21:            stosb                   ; update next pixel, increment x
  22049.  
  22050.                  add     di,bx           ; increment y
  22051.  
  22052.  L22:            or      si,si           ; test sign of d
  22053.                  jns     L23             ; jump if d >= 0
  22054.  
  22055.                  add     si,VARincr1     ; d := d + incr1
  22056.                  dec     di              ; decrement x (already incremented
  22057.                                          ;  by stosb)
  22058.                  loop    L21
  22059.                  jmp     short Lexit
  22060.  
  22061.  
  22062.  L23:            add     si,VARincr2     ; d := d + incr2
  22063.                  loop    L21
  22064.  
  22065.  
  22066.  Lexit:          pop     di              ; restore registers and return
  22067.                  pop     si
  22068.                  mov     sp,bp
  22069.                  pop     bp
  22070.                  ret
  22071.  
  22072.  _Line13         ENDP
  22073.  
  22074.  _TEXT           ENDS
  22075.  
  22076.                  END
  22077.  
  22078.  
  22079.  
  22080.  ───────────────────────────────────────────────────────────────────────────
  22081.  Listing 6-10.  A line-drawing routine for the InColor Card's 720-by-348
  22082.                 16-color mode.
  22083.  ───────────────────────────────────────────────────────────────────────────
  22084.  
  22085.                  TITLE   'Listing 6-10'
  22086.                  NAME    LineInC
  22087.                  PAGE    55,132
  22088.  
  22089.  ;
  22090.  ; Name:         LineInC
  22091.  ;
  22092.  ; Function:     Draw a line in Hercules InColor 720x348 16-color mode
  22093.  ;
  22094.  ; Caller:       Microsoft C:
  22095.  ;
  22096.  ;                       void LineInC(x1,y1,x2,y2,n);
  22097.  ;
  22098.  ;                       int x1,y1,x2,y2;        /* pixel coordinates */
  22099.  ;
  22100.  ;                       int n;                  /* pixel value */
  22101.  ;
  22102.  
  22103.  ARGx1           EQU     word ptr [bp+4] ; stack frame addressing
  22104.  ARGy1           EQU     word ptr [bp+6]
  22105.  ARGx2           EQU     word ptr [bp+8]
  22106.  ARGy2           EQU     word ptr [bp+10]
  22107.  ARGn            EQU     byte ptr [bp+12]
  22108.  VARleafincr     EQU     word ptr [bp-6]
  22109.  VARincr1        EQU     word ptr [bp-8]
  22110.  VARincr2        EQU     word ptr [bp-10]
  22111.  VARroutine      EQU     word ptr [bp-12]
  22112.  
  22113.  ByteOffsetShift EQU     3               ; used to convert pixels to byte
  22114.                                          ;  offset
  22115.  DefaultRWColor  EQU     0Fh             ; default value for R/W Color
  22116.                                          ;  register
  22117.  
  22118.  
  22119.  _TEXT           SEGMENT byte public 'CODE'
  22120.                  ASSUME  cs:_TEXT
  22121.  
  22122.                  EXTRN   PixelAddrHGC:near
  22123.  
  22124.                  PUBLIC  _LineInC
  22125.  _LineInC        PROC    near
  22126.  
  22127.                  push    bp              ; preserve caller registers
  22128.                  mov     bp,sp
  22129.                  sub     sp,8            ; stack space for local variables
  22130.                  push    si
  22131.                  push    di
  22132.  
  22133.                  mov     si,2000h        ; increment for video buffer
  22134.                                          ;  interleave
  22135.                  mov     di,90-8000h     ; increment from last to first
  22136.                                          ;  interleave
  22137.  
  22138.  ; set up InColor control registers
  22139.  
  22140.                  mov     dx,3B4h         ; DX := CRTC I/O port
  22141.                  mov     ax,5F19h        ; AH bit 6 := 1 (Mask Polarity)
  22142.                                          ; AH bits 5-4 := 1 (Write Mode)
  22143.                                          ; AH bits 3-0 := "don't care" bits
  22144.                                          ; AL := R/W Control Register number
  22145.                  out     dx,ax           ; set R/W Control Register
  22146.  
  22147.                  inc     ax              ; AL := 1Ah (R/W Color Reg number)
  22148.                  mov     ah,ARGn         ; AH := foreground value
  22149.                  out     dx,ax           ; set R/W color register
  22150.  
  22151.  
  22152.                  mov     cx,ARGx2
  22153.                  sub     cx,ARGx1        ; CX := x2 - x1
  22154.                  jz      VertLineInC     ; jump if vertical line
  22155.  
  22156.  ; force x1 < x2
  22157.  
  22158.                  jns     L01             ; jump if x2 > x1
  22159.  
  22160.                  neg     cx              ; CX := x1 - x2
  22161.  
  22162.                  mov     bx,ARGx2        ; exchange x1 and x2
  22163.                  xchg    bx,ARGx1
  22164.                  mov     ARGx2,bx
  22165.  
  22166.                  mov     bx,ARGy2        ; exchange y1 and y2
  22167.                  xchg    bx,ARGy1
  22168.                  mov     ARGy2,bx
  22169.  
  22170.  ; calculate dy = ABS(y2-y1)
  22171.  
  22172.  L01:            mov     bx,ARGy2
  22173.                  sub     bx,ARGy1        ; BX := y2 - y1
  22174.                  jz      HorizLineInC    ; jump if horizontal line
  22175.  
  22176.                  jns     L03
  22177.  
  22178.                  neg     bx              ; BX := y1 - y2
  22179.                  neg     si              ; negate increments for buffer
  22180.                                          ;  interleave
  22181.                  neg     di
  22182.  
  22183.  ; select appropriate routine for slope of line
  22184.  
  22185.  L03:            mov     VARleafincr,di  ; save increment for buffer
  22186.                                          ;  interleave
  22187.  
  22188.                  mov     VARroutine,offset LoSlopeLineInC
  22189.                  cmp     bx,cx
  22190.                  jle     L04             ; jump if dy <= dx (slope <= 1)
  22191.                  mov     VARroutine,offset HiSlopeLineInC
  22192.                  xchg    bx,cx           ; exchange dy and dx
  22193.  
  22194.  ; calculate initial decision variable and increments
  22195.  
  22196.  L04:            shl     bx,1            ; BX := 2 * dy
  22197.                  mov     VARincr1,bx     ; incr1 := 2 * dy
  22198.                  sub     bx,cx
  22199.                  mov     di,bx           ; DI := d = 2 * dy - dx
  22200.                  sub     bx,cx
  22201.                  mov     VARincr2,bx     ; incr2 := 2 * (dy - dx)
  22202.  
  22203.  ; calculate first pixel address
  22204.  
  22205.                  push    cx              ; preserve this register
  22206.                  mov     ax,ARGy1        ; AX := y
  22207.                  mov     bx,ARGx1        ; BX := x
  22208.                  call    PixelAddrHGC    ; AH := bit mask
  22209.                                          ; ES:BX -> buffer
  22210.                                          ; CL := # bits to shift left
  22211.  
  22212.                  shl     ah,cl
  22213.                  mov     dh,ah           ; DH := bit mask in proper position
  22214.  
  22215.                  pop     cx              ; restore this register
  22216.                  inc     cx              ; CX := # of pixels to draw
  22217.  
  22218.                  jmp     VARroutine      ; jump to appropriate routine for
  22219.                                          ;  slope
  22220.  
  22221.  
  22222.  ; routine for vertical lines
  22223.  
  22224.  VertLineInC:    mov     ax,ARGy1        ; AX := y1
  22225.                  mov     bx,ARGy2        ; BX := y2
  22226.                  mov     cx,bx
  22227.                  sub     cx,ax           ; CX := dy
  22228.                  jge     L31             ; jump if dy >= 0
  22229.  
  22230.                  neg     cx              ; force dy >= 0
  22231.                  mov     ax,bx           ; AX := y2
  22232.  
  22233.  L31:            inc     cx              ; CX := # of pixels to draw
  22234.                  mov     bx,ARGx1        ; BX := x
  22235.                  push    cx              ; preserve this register
  22236.                  call    PixelAddrHGC    ; AH := bit mask
  22237.                                          ; ES:BX -> video buffer
  22238.                                          ; CL := # bits to shift left
  22239.                  shl     ah,cl           ; AH := bit mask in proper position
  22240.                  pop     cx              ; restore this register
  22241.  
  22242.  L32:            or      es:[bx],ah      ; update pixel in buffer
  22243.  
  22244.                  add     bx,si           ; increment to next portion of
  22245.                                          ;  interleave
  22246.                  jns     L33
  22247.  
  22248.                  add     bx,di           ; increment to first portion of
  22249.                                          ;  interleave
  22250.  
  22251.  L33:            loop    L32
  22252.  
  22253.                  jmp     Lexit
  22254.  
  22255.  ; routine for horizontal lines (slope = 0)
  22256.  
  22257.  HorizLineInC:   mov     ax,ARGy1
  22258.                  mov     bx,ARGx1
  22259.                  call    PixelAddrHGC    ; AH := bit mask
  22260.                                          ; ES:BX -> video buffer
  22261.                                          ; CL := # bits to shift left
  22262.                  mov     di,bx           ; ES:DI -> buffer
  22263.                  mov     dh,ah
  22264.                  not     dh              ; DH := unshifted bit mask for
  22265.                                          ;        leftmost byte
  22266.                  mov     dl,0FFh         ; DL := unshifted bit mask for
  22267.                                          ;        rightmost byte
  22268.  
  22269.                  shl     dh,cl           ; DH := reverse bit mask for first
  22270.                                          ;        byte
  22271.                  not     dh              ; DH := bit mask for first byte
  22272.                  mov     cx,ARGx2
  22273.                  and     cl,7
  22274.                  xor     cl,7            ; CL := number of bits to shift
  22275.                                          ;        left
  22276.                  shl     dl,cl           ; DL := bit mask for last byte
  22277.  
  22278.  ; determine byte offset of first and last pixel in the line
  22279.  
  22280.                  mov     ax,ARGx2        ; AX := x2
  22281.                  mov     bx,ARGx1        ; BX := x1
  22282.  
  22283.                  mov     cl,ByteOffsetShift      ; number of bits to shift
  22284.                                                  ;  to convert pixels to
  22285.                                                  ;  bytes
  22286.  
  22287.                  shr     ax,cl           ; AX := byte offset of x2
  22288.                  shr     bx,cl           ; BX := byte offset of x1
  22289.                  mov     cx,ax
  22290.                  sub     cx,bx           ; CX := (# bytes in line) - 1
  22291.  
  22292.  ; set pixels in leftmost byte of the line
  22293.  
  22294.                  or      dh,dh
  22295.                  js      L43             ; jump if byte-aligned (x1 is
  22296.                                          ;  leftmost pixel in byte)
  22297.                  or      cx,cx
  22298.                  jnz     L42             ; jump if more than one byte in the
  22299.                                          ;  line
  22300.  
  22301.                  and     dl,dh           ; bit mask for the line
  22302.                  jmp     short L44
  22303.  
  22304.  L42:            or      es:[di],dh      ; update masked pixels in buffer
  22305.                  inc     di
  22306.                  dec     cx
  22307.  
  22308.  ; use a fast 8086 machine instruction to draw the remainder of the line
  22309.  
  22310.  L43:            mov     al,0FFh         ; 8-pixel bit mask
  22311.                  rep     stosb           ; update all pixels in the line
  22312.  
  22313.  ; set pixels in the rightmost byte of the line
  22314.  
  22315.  L44:            or      es:[di],dl      ; update masked pixels in buffer
  22316.                  jmp     Lexit
  22317.  
  22318.  ; routine for dy <= dx (slope <= 1)     ; ES:BX -> video buffer
  22319.                                          ; CX = # pixels to draw
  22320.                                          ; DH = bit mask
  22321.                                          ; SI = buffer interleave increment
  22322.                                          ; DI = decision variable
  22323.  LoSlopeLineInC:
  22324.  
  22325.  L10:            mov     ah,es:[bx]      ; latch bit planes
  22326.                                          ; AH := 0 because all planes
  22327.                                          ;  are "don't care"
  22328.  
  22329.  L11:            or      ah,dh           ; set pixel value in byte
  22330.  
  22331.                  ror     dh,1            ; rotate bit mask
  22332.                  jc      L14             ; jump if bit mask rotated to
  22333.                                          ;  leftmost pixel position
  22334.  
  22335.  ; bit mask not shifted out
  22336.  
  22337.                  or      di,di           ; test sign of d
  22338.                  jns     L12             ; jump if d >= 0
  22339.  
  22340.                  add     di,VARincr1     ; d := d + incr1
  22341.                  loop    L11
  22342.  
  22343.                  mov     es:[bx],ah      ; store remaining pixels in buffer
  22344.                  jmp     short Lexit
  22345.  
  22346.  L12:            add     di,VARincr2     ; d := d + incr2
  22347.                  mov     es:[bx],ah      ; update buffer
  22348.  
  22349.                  add     bx,si           ; increment y
  22350.                  jns     L13             ; jump if not in last interleave
  22351.  
  22352.                  add     bx,VARleafincr  ; increment into next interleave
  22353.  
  22354.  L13:            loop    L10
  22355.                  jmp     short Lexit
  22356.  
  22357.  ; bit mask shifted out
  22358.  
  22359.  L14:            mov     es:[bx],ah      ; update buffer
  22360.                  inc     bx              ; BX := offset of next byte
  22361.  
  22362.                  or      di,di           ; test sign of d
  22363.                  jns     L15             ; jump if non-negative
  22364.  
  22365.                  add     di,VARincr1     ; d := d + incr1
  22366.                  loop    L10
  22367.                  jmp     short Lexit
  22368.  
  22369.  L15:            add     di,VARincr2     ; d := d + incr2
  22370.  
  22371.                  add     bx,si           ; increment y
  22372.                  jns     L16             ; jump if not in last interleave
  22373.  
  22374.                  add     bx,VARleafincr  ; increment into next interleave
  22375.  
  22376.  L16:            loop    L10             ; loop until all pixels are set
  22377.                  jmp     short Lexit
  22378.  
  22379.  
  22380.  ; routine for dy > dx (slope > 1)       ; ES:BX -> video buffer
  22381.                                          ; CX = # pixels to draw
  22382.                                          ; DH = bit mask
  22383.                                          ; SI = buffer interleave increment
  22384.                                          ; DI = decision variable
  22385.  HiSlopeLineInC:
  22386.  
  22387.  L21:            or      es:[bx],dh      ; set pixel value in video buffer
  22388.  
  22389.                  add     bx,si           ; increment y
  22390.                  jns     L22             ; jump if not in last interleave
  22391.  
  22392.                  add     bx,VARleafincr  ; increment into next interleave
  22393.  
  22394.  L22:            or      di,di
  22395.                  jns     L23             ; jump if d >= 0
  22396.  
  22397.                  add     di,VARincr1     ; d := d + incr1
  22398.                  loop    L21
  22399.                  jmp     short Lexit
  22400.  
  22401.  
  22402.  L23:            add     di,VARincr2     ; d := d + incr2
  22403.  
  22404.                  ror     dh,1            ; rotate bit mask
  22405.                  adc     bx,0            ; BX := offset of next byte
  22406.                                          ;  (incremented if bit mask rotated
  22407.                                          ;  to leftmost pixel position)
  22408.  
  22409.                  loop    L21
  22410.                  jmp     short Lexit
  22411.  
  22412.  
  22413.  Lexit:          mov     dx,3B4h         ; DX := CRTC I/O port
  22414.                  mov     ax,0F18h
  22415.                  out     dx,ax           ; restore default Plane Mask value
  22416.  
  22417.                  mov     ax,4019h        ; restore default R/W Control value
  22418.                  out     dx,ax
  22419.  
  22420.                  inc     ax              ; restore default R/W Color value
  22421.                  mov     ah,DefaultRWColor
  22422.                  out     dx,ax
  22423.  
  22424.                  pop     di              ; restore registers and return
  22425.                  pop     si
  22426.                  mov     sp,bp
  22427.                  pop     bp
  22428.                  ret
  22429.  
  22430.  _LineInC        ENDP
  22431.  
  22432.  _TEXT           ENDS
  22433.  
  22434.                  END
  22435.  
  22436.  
  22437.  
  22438.  ───────────────────────────────────────────────────────────────────────────
  22439.  Listing 6-11.  An implementation of the Sutherland-Cohen clipping
  22440.                 algorithm.
  22441.  ───────────────────────────────────────────────────────────────────────────
  22442.  
  22443.  /* Listing 6-11 */
  22444.  
  22445.  struct  EndpointStruct          /* endpoints for clipped line */
  22446.  {
  22447.    int   x1,y1;
  22448.    int   x2,y2;
  22449.  };
  22450.  
  22451.  struct  RegionStruct            /* rectangular clipping region */
  22452.  {
  22453.    int   Xul;
  22454.    int   Yul;
  22455.    int   Xlr;
  22456.    int   Ylr;
  22457.  };
  22458.  
  22459.  
  22460.  union OutcodeUnion      /* outcodes are represented as bit fields */
  22461.  {
  22462.    struct
  22463.    {
  22464.      unsigned code0 : 1;         /* x < Xul */
  22465.      unsigned code1 : 1;         /* y < Yul */
  22466.      unsigned code2 : 1;         /* x > Xlr */
  22467.      unsigned code3 : 1;         /* y > Ylr */
  22468.    }
  22469.          ocs;
  22470.  
  22471.    int   outcodes;
  22472.  };
  22473.  
  22474.  
  22475.  #define X1      ep->x1
  22476.  #define Y1      ep->y1
  22477.  #define X2      ep->x2
  22478.  #define Y2      ep->y2
  22479.  #define XUL     r->Xul
  22480.  #define YUL     r->Yul
  22481.  #define XLR     r->Xlr
  22482.  #define YLR     r->Ylr
  22483.  
  22484.  
  22485.  Clip(ep,r)
  22486.  struct  EndpointStruct  *ep;
  22487.  struct  RegionStruct    *r;
  22488.  {
  22489.          union OutcodeUnion      ocu1,ocu2;
  22490.          int     Inside;
  22491.          int     Outside;
  22492.  
  22493.  
  22494.          /* initialize 4-bit codes */
  22495.  
  22496.          SetOutcodes( &ocu1, r, X1, Y1 );        /* initial 4-bit codes */
  22497.          SetOutcodes( &ocu2, r, X2, Y2 );
  22498.  
  22499.          Inside  = ((ocu1.outcodes | ocu2.outcodes) == 0);
  22500.          Outside = ((ocu1.outcodes & ocu2.outcodes) != 0);
  22501.  
  22502.          while (!Outside && !Inside)
  22503.          {
  22504.            if (ocu1.outcodes==0)     /* swap endpoints if necessary so */
  22505.            {                         /*  that (x1,y1) needs to be clipped */
  22506.              Swap( &X1, &X2 );
  22507.              Swap( &Y1, &Y2 );
  22508.              Swap( &ocu1, &ocu2 );
  22509.            }
  22510.  
  22511.  
  22512.            if (ocu1.ocs.code0)                   /* clip left */
  22513.            {
  22514.              Y1 += (long)(Y2-Y1)*(XUL-X1)/(X2-X1);
  22515.              X1 = XUL;
  22516.            }
  22517.  
  22518.            else if (ocu1.ocs.code1)              /* clip above */
  22519.            {
  22520.              X1 += (long)(X2-X1)*(YUL-Y1)/(Y2-Y1);
  22521.              Y1 = YUL;
  22522.            }
  22523.  
  22524.            else if (ocu1.ocs.code2)              /* clip right */
  22525.            {
  22526.              Y1 += (long)(Y2-Y1)*(XLR-X1)/(X2-X1);
  22527.              X1 = XLR;
  22528.            }
  22529.  
  22530.            else if (ocu1.ocs.code3)              /* clip below */
  22531.            {
  22532.              X1 += (long)(X2-X1)*(YLR-Y1)/(Y2-Y1);
  22533.              Y1 = YLR;
  22534.            }
  22535.  
  22536.            SetOutcodes( &ocu1, r, X1, Y1 );      /* update for (x1,y1) */
  22537.  
  22538.            Inside  = ((ocu1.outcodes | ocu2.outcodes) == 0); /* update  */
  22539.            Outside = ((ocu1.outcodes & ocu2.outcodes) != 0); /*  4-bit  */
  22540.                                                              /*   codes  */
  22541.          }
  22542.  
  22543.          return( Inside );
  22544.  }
  22545.  
  22546.  
  22547.  SetOutcodes( u, r, x, y )
  22548.  union OutcodeUnion      *u;
  22549.  struct RegionStruct     *r;
  22550.  int     x,y;
  22551.  {
  22552.          u->outcodes = 0;
  22553.          u->ocs.code0 = (x < XUL);
  22554.          u->ocs.code1 = (y < YUL);
  22555.          u->ocs.code2 = (x > XLR);
  22556.          u->ocs.code3 = (y > YLR);
  22557.  }
  22558.  
  22559.  
  22560.  Swap( pa, pb )
  22561.  int     *pa,*pb;
  22562.  {
  22563.          int     t;
  22564.  
  22565.          t = *pa;
  22566.          *pa = *pb;
  22567.          *pb = t;
  22568.  }
  22569.  
  22570.  
  22571.  
  22572.  ───────────────────────────────────────────────────────────────────────────
  22573.  Listing 7-1.  Drawing an ellipse using the equation of the ellipse.
  22574.  ───────────────────────────────────────────────────────────────────────────
  22575.  
  22576.  /* Listing 7-1 */
  22577.  
  22578.  Ellipse( xc, yc, a0, b0 )       /* using equation of ellipse */
  22579.  int     xc,yc;                  /* center of ellipse */
  22580.  int     a0,b0;                  /* major and minor axes */
  22581.  {
  22582.  
  22583.          double  x = 0;
  22584.          double  y = b0;
  22585.          double  Bsquared = (double)b0 * (double)b0;
  22586.          double  Asquared = (double)a0 * (double)a0;
  22587.          double  sqrt();
  22588.  
  22589.          do                      /* do while dy/dx >= -1 */
  22590.          {
  22591.            y = sqrt( Bsquared - ((Bsquared/Asquared) * x * x) );
  22592.            Set4Pixels( (int)x, (int)y, xc, yc, PixelValue );
  22593.            ++x;
  22594.          }
  22595.          while ( (x <= a0) && (Bsquared*x > Asquared*y) );
  22596.  
  22597.          while (y >= 0)          /* do while dy/dx < -1 */
  22598.          {
  22599.            x = sqrt( Asquared - ((Asquared/Bsquared) * y*y) );
  22600.            Set4Pixels( (int)x, (int)y, xc, yc, PixelValue );
  22601.            --y;
  22602.          }
  22603.  }
  22604.  
  22605.  Set4Pixels( x, y, xc, yc, n )   /* set pixels in 4 quadrants by symmetry */
  22606.  int     x,y;
  22607.  int     xc,yc;
  22608.  int     n;
  22609.  {
  22610.          SPFunc(xc+x, yc+y, n);
  22611.          SPFunc(xc-x, yc+y, n);
  22612.          SPFunc(xc+x, yc-y, n);
  22613.          SPFunc(xc-x, yc-y, n);
  22614.  }
  22615.  
  22616.  
  22617.  
  22618.  ───────────────────────────────────────────────────────────────────────────
  22619.  Listing 7-2.  A high-level implementation of the midpoint algorithm.
  22620.  ───────────────────────────────────────────────────────────────────────────
  22621.  
  22622.  /* Listing 7-2 */
  22623.  
  22624.  Ellipse( xc, yc, a0, b0 )
  22625.  int     xc,yc;                  /* center of ellipse */
  22626.  int     a0,b0;                  /* semiaxes */
  22627.  
  22628.  {
  22629.          int     x = 0;
  22630.          int     y = b0;
  22631.  
  22632.          long    a = a0;                         /* use 32-bit precision */
  22633.          long    b = b0;
  22634.  
  22635.          long    Asquared = a * a;               /* initialize values */
  22636.                                                  /*  outside */
  22637.          long    TwoAsquared = 2 * Asquared;     /*  of loops */
  22638.          long    Bsquared = b * b;
  22639.          long    TwoBsquared = 2 * Bsquared;
  22640.  
  22641.          long    d;
  22642.          long    dx,dy;
  22643.  
  22644.  
  22645.          d = Bsquared - Asquared*b + Asquared/4L;
  22646.          dx = 0;
  22647.          dy = TwoAsquared * b;
  22648.  
  22649.          while (dx<dy)
  22650.          {
  22651.            Set4Pixels( x, y, xc, yc, PixelValue );
  22652.  
  22653.            if (d > 0L)
  22654.            {
  22655.              --y;
  22656.              dy -= TwoAsquared;
  22657.              d -= dy;
  22658.            }
  22659.  
  22660.            ++x;
  22661.            dx += TwoBsquared;
  22662.            d += Bsquared + dx;
  22663.          }
  22664.  
  22665.  
  22666.          d += (3L*(Asquared-Bsquared)/2L - (dx+dy)) / 2L;
  22667.  
  22668.          while (y>=0)
  22669.          {
  22670.            Set4Pixels( x, y, xc, yc, PixelValue );
  22671.  
  22672.            if (d < 0L)
  22673.            {
  22674.              ++x;
  22675.              dx += TwoBsquared;
  22676.              d += dx;
  22677.            }
  22678.  
  22679.            --y;
  22680.            dy -= TwoAsquared;
  22681.            d += Asquared - dy;
  22682.          }
  22683.  }
  22684.  
  22685.  Set4Pixels( x, y, xc, yc, n )       /* set pixels by symmetry in 4 */
  22686.                                      /*  quadrants */
  22687.  int     x,y;
  22688.  int     xc,yc;
  22689.  int     n;
  22690.  {
  22691.          SetPixel( xc+x, yc+y, n );
  22692.          SetPixel( xc-x, yc+y, n );
  22693.          SetPixel( xc+x, yc-y, n );
  22694.          SetPixel( xc-x, yc-y, n );
  22695.  }
  22696.  
  22697.  
  22698.  
  22699.  ───────────────────────────────────────────────────────────────────────────
  22700.  Listing 7-3.  A modified version of Set4Pixels that avoids updating
  22701.                the same pixel twice.
  22702.  ───────────────────────────────────────────────────────────────────────────
  22703.  
  22704.  /* Listing 7-3 */
  22705.  
  22706.  Set4Pixels( x, y, xc, yc, n )   /* avoids setting the same pixel twice */
  22707.  int     x,y;
  22708.  int     xc,yc;
  22709.  int     n;
  22710.  {
  22711.          if (x!=0)
  22712.          {
  22713.            SetPixel( xc+x, yc+y, n );
  22714.            SetPixel( xc-x, yc+y, n );
  22715.            if (y!=0)
  22716.            {
  22717.              SetPixel( xc+x, yc-y, n );
  22718.              SetPixel( xc-x, yc-y, n );
  22719.            }
  22720.          }
  22721.          else
  22722.          {
  22723.            SetPixel( xc, yc+y, n );
  22724.            if (y!=0)
  22725.              SetPixel( xc, yc-y, n );
  22726.          }
  22727.  }
  22728.  
  22729.  
  22730.  
  22731.  ───────────────────────────────────────────────────────────────────────────
  22732.  Listing 7-4.  An assembly-language implementation of the midpoint
  22733.                algorithm.
  22734.  ───────────────────────────────────────────────────────────────────────────
  22735.  
  22736.                  TITLE   'Listing 7-4'
  22737.                  NAME    Ellipse10
  22738.                  PAGE    55,132
  22739.  
  22740.  ;
  22741.  ; Name:         Ellipse10
  22742.  ;
  22743.  ; Function:     Draw an ellipse in native EGA/VGA graphics modes.
  22744.  ;
  22745.  ; Caller:       Microsoft C:
  22746.  ;
  22747.  ;                       void Ellipse10(xc,yc,a,b,n);
  22748.  ;
  22749.  ;                       int xc,yc;              /* center of ellipse */
  22750.  ;
  22751.  ;                       int a,b;                /* major and minor axes */
  22752.  ;
  22753.  ;                       int n;                  /* pixel value */
  22754.  ;
  22755.  
  22756.  ARGxc           EQU     word ptr [bp+4] ; stack frame addressing
  22757.  ARGyc           EQU     word ptr [bp+6]
  22758.  ARGa            EQU     word ptr [bp+8]
  22759.  ARGb            EQU     word ptr [bp+10]
  22760.  ARGn            EQU     byte ptr [bp+12]
  22761.  
  22762.  ULAddr          EQU     word ptr [bp-6]
  22763.  URAddr          EQU     word ptr [bp-8]
  22764.  LLAddr          EQU     word ptr [bp-10]
  22765.  LRAddr          EQU     word ptr [bp-12]
  22766.  LMask           EQU     byte ptr [bp-14]
  22767.  RMask           EQU     byte ptr [bp-16]
  22768.  
  22769.  VARd            EQU     word ptr [bp-20]
  22770.  VARdx           EQU     word ptr [bp-24]
  22771.  VARdy           EQU     word ptr [bp-28]
  22772.  Asquared        EQU     word ptr [bp-32]
  22773.  Bsquared        EQU     word ptr [bp-36]
  22774.  TwoAsquared     EQU     word ptr [bp-40]
  22775.  TwoBsquared     EQU     word ptr [bp-44]
  22776.  
  22777.  RMWbits         EQU     00h             ; read-modify-write bits
  22778.  BytesPerLine    EQU     80
  22779.  
  22780.  
  22781.  _TEXT           SEGMENT byte public 'CODE'
  22782.                  ASSUME  cs:_TEXT
  22783.  
  22784.                  EXTRN   PixelAddr10:near
  22785.  
  22786.                  PUBLIC  _Ellipse10
  22787.  _Ellipse10      PROC    near
  22788.  
  22789.                  push    bp              ; preserve caller registers
  22790.                  mov     bp,sp
  22791.                  sub     sp,40           ; reserve local stack space
  22792.                  push    si
  22793.                  push    di
  22794.  
  22795.  ; set Graphics Controller Mode register
  22796.  
  22797.                  mov     dx,3CEh         ; DX := Graphics Controller I/O
  22798.                                          ;        port
  22799.                  mov     ax,0005h        ; AL := Mode register number
  22800.                                          ; AH := Write Mode 0 (bits 0,1)
  22801.                  out     dx,ax           ;        Read Mode 0 (bit 4)
  22802.  
  22803.  ; set Data Rotate/Function Select register
  22804.  
  22805.                  mov     ah,RMWbits      ; AH := Read-Modify-Write bits
  22806.                  mov     al,3            ; AL := Data Rotate/Function Select
  22807.                                          ;        reg
  22808.                  out     dx,ax
  22809.  
  22810.  ; set Set/Reset and Enable Set/Reset registers
  22811.  
  22812.                  mov     ah,ARGn         ; AH := pixel value
  22813.                  mov     al,0            ; AL := Set/Reset reg number
  22814.                  out     dx,ax
  22815.  
  22816.                  mov     ax,0F01h        ; AH := value for Enable Set/Reset
  22817.                                          ;        (all bit planes enabled)
  22818.                  out     dx,ax           ; AL := Enable Set/Reset reg number
  22819.  
  22820.  ; initial constants
  22821.  
  22822.                  mov     ax,ARGa
  22823.                  mul     ax
  22824.                  mov     Asquared,ax
  22825.                  mov     Asquared+2,dx   ; a^2
  22826.                  shl     ax,1
  22827.                  rcl     dx,1
  22828.                  mov     TwoAsquared,ax
  22829.                  mov     TwoAsquared+2,dx ; 2 * a^2
  22830.  
  22831.                  mov     ax,ARGb
  22832.                  mul     ax
  22833.                  mov     Bsquared,ax
  22834.                  mov     Bsquared+2,dx   ; b^2
  22835.                  shl     ax,1
  22836.                  rcl     dx,1
  22837.                  mov     TwoBsquared,ax
  22838.                  mov     TwoBsquared+2,dx ; 2 * b^2
  22839.  ;
  22840.  ; plot pixels from (0,b) until dy/dx = -1
  22841.  ;
  22842.  
  22843.  ; initial buffer address and bit mask
  22844.  
  22845.                  mov     ax,BytesPerLine ; AX := video buffer line length
  22846.                  mul     ARGb            ; AX := relative byte offset of b
  22847.                  mov     si,ax
  22848.                  mov     di,ax
  22849.  
  22850.                  mov     ax,ARGyc        ; AX := yc
  22851.                  mov     bx,ARGxc        ; BX := xc
  22852.                  call    PixelAddr10     ; AH := bit mask
  22853.                                          ; ES:BX -> buffer
  22854.                                          ; CL := # bits to shift left
  22855.                  mov     ah,1
  22856.                  shl     ah,cl           ; AH := bit mask for first pixel
  22857.                  mov     LMask,ah
  22858.                  mov     RMask,ah
  22859.  
  22860.                  add     si,bx           ; SI := offset of (0,b)
  22861.                  mov     ULAddr,si
  22862.                  mov     URAddr,si
  22863.                  sub     bx,di           ; AX := offset of (0,-b)
  22864.                  mov     LLAddr,bx
  22865.                  mov     LRAddr,bx
  22866.  
  22867.  ; initial decision variables
  22868.  
  22869.                  xor     ax,ax
  22870.                  mov     VARdx,ax
  22871.                  mov     VARdx+2,ax      ; dx = 0
  22872.  
  22873.                  mov     ax,TwoAsquared
  22874.                  mov     dx,TwoAsquared+2
  22875.                  mov     cx,ARGb
  22876.                  call    LongMultiply    ; perform 32-bit by 16-bit multiply
  22877.                  mov     VARdy,ax
  22878.                  mov     VARdy+2,dx      ; dy = TwoAsquared * b
  22879.  
  22880.                  mov     ax,Asquared
  22881.                  mov     dx,Asquared+2   ; DX:AX = Asquared
  22882.                  sar     dx,1
  22883.                  rcr     ax,1
  22884.                  sar     dx,1
  22885.                  rcr     ax,1            ; DX:AX = Asquared/4
  22886.  
  22887.                  add     ax,Bsquared
  22888.                  adc     dx,Bsquared+2   ; DX:AX = Bsquared + Asquared/4
  22889.                  mov     VARd,ax
  22890.                  mov     VARd+2,dx
  22891.  
  22892.                  mov     ax,Asquared
  22893.                  mov     dx,Asquared+2
  22894.                  mov     cx,ARGb
  22895.                  call    LongMultiply    ; DX:AX = Asquared*b
  22896.                  sub     VARd,ax
  22897.                  sbb     VARd+2,dx       ; d = Bsquared - Asquared*b +
  22898.                                          ;      Asquared/4
  22899.  
  22900.  ; loop until dy/dx >= -1
  22901.  
  22902.                  mov     bx,ARGb         ; BX := initial y-coordinate
  22903.  
  22904.                  xor     cx,cx           ; CH := 0 (initial y-increment)
  22905.                                          ; CL := 0 (initial x-increment)
  22906.  L10:            mov     ax,VARdx
  22907.                  mov     dx,VARdx+2
  22908.                  sub     ax,VARdy
  22909.                  sbb     dx,VARdy+2
  22910.                  jns     L20             ; jump if dx>=dy
  22911.  
  22912.                  call    Set4Pixels
  22913.  
  22914.                  mov     cx,1            ; CH := 0 (y-increment)
  22915.                                          ; CL := 1 (x-increment)
  22916.                  cmp     VARd+2,0
  22917.                  js      L11             ; jump if d < 0
  22918.  
  22919.                  mov     ch,1            ; increment in y direction
  22920.                  dec     bx              ; decrement current y-coordinate
  22921.  
  22922.                  mov     ax,VARdy
  22923.                  mov     dx,VARdy+2
  22924.                  sub     ax,TwoAsquared
  22925.                  sbb     dx,TwoAsquared+2 ; DX:AX := dy - TwoAsquared
  22926.                  mov     VARdy,ax
  22927.                  mov     VARdy+2,dx      ; dy -= TwoAsquared
  22928.  
  22929.                  sub     VARd,ax
  22930.                  sbb     VARd+2,dx       ; d -= dy
  22931.  
  22932.  L11:            mov     ax,VARdx
  22933.                  mov     dx,VARdx+2
  22934.                  add     ax,TwoBsquared
  22935.                  adc     dx,TwoBsquared+2 ; DX:AX := dx + TwoBsquared
  22936.                  mov     VARdx,ax
  22937.                  mov     VARdx+2,dx      ; dx += TwoBsquared
  22938.  
  22939.                  add     ax,Bsquared
  22940.                  adc     dx,Bsquared+2   ; DX:AX := dx + Bsquared
  22941.                  add     VARd,ax
  22942.                  adc     VARd+2,dx       ; d += dx + Bsquared
  22943.  
  22944.                  jmp     L10
  22945.  ;
  22946.  ; plot pixels from current (x,y) until y < 0
  22947.  ;
  22948.  
  22949.  ; initial buffer address and bit mask
  22950.  
  22951.  L20:            push    bx              ; preserve current y-coordinate
  22952.                  push    cx              ; preserve x- and y-increments
  22953.  
  22954.                  mov     ax,Asquared
  22955.                  mov     dx,Asquared+2
  22956.                  sub     ax,Bsquared
  22957.                  sbb     dx,Bsquared+2   ; DX:AX := Asquared-Bsquared
  22958.  
  22959.                  mov     bx,ax
  22960.                  mov     cx,dx           ; CX:BX := (Asquared-Bsquared)
  22961.  
  22962.                  sar     dx,1
  22963.                  rcr     ax,1            ; DX:AX := (Asquared-Bsquared)/2
  22964.                  add     ax,bx
  22965.                  adc     dx,cx           ; DX:AX := 3*(Asquared-Bsquared)/2
  22966.  
  22967.                  sub     ax,VARdx
  22968.                  sbb     dx,VARdx+2
  22969.                  sub     ax,VARdy
  22970.                  sbb     dx,VARdy+2      ; DX:AX := 3*(Asquared-Bsquared)/2
  22971.                                          ;           - (dx+dy)
  22972.  
  22973.                  sar     dx,1
  22974.                  rcr     ax,1            ; DX:AX :=
  22975.                                          ;  ( 3*(Asquared-Bsquared)/2 -
  22976.                                          ;  (dx+dy) )/2
  22977.                  add     VARd,ax
  22978.                  adc     VARd+2,dx       ; update d
  22979.  
  22980.  ; loop until y < 0
  22981.  
  22982.                  pop     cx              ; CH,CL := y- and x-increments
  22983.                  pop     bx              ; BX := y
  22984.  
  22985.  L21:            call    Set4Pixels
  22986.  
  22987.                  mov     cx,100h         ; CH := 1 (y-increment)
  22988.                                          ; CL := 0 (x-increment)
  22989.  
  22990.                  cmp     VARd+2,0
  22991.                  jns     L22             ; jump if d >= 0
  22992.  
  22993.                  mov     cl,1            ; increment in x direction
  22994.  
  22995.                  mov     ax,VARdx
  22996.                  mov     dx,VARdx+2
  22997.                  add     ax,TwoBsquared
  22998.                  adc     dx,TwoBsquared+2 ; DX:AX := dx + TwoBsquared
  22999.                  mov     VARdx,ax
  23000.                  mov     VARdx+2,dx      ; dx += TwoBsquared
  23001.  
  23002.                  add     VARd,ax
  23003.                  adc     VARd+2,dx       ; d += dx
  23004.  
  23005.  L22:            mov     ax,VARdy
  23006.                  mov     dx,VARdy+2
  23007.                  sub     ax,TwoAsquared
  23008.                  sbb     dx,TwoAsquared+2 ; DX:AX := dy - TwoAsquared
  23009.                  mov     VARdy,ax
  23010.                  mov     VARdy+2,dx      ; dy -= TwoAsquared
  23011.  
  23012.                  sub     ax,Asquared
  23013.                  sbb     dx,Asquared+2   ; DX:AX := dy - Asquared
  23014.                  sub     VARd,ax
  23015.                  sbb     VARd+2,dx       ; d += Asquared - dy
  23016.  
  23017.                  dec     bx              ; decrement y
  23018.                  jns     L21             ; loop if y >= 0
  23019.  
  23020.  ; restore default Graphics Controller registers
  23021.  
  23022.  Lexit:          mov     ax,0FF08h       ; default Bit Mask
  23023.                  mov     dx,3CEh
  23024.                  out     dx,ax
  23025.  
  23026.                  mov     ax,0003         ; default Function Select
  23027.                  out     dx,ax
  23028.  
  23029.                  mov     ax,0001         ; default Enable Set/Reset
  23030.                  out     dx,ax
  23031.  
  23032.                  pop     di
  23033.                  pop     si
  23034.                  mov     sp,bp
  23035.                  pop     bp
  23036.                  ret
  23037.  
  23038.  _Ellipse10      ENDP
  23039.  
  23040.  
  23041.  Set4Pixels      PROC    near            ; Call with:    CH := y-increment
  23042.                                          ;                      (0, -1)
  23043.                                          ;               CL := x-increment
  23044.                                          ;                      (0, 1)
  23045.  
  23046.                  push    ax              ; preserve these regs
  23047.                  push    bx
  23048.                  push    dx
  23049.  
  23050.                  mov     dx,3CEh         ; DX := Graphics Controller port
  23051.  
  23052.                  xor     bx,bx           ; BX := 0
  23053.                  test    ch,ch
  23054.                  jz      L30             ; jump if y-increment = 0
  23055.  
  23056.                  mov     bx,BytesPerLine ; BX := positive increment
  23057.                  neg     bx              ; BX := negative increment
  23058.  
  23059.  L30:            mov     al,8            ; AL := Bit Mask reg number
  23060.  
  23061.  ; pixels at (xc-x,yc+y) and (xc-x,yc-y)
  23062.  
  23063.                  xor     si,si           ; SI := 0
  23064.                  mov     ah,LMask
  23065.  
  23066.                  rol     ah,cl           ; AH := bit mask rotated
  23067.                                          ;        horizontally
  23068.                  rcl     si,1            ; SI := 1 if bit mask rotated
  23069.                                          ;        around
  23070.                  neg     si              ; SI := 0 or -1
  23071.  
  23072.                  mov     di,si           ; SI,DI := left horizontal
  23073.                                          ;           increment
  23074.  
  23075.                  add     si,ULAddr       ; SI := upper left addr + horiz
  23076.                                          ;        incr
  23077.                  add     si,bx           ; SI := new upper left addr
  23078.                  add     di,LLAddr
  23079.                  sub     di,bx           ; DI := new lower left addr
  23080.  
  23081.                  mov     LMask,ah        ; update these variables
  23082.                  mov     ULAddr,si
  23083.                  mov     LLAddr,di
  23084.  
  23085.                  out     dx,ax           ; update Bit Mask register
  23086.  
  23087.                  mov     ch,es:[si]      ; update upper left pixel
  23088.                  mov     es:[si],ch
  23089.                  mov     ch,es:[di]      ; update lower left pixel
  23090.                  mov     es:[di],ch
  23091.  
  23092.  
  23093.  ; pixels at (xc+x,yc+y) and (xc+x,yc-y)
  23094.  
  23095.                  xor     si,si           ; SI := 0
  23096.                  mov     ah,RMask
  23097.  
  23098.                  ror     ah,cl           ; AH := bit mask rotated
  23099.                                          ;        horizontally
  23100.                  rcl     si,1            ; SI := 1 if bit mask rotated
  23101.                                          ;        around
  23102.  
  23103.                  mov     di,si           ; SI,DI := right horizontal
  23104.                                          ;           increment
  23105.  
  23106.                  add     si,URAddr       ; SI := upper right addr + horiz
  23107.                                          ;        incr
  23108.                  add     si,bx           ; SI := new upper right addr
  23109.                  add     di,LRAddr
  23110.                  sub     di,bx           ; DI := new lower right addr
  23111.  
  23112.                  mov     RMask,ah        ; update these variables
  23113.                  mov     URAddr,si
  23114.                  mov     LRAddr,di
  23115.  
  23116.                  out     dx,ax           ; update Bit Mask register
  23117.  
  23118.                  mov     ch,es:[si]      ; update upper right pixel
  23119.                  mov     es:[si],ch
  23120.                  mov     ch,es:[di]      ; update lower right pixel
  23121.                  mov     es:[di],ch
  23122.  
  23123.                  pop     dx              ; restore these regs
  23124.                  pop     bx
  23125.                  pop     ax
  23126.                  ret
  23127.  
  23128.  Set4Pixels      ENDP
  23129.  
  23130.  
  23131.  LongMultiply    PROC    near            ; Caller:   DX = u1 (hi-order word
  23132.                                          ;                 of 32-bit number)
  23133.                                          ;           AX = u2 (lo-order word)
  23134.                                          ;           CX = v1 (16-bit number)
  23135.                                          ; Returns:  DX:AX = 32-bit result)
  23136.  
  23137.                  push    ax              ; preserve u2
  23138.                  mov     ax,dx           ; AX := u1
  23139.                  mul     cx              ; AX := hi-order word of result
  23140.                  xchg    ax,cx           ; AX := v1, CX := hi-order word
  23141.                  pop     dx              ; DX := u2
  23142.                  mul     dx              ; AX := lo-order word of result
  23143.                                          ; DX := carry
  23144.                  add     dx,cx           ; CX := hi-order word of result
  23145.                  ret
  23146.  
  23147.  LongMultiply    ENDP
  23148.  
  23149.  _TEXT           ENDS
  23150.  
  23151.                  END
  23152.  
  23153.  
  23154.  
  23155.  ───────────────────────────────────────────────────────────────────────────
  23156.  Listing 7-5.  Using pixel coordinate scaling to display a circle in
  23157.                640-by-350 16-color mode.
  23158.  ───────────────────────────────────────────────────────────────────────────
  23159.  
  23160.  /* Listing 7-5 */
  23161.  
  23162.  Circle10( xc, yc, xr, yr, n)         /* circles in 640x350 16-color mode */
  23163.  int     xc,yc;          /* center of circle */
  23164.  int     xr,yr;          /* point on circumference */
  23165.  int     n;              /* pixel value */
  23166.  {
  23167.          double  x,y;
  23168.          double  sqrt();
  23169.          double  Scale10 = 1.37;         /* pixel scaling factor */
  23170.          int     a,b;
  23171.  
  23172.          x = xr - xc;                    /* translate center of ellipse */
  23173.          y = (yr - yc) * Scale10;        /*  to origin */
  23174.  
  23175.  
  23176.          a = sqrt( x*x + y*y );          /* compute major and minor axes */
  23177.          b = a / Scale10;
  23178.  
  23179.          Ellipse10( xc, yc, a, b, n);    /* draw it */
  23180.  }
  23181.  
  23182.  
  23183.  
  23184.  ───────────────────────────────────────────────────────────────────────────
  23185.  Listing 8-1.  Filling a rectangle with horizontal lines.
  23186.  ───────────────────────────────────────────────────────────────────────────
  23187.  
  23188.  /* Listing 8-1 */
  23189.  
  23190.  FilledRectangle( x1, y1, x2, y2, n )
  23191.  int     x1,y1;                  /* upper left corner */
  23192.  int     x2,y2;                  /* lower right corner */
  23193.  int     n;                      /* pixel value */
  23194.  {
  23195.          int     y;
  23196.  
  23197.          for (y = y1; y< = y2; y++)      /* draw rectangle as a set of */
  23198.            Line( x1, y, x2, y, n );      /*  adjacent horizontal lines */
  23199.  }
  23200.  
  23201.  
  23202.  
  23203.  ───────────────────────────────────────────────────────────────────────────
  23204.  Listing 8-2.  A simple recursive region fill.
  23205.  ───────────────────────────────────────────────────────────────────────────
  23206.  
  23207.  /* Listing 8-2 */
  23208.  
  23209.  int     FillValue;      /* value of pixels in filled region */
  23210.  int     BorderValue;    /* value of pixels in border */
  23211.  
  23212.  PixelFill( x, y )
  23213.  int     x,y;
  23214.  {
  23215.          int     v;
  23216.  
  23217.          v = ReadPixel( x, y );
  23218.          if ( (v!=FillValue) && (v!=BorderValue) )
  23219.          {
  23220.            SetPixel( x, y, FillValue );
  23221.  
  23222.            PixelFill( x-1, y );
  23223.            PixelFill( x+1, y );
  23224.            PixelFill( x, y-1 );
  23225.            PixelFill( x, y+1 );
  23226.          }
  23227.  }
  23228.  
  23229.  
  23230.  
  23231.  ───────────────────────────────────────────────────────────────────────────
  23232.  Listing 8-3.  A line-adjacency fill routine.
  23233.  ───────────────────────────────────────────────────────────────────────────
  23234.  
  23235.  /* Listing 8-3 */
  23236.  
  23237.  #define UP      -1
  23238.  #define DOWN    1
  23239.  
  23240.  
  23241.  LineAdjFill( SeedX, SeedY, D, PrevXL, PrevXR )
  23242.  int     SeedX,SeedY;           /* seed for current row of pixels */
  23243.  int     D;                     /* direction searched to find current row */
  23244.  int     PrevXL,PrevXR;         /* endpoints of previous row of pixels */
  23245.  {
  23246.          int     x,y;
  23247.          int     xl,xr;
  23248.          int     v;
  23249.