[comp.graphics] VGA 360x480x256 mode usage code examples

Hoke@cup.portal.com (Hoke S Johnson) (10/30/89)

Since I received a large number of requests for code examples on how
to set and use the 360x480x256 color mode on the VGA card I am posting
some Turbo Pascal Code routines and code fragments that demonstrate
the use of this mode.  I have also included a 320x400x256 color mode
set as well which is very similar to the 360x480x256 color mode.  This
code has been tried on the Compaq VGA card and on the Orchid Designer
VGA card and it works on both.  I cannot guarantee the suitability of
this code for any particular VGA card but believe that it will work
fine for all register level compatible VGA cards.  Have fun and enjoy.


The following VGA 360x480x256 mode set procedure was converted from assembly
language code which appeared in the Sept/Oct 1989 Programmer's Journal
Volume 7.5 in an article by Michael Abrash,  who received the 360x480x256
mode set code from John Bridges who has placed them in the public domain.
The conversion from assembly language to Turbo Pascal Version 5.0 was
done by Hoke Johnson.

{Global Declarations}

Var
    Regs:Registers;                  {Turbo predefined variable}

Const
    VGAPage:Word = $A000;            {VGA video RAM segment address}

Procedure VGA360;     {Procedure to set standard VGA card into
                       360x480x256 color mode}
Var   Inbyte : Byte;

Begin

  Regs.AX := $0013;
  Intr ($10,Regs);    {Let the bios set up into 320x200x256 mode first}

  Port[$3c4] := $04;
  Port[$3c5] := $06;  {Disable chain 4}

  {Clear out the display memory, this did not appear in the Journal
   article}

  Port[$3c4] := $02;
  Port[$3c5] := $0F;
  FillChar(Mem[VGAPage:0],65535,chr($00));

  {The following Sync reset was included in the Programmer's Journal
   article, but I had to disable it since it causes all of my PCs to hang
   when I include it}

{ Port[$3c4] := $00;
  Port[$3c5] := $01;} {Sync reset}

  Port[$3c2] := $E7;  {Use the 28mHz clock}

  Port[$3d4] := $11;
  Inbyte := Port[$3d5];
  Inbyte := Inbyte and $7f;
  Port[$3d4] := $11;
  Port[$3d5] := Inbyte;      {Enable the writing of CRTC Registers}

  {Write the CRTC Registers}

  Port[$3d4] := $00;
  Port[$3d5] := $6b;

  Port[$3d4] := $01;
  Port[$3d5] := $59;

  Port[$3d4] := $02;
  Port[$3d5] := $5a;

  Port[$3d4] := $03;
  Port[$3d5] := $8e;

  Port[$3d4] := $04;
  Port[$3d5] := $5e;

  Port[$3d4] := $05;
  Port[$3d5] := $8a;

  Port[$3d4] := $06;
  Port[$3d5] := $0d;

  Port[$3d4] := $07;
  Port[$3d5] := $3e;

  Port[$3d4] := $09;
  Port[$3d5] := $40;

  Port[$3d4] := $10;
  Port[$3d5] := $ea;

  Port[$3d4] := $12;
  Port[$3d5] := $df;

  Port[$3d4] := $13;
  Port[$3d5] := $2d;

  Port[$3d4] := $14;
  Port[$3d5] := $00;

  Port[$3d4] := $15;
  Port[$3d5] := $e7;

  Port[$3d4] := $16;
  Port[$3d5] := $06;

  Port[$3d4] := $17;
  Port[$3d5] := $e3;

  Port[$3d4] := $11;
  Port[$3d5] := $ac;

End;

The following code fragment shows how to write an arbitrary scanline
of pixels on the screen in 360x480x256 VGA mode.  The Linenumbers are
numbered from 0 to 479 starting from the upper left.  A byte array
(Scanline) contains the pixels to be displayed.

I,J,K,L are Words or Integers
BT is a Byte

