/* 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 */
/* EGA support from EGAlib by Kapil Paranjape */
/* partially copyrighted (C) 1993 by Hartmut Schirmer */


#include <stdio.h>
#include <string.h>
#include "vga.h"
#include "libvga.h"
#include "driver.h"

static int ega_init(int force, int par1, int par2);

/* BIOS mode 0Dh - 320x200x16 */
static char g320x200x16_regs[60] =
{
  0x37, 0x27, 0x2D, 0x37, 0x30, 0x14, 0x04, 0x11, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0xE1, 0x24, 0xC7, 0x14, 0x00, 0xE0, 0xF0, 0xE3,
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
    0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00,
    0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
    0x03, 0x0B, 0x0F, 0x00, 0x06,
    0x23
};

/* BIOS mode 0Eh - 640x200x16 */
static char g640x200x16_regs[60] =
{
  0x70, 0x4F, 0x59, 0x2D, 0x5E, 0x06, 0x04, 0x11, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0xE0, 0x23, 0xC7, 0x28, 0x00, 0xDF, 0xEF, 0xE3,
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
    0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x0F, 0x00, 0x00,
    0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
    0x03, 0x01, 0x0F, 0x00, 0x06,
    0x23
};

/* BIOS mode 10h - 640x350x16 */
static char g640x350x16_regs[60] =
{
  0x5B, 0x4F, 0x53, 0x37, 0x52, 0x00, 0x6C, 0x1F, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x5E, 0x2B, 0x5E, 0x28, 0x0F, 0x5F, 0x0A, 0xE3,
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
    0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00,
    0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0F, 0xFF,
    0x03, 0x01, 0x0F, 0x00, 0x06,
    0xA7
};

/* EGA registers for saved text mode 03* */
static char text_regs[60] =
{
  0x5B, 0x4F, 0x53, 0x37, 0x51, 0x5B, 0x6C, 0x1F, 0x00, 0x0D, 0x0a, 0x0c,
  0x00, 0x00, 0x00, 0x00, 0x5E, 0x2B, 0x5D, 0x28, 0x0F, 0x5E, 0x0A, 0xA3,
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
    0x3C, 0x3D, 0x3E, 0x3F, 0x0A, 0x00, 0x0F, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x00, 0xFF,
    0x03, 0x01, 0x03, 0x00, 0x03,
    0xA7
};

/* Mode table */
static ModeTable __ega_modes[] =
{
/* *INDENT-OFF* */
    OneModeEntry(320x200x16),
    OneModeEntry(640x200x16),
    OneModeEntry(640x350x16),
    END_OF_MODE_TABLE
/* *INDENT-ON* */
};

static ModeTable *ega_modes = NULL;


/* Fill in chipset-specific modeinfo */

static void getmodeinfo(int mode, vga_modeinfo * modeinfo)
{
    /* Taken from vga 16 colors. Correct ??? */
    modeinfo->maxpixels = 65536 * 8;
    modeinfo->startaddressrange = 0x7ffff;
    modeinfo->maxlogicalwidth = 2040;
    modeinfo->haveblit = 0;
    modeinfo->flags &= ~(IS_INTERLACED | HAVE_RWPAGE);
}

static void nothing(void)
{
}


/* Return nonzero if mode available */

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

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

static int lastmode = TEXT;

static int saveregs(unsigned char *regs)
{
    /* We can't read the registers from an EGA card. */
    /* We just report the expected values. */

    const unsigned char *r;
    int i;

    if (lastmode == TEXT)
	r = text_regs;
    else
	r = LOOKUPMODE(ega_modes, lastmode);
    if (r == NULL) {
	fprintf(stderr,"svgalib: egadrv.c/saveregs(): internal error\n");
	exit(-1);
    }
    memcpy(regs, r, CRT_C + ATT_C + GRA_C + SEQ_C + MIS_C);

    /* save all readable EGA registers; others are default */
    /* is this correct (even in graphics mode) ?? */
    for (i = 0x0C; i < 0x10; i++) {
	port_out(i, __svgalib_CRT_I);
	regs[CRT + i] = port_in(__svgalib_CRT_D);
    }

    return CRT_C + ATT_C + GRA_C + SEQ_C + MIS_C;
}


/* Set chipset-specific registers */

static void setregs(const unsigned char regs[], int mode)
{
    /* Enable graphics register modification */
    port_out(0x00, GRA_E0);
    port_out(0x01, GRA_E1);

    __svgalib_setregs(regs);
}

/* Set a mode */

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

    regs = LOOKUPMODE(ega_modes, mode);
    if (regs == NULL || regs == DISABLE_MODE)
	return 1;
    lastmode = mode;

    setregs(regs, mode);
    return 0;
}


/* Set display start */

static void setdisplaystart(int address)
{
    port_in(__svgalib_IS1_R);
    __svgalib_delay();
    port_out_r(ATT_IW, 0x13 + 0x20);
    __svgalib_delay();
    port_out_r(ATT_IW, (address & 7));
    /* write sa0-2 to bits 0-2 */
    address >>= 3;
    port_outw_r(__svgalib_CRT_I, 0x0d + (address & 0x00ff) * 256);	/* sa0-sa7 */
    port_outw_r(__svgalib_CRT_I, 0x0c + (address & 0xff00));	/* sa8-sa15 */
}

static void setlogicalwidth(int width)
{
    port_outw_r(__svgalib_CRT_I, 0x13 + (width >> 3) * 256);	/* lw3-lw11 */
}


/* Indentify chipset; return non-zero if detected */

static int ega_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) {
	ega_init(0, 0, 0);
	return 1;
    }
    return 0;
}


DriverSpecs __svgalib_ega_driverspecs =
{				/* EGA */
    saveregs,
    setregs,
    nothing /* unlock */ ,
    nothing /* lock */ ,
    ega_test,
    ega_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 ega_init(int force, int par1, int par2)
{

    if (__svgalib_driver_report)
	fprintf(stderr,"Using EGA driver.\n");

    /* Read additional modes from file if available */
    if (ega_modes == NULL) {
	ega_modes = __ega_modes;
#ifdef EGA_REGS
	{
	    FILE *regs;
	    regs = fopen(EGA_REGS, "r");
	    if (regs != 0) {
		__svgalib_readmodes(regs, &ega_modes, NULL, NULL);
		fclose(regs);
	    }
	}
#endif
    }
    __svgalib_driverspecs = &__svgalib_ega_driverspecs;

    __svgalib_banked_mem_base=0xa0000;
    __svgalib_banked_mem_size=0x10000;

    return 0;
}
