Programming the Hercules Graphics Card

With the introduction of the IBM-PC in 1980 two different graphic adapters were available. The Monochrome Display Adapter MDA, featuring high resolution text mode, was designed for business use, and the Color Graphics Adapter CGA for use in entertainment and at home. While the MDA only displayed text using a 8x14 font, the CGA could handle bitmapped graphics with a resolution of either 320x200x4 of 640x200x2 colors. The cost of bitmapped graphics was the ugly text mode using a 8x8 font.

The Hercules Graphics Card combined the best features of both cards. It could handle standard text mode with a 8x14 font, highlightning and underscore plus a high resolution graphics mode with a 720x348 monochrome bitmap.

Textmode
In textmode the HGC displays 25 rows with 80 columns (the standard text mode for IBM compatibles). Each character consists of 8x14 pixels. Unlike EGA or VGA the bitmap for each char is stored onboard in ROM, so the card can not use user defined fonts.
The physical screen consists of 720x350 pixels. So each char covers 9x14 pixels of physical screen space. The 8th pixel column of each char with ascii value of 192-223 is doubled so that the graphic ascii chars have no gaps. For all other chars the 9th column is set to black. This trick saves much memory for the character ROM (remember memory was horribly expensive in 1980) and makes handling more easy as each row occupies 8 bits <=> 1 byte.

For each char on the screen one attribute byte is reserved. The attribute is the same for every graphics card (MDA-VGA), but due to the monochrome nature of the HGC only very few bit combinations are usable.

the attribute byte
7 6 5 4 3 2 1 0
blink back2 back1 back0 int fore2 fore1 fore0
blink: enable blinking - 0=off ; 1=on
back2-back0: background color (useless in monochrome)
int: intensity - 0=normal ; 1=highlight char
fore2-fore0: foreground color (useless in monochrome)

The background color is always black. Colors with the intensity bit set (8-15) are displayed with high intensity. All other colors are displayed using normal intensity. The 6845 can handle two more character styles:

If you load a character into a 16bit register the character is stored in the lower part (al), while the attribute goes to the upper byte (ah). Remember that due to the little-endian format of Intel processors the byte order in memory is character-attribute !

The 6845 mirrors its internal memory onto the main memory from B000h - BFFFh. As modern VGA cards have their text mode memory from B800h-BFFFh, the BIOS sets a flag for the 6845 to prevent it from accessing above B7FFh. You can change this, but be warned that the VGA card will surely interfer with your HGC.

The textmode memory starts at B000h and occupies 80*2*25 = 4000 bytes. So you can have up to 8 different text mode pages. Unfortunately not all BIOSes support multiple pages for HGC, so you may have to program the HGC directly to access all pages. But anyway we mostly need one textmode page, as textmode normally should be fast enough for scrolling etc.

Graphics Mode
In graphics mode the HGC behaves totally different from textmode. We now have controll over each individual pixel with a resolution of 720x348 pixel. The last two rows can not be accessed due to internal synchronization details.
A graphics mode screen needs about 31320bytes = 32K, so the HGC can handle two graphics mode pages. But Page 2 lies within the VGA textmode memory (B800-BFFFh) , so it should not be used, when working with two graphics cards.

The memory layout in graphics mode is totally different from text mode. Each Page is divided into 4 Banks each using 8K.

Each row covers 90 bytes in memory, with each pixel using one bit. 8bit * 90 bytes = 720 pixels. Sadely the engineers from Hercules didn't make it easy for us coders to access a pixel, as the rows are splitted among the 4 Banks the following way: So to calculate the offset of one pixel (x;y) use the following formula:
[bit in memory]=B0000h + 8192*(y%4) + 90*(y/4) + (x/8)
here is a function to plot a pixel: (protected mode)
void hgcplot(unsigned short x, unsigned short y)
{
  unsigned char mem=(unsigned char *)0xB0000;
  mem[((y&3)<<13)+(y>>2)*90+(x>>3)]|=1<<(x&7);
}
programing the HGC using ports
Modern BIOSes do not support the HGC very well. The best alternative to hardly working BIOS routines is to make it your own way and access the 6845 directly.
WARNING: you are directly programing the video signal generator. Wrong values in the wrong registers can damage your card or monitor. So only poke around if you know what to do. Especially registers with syncing information should be handled with great care.

