VOGONS


CGA pseudo-graphic modes

Topic actions

Reply 141 of 152, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've just adjusted my own implementation of CGA/MDA a bit. Now it always is in planar mode (in VGA terms). So basically, the registers function like they would as if addressing memory directly.
Luckily, the fetching of character bytes already used a scheme to load even and odd bytes. I just needed to modify the attribute controller byte to be fetched from plane 0 (linear memory in this case) instead of plane 1 (odd/even memory).
And of course modify odd/even mode to linear mode in all cases.

Then I just need to modify the graphics mode loading to always load from plane 0 (where the start address register points to directly). Essentially CGA memory mapping done using VGA internals in my emulator's case (essentially all is the same as on the Tseng graphics cards (using VGA with half clocking functionality added), except for some adjustments for odd/even loading and loading pixels every 4(non-mode 6) or 8(mode 6) clocks (which my latest implementation of the CGA/MDA already performs)).

I'm wondering though, what happens when you're in text mode and you make the start address register a odd number while moving said memory one byte ahead? Does that work as expected (display rendering the text/attributes normally)?

Edit: Interlacing seems to be the same as on VGA, with memory address 13 (when in planar mode or 'linear' mode in this case) being from the row scan counter bit 0?
But somehow, the scanlines aren't rendered correctly now (some are, some periodically aren't)?
Edit: OK. Part of it was the VGA-calculated offset between rows. It's working in 40 column mode now.
Now I just need to fix the monochrome graphics mode horizontal timings now.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 142 of 152, by superfury

User metadata
Rank l33t++
Rank
l33t++

OK. I'm seeing some weird stuff.
Currently I'm using the horizontal total (times 2) to calculate the size of bytes to add to the current scanline start to obtain the next scanline to load in the VRAM. Perhaps horizontal total isn't the correct register to use, but instead register 1? But that contains 28h in mode 6(640x200 monochrome graphics)? That's only half of what is needed (40 columns)?
Does the hardware do something strange in monochrome graphics mode?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 143 of 152, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote on 2026-01-30, 21:21:

OK. I'm seeing some weird stuff.
Currently I'm using the horizontal total (times 2) to calculate the size of bytes to add to the current scanline start to obtain the next scanline to load in the VRAM. Perhaps horizontal total isn't the correct register to use, but instead register 1? But that contains 28h in mode 6(640x200 monochrome graphics)? That's only half of what is needed (40 columns)?
Does the hardware do something strange in monochrome graphics mode?

Nothing strange is going on. Each CRTC character corresponds to 2 VRAM bytes in all modes.

Reply 144 of 152, by mkarcher

User metadata
Rank l33t
Rank
l33t
reenigne wrote on 2026-01-30, 22:28:

Nothing strange is going on. Each CRTC character corresponds to 2 VRAM bytes in all modes.

Which means 16 pixels per CRTC character in monochrome graphics mode. This applies to the Hercules card (45 characters for 720 pixels) as well as the CGA card.

Reply 145 of 152, by SoftCat

User metadata
Rank Member
Rank
Member
reenigne wrote on 2026-01-30, 22:28:

Nothing strange is going on. Each CRTC character corresponds to 2 VRAM bytes in all modes.

By the way, on EGA and VGA in mode 6, each CRTC character corresponds to 1 byte of VRAM.

Reply 146 of 152, by superfury

User metadata
Rank l33t++
Rank
l33t++

