[comp.graphics] Double Buffering on a ATI VGA Wonder

david@kessner.denver.co.us (David Kessner) (05/20/91)

Here is the story...  I have a 512K ATI VGA Wonder board and have written
several routines to support it's 256 color modes-- like a blindingly 
fast line drawing function.

I am doing rather complex 3-D animations and have also been very
sucsessful at that also.  (I've been rotating a wire frame couch at
26 frames/sec, amung other things-- using floating point even!).

What I need now is how to double buffer the display.  What I'd like to do
is use a 640x400 display mode (which uses almost 256K of RAM) and divide
the video RAM into two logical "screens".  Then display one while drawing
on the other.  Sounds simple enough.  While I have been doing graphics
for quite some time, VGA programming is another thing-- but I am not
afraid to get my hands dirty with some assembly...

So, how do I do this?  BIOS Calls?  VGA Register programming?  (I am also
not afraid to make this ATI VGA Wonder specific-- read on.)

While code fragments would be nice, an answer like, "Look up INT 10h
Function ??, etc" would suffice quite nicely...

While on the subject... This program(s) are really for my own amusement
and learning-- but if you happen to own a 512K VGA Wonder and a math
chip (386 prefered, as well as an IIT 3c87 FPU) I'd be willing to share
what I have-- which is minimal but could spin store logo's for you...

-- 
David Kessner - david@kessner.denver.co.us            |
1135 Fairfax, Denver CO  80220  (303) 377-1801 (p.m.) | Reunite PANGEA!
Why can't everyone have three or four line .sig's?    |

frank@cavebbs.gen.nz (Frank van der Hulst) (05/21/91)

In article <1991May20.063352.1561@kessner.denver.co.us> david@kessner.denver.co.us (David Kessner) writes:
>Here is the story...  I have a 512K ATI VGA Wonder board and have written
>several routines to support it's 256 color modes-- like a blindingly 
>fast line drawing function.
>
>What I need now is how to double buffer the display.  What I'd like to do
>is use a 640x400 display mode (which uses almost 256K of RAM) and divide
>the video RAM into two logical "screens".  Then display one while drawing
>on the other.  Sounds simple enough.  While I have been doing graphics
>for quite some time, VGA programming is another thing-- but I am not
>afraid to get my hands dirty with some assembly...

I've done this with 320x400 mode (256 colours), giving 2 pages. I realise that's not quite what you were after, but maybe it'll give you some ideas..

Following is the code, which is based on Michael Abrash's article in an
issue of Programmner's Journal. Last time I mentioned this (inn rec.games.prog)
several people requested the code, so this time I've made a pre-emptive strike!

According to Abrash, almost any VGA card should work in 320*400 mode.

/*

VGA 320 * 400 * 256 * 2 frames routines.

Written by: F van der Hulst, 20/2/91

These routines display pixels in 320*400 mode by modifying the VGA
registers, as outlined in Programmer's Journal V7.1 (Jan/Feb '89)
article, pages 18-30, by Michael Abrash.

The advantage of 320 * 400, is that it gives two separate video pages,
which can be displayed on the screen independently. These can contain
two views of a scene, taken from slightly different viewpoints. These
are displayed alternately on the screen, in sync with a pair of
"chopper glasses", to give a 3D effect.
*/

#include <conio.h>

typedef unsigned char DacPalette[256][3];

/* Setvgapalette sets the entire 256 color palette     */
/* PalBuf contains RGB values for all 256 colors       */
/* R,G,B values range from 0 to 63	                   */
/* Taken from SVGA256.H, by Jordan Hargraphix Software */

void setvgapalette(DacPalette *PalBuf)
{
  struct REGPACK reg;

  reg.r_ax = 0x1012;
  reg.r_bx = 0;
  reg.r_cx = 256;
  reg.r_es = FP_SEG(PalBuf);
  reg.r_dx = FP_OFF(PalBuf);
  intr(0x10,&reg);
}


unsigned int act_page = 0; /* Current page being written to */


#define VGA_SEGMENT        0xa000
#define SC_INDEX           0x3c4
#define GC_INDEX           0x3ce
#define CRTC_INDEX         0x3d4
#define DISPIO             0x3DA

#define MAP_MASK           2
#define MEMORY_MODE        4
#define GRAPHICS_MODE      5
#define MISCELLANEOUS      6
#define VRT_bit            8
#define MAX_SCAN_LINE      9
#define START_ADDRESS_HIGH 0x0c
#define UNDERLINE          0x14
#define MODE_CONTROL       0x17

void writepixel(int x, int y, unsigned char colour)
{
long addr;

	addr = ((x >> 2) + 320/4 * y + act_page);
	addr = ((addr & 0xffff0000l) << 4) + (addr & 0xffffL) + ((long) VGA_SEGMENT << 16);
	outport(SC_INDEX, (0x100 << (x & 3)) | MAP_MASK);
	*(char far*)addr = colour;
}