The 6845 uses 7 ports to communicate with the rest of the computer. All ports are 8 bit.
3B4hIndex Registerw
3B5hData Registerr/w
3B8hMode Registerw
3B9hLight-Pen setw
3BAhState Registerr
3BBhLight-Pen resetw
3BFhConfiguration Registerw

Mode Register 3B8h
7 6 5 4 3 2 1 0
pag res bli res blk res t/m res
pag: active page0 = page 1
1 = page 2
bli: blinking0 = off
1 = on
blk: video signal0 = off (blank)
1 = on
t/m: video mode0 = textmode
1 = graphics mode
if you enable the t/m bit you immidiately have to reprogram the 6845 to handle graphics mode. this procedure is covered later on.

State Register 3BAh
7 6 5 4 3 2 1 0
vre res res res dot res res hre
vre: vertical retrace0 = vertical retrace
1 = screen is active
dot: current pixel0 = black
1 = white
hre: horizontal retrace0 = normal char
1 = horizontal sync

Config Register 3BFh
7 6 5 4 3 2 1 0
res res res res res res msk gen
msk: mask page 1 if this bit is set the adresses from B800h-BFFFh are not accesible for the 6845 (set this if you are using a VGA card)
gen: allow graphics mode you have to set this bit to be able to enable the graphics mode using the t/m bit of the mode register. otherwise any attempt to switch to graphics mode will be ignored. This bit does not prevent the reprogramming of the 6845! Be sure to set this register to the right value before enabling graphics mode.

Index Register 3B4h and Data Register 3B5h
18 internal registers are covered behind these two ports. Each register is accessible by writing its index number to port 3B4h and then the desired value to port 3B5h. These ports are mainly used to reprogram the 6845 for generating a new video signal.
The following values are only valid for europe and countries with a refresh frequency of 50Hz. As most monitors synchronize themselvs using the frequency provided by the alternating current of the electricity network I strongly assume that hercules monitors in USA run with a refreshing rate of 60Hz. So poking the following numbers into the hercules card will interfere with the monitor setup. The clock tick of the HGC is 1.778MHz, which is equal to 0.5625ęs.
indexregisterr/wfunction
0horizontal total w total number of chars per row including sync minus 1
1horizontal visiblew visible chars per row
2hsync position w position of the HSYNC signal
3sync with w chars processed during HSYNC
4vertical total w total number of rows including row while VSYNC minus 1
5vertical finetune w additional scanlines
6vertical visible w number of visible rows
7vsync position w row number for starting the VSYNC
8interlace mode w (non)interlaced mode
9max rasterzeile w max. rasterline per character minus 1
10cursor start linew first rasterline of cursor
11cursor end line w last rasterline of cursor
12start adress highw adress of video memory (high)
13start adress low w adress of video memory (low)
14cursor start highr/woffset of cursor in video memory (high)
15cursor start low r/woffset of cursor in video memory (low)
16light-pen high r offset of ligh-pen (high)
17light-pen low r offset of ligh-pen (low)

horizontal total (0) = 97
this register defines the horizontal frequency in chars. If the internal counter of the 6845 has reached this value a HSYNC is generated leading to a horizontal retrace. Load this register with 97 in text mode. So this leads to 97chars*0.5625ęs=54ęs <=> 18.45Khz, the horizonzal frequency.

horizontal visible (1) = 80
this register hold the visible characters per row. This value has to be lower than the horizontal total value. The lesser the difference between those two values gets, the faster the horizontal retrace has to occur. Not all monitors can handle fast retraces, resulting in damaged monitors. So leave this value at 80 chars per row.

hsync position (2) = 82
at the character position set with this register the horizontal retrace (hsync signal) is activated. this register has to be greater than horizontal visible (1) and the sum of hsync position (2) and sync width (3) has to be lower than horizonzal total (0). If you increase this register the entire screen will be moved leftwards and vice versa. Set this register to 82. So one row consits of 80 printable characters, one invisble character and 15 chars used for the horizontal retrace. the hsync signal is therefore activated for 15chars=8.4375ęs.

