• Please review our updated Terms and Rules here

Displaying CGA on CGA

Great Hierophant

Veteran Member
Joined
Mar 22, 2006
Messages
1,928
Location
Massachusetts, USA
Several years ago, I found a way to display images from standard file formats on even the most humble hardware : https://nerdlypleasures.blogspot.com/2016/01/displaying-dosbox-screenshots-on-real.html

But I found that the solution for CGA cards was rather limited (and slow). Of all the graphics adapters, CGA is only one of two graphics cards where its graphics modes cannot fully display an image of its text modes. CGA can show all 16 colors in text modes but for graphics modes which support the effective text mode resolutions, the colors are limited to 4 and 2 and they are not freely selectable. Moreover, the CompuShow program is limited to BIOS functionality when it comes to CGA color. This means no 640x200 composite color, no selectable foreground color in 640x200, no non-intense palettes, no alternate palettes, no background/border select for 320x200.

If someone were to write an image display program to run on older PCs, the program would have difficulties with displaying images. I think the easiest way to overcome these difficulties is by adding a header which can tell a program what it needs to do with the CGA registers to display the image properly. Here is what I suggest as the structure for the header :

00 - $43 "C"
01 - $47 "G"
02 - $41 "A"
03 - $1A DOS End-of-File
04 - 6845 R0
05 - 6845 R1
06 - 6845 R2
07 - 6845 R3
08 - 6845 R4
09 - 6845 R5
0A - 6845 R6
0B - 6845 R7
0C - 6845 R8
0D - 6845 R9
0E - 6845 R10
0F - 6845 R11
10 - 6845 R12
11 - 6845 R13
12 - 6845 R14
13 - 6845 R15
14 - CGA Mode Control ($00-$3F)
15 - CGA Color Select ($00-$3F)
16 - Palette Change Scanline (FF = No palette Change, otherwise $00-C8)
17 - CGA Color Select ($00-$3F)
18 - File Type (00 = Respect Image File Header, 01 = Raw Index Values, 02 = Raw Memory Values)
19-1F - Reserved for Future Expansion

So, the header should be reasonably self-explanatory, the CGA register values are included to ensure image fidelity, then there is a byte to indicate whether there a palette change will occur. For most images, this will not happen, but if it does then the image must indicate the scanline where the palette will change and the image viewer will have to try to accommodate that.

The image should be in an indexed format limited to no more than 16 colors. Common, low-color friendly uncompressed or losslessly compressed format like bmp, png, gif or pcx support color indexing. These files can be converted to RAW files that contain only the indexes and are 64000 bytes in size.
The palette order for the indexes must be in the CGA order, and for DOSBox and other emulators, it typically is.

If you wish to show any image that is based in a text mode, you will need to dump a raw memory dump which will indicate which ASCII characters are being used and each cell's attributes.
 
I have written something very similar to what you describe (although my format is slightly more complicated). http://www.reenigne.org/misc/cgadview.zip . CGAArt (http://www.reenigne.org/misc/cgaart.zip) can create .cgad files from existing images (either losslessly if possible or finding the best match to a given input image). A description of the file format follows:

The CGAD format describes data for an extended version of CGA which allows arbitrary large resolutions and amounts of VRAM. It's also at its heart a video format for a looping video that can be just under 5 minutes long at most. However, at the moment CGAArt only stores a single frame and cgadview only displays a single frame. The standard CGA registers, extended CGA registers and VRAM are placed into a single address space (negative numbers for registers and 0 onwards for VRAM). The values stored in these addresses are each one byte in length:

-26: log(characters per bank)/log(2). Normally 12, meaning 2^12 = 4096 characters per bank or 8kB per bank (as on an actual CGA card).
-25: scanlines repeat "register" (normally 1 as the CGA card doesn't repeat scanlines).
-24: high byte of extended "horizontal total" register (normally 0).
-23: high byte of extended "horizontal displayed" register (normally 0).
-22: high byte of extended "horizontal sync position" register (normally 0).
-21: high byte of extended "vertical total" register (normally 0).
-20: high byte of extended "vertical displayed" regsiter (normally 0).
-19: high byte of extended "vertical sync position" register (normally 0).

Next come the actual CGA registers: mode, palette and CRTC:

-18: mode register (port 0x3d8 value)
-17: palette register (port 0x3d9 value)
Then the CRTC registers (CGAD address number = CRTC register number - 16):
-16: horizontal total
-15: horizontal displayed
-14: horizontal sync position
-13: horizontal sync width
-12: vertical total
-11: vertical total adjust
-10: vertical displayed
-9: vertical sync position
-8: interlace mode
-7: maximum scanline
-6: cursor start
-5: cursor end
-4: start address high
-3: start address low
-2: cursor address high
-1: cursor address low
0 onwards: VRAM contents (addresses are offsets in CGA VRAM as seen by the CPU i.e. segment 0xb800). The CGAD format doesn't define a maximum VRAM address. On a real CGA it's 0x3fff obviously and there isn't really much point having more than 4 << (log characters per bank) VRAM bytes but from the CGAD perspective VRAM is unlimited. Any bytes not set in the file default to 0.

Now that we've got that out of the way we can describe the actual file format.

First is the file header:

4 bytes: magic file identifier "CGAD".
4 bytes: version number - only 0 used so far.
4 bytes: total number of hdot periods that the CGAD file covers - normally 238944 (912*262) for a single frame.
4 bytes: sample count for the horizontal phase locked loop - normally 910 which is NTSC standard - note that this doesn't have to be exactly the same as the number of samples per scanline (912) but it has to be close enough. The actual width and height of the resulting image are set by the sync pulses in the CGA (real or emulated).
4 bytes: sample count for the vertical phase locked loop - normally 238875 (910*262.5).

Following the file header is some number of blocks, describing a change to set of addresses. The first of these describes the entire initial CGA state. Currently CGAArt only generates a single block but the plan is that a future version will support animation and effects that require the state to change over time.

Each block starts with a block header:

4 bytes: time (in samples at which this block's change occurs). 0 for the first block.
4 bytes: address of start of change. Normally -26 (i.e. the first register).
4 bytes: N = number of bytes of addresses to change. Normally 26+0x4000 (i.e. all registers and VRAM) for the first block.
N bytes: the actual change data.
0-3 bytes: Zero padding so that the following block is 4-byte aligned.
 
Yep -- also, there's a patch for DOSBox to capture screens in reenigne's CGAD format.

I've found that it's quite convenient if you want to easily prepare screenshots for later display on real CGA hardware (using the aforementioned cgadview).
 
CGA can show all 16 colors in text modes but for graphics modes which support the effective text mode resolutions, the colors are limited to 4 and 2 and they are not freely selectable.

That is the reason in my 5155 computer I changed to an EGA card, running in CGA mode. Either the IBM one with the added memory module, or the other types like Video 7 cards worked. Then they can display a 640 x 200, 16 color graphics mode CGA image, which is very good. I was able with some help (from reenigne) to doctor images in photo editing software to then create .gif files that would display in PICEM. I tried to get CompuShow to work, but I had no luck and was not sure why.
 
Reengine's program looks very promising and appears to be able to show everything CGA can show but mid-frame palette & background changes. I am having trouble converting DOSBox's pngs into cgad files using the cgaart program. I think the config file can be set to values that will do a 1:1 conversion, but I am having difficulty. Can anyone help me out?
 
Reengine's program looks very promising and appears to be able to show everything CGA can show but mid-frame palette & background changes.

That's right. The CGAD format can represent mid-frame (and even mid-line) palette/background/mode changes but the current version of CGADView won't display them and the current version of CGAArt won't generate them. That is something I want to add in the future, though.

I am having trouble converting DOSBox's pngs into cgad files using the cgaart program. I think the config file can be set to values that will do a 1:1 conversion, but I am having difficulty. Can anyone help me out?

CGAArt won't detect the mode, palette and scanlines per row that were used to make the screenshot, so you have to set that manually in the UI (or in the config file). If it defaults to composite (which it shouldn't do for an RGBI screenshot) you'll have to change the output to RGBI as well. Finally you may also need to set horizontal diffusion and vertical diffusion to 0 and quality to 1. If that doesn't solve the problem, send me the image you're trying to convert and I'll see if I can figure out what's going on.
 
Here are the screenshots I am trying to convert. I have standard 40-column, standard 80-column and "standard" 160x100 graphics modes. These seem to be the most difficult to get 1:1.
 

Attachments

  • capture.zip
    7 KB · Views: 1
Here are the screenshots I am trying to convert. I have standard 40-column, standard 80-column and "standard" 160x100 graphics modes. These seem to be the most difficult to get 1:1.

Ah, looks like I have a bug in the prescaler. If you resize the screenshots to 640x200 in another program first (using nearest-neighbour scaling) then everything works. I will look into fixing this later.
 
After resizing to 640x200, I can get perfect results with the 320x200 and 160x100 pngs, and near perfect with the 640x200. However, when I go to display the cgad file with cgadview, the pictures repeat the top half of the image, skewed, halfway through the screen.
 
After resizing to 640x200, I can get perfect results with the 320x200 and 160x100 pngs, and near perfect with the 640x200. However, when I go to display the cgad file with cgadview, the pictures repeat the top half of the image, skewed, halfway through the screen.

That sounds like you're converting an image in a graphics mode, but using a value of 1 instead of 2 for scanlines per row. The image will look the same in CGAArt, but the memory format is different.
 
I was able to get the 160x100 image displaying correctly by using the low-resolution mode and the scanline per row value of 2. I wasn't quite able to get the 320x200 image 100% using the same parameters.
 
These two in the capture.zip file linked above :

View attachment 54047

View attachment 54048

These links give me "invalid attachment specified". Are you talking about cgacal.png? (I thought by 320x200 you meant a 320x200x4 graphics mode image but there wasn't one of those in your archive). After resizing cgacal.png to 640x200 and converting (with default parameters except for changing the mode to "low-resolution text", background to 0 and scanlines per row to 8) this is what I get:View attachment cgacal.zip (and displaying the resulting .cgad file with cgadview in DOSBox looks correct to me). Do you get something different, or are we talking at cross-purposes?
 
Okay, with that guidance, I have been able to render what appear to be 1:1 for the input image for both the 320x200 and 640x200 images I posted. I just have one question remaining. The conversion is accurate, but really slow. Are there any settings that can be used to speed them up?
 
Okay, with that guidance, I have been able to render what appear to be 1:1 for the input image for both the 320x200 and 640x200 images I posted. I just have one question remaining. The conversion is accurate, but really slow. Are there any settings that can be used to speed them up?

Yes, turn down "quality" (I think it should work with quality set to 0, the fastest setting). I previously thought quality 1 was needed when really it was a 640x200 input image that was needed.

I know it's still not super speedy but exact conversions weren't my primary design goal. I might add some code to detect and switch to a faster method for the exact case.
 
Yes, turn down "quality" (I think it should work with quality set to 0, the fastest setting). I previously thought quality 1 was needed when really it was a 640x200 input image that was needed.

I know it's still not super speedy but exact conversions weren't my primary design goal. I might add some code to detect and switch to a faster method for the exact case.

Now that I know better how it works I must congratulate you on such a neat little program. I hope you continue to work on it, but this right now seems to be able to handle 99% of CGA.
 
Now that I know better how it works I must congratulate you on such a neat little program. I hope you continue to work on it, but this right now seems to be able to handle 99% of CGA.

Thanks! Yes, I have a lot of plans for it (more plans than time at the moment). Eventually I hope that it will be a full-fledged graphics editor as well as converter and renderer, and (as previously mentioned) support time-based features like raster-synchronized palette/mode changes and animation.
 
You should look on eBay for an EGA card.
They are compatible with the exact same hardware, and they can display a graphics mode display in 16 colors.
Plus, the resolution is 640x350 instead of 640x200.
EGA is much better than CGA.
 
Back
Top