I := Linenumber * 90;  {The start of each graphic scan line is 360/4 or 90
                        memory addresses after the previous scan line.
                        Each scan line occupies 90 bytes of address space
                        and since there are four memory planes there are
                        90 X 4 bytes of memory (360) for each scan line.

                        If the upper left pixel is pixel number 0,  and
                        the lower right pixel is pixel number
                        360x480-1, an arbitrary pixel is accessed by
                        finding the VGA memory address which is the
                        pixel number shifted right 2 places relative
                        to the start of VGA memory and the memory
                        plane number is the 2 LSBs of the pixel
                        number}


  For J := 0 to DispWidth - 1 do
    Begin
      L := I + j div 4;               {Calculate VGA memory address offset}
      k := j mod 4;                   {Set K = 2 LSBs of pixel number}
      BT := $01 shl k;                {Set BT = memory plane mask code}
      Port[$3c4] := $02;              {Index Timing Sequencer register 2}
      Port[$3c5] := BT;               {Write memory plane select mask}
      Mem[VGAPage:L] := ScanLine[j];  {Write pixel}
    End;

The following routine sets the standard VGA into 320x400x256 mode, which
is very similar to the 360x480x256 mode.  Writing to the screen in
320x400x256 is the same as 360x480x256 mode, the only difference is
the address span of each scan line which is 80 rather than 90.


Procedure VGA320x400;

Var   Inbyte : Byte;

Begin

  Regs.AX := $0013;
  Intr ($10,Regs);

  Port[$3c4] := $04;
  Port[$3c5] := $06;  {Disable chain 4}

  Port[$3c4] := $02;
  Port[$3c5] := $0F;
  FillChar(Mem[VGAPage:0],65535,chr($00));

  Port[$3c2] := $E3;  {Set the 25mHz clock}

  Port[$3d4] := $11;
  Inbyte := Port[$3d5];
  Inbyte := Inbyte and $7f;
  Port[$3d4] := $11;
  Port[$3d5] := Inbyte;      {Enable the writing of CRTC Registers}

  Port[$3d4] := $09;
  Port[$3d5] := $40;

  Port[$3d4] := $14;
  Port[$3d5] := $00;

  Port[$3d4] := $17;
  Port[$3d5] := $e3;

End;

Hoke Johnson

hoke@cup.portal.com

nelson@sun.soe.clarkson.edu (Russ Nelson) (10/31/89)

/* I don't have Turbo Pascal, but I wanted to see what these looked like,
   and whether or not they worked on my VGA.  I've got a clone VGA with a
   "Trident" bios.  I've got 512 K.  Does anyone know how to do a similar
   trick that gets me 640x480x256?  Or even 640x400x256 for those of us
   without 512K VGAs?
   -- russ (nelson@clutx [.bitnet | .clarkson.edu])
   Live up to the light thou hast, and more will be granted thee.

*/

#include <dos.h>

/*
From Hoke@cup.portal.com Mon Oct 30 09:52:08 1989
From: Hoke@cup.portal.com (Hoke S Johnson)
Newsgroups: comp.graphics
Subject: VGA 360x480x256 mode usage code examples
Date: 30 Oct 89 06:48:05 GMT
Organization: The Portal System (TM)

Since I received a large number of requests for code examples on how
to set and use the 360x480x256 color mode on the VGA card I am posting
some Turbo Pascal Code routines and code fragments that demonstrate
the use of this mode.  I have also included a 320x400x256 color mode
set as well which is very similar to the 360x480x256 color mode.  This
code has been tried on the Compaq VGA card and on the Orchid Designer
VGA card and it works on both.  I cannot guarantee the suitability of
this code for any particular VGA card but believe that it will work
fine for all register level compatible VGA cards.  Have fun and enjoy.


The following VGA 360x480x256 mode set procedure was converted from assembly
language code which appeared in the Sept/Oct 1989 Programmer's Journal
Volume 7.5 in an article by Michael Abrash,  who received the 360x480x256
mode set code from John Bridges who has placed them in the public domain.
The conversion from assembly language to Turbo Pascal Version 5.0 was
done by Hoke Johnson.
*/


#define	VGAPage	0xA000			/* VGA video RAM segment address */