sync width (3) = 15
the width (time) used for the hsync signal (as always defined in characters). you can use values from 1-15 in this register. 0 would disable the hsync. load this register with 15 for the standard text mode.

vertical total (4) = 25
this register defines the number of rows to process -1. remember that the 6845 has to produce a refresh rate of 50Hz. this leads to a fractional number of rows per frame. the finetuning of the vertical retrace is covered in the following register. Set this register to 25.

vertical finetune (5) = 6
this register sets additional raster lines to register (4). Standard value for this register is 6. So the HGC produces 6 invisible raster lines of the 26 defined above, before activating the VSYNC. this leads to a perfect refresh rate of 50Hz.

vertical visible (6) = 25
the number of visible rows. Should be set to 25.

vsync position (7) = 25
the row -1 in which the vsync signal is generated. this register has to be greater or equal to register (6). if you increase this value the screen will move upwards and vice versa. Standard value is 25.

interlace mode (8) = 2
the HGC can use the following interlaced modes:

standard value is 2. in the interlaced mode each line is doubled which can lead to a better visualization of the chars, but the resolution stays the same. In interlaced/video mode the resolution on the y-axis is doubled. If you like to program an interlaced mode you have to remember the following: I never have been able to make any use of the interlaced modes although it seems a total resolution of 80x50 in textmode can be established. but perhaps only 100% orginal hgc cards were able to create this video mode.

max.rasterline (9) = 13
this register defines the number of rasterlines per textmode row -1. This highest possible value is 32. Standard value is 13.

cursor start line (10) = 11
rasterline in which the cursor starts. bits 0 - 4 define the rasterline.
bit 6bit 5
00no blinking
01cursor off
10blink with 16*period
11blink with 32*period
the period is defined between two vsync signals. Standard Value 11.

cursor end line (11) = 12
standard value 12. so the cursor is two rasterlines high.

start adress high (12) / low (13) = 0
a 14bit value defining the start adress of the textmode/graphics memory. Standard value is 0. you can use this value to make efficient hardware scrolling in text mode.

cursor adress high (14) / low (15) = 0
this 14bit value hold the cursor position inside the memory.

light-pen high (16) / low (17) = 40
the position of the light-pen as a 14bit value.

If you want to change the display mode of the HGC use the following table.

index012345678910111213
text978082152562525213111200
gfx 5345467 912878723 0 0 00

Now some C source code to enable/disable the text mode on a hercules card.

#include <conio.h>
#define hgc_index 0x3b4
#define hgc_data  0x3b5
#define hgc_mode  0x3b8
#define hgc_conf  0x3bf
void hgcGfxOn(void)
{
  unsigned char table[]={53,45,46,7,91,2,87,87,2,3,0,0,0,0,0,0};
  outp(hgc_conf, 1); // allow graphics mode
  outp(hgc_mode, 2); // gfxmode on; screen off
  for ( int i=0; i<16; i++ ) {
    outp(hgc_index, i);
    outp(hgc_data, table[i]);
  }
  outp(hgc_mode, 2+8); // screen on;
}
void hgcGfxOff(void)
{
  unsigned char table[]={97,80,82,15,25,6,25,25,2,13,11,12,0,0,0,0};
  outp(hgc_mode, 32); // textmode on; blink off; screen off;
  for ( int i=0; i<16; i++ ) {
    outp(hgc_index, i);
    outp(hgc_data, table[i]);
  }
  outp(hgc_mode, 32+8); // screen on;
}

Disabling printer on monochrome cards
I found this tip on the Hercules homepage:
24 13
notch -> BIOS
1 12
Bend/break pin 12 on the BIOS chip so it does not go in the slot. Pin 12 is on the LOWER LEFT when the notch on the BIOS chip is at the top. it seems that only original hercules cards have this bios chip onboard.

IBM 9 pin monochrome pinout
1 Ground
5_______1
\       /
 \_____/
  6   9

Connector at Display Adaptor
2Ground
3not used
4not used
5not used
6+Intensity
7+Video
8+Horizontal
9-Vertical
Signal Voltages are 0.0 to 0.6 Vdc at down level and +2.4 to 3.5 Vdc at high level.


Programing the Hercules Graphics Card v1.0 by doj / cubic. Home Site www.cubic.org