void set320x400mode(void)
{
struct REGPACK regs;
unsigned char x;

	regs.r_ax = 0x13;				/* Set 320*200*256 graphics mode via BIOS */
	intr(0x10, &regs);

/* Change CPU addressing of video memory to linear (not odd/even, chain, or
	chain 4), to allow access to all 256K of display memory. Each byte will now
	control one pixel, with 4 adjacent pixels at any given address, one pixel
	per plane. */

	outportb(SC_INDEX, MEMORY_MODE);
	x = inportb(SC_INDEX+1);
	x &= 0xf7;									/* Turn off chain 4  */
	x |= 4;										/* Turn off odd/even */
	outportb(SC_INDEX+1, x);
	outportb(GC_INDEX, GRAPHICS_MODE);
	x = inportb(GC_INDEX+1);
	x &= 0xef;									/* Turn off odd/even */
	outportb(GC_INDEX+1, x);
	outportb(GC_INDEX, MISCELLANEOUS);
	x = inportb(GC_INDEX+1);
	x &= 0xfd;									/* Turn off chain */
	outportb(GC_INDEX+1, x);

/* Now clear the whole screen, since the mode 13h set only clears 64K. Do this
	before switching CRTC out of mode 13h, so that we don't see grabage on the
	screen. */

	outport(SC_INDEX, 0x0f00 | MAP_MASK);			/* Write to 4 planes at once */
	setmem(MK_FP(VGA_SEGMENT, 0), 0xffff, 0);

/* Change mode to 320*400 by not scanning each line twice. */
	outportb(CRTC_INDEX, MAX_SCAN_LINE);
	x = inportb(CRTC_INDEX+1);
	x &= 0xe0;								/* Set maximum scan line to 0 */
	outportb(CRTC_INDEX+1, x);

/* Change CRTC scanning from doubleword to byte mode, allowing the CRTC to
	scan more than 64K */
	outportb(CRTC_INDEX, UNDERLINE);
	x = inportb(CRTC_INDEX+1);
	x &= 0xbf;					/* Turn off doubleword */
	outportb(CRTC_INDEX+1, x);
	outportb(CRTC_INDEX, MODE_CONTROL);
	x = inportb(CRTC_INDEX+1);
	x |= 0x40;					/* Turn on the byte mode bit, so memory is linear */
	outportb(CRTC_INDEX+1, x);
}

void end320x400mode(void)
{
struct REGPACK regs;

	regs.r_ax = 3;				/* Return to text mode */
	intr(0x10, &regs);
}

/* Set visible page */

void setvispage(int page)
{
	outport(CRTC_INDEX, (page << 15) | START_ADDRESS_HIGH);
}

/* Set active page (page being written to */

void setactpage(int page)
{
	act_page = page ? 0x8000 : 0;
}

void WaitForVerticalRetrace(void)
{
static char chopper = 1;

	while (inportb(DISPIO) & VRT_bit) /* wait */ ;
	while ((inportb(DISPIO) & VRT_bit) == 0) /* wait */ ;
	if ((chopper++ & 1)== 0)		outportb(0x3fc, 1);
	else									outportb(0x3fc, 3);
}

void main(int argc, char *argv[])
{
   set320x400mode();

/* Now fill the rgb_palette structure in memory with colour info */

   setvgapalette(&rgb_palette);

   setactpage(0);
/* Now call writepixel to put stuff on page 0 */
   setactpage(1);
/* Now call writepixel to put stuff on page 1 */

   while (!kbhit()) {
	   WaitForVerticalRetrace();
	   setvispage(0);
	   WaitForVerticalRetrace();
	   setvispage(1);
   }
   getch();
   end320x400mode();
}

-- 

Take a walk on the wild side, and I don't mean the Milford Track.
Kayaking: The art of appearing to want to go where your boat is taking you.

jrd@cc.usu.edu (05/27/91)

In article <1991May20.063352.1561@kessner.denver.co.us>, david@kessner.denver.co.us (David Kessner) writes:
> Here is the story...  I have a 512K ATI VGA Wonder board and have written
> several routines to support it's 256 color modes-- like a blindingly 
> fast line drawing function.
> 
> I am doing rather complex 3-D animations and have also been very
> sucsessful at that also.  (I've been rotating a wire frame couch at
> 26 frames/sec, amung other things-- using floating point even!).
> 
> What I need now is how to double buffer the display.  What I'd like to do
> is use a 640x400 display mode (which uses almost 256K of RAM) and divide
> the video RAM into two logical "screens".  Then display one while drawing
> on the other.  Sounds simple enough.  While I have been doing graphics
> for quite some time, VGA programming is another thing-- but I am not
> afraid to get my hands dirty with some assembly...
> 
> So, how do I do this?  BIOS Calls?  VGA Register programming?  (I am also
> not afraid to make this ATI VGA Wonder specific-- read on.)
> 
> While code fragments would be nice, an answer like, "Look up INT 10h
> Function ??, etc" would suffice quite nicely...
> 
> While on the subject... This program(s) are really for my own amusement
> and learning-- but if you happen to own a 512K VGA Wonder and a math
> chip (386 prefered, as well as an IIT 3c87 FPU) I'd be willing to share
> what I have-- which is minimal but could spin store logo's for you...
> 
> -- 
> David Kessner - david@kessner.denver.co.us            |
> 1135 Fairfax, Denver CO  80220  (303) 377-1801 (p.m.) | Reunite PANGEA!
> Why can't everyone have three or four line .sig's?    |
---------------
David,
	And the answer is... register twiddling. If your vendor doc's are
inadequate for the purpose then one book worth buying is "Advanced Programmer's
Guide to SuperVGAs" by George Sutty and Steve Blair, Brady/Prentice Hall, 1990.
Recall that those extra 256KB are well hidden out of the way with vendor-
specific techniques. Hence the register stuff.
	Joe D.