[comp.os.msdos.misc] CGA 16-color mode

kistler@iowasp.physics.uiowa.edu (12/18/90)

The CGA does indeed have a 16-color graphics mode which is set up as a
quasi-text mode.  Before I give you the scoop, I must acknowledge a previous
note from hpa@casbah.acns.nwu.edu (H. Peter Anvin) which pointed me in the
critical direction and supplied most of the details.  So here's how it's done.

STEP 1:  Program the CRTC with the magic numbers (given in hex).

   CGA index register:  port 03D4
   CGA data  register:  port 03D5
   CGA mode  register:  port 03D8

   The magic numbers are:

   index:  00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D
   value:  71  50  5A  0A  7F  06  64  70  02  01  06  07  00  00

   mode :  09

STEP 2:  Fill CGA video memory with character 0DEh.  Attribute 00 clears the
   screen.

STEP 3:  Set the foreground or background color for specific "characters" to
   set given pixels.  Mode 09 insures that all 16 colors are available for
   both the foreground and the background.  Attributes are addressed as if
   there were 100 lines of 80 characters each.

Although a real programmer (whatever that is) wouldn't do all this in Turbo
Pascal, here's an example program I used (among a few others) to test this
method.  If you find a diagonal line an unconvincing test, insert your
favorite geometric shape or something.

--- begin, cut here ---
.program temp;
.
.uses
.   dos;
.
.type
.   crtcarr = array [0..$0D] of byte;
.
.const
.   crtc: crtcarr = ($71,$50,$5A,$0A,$7F,$06,$64,$70,$02,$01,$06,$07,$00,$00);
.
.var
.   i,j: word;
.   regs: registers;
.
.{-}
.
.procedure setpix(x,y,z: word);
.
.begin
.   y := 2*((x shr 1)+80*y)+1;
.   if odd(x) then
.      mem[$B800:y] := (mem[$B800:y] and $F0) or z
.   else
.      mem[$B800:y] := (mem[$B800:y] and $0F) or (z shl 4);
.   end;
.
.{-}
.
.begin
.   port[$3D8] := $01;
.   for i := 0 to $d do begin            { program crtc }
.      port[$3D4] := i;
.      port[$3D5] := crtc[i];
.      end;
.   port[$3D8] := $09;
.   for i := 0 to 7999 do
.      memw[$B800:2*i] := $DE;           { clear the screen }
.
.   for i := 0 to 159 do begin           { plot some garbage }
.      j := i mod 100;
.      setpix(i,j,i div 10);
.      end;
.
.   readln;
.   regs.ax := $03;
.   intr($10,regs);                      { back to Kansas }
.   end.
--- end, cut here ---

Allen Kistler
Physics and Astronomy
University of Iowa
kistler@iowa.physics.uiowa.edu