/* VGAlib version 1.2 - (c) 1993 Tommy Frandsen 		   */
/*								   */
/* This library is free software; you can redistribute it and/or   */
/* modify it without any restrictions. This library is distributed */
/* in the hope that it will be useful, but without any warranty.   */

/* Multi-chipset support Copyright 1993 Harm Hanemaayer */
/* partially copyrighted (C) 1993 by Hartmut Schirmer */


#include <stdlib.h>	/* for NULL */
#include "vga.h"
#include "libvga.h"
#include "driver.h"

/* BIOS mode 0Dh - 320x200x16 */
static const unsigned char g320x200x16_regs[60] = {
  0x2D,0x27,0x28,0x90,0x2B,0x80,0xBF,0x1F,0x00,0xC0,0x00,0x00, 
  0x00,0x00,0x00,0x00,0x9C,0x8E,0x8F,0x14,0x00,0x96,0xB9,0xE3, 
  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B, 
  0x0C,0x0D,0x0E,0x0F,0x01,0x00,0x0F,0x00,0x00, 
  0x00,0x0F,0x00,0x20,0x00,0x00,0x05,0x0F,0xFF, 
  0x03,0x09,0x0F,0x00,0x06, 
  0x63
};

/* BIOS mode 0Eh - 640x200x16 */
static const unsigned char g640x200x16_regs[60] = {
  0x5F,0x4F,0x50,0x82,0x54,0x80,0xBF,0x1F,0x00,0xC0,0x00,0x00, 
  0x00,0x00,0x00,0x00,0x9C,0x8E,0x8F,0x28,0x00,0x96,0xB9,0xE3, 
  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B, 
  0x0C,0x0D,0x0E,0x0F,0x01,0x00,0x0F,0x00,0x00, 
  0x00,0x0F,0x00,0x20,0x00,0x00,0x05,0x0F,0xFF, 
  0x03,0x01,0x0F,0x00,0x06, 
  0x63
};

/* BIOS mode 10h - 640x350x16 */
static const unsigned char g640x350x16_regs[60] = {
  0x5F,0x4F,0x50,0x82,0x54,0x80,0xBF,0x1F,0x00,0x40,0x00,0x00, 
  0x00,0x00,0x00,0x00,0x83,0x85,0x5D,0x28,0x0F,0x63,0xBA,0xE3, 
  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B, 
  0x0C,0x0D,0x0E,0x0F,0x01,0x00,0x0F,0x00,0x00, 
  0x00,0x0F,0x00,0x20,0x00,0x00,0x05,0x0F,0xFF, 
  0x03,0x01,0x0F,0x00,0x06, 
  0xA3
};

/* BIOS mode 12h - 640x480x16 */
static const unsigned char g640x480x16_regs[60] = {
  0x5F,0x4F,0x50,0x82,0x54,0x80,0x0B,0x3E,0x00,0x40,0x00,0x00,
  0x00,0x00,0x00,0x00,0xEA,0x8C,0xDF,0x28,0x00,0xE7,0x04,0xE3,
  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,
  0x0C,0x0D,0x0E,0x0F,0x01,0x00,0x0F,0x00,0x00,
  0x00,0x0F,0x00,0x20,0x00,0x00,0x05,0x0F,0xFF,
  0x03,0x01,0x0F,0x00,0x06,
  0xE3
};

/* BIOS mode 13h - 320x200x256 */
static const unsigned char g320x200x256_regs[60] = {
  0x5F,0x4F,0x50,0x82,0x54,0x80,0xBF,0x1F,0x00,0x41,0x00,0x00, 
  0x00,0x00,0x00,0x00,0x9C,0x8E,0x8F,0x28,0x40,0x96,0xB9,0xA3, 
  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B, 
  0x0C,0x0D,0x0E,0x0F,0x41,0x00,0x0F,0x00,0x00, 
  0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0F,0xFF, 
  0x03,0x01,0x0F,0x00,0x0E, 
  0x63
};

/* non-BIOS mode - 320x240x256 */
static const unsigned char g320x240x256_regs[60] = {
  0x5F,0x4F,0x50,0x82,0x54,0x80,0x0D,0x3E,0x00,0x41,0x00,0x00, 
  0x00,0x00,0x00,0x00,0xEA,0xAC,0xDF,0x28,0x00,0xE7,0x06,0xE3, 
  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B, 
  0x0C,0x0D,0x0E,0x0F,0x41,0x00,0x0F,0x00,0x00, 
  0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0F,0xFF, 
  0x03,0x01,0x0F,0x00,0x06, 
  0xE3
};

/* non-BIOS mode - 320x400x256 */
static const unsigned char g320x400x256_regs[60] = {
  0x5F,0x4F,0x50,0x82,0x54,0x80,0xBF,0x1F,0x00,0x40,0x00,0x00, 
  0x00,0x00,0x00,0x00,0x9C,0x8E,0x8F,0x28,0x00,0x96,0xB9,0xE3, 
  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B, 
  0x0C,0x0D,0x0E,0x0F,0x41,0x00,0x0F,0x00,0x00, 
  0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0F,0xFF, 
  0x03,0x01,0x0F,0x00,0x06, 
  0x63
};

/* non-BIOS mode - 360x480x256 */
static const unsigned char g360x480x256_regs[60] = {
  0x6B,0x59,0x5A,0x8E,0x5E,0x8A,0x0D,0x3E,0x00,0x40,0x00,0x00, 
  0x00,0x00,0x00,0x00,0xEA,0xAC,0xDF,0x2D,0x00,0xE7,0x06,0xE3, 
  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B, 
  0x0C,0x0D,0x0E,0x0F,0x41,0x00,0x0F,0x00,0x00, 
  0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0F,0xFF, 
  0x03,0x01,0x0F,0x00,0x06, 
  0xE7
};