/* Procedure to set standard VGA card into 360x480x256 color mode */
void VGA360x480()
{
	char         Inbyte;

	_AX = 0x0013;
	geninterrupt(0x10);		/* Let the bios set up into 320x200x256 mode first */

	outportb(0x3c4, 0x04);
	outportb(0x3c5, 0x06);		/* Disable chain 4 */


/* Clear out the display memory, this did not appear in the Journal article */

	outportb(0x3c4, 0x02);
	outportb(0x3c5, 0x0F);

	memset(MK_FP(VGAPage,0), 0, 65535);

	/* The following Sync reset was included in the Programmer's Journal
	article, but I had to disable it since it causes all of my PCs to hang
	when I include it */

#if 0
	outportb($3c4,  $00);
	outportb($3c5,  $01);
#endif
	/* Sync reset */
	outportb(0x3c2, 0xE7);		/* Use the 28mHz clock */

	outportb(0x3d4, 0x11);
	Inbyte = inportb(0x3d5) & 0x7f;
	outportb(0x3d4, 0x11);
	outportb(0x3d5, Inbyte);	/* Enable the writing of CRTC Registers */

/* Write the CRTC Registers */

	outportb(0x3d4, 0x00);
	outportb(0x3d5, 0x6b);

	outportb(0x3d4, 0x01);
	outportb(0x3d5, 0x59);

	outportb(0x3d4, 0x02);
	outportb(0x3d5, 0x5a);

	outportb(0x3d4, 0x03);
	outportb(0x3d5, 0x8e);

	outportb(0x3d4, 0x04);
	outportb(0x3d5, 0x5e);

	outportb(0x3d4, 0x05);
	outportb(0x3d5, 0x8a);

	outportb(0x3d4, 0x06);
	outportb(0x3d5, 0x0d);

	outportb(0x3d4, 0x07);
	outportb(0x3d5, 0x3e);

	outportb(0x3d4, 0x09);
	outportb(0x3d5, 0x40);

	outportb(0x3d4, 0x10);
	outportb(0x3d5, 0xea);

	outportb(0x3d4, 0x12);
	outportb(0x3d5, 0xdf);

	outportb(0x3d4, 0x13);
	outportb(0x3d5, 0x2d);

	outportb(0x3d4, 0x14);
	outportb(0x3d5, 0x00);

	outportb(0x3d4, 0x15);
	outportb(0x3d5, 0xe7);

	outportb(0x3d4, 0x16);
	outportb(0x3d5, 0x06);

	outportb(0x3d4, 0x17);
	outportb(0x3d5, 0xe3);

	outportb(0x3d4, 0x11);
	outportb(0x3d5, 0xac);
}

/*

The following code fragment shows how to write an arbitrary scanline
of pixels on the screen in 360x480x256 VGA mode.  The Linenumbers are
numbered from 0 to 479 starting from the upper left.  A byte array
(Scanline) contains the pixels to be displayed.

I,J,K,L are Words or Integers
BT is a Byte

I := Linenumber * 90;  {The start of each graphic scan line is 360/4 or 90
                        memory addresses after the previous scan line.
                        Each scan line occupies 90 bytes of address space
                        and since there are four memory planes there are
                        90 X 4 bytes of memory (360) for each scan line.

                        If the upper left pixel is pixel number 0,  and
                        the lower right pixel is pixel number
                        360x480-1, an arbitrary pixel is accessed by
                        finding the VGA memory address which is the
                        pixel number shifted right 2 places relative
                        to the start of VGA memory and the memory
                        plane number is the 2 LSBs of the pixel
                        number}


  For J := 0 to DispWidth - 1 do
    Begin
      L := I + j div 4;               {Calculate VGA memory address offset}
      k := j mod 4;                   {Set K = 2 LSBs of pixel number}
      BT := $01 shl k;                {Set BT = memory plane mask code}
      Port[$3c4] := $02;              {Index Timing Sequencer register 2}
      Port[$3c5] := BT;               {Write memory plane select mask}
      Mem[VGAPage:L] := ScanLine[j];  {Write pixel}
    End;

The following routine sets the standard VGA into 320x400x256 mode, which
is very similar to the 360x480x256 mode.  Writing to the screen in
320x400x256 is the same as 360x480x256 mode, the only difference is
the address span of each scan line which is 80 rather than 90.

*/


