title Ega720 Hercules-compatible resident driver for EGA cards
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; This program demonstrates how to program an Enhanced Graphics Adapter driving
; a Monochrome display in a Hercules-compatible 720x348 mode. It will work
; with *any* EGA card with at least 128K memory. It does not require an
; EGA clones with built-in Hercules emulation. However, it will NOT work if
; you have a CGA installed in addition to the EGA.
; You can run this as a "stay resident" program, and patch existing Hercules-
; compatible programs to call it so they can run on an EGA in graphics mode.
; (Or similarly, patch a program that does know about the EGA to run in the
; higher 720x348 resolution instead of the normal 640x350 EGA resolution, by
; using its Hercules driver instead of its EGA driver.)
; Or, you can incorporate this code into your own program to provide a higher
; resolution mode on Monochrome EGA's. In this case, ignore all the details
; about how to patch existing programs and look at the "DoInt" subroutine and
; the "HOW IT WORKS" discussion.
; The program does not make the EGA register-compatible with the Hercules,
; however it does give the same memory map and 720 pixel wide display as the
; Hercules card. To make an existing Hercules program work with this program,
; you will have to patch that program so that its initialization code calls
; this routine instead of loading the Hercules registers. Once the card is
; initialized, the program can write to the display buffer just as if it were
; writing to a Hercules card.
; ***HOW TO INSTALL THIS PROGRAM***
; Assemble, link, and exe2bin this file. Then run it. It installs a routine
; under Interrupt 68h (normally unused). If INT 68h is in use for something
; else on your system, change the interrupt number below.
; ***HOW TO PATCH AN EXISTING PROGRAM***
; To patch an existing Hercules-compatible program, you will need to use
; DEBUG to: 1) find the Hercules initialization code, 2) determine whether it
; uses Hercules Page 0, Page 1, or both, and 3) patch in an appropriate INT 68h
; Hercules initialization code will consist of some OUT instructions to these
; registers: 3BFh, 3B8h, 3B4h, and 3B5h. The sequence will be something like:
; Output the value 1 or 3 to 3BFh (this may be omitted in older programs)
; Output any value with bit 3 off to 3B8h (turns video off)
; A loop writing various values to 3B4h and 3B5h (loads CRTC registers)
; Output a value with bit 3 on to 3B8h (turns video back on)
; * This last output gives you some critical information. Look at the values
; of bits 1 and 7 in particular:
; Bit 1 (mask 02h): 0 = text mode, 1 = graphics mode.
; Bit 7 (mask 80h): 0 = page 0, 1 = page 1.
; To find the code, you can use the Search command in DEBUG something like:
; -s 100 l xxxx bf 3
; -s 100 l xxxx b8 3
; where xxxx is the value in register CX. If the program has a separate
; Hercules driver, that's the file you should be looking in. If not, try
; the program file itself (if a .EXE file do the usual trick of renaming it
; to a different extension, because you will need to write it back out.)
; If the initialization sequence is setting TEXT mode, replace it with a
; normal BIOS "set mode" call.
; mov ah,0 ; (You're in DEBUG, so hex is default)
; mov al,7 ; (Yes, this could just be a MOV AX)
; int 10
; You may find code that loads the Hercules registers and then also does a
; BIOS set mode call. In that case, just patch out the Hercules code and
; let the set mode do its thing. Or there may just be a BIOS call. Leave
; it alone.
; If the initialization sequence is setting GRAPHICS mode, replace it with
; a call to this resident routine:
; mov al,80 *or* 00 ; 80 for page 1, 00 for page 0
; int 68
; Use 80 or 00 depending on whether you found the initialization code to be
; using page 1 or page 0. For example, Microsoft Windows uses page 1, while
; Framework version 1 uses page 0.
; Naturally, when making these patches you should look at the surrounding code
; to duplicate any register saving, return values, NEAR or FAR return, etc.
; Note that the INT 10 and INT 68 calls preserve all registers except AX.
; These patches will be sufficient for many programs. If you have a program
; that uses both Hercules pages 0 and 1 and flips back and forth between them,
; you will need to do some additional work. The program will contain
; additional output instructions to port 3B8, with bit 7 (mask 80h) on or off.
; Replace these with:
; mov al,81 *or* 01 ; 81 for page 1, 01 for page 0
; int 68
; *Note* I haven't tested this option, sorry.
; ***A SAMPLE PATCH - MICROSOFT WINDOWS***
; To patch Microsoft Windows for this high resolution mode, do the following:
; (This is for Windows version 1.01, either the retail or Developer's version)
; 1. **Make COPIES of your SETUP and BUILD disks**
; **Use the COPIES. DO NOT MODIFY YOUR ORIGINALS!!**
; 2. On your SETUP disk, apply this patch:
; A>debug hercules.drv
; -a a97
; xxxx:0A97 mov al,80
; xxxx:0A99 int 68
; xxxx:0A9B mov al,1
; xxxx:0A9D ret
; -a ab2
; xxxx:0AB2 mov ah,0
; xxxx:0AB4 int 10
; xxxx:0AB6 mov al,1
; xxxx:0AB8 ret
; 3. On your BUILD disk, apply this patch:
; A>copy egamono.grb hercules.grb
; A>debug hercules.lgo
; -a 343
; xxxx:0343 mov al,80
; xxxx:0345 int 68
; xxxx:0347 ret
; 4. Run SETUP to install Windows. When it asks for the display type,
; specify Hercules.
; You should be up and running in the higher resolution mode!
; The only problem I have found is that if you run an "old" application
; that takes over the screen it doesn't display the "press any key to
; return to Windows" message. This is done by HERCULES.GRB, which I didn't
; get patched, but instead took the shortcut of copying EGAMONO.GRB.
; NOTE: Microsoft has OK'd my uploading this patch (see BIX message
; microsoft/applications #192), but of course does not endorse or take
; any responsibility for it. Thank you Microsoft for a sensible attitude.
; I have also successfully patched Lotus 1-2-3 to run on the EGA in
; this mode, but will NOT make that patch available because Lotus is very
; unfriendly to people who patch their products. See BIX message
; spreadsheets/other #24. The words I have for Lotus are not printable.
; (I have also patched Framework version 1.1 but haven't yet checked with
; Ashton-Tate about uploading patches. Hint: It's HERCULES.DRV, and they
; use page 0, unlike Windows which uses page 1.)
; ***HOW IT WORKS***
; If you want to support the 720x348 mode in a program of your own, this is
; the section of interest to you. (Or you can ignore this and just use the
; This program uses some EGA features which are not used in the standard modes.
; Page number references are from the August 2, 1984 EGA manual.
; The "Memory Map" bits in Graphics Controller register 6 ("Miscellaneous
; Register") are set to 00. This maps the EGA display memory into a full
; 128K bytes from A0000h to BFFFFh. This is why you can't have a CGA in
; the same machine. Theoretically, you could set these to 10 if you were
; using Hercules page 0 (B0000-B7FFF) and then you could co-exist with a CGA.
; I didn't get this to work, however. (p.53)
; In this 128K mode (only), bit 5 of the Miscellaneous Output Register selects
; which 64K block is displayed. This is described wrong in the manual. All
; standard modes set this to 1. We set it to 0, which selects the higher
; 64K block (B0000-BFFFF). 1 would give us A0000-AFFFF. (p.13)
; Like in the standard modes, we then use CRTC registers 0C/0D (Start Address)
; to select the display base address within that 64K block. This selects
; Hercules page 0 or 1. (p.34)
; The "native" EGA graphics modes use linear addressing, where each scan line
; begins in memory right after the previous one. The CGA has a strange
; interleaving, where alternate rows are offset by 2000h. The Hercules card
; is like the CGA except it uses groups of four rows instead of two. So your
; row addressing is like this:
; Row 0: 0000h
; Row 1: 2000h
; Row 2: 4000h
; Row 3: 6000h
; Row 4: 0000h + (size of one row = 90 bytes)
; Row 5: 2000h + 90
; CRTC register 17h (Mode Control) contains two bits (bits 0 and 1) which
; control this addressing. In the EGA native modes, these bits are both 1,
; and addressing is linear. If bit 0 is 0, you get the CGA compatibility
; mode, with alternating lines offset by 2000h. If bits 0 and 1 are both 0,
; you get the Hercules addressing mode as shown above. Don't be misled by
; the funny name of bit 1 in the manual. It should have been called CMS 1
; because it is exactly like bit 0 (CMS 0). (p.41-42)
; If you are writing your own software to support the 720 mode, you may find
; it easier to use linear addressing instead of the funny Hercules/CGA style
; addressing. To do this, set both these bits to 1 instead of 0. Fiddle
; with the CRTC registers a little and you will then get a 720x350 mode.
; (Hercules is 348 because it had to be divisible by 4.) I haven't tested
; this, however.
; ***NO NONSENSE PUBLIC DOMAIN STATEMENT***
; This program and documentation are placed in the public domain. Period.
; June 22, 1986
; Michael Geary
; P.O. Box 1479
; Los Gatos, CA 95031
; BIX: geary
HercInt equ 68h ; Interrupt to put EGA in Hercules mode
; Change this if INT 68h conflicts
; with something else
Zseg SEGMENT at 0
org HercInt*4 ; Our interrupt vector
IntOff dw ?
IntSeg dw ?
org 4A8h ; EGA BIOS "Save Table" pointer
SvXoff dw ?
SvXseg dw ?
Data SEGMENT byte
; This is a "Save Table" as used by the EGA BIOS. Since it is just used
; temporarily for our mode setting, we provide only the single parameter
; table instead of the full set.
Table label dword
dw offset Com:Params - (11h*40h)
TblSeg dw ?
dd 7 dup (?)
Params label byte
; "Hercules" Mode F (large memory)
; cols, rows, lines
db 05Ah, 018h, 00Eh
; page len
db 001h, 001h, 000h, 006h
db 06Ch, 059h, 05Ch, 02Ch
db 05Bh, 04Ch, 070h, 01Fh
db 000h, 003h, 000h, 000h
PageBit label byte
db 000h, 000h, 000h, 000h
db 05Dh, 02Dh, 05Bh, 02Dh
db 00Dh, 05Ch, 06Ch, 0E0h, 0FFh
db 000h, 008h, 008h, 008h, 008h, 008h, 008h, 008h
db 008h, 008h, 008h, 008h, 008h, 008h, 008h, 008h
db 003h, 000h, 001h, 000h
db 000h, 000h, 000h, 000h, 000h, 000h, 001h, 00Fh, 0FFh
Com GROUP Code, Data, Top
ASSUME cs:Com, ds:Com, es:nothing, ss:nothing
; This is the code to install the resident driver. You won't need it if you
; are incorporating the 720 technique into your own program, of course.
xor ax, ax
mov es, ax
mov IntOff, offset Com:DoInt
mov IntSeg, cs
mov TblSeg, cs
mov dx, offset Com:TopOff
shr dx, 1
shr dx, 1
shr dx, 1
shr dx, 1
mov ax, 3100h
int 21h ; Terminate and stay resident
; This the code to set up the 720x348 mode. To make life easy, we
; temporarily set up a pointer to our register table and then let the BIOS
; do the work of loading all the registers.
test al, 1 ; is it just a page flip?
jnz Flip ; yes, just go do that
; Set up Hercules mode from scratch
and al, 80h
mov PageBit, al ; set page 0 or 1
xor ax, ax
mov es, ax
push SvXoff ; save previous parameter table
mov SvXoff, offset Com:Table ; temporarily set to our table
mov SvXseg, cs
mov ax, 000Fh ; call BIOS to do the mode setting
int 10h ; using our parameters
pop SvXseg ; restore previous parameter table
; Do a simple page flip. *Note* I haven't tested this! -MG
; If you are using the 720 technique in your own program you probably won't
; want to use this code anyway.
and al, 80h
mov dx, 3B4h
mov ah, al
mov al, 0Ch
out dx, ax
jmp short return
Top SEGMENT para