HGC cards on IBM PCs used to be able to run CGA software by using a CGA emulator. How exactly did those programs work? What were the limitations?
How exactly did those programs work?
They were based on the ability of the Hercules card use the CGA's memory space.
The original MDPA (*1) had (only) 4 KiB of memory mapped at B0000h, while the CGA had 16 KiB of Memory at B8000h. The Hercules card in contrast had 64 KiB of memory (*2), starting at B0000h. To avoid conflicts with a CGA in the same system the upper 32 KiB were by default disabled. By default the Hercules was 100% compatible. In fact, so compatible that the PCs BIOS did recognize and use it as MDA (with 4 KiB Memory). Unlike the MDA, the Hercules card did offer a full graphics mode of 720x348 (*3,4), which occupied 32 KiB Memory, thus offering up to two pages.
CGA emulations used the fact that the second 32 KiB were not only free (when only the first graphics page was used) but also that memory could be located where the CGA's 16 KiB are, at B8000h..BBFFFh. Programs assuming a CGA (and direct screen access) would go ahead and paint their output into this memory instead. A timer controlled interrupt would now read the virtual CGA buffer at B8000h and transform its content into the real Hercules buffer at B0000h. On fast machines that could happen for every frame.
This was also supported by the fact that while many programs did direct screen updates, configuration was mostly done via BIOS calls, which could also be intercepted by the emulation and handled accordingly. For games with direct control of the CGA's registers, patches were sometimes available.
What were the limitations?
Most are related to the fact that the conversion of colour and resolution was done in software. To make it happen, the emulator had to steal the cycles to do so from the game. While not an issue with later PCs, it was a deal breaker early on.
Most notable Speed - Games got slowed down. While not much of an issue for round based or adventure games, it was real hurdle for real time/action games.
As well as Lag - One way to reduce the impact on the game performance was to skip frames. Doing the conversion only every second, third or fourth frame, reducing the effective frame rate. Again sometimes to an unplayable low when it came to (re)action games.
Weird Screen Format - Again due to the complexity of conversion, some emulators offered modes where less complex calculations were used by restricting the display area and colour resolution. As a result, only a part of the screen was used (or parts of the picture cropped) and colour information was dropped.
The other big drawbacks were ugly pictures due to the fact that converting a colour picture to monochrome always loses information. Unlike on a TV, there was no grayscale. Depending on the software this was compensated by replacing the colours by fixed patterns. This worked fine on pictures with sharp contrasts (i.e. always black lines in between) but usually only gave a rough impression of different coloured areas. With graphics based on colours (like with 160x200 mode) and fine details the result was awful. Keep in mind, these were just replacement patterns and nothing like real dithering.
And last, programs that manipulated the CGA's (6845) registers directly did not work at all. There weren't many and some emulators even had patches for common ones, still another hurdle to take.
*1 - Monochrome Display and Printer Adapter as the original IBM MDPA also offered a parallel interface. Obviously IBM assumed that an office user would need a printer interface anyway, thus creating the eventual first Multi-Function-Card :))
*2 - The large memory was meant to support loadable character sets and full graphics, as its developer wanted to use Thai characters.
*3 - Descriptions often mention 350 lines, but the last two weren't visible.
*4 - 720x350 at 50 Hz is, BTW, what can be done with a standard CRT when tweaking within the (European) boundaries.
The basic idea is to exploit the fact that both CGA and Hercules use the 6845.
I have the source code of an emulator at hand, so I can say what it does:
- it handles the mode-setting function of interrupt 0x10; for all non-Hercules modes, it includes tables of register settings to use to program the Hercules for the appropriate resolution and framebuffer layout;
- it piggy-backs on the timer interrupt to copy the framebuffers when an emulated mode is active (from 0xB800, which is where programs would write when using CGA, to 0xB000, the monochrome buffer).
There are quite a number of different emulators with different levels of success with various programs, so there are no doubt different ways of implementing the emulation.
The limitations are mostly related to hardware handling. Any program writing to the CGA registers won’t work, and any program relying on the Hercules vertical blank interval won’t behave quite right (CGA refreshes at 60Hz, Hercules at 50Hz). The status registers are different too, so polling CGA-style won’t work on Hercules at all. As mentioned by tofro, copying the framebuffers took a noticeable amount of time too (especially on CGA/Hercules-era systems), which would affect the system’s performance.