void VGA320x400()
{
	char         Inbyte;

	_AX = 0x0013;
	geninterrupt(0x10);

	outportb(0x3c4, 0x04);
	outportb(0x3c5, 0x06);		/* Disable chain 4 */

	outportb(0x3c4, 0x02);
	outportb(0x3c5, 0x0F);

	memset(MK_FP(VGAPage,0), 0, 65535);

	outportb(0x3c2, 0xE3);		/* Set the 25mHz clock */

	outportb(0x3d4, 0x11);
	Inbyte = inportb(0x3d5) & 0x7f;
	outportb(0x3d4, 0x11);
	outportb(0x3d5, Inbyte);	/* Enable the writing of CRTC Registers */

	outportb(0x3d4, 0x09);
	outportb(0x3d5, 0x40);

	outportb(0x3d4, 0x14);
	outportb(0x3d5, 0x00);

	outportb(0x3d4, 0x17);
	outportb(0x3d5, 0xe3);
}

void
testpat(int x,int y)
{
	int i,j;
	int k;
	char far *cp;

	outportb(0x3c4, 2);
	for (j = 0; j < y; j++) {
		k = j * (x >> 2);
		for (i = 0; i < x; i++) {
			outportb(0x3c5, 1 << (i & 3));
			cp = MK_FP(VGAPage,k + (i >> 2));
			*cp = (i + j);
		}
	}
}


main()
{
	VGA320x400();
	testpat(320,400);
	getch();
	VGA360x480();
	testpat(360,480);
	getch();
	_AX = 0x0003;
	geninterrupt(0x10);
}
--
--russ (nelson@clutx [.bitnet | .clarkson.edu])
Live up to the light thou hast, and more will be granted thee.
A recession now appears more than 2 years away -- John D. Mathon, 4 Oct 1989.

lurifax@freja.diku.dk (Michael Brian Moustgaard) (11/15/89)

------------ cut here ------------
/* This is a revised version of vgapals.c for those
 * who don't like assembler, or doesn't have an assembler.
 *
 * It has compiled with TC 2.0, it would probably
 * too with MSC 5.1
 *
 * I have changed the program just a little.
 *
 *
 * Greetings, Michael B. Moustgaard
 *
 *
 */

/* vgapals.c
 *
 * (c) 1989 Michael K. Finegan, Jr.
 *
 * Simplistic, and inefficient, way to display
 * unsigned char image on 320x200 vga display,
 * but illustrates use of LoadPals() with display
 * of centered 200 x 256 image - a grey scale ramp.
 *
 */
#include <stdio.h>
#include <dos.h>

#define FALSE 0
#define TRUE 1


/* used by LoadPals */
unsigned char NewLut[800], OldLut[800];

main()
{
    int i, x, y;
    void LoadPals();
    void WaitKey();
    void setvmode(), writepix();


   /* Load palette with reduced (64 level) gray scale (R = G = B).
    *
    * Only the 6 least signifigant bits of R,G,B bytes are
    * used for the output Look Up Table.
    *
    * NOTE: you could load your LUT (256*3 bytes), maybe
    * previously saved to file, into NewLut, instead ...
    */
    for(i=0;i<256;i++) {
	NewLut[3*i + 0] = (unsigned char)(i/4);	/* R */
	NewLut[3*i + 1] = (unsigned char)(i/4);	/* G */
	NewLut[3*i + 2] = (unsigned char)(i/4);	/* B */
    }

    setvmode(0x13);	/* 0x13 <==> 256 color 320 by 200 VGA */
    LoadPals(1);

   /*
    * draw a grey scale 'ramp'
    *
    * "x + 32" shifts 256 column image to center of screen.
    */
    for(y=0;y<200;y++) {
         for(x=0;x<256;x++)
	     writepix(x + 32,y,(unsigned char)x);
    }

    WaitKey();		/* i.e. wait for keystroke before quitting ... */

    LoadPals(0);
    setvmode(0x3);	/* 0x3 <==> text; should restore previous mode ... */
}