/* monochrome mode based on BIOS mode 12h - 640x480x2 */
#define g640x480x2_regs g640x480x16_regs

/* non BIOS mode - 720x348x2 based on mode 10h */
static const unsigned char g720x348x2_regs[60] = {
  0x6B,0x59,0x5A,0x8E,0x5E,0x8A,0xBF,0x1F,0x00,0x40,0x00,0x00,
  0x00,0x00,0x00,0x00,0x83,0x85,0x5D,0x2D,0x0F,0x63,0xBA,0xE3,
  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,
  0x0C,0x0D,0x0E,0x0F,0x01,0x00,0x0F,0x00,0x00,
  0x00,0x0F,0x00,0x20,0x00,0x00,0x05,0x0F,0xFF,
  0x03,0x01,0x0F,0x00,0x06,
  0xA7
};


/* Mode table */
static ModeTable vga_modes[] = {
        OneModeEntry(640x480x2),
	OneModeEntry(720x348x2),
	OneModeEntry(320x200x16),
	OneModeEntry(640x200x16),
	OneModeEntry(640x350x16),
        OneModeEntry(640x480x16),
	OneModeEntry(320x200x256),
	OneModeEntry(320x240x256),
	OneModeEntry(320x400x256),
	OneModeEntry(360x480x256),
#ifdef G720x350x16
	OneModeEntry(720x350x16).
#endif
	END_OF_MODE_TABLE
};


/* Fill in chipset-specific modeinfo */

static int getmodeinfo( int mode, vga_modeinfo *modeinfo ) {
	if (modeinfo->bytesperpixel == 1) {	/* 320x200x256 linear mode */
		modeinfo->maxpixels = 65536;
		modeinfo->startaddressrange = 0xffff;
	}
	else
		switch (modeinfo->colors) {
		case 16 :	/* 4-plane 16 color mode */
			modeinfo->maxpixels = 65536 * 8;
			modeinfo->startaddressrange = 0x7ffff;
			break;
		case 256 :	/* 4-plane 256 color mode */
			modeinfo->maxpixels = 65536 * 4;
			modeinfo->startaddressrange = 0x3ffff;
			break;
		}
	modeinfo->maxlogicalwidth = 2040;
	modeinfo->haveblit = 0;
	modeinfo->flags &= ~(IS_INTERLACED | HAVE_RWPAGE);

	return 0;
}

static int nothing() { return 0; }


/* Return nonzero if mode available */

static int modeavailable( int mode ) {
	const unsigned char *regs;

	regs = LOOKUPMODE(vga_modes, mode);
	if (regs != NULL && regs != DISABLE_MODE)
	  return STDVGADRV;
	return 0;
}


/* Set a mode */

static int lastmode;

static int setmode( int mode, int prv_mode )   {
/* standard VGA driver: setmode */
	const unsigned char *regs;

	if (mode == TEXT)
		return 0;	/* Do nothing. */

	regs = LOOKUPMODE(vga_modes, mode);
	if (regs == NULL || regs == DISABLE_MODE)
		return 1;
	lastmode = mode;
	__vga_setregs(regs);
	return 0;
}


/* Set display start */

static int setdisplaystart( int address ) {
	vga_modeinfo *modeinfo;
	modeinfo = vga_getmodeinfo(lastmode);
	if (modeinfo->bytesperpixel == 0)	/* not 320x200x256 linear */
		switch (modeinfo->colors) {
		case 16 :	/* planar 16-color mode */
			inb(0x3da);
			outb(0x3c0, 0x13 + 0x20);
			outb(0x3c0, (inb(0x3c1) & 0xf0) | (address & 7));
				/* write sa0-2 to bits 0-2 */
			address >>= 3;
			break;
		case 256 :	/* planar 256-color mode */
			inb(0x3da);
			outb(0x3c0, 0x13 + 0x20);
			outb(0x3c0, (inb(0x3c1) & 0xf0) | ((address & 3) << 1));
				/* write sa0-1 to bits 1-2 */
			address >>= 2;
			break;
		}
	outw(0x3d4, 0x0d + (address & 0x00ff) * 256);		/* sa0-sa7 */
	outw(0x3d4, 0x0c + (address & 0xff00));			/* sa8-sa15 */

	return 0;
}

static int setlogicalwidth( int width ) {
	outw(0x3d4, 0x13 + (width >> 3) * 256);	/* lw3-lw11 */

	return 0;
}

static int vgadrv_init( int, int, int );

static int vga_test()
{
	unsigned char save, back;

	/* Check if a DAC is present */
	save = inb(PEL_IW);
	__vga_delay();
	outb(PEL_IW, ~save);
	__vga_delay();
	back = inb(PEL_IW);
	__vga_delay();
	outb(PEL_IW, save);
	save = ~save;
	if (back == save) {
	    vgadrv_init(0,0,0);
	    return 1;
	}
	return 0;
}


int (*vga_chipsetfunctions[])() = {		/* standard VGA */
	nothing,
	nothing,
	nothing, /* unlock */
	nothing, /* lock */
	vga_test,
	vgadrv_init,
	nothing, /* setpage */
	nothing, /* setrdpage */
	nothing, /* setwrpage */
	setmode,
	modeavailable,
	setdisplaystart,
	setlogicalwidth,
	getmodeinfo
}; 

/* Initialize chipset (called after detection) */

static int vgadrv_init( int force, int par1, int par2) {
	if (__svgalib_driver_report)
		printf("Using VGA driver.\n");
	chipsetfunctions = vga_chipsetfunctions;

	return 0;
}
