/* 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
};

/* non-BIOS mode - 400x300x256 - added by Ark 28-JAN-2001 */
static const unsigned char g400x300x256X_regs[60] =
{
  0x71, 0x63, 0x64, 0x92, 0x65, 0x82, 0x46, 0x1F, 0x00, 0x40, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x31, 0x80, 0x2B, 0x32, 0x00, 0x2F, 0x44, 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,
    0xA7
};


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


/* Fill in chipset-specific modeinfo */

static void 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);
}

static void nothing(void)
{
}

static int saveregs(unsigned char regs[])
{
    return 0;
}

static void setregs(const unsigned char regs[], int mode)
{
}

/* 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;
    __svgalib_setregs(regs);
    return 0;
}

/* Set display start */

static void 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 */
            __svgalib_outatt(0x33,(__svgalib_inatt(0x33)&0xf0) | (address & 7));
	    /* write sa0-2 to bits 0-2 */
	    address >>= 3;
	    break;
	case 256:		/* planar 256-color mode */
	    /* write sa0-1 to bits 1-2 */
            __svgalib_outatt(0x33,(__svgalib_inatt(0x33)&0xf0) | ((address & 3)<<1) );
	    address >>= 2;
	    break;
	}
    __svgalib_outcrtc(0x0d, address & 0x00ff);
    __svgalib_outcrtc(0x0c, (address & 0xff00) >> 8);
}

static void setlogicalwidth(int width)
{
    __svgalib_outcrtc(0x13, width >> 3);
}

static int vgadrv_init(int, int, int);

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

    /* Check if a DAC is present */
    save = port_in(PEL_IW);
    __svgalib_delay();
    port_out_r(PEL_IW, ~save);
    __svgalib_delay();
    back = port_in(PEL_IW);
    __svgalib_delay();
    port_out_r(PEL_IW, save);
    save = ~save;
    if (back == save) {
	vgadrv_init(0, 0, 0);
	return 1;
    }
    return 0;
}


DriverSpecs __svgalib_vga_driverspecs =
{				/* standard VGA */
    saveregs,
    setregs,
    nothing,			/* unlock */
    nothing,			/* lock */
    vga_test,
    vgadrv_init,
    (void (*)(int)) nothing,	/* __svgalib_setpage */
    (void (*)(int)) nothing,	/* __svgalib_setrdpage */
    (void (*)(int)) nothing,	/* __svgalib_setwrpage */
    setmode,
    modeavailable,
    setdisplaystart,
    setlogicalwidth,
    getmodeinfo,
    0,				/* bitblt */
    0,				/* imageblt */
    0,				/* fillblt */
    0,				/* hlinelistblt */
    0,				/* bltwait */
    0,				/* extset */
    0,
    0,				/* linear */
    NULL,                       /* Accelspecs */
    NULL,                       /* Emulation */
};

/* Initialize chipset (called after detection) */

static int vgadrv_init(int force, int par1, int par2)
{
    if (__svgalib_driver_report)
	fprintf(stderr,"Using VGA driver.\n");
    __svgalib_driverspecs = &__svgalib_vga_driverspecs;
    __svgalib_banked_mem_base=0xa0000;
    __svgalib_banked_mem_size=0x10000;

    return 0;
}