void setvmode(mode)
int mode;
{
    union REGS inregs, outregs;

    inregs.h.ah = 0;
    inregs.h.al = mode;
    int86(0x10,&inregs,&outregs);
}


void writepix(x,y,value)
int x, y;
unsigned char value;
{
    union REGS inregs, outregs;

   /* simple video bios call, could write directly to memory ...
    *
    * image processing coordinate system - 0,0 @ upper left corner
    */
    inregs.h.al = value;
    inregs.h.ah = 0x0C;		/* write dot */
    inregs.h.bh = 0;		/* assume page 0 */
    inregs.x.cx = (unsigned)x;
    inregs.x.dx = (unsigned)y;
    int86(0x10,&inregs,&outregs);
}
/*
;
;	LoadPals      Load VGA 256 color palette
;
;	Call  LoadPals(AX)
;		Where AX :
;		      3 ==> Just Load pals
;		      1 ==> Save current pals, then Load new pals
;		      2 ==> Just Restore pals
;		      0 ==> Save current pals, then Restore old pals
;		Assume:
;		      NewLut filled with new LUT; OldLut holds saved LUT,
;		      depending on calling sequence.
;
;	LoadPals(1) followed by LoadPals(2) leaves palette unchanged ...
;
*/
void LoadPals(ax)
int ax;
{
    union REGS inregs, outregs;
    struct SREGS sreg;

	if (ax & 1 == 1)
	{
	   if (ax==1)
	   {
/* save old */
    inregs.h.ah = 0x10;    /* set palette reg.s/intensity/blink function */
    inregs.h.al = 0x17;    /* read a block of DAC color registers */
    inregs.x.bx = 0;       /* start with register 0 */
    inregs.x.cx = 0x100;   /* and read all 256 */
    segread(&sreg);
    sreg.es = sreg.ds;
    inregs.x.dx = (unsigned)&OldLut;
    int86x(0x10,&inregs,&outregs,&sreg);
	   }
/* load new */
    inregs.h.ah = 0x10;    /* set palette reg.s/intensity/blink function */
    inregs.h.al = 0x12;    /* update a block of DAC color registers */
    inregs.x.bx = 0;       /* start with register 0 */
    inregs.x.cx = 0x100;   /* and change all 256 */
    segread(&sreg);
    sreg.es = sreg.ds;
    inregs.x.dx = (unsigned)&NewLut;
    int86x(0x10,&inregs,&outregs,&sreg);
        }
	else
	{
/* save new */
	   if (ax==0)
	   {
    inregs.h.ah = 0x10;    /* set palette reg.s/intensity/blink function */
    inregs.h.al = 0x17;    /* read a block of DAC color registers */
    inregs.x.bx = 0;       /* start with register 0 */
    inregs.x.cx = 0x100;   /* and read all 256 */
    segread(&sreg);
    sreg.es = sreg.ds;
    inregs.x.dx = (unsigned)&NewLut;
    int86x(0x10,&inregs,&outregs,&sreg);
	   }
/* restore old */
    inregs.h.ah = 0x10;    /* set palette reg.s/intensity/blink function */
    inregs.h.al = 0x12;    /* update a block of DAC color registers */
    inregs.x.bx = 0;       /* start with register 0 */
    inregs.x.cx = 0x100;   /* and change all 256 */
    segread(&sreg);
    sreg.es = sreg.ds;
    inregs.x.dx = (unsigned)&OldLut;
    int86x(0x10,&inregs,&outregs,&sreg);
        }
}

/*
; WaitKey read a char from keyboard, with no Cntrl-Brk
; check, no echo, and the <scan code,char> .
*/
void WaitKey()
{
    union REGS inregs, outregs;

	inregs.h.ah = 0;
	int86(0x16,&inregs,&outregs);
}

------------------- cut here too -------------