/*

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 <stdio.h>
#include <dos.h>
#include "mode_x.h"


/* 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(Palette *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 */


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
        thisbefore 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)
{
    while (inportb(DISPIO) & VRT_bit);          /* wait */
    while ((inportb(DISPIO) & VRT_bit) == 0);   /* wait */
}