Well, behind the scenes it's partly using VGA registers for various functionality.
VRAM is basically always mapped like mode 6 VGA (64K VRAM, planar mode), except timings for text mode use a half clock to fetch bytes from plane 0 (2 per character clock). Same for 4 color mode. 8 color mode is exactly handled like the VGA, but there is an issue with it being programmed for 40 horizontal clocks, or slightly more for horizontal total, which it uses for the VGA-compatible offset register calculation. By fixing all modes to double, that seems fixed for the vertical timing, but the horizontal timing is still messing up (on the CRTC main clock part, which in turn drives the active display/overscan clocks (which read from RAM every divided dot clock).
Also, I've improved the dot clock to double display width for rendered pixels, essentially ticking every other pixel (for text modes on clock 1 (character) and 4(attribute), with clock 8 using the data). That's in VGA dot clock terms (pixel clock div 2=dot clock, pixel clock div 8=character clock (CRT POV), dot clock div 8=character clock (VRAM POV).
The CRT POV is what's going wrong (only half in monochrome graphics mode only).
Is the amount of bytes to the next scanline based off CRTC reg 00h (current implementation, horizontal total) or 01h(end horizontal display) on a CGA?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 147 of 152, by SoftCat

User metadata
Rank Member
Rank
Member
superfury wrote on 2026-01-30, 23:15:

Is the amount of bytes to the next scanline based off CRTC reg 00h (current implementation, horizontal total) or 01h(end horizontal display) on a CGA?

The number of words (2 bytes each) per line is specified on the CGA in register R1. For graphics modes (formally there are 100 lines), this must be multiplied by 2, since there is one line from each bank.

Reply 148 of 152, by mkarcher

User metadata
Rank l33t
Rank
l33t
superfury wrote on 2026-01-30, 23:15:

Is the amount of bytes to the next scanline based off CRTC reg 00h (current implementation, horizontal total) or 01h(end horizontal display) on a CGA?

01. If I understood the 6845 datasheet correctly, the address counter inside the CRTC is incremented on every clock, even during blanking, but it is sampled at display end, and restored to that sampled value on the start of the next scanline.

Reply 149 of 152, by superfury

User metadata
Rank l33t++
Rank
l33t++
mkarcher wrote on Yesterday, 08:12:
superfury wrote on 2026-01-30, 23:15:

Is the amount of bytes to the next scanline based off CRTC reg 00h (current implementation, horizontal total) or 01h(end horizontal display) on a CGA?

01. If I understood the 6845 datasheet correctly, the address counter inside the CRTC is incremented on every clock, even during blanking, but it is sampled at display end, and restored to that sampled value on the start of the next scanline.

So, unlike the EGA/VGA, there is no 'offset register' addition during horizontal total and the normal dot clock during active display is the one that increases it to the next scanline? How are the row scan counter scanlines handled, then? Since they need to reset the counter to the start of the scanline for another row scan? Or is that done by the CRTC itself?

Edit: OK. Just made the CRTC itself perform that function now.
So it will increase the memory address clock accordingly in both text and graphics modes. Then when it needs to duplicate a scanline (for double scanning or next row scan counter), it will simply set the start of the new scanline to use to the current memory address counter, which is substracted by one in text mode (since the attribute fetch of the last character clock left it one too late). Graphics modes function normally, since they tick as needed (no double clocking for fetching multiple bytes from RAM).

Edit: Just ran Ultima II again. It renders correctly in it's graphics and text modes now! 😁
Edit: And after a bit of VGA-compatible planar memory fixes (various register settings being incorrect for planar mode), the graphics mode on the emulated CGA/MDA all work correctly again! 😁

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 150 of 152, by superfury

User metadata
Rank l33t++
Rank
l33t++

Ran the CGA compatibility tester on my emulator again.
It runs fine, except for the CGA card determination test.
In one case I get garbled junk. In another case I get the correct screen, but it starts rolling from right to left, from top to bottom on the screen. Almost like the final clocks of a frame aren't loading a new frame start address correctly somehow?
Edit: OK. The horizontal timings are seemingly handled correctly. It's just that the final scanline of the frame isn't detected properly, thus never triggering required vertical total effects (like reloading the address counter or start address etc.).
Edit: The issue was in the scanline comparator logic for the registers 4/5 handling. Fixed that, now no more rolling occurs.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 151 of 152, by SoftCat

User metadata
Rank Member
Rank
Member

It's possible to achieve 320x200 16-color mode on CGA. However, this will result in attribute conflicts, and a special character generator will be required. Mode 3 is set, the character height is programmed to 2 dots, and all 256 combinations are used, with horizontal dot duplication.

00110011
11001100

Reply 152 of 152, by SoftCat

User metadata
Rank Member
Rank
Member

Since each character can be used with its inverse, 128 characters will suffice.
I understand that this is completely irrelevant now. But CGA could be much better for the same price.