Source to src/od-dos/video/vbe.c


Enter a symbol's name here to quickly find it.

 /*
  * UAE - The Un*x Amiga Emulator
  *
  * DOS VESA BIOS / VGA / Mode X graphics interface.
  *
  * (c) 1997 Gustavo Goedert
  */

#include "sysconfig.h"
#include "sysdeps.h"

#include <stdlib.h>
#include <dos.h>
#include <go32.h>
#include <dpmi.h>
#include <sys/farptr.h>

#include "vbe.h"

#define ALIGN(x) ((((int)(x)>>12)<<12)+4096)
#define PACKED __attribute__ ((packed))

/* externs in vbe.h */
int NumberOfModes = 0;
T_ModeInfo *ModeList = NULL;
T_ModeInfo CurrentMode;
int CanMapBuffer = 0;
int CurrentBankNumber = 0;
/* end from vbe.h */

enum VBE_MemoryModels {
    TextMode,    CGA_Graphics, HerculesGraphics, Planar,
    PackedPixel, ModeX,        DirectColor,      YUV
};

typedef struct {
    char VbeSignature[4] PACKED; // = VBE2
    short VbeVersion PACKED;
    char *OemStringPtr PACKED;
    char Capabilities[4] PACKED;
    unsigned int VideoModePtr PACKED;
    short TotalMemory PACKED;
    short OemSoftwareRev PACKED;
    char *OemVendorNamePtr PACKED;
    char *OemProductNamePtr PACKED;
    char *OemProductRevPtr PACKED;
    char Reserved[222] PACKED;
    char OemData[256] PACKED;
} T_VBE_VbeInfoBlock;

typedef struct {
    // Mandatory information for all VBE revisions
    short ModeAttributes PACKED;
    char WinAAttributes PACKED;
    char WinBAttributes PACKED;
    short WinGranularity PACKED;
    unsigned short WinSize PACKED;
    unsigned short WinASegment PACKED;
    unsigned short WinBSegment PACKED;
    unsigned int WinFuncPtr PACKED;
    short BytesPerScanLine PACKED;
    // Mandatory information for VBE 1.2 and above
    short XResolution PACKED;
    short YResolution PACKED;
    char XCharSize PACKED;
    char YCharSize PACKED;
    char NumberOfPlanes PACKED;
    char BitsPerPixel PACKED;
    char NumberOfBanks PACKED;
    char MemoryModel PACKED;
    char BankSize PACKED;
    char NumberOfImagePages PACKED;
    char ReservedA PACKED; // = 1
    // Direct Color fields (required for direct/6 and YUV/7 memory models)
    char RedMaskSize PACKED;
    char RedFieldPosition PACKED;
    char GreenMaskSize PACKED;
    char GreenFieldPosition PACKED;
    char BlueMaskSize PACKED;
    char BlueFieldPosition PACKED;
    char RsvdMaskSize PACKED;
    char RsvdFieldPosition PACKED;
    char DirectColorModeInfo PACKED;
    // Mandatory information for VBE 2.0 and above
    unsigned int PhysBasePtr PACKED;
    unsigned int OffScreenMemOffset PACKED;
    short OffScreenMemSize PACKED;
    char ReservedB[206] PACKED;
} T_VBE_ModeInfoBlock;

typedef struct {
    short SetWindowOffset PACKED;
    short SetDisplayStartOffset PACKED;
    short SetPrimaryPaletteDataOffset PACKED;
    short PortsMemoryOffset PACKED;
} T_VBE_ProtectedModeInterface;

typedef enum {TVBE, TVGA, TModeX} T_ControlType;

typedef struct {
    T_ModeType ModeType;
    int OriginalModeNumber;
    int ModeWidth, ModeHeight;
    int BitsPerPixel, BitsPerColor;
    int RedSize, RedPosition;
    int GreenSize, GreenPosition;
    int BlueSize, BluePosition;
} T_VBE_Table;

typedef struct {
    T_ControlType ControlType;
    int OriginalModeNumber;
    int WriteWindow;
    int FastWinCalc;
    int GranularityFix;
    unsigned int WinMask1, WinMask2;
    unsigned int WinFuncPtrSegment, WinFuncPtrOffset;
    unsigned int WindowPosition, LinearPosition;
} T_ModeInternalInfo;

T_ModeInfo VGA_InfoTable[7] = {
    {T16, 320, 200, 8, 4, 0, 0, 0, 0, 0, 0, 40, 40, 0, 0, 0x10000, 0xa0000, 0, 0, 0, 0, NULL, NULL, NULL},
    {T16, 640, 200, 8, 4, 0, 0, 0, 0, 0, 0, 80, 80, 0, 0, 0x10000, 0xa0000, 0, 0, 0, 0, NULL, NULL, NULL},
    {T16, 640, 350, 8, 4, 0, 0, 0, 0, 0, 0, 80, 80, 0, 0, 0x10000, 0xa0000, 0, 0, 0, 0, NULL, NULL, NULL},
    {T16, 640, 480, 8, 4, 0, 0, 0, 0, 0, 0, 80, 80, 0, 0, 0x10000, 0xa0000, 0, 0, 0, 0, NULL, NULL, NULL},
    {T256, 320, 200, 8, 8, 0, 0, 0, 0, 0, 0, 320, 320, 1, 0x10000, 0x12000, 0xa0000, 1, 0x10000, 0x12000, 0xa0000, NULL, NULL, NULL},
    {T256, 320, 240, 8, 8, 0, 0, 0, 0, 0, 0, 80, 80, 0, 0, 0x10000, 0xa0000, 0, 0, 0, 0, NULL, NULL, NULL},
    {T256, 320, 400, 8, 8, 0, 0, 0, 0, 0, 0, 80, 80, 0, 0, 0x10000, 0xa0000, 0, 0, 0, 0, NULL, NULL, NULL}
/*  {T256, 320, 480
    {T256, 360, 200
    {T256, 360, 400
    {T256, 360, 480*/
};

T_ModeInternalInfo VGA_InternalInfoTable[7] = {
    {TVGA, 0x0d, 0, 0, 0, 0, 0, 0, 0, 0xa0000, 0},
    {TVGA, 0x0e, 0, 0, 0, 0, 0, 0, 0, 0xa0000, 0},
    {TVGA, 0x10, 0, 0, 0, 0, 0, 0, 0, 0xa0000, 0},
    {TVGA, 0x12, 0, 0, 0, 0, 0, 0, 0, 0xa0000, 0},
    {TVGA, 0x13, 0, 0, 0, 0, 0, 0, 0, 0xa0000, 0xa0000},
    {TModeX, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {TModeX, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0}
/*  {TModeX, 0x03
    {TModeX, 0x04
    {TModeX, 0x05
    {TModeX, 0x06*/
};

T_VBE_Table VBE_Table[24] = {
    {T16, 0x6a, 800, 600, 8, 4, 0, 0, 0, 0, 0, 0},
    {T256, 0x100, 640, 400, 8, 8, 0, 0, 0, 0, 0, 0},
    {T256, 0x101, 640, 480, 8, 8, 0, 0, 0, 0, 0, 0},
    {T16, 0x102, 800, 600, 8, 4, 0, 0, 0, 0, 0, 0},
    {T256, 0x103, 800, 600, 8, 8, 0, 0, 0, 0, 0, 0},
    {T16, 0x104, 1024, 768, 8, 4, 0, 0, 0, 0, 0, 0},
    {T256, 0x105, 1024, 768, 8, 8, 0, 0, 0, 0, 0, 0},
    {T16, 0x106, 1280, 1024, 8, 4, 0, 0, 0, 0, 0, 0},
    {T256, 0x107, 1280, 1024, 8, 8, 0, 0, 0, 0, 0, 0},
    {T32K, 0x10d, 320, 200, 16, 15, 5, 10, 5, 5, 5, 0},
    {T64K, 0x10e, 320, 200, 16, 16, 5, 11, 6, 5, 5, 0},
    {T16M, 0x10f, 320, 200, 24, 24, 8, 16, 8, 8, 8, 0},
    {T32K, 0x110, 640, 480, 16, 15, 5, 10, 5, 5, 5, 0},
    {T64K, 0x111, 640, 480, 16, 16, 5, 11, 6, 5, 5, 0},
    {T16M, 0x112, 640, 480, 24, 24, 8, 16, 8, 8, 8, 0},
    {T32K, 0x113, 800, 600, 16, 15, 5, 10, 5, 5, 5, 0},
    {T64K, 0x114, 800, 600, 16, 16, 5, 11, 6, 5, 5, 0},
    {T16M, 0x115, 800, 600, 24, 24, 8, 16, 8, 8, 8, 0},
    {T32K, 0x116, 1024, 768, 16, 15, 5, 10, 5, 5, 5, 0},
    {T64K, 0x117, 1024, 768, 16, 16, 5, 11, 6, 5, 5, 0},
    {T16M, 0x118, 1024, 768, 24, 24, 8, 16, 8, 8, 8, 0},
    {T32K, 0x119, 1280, 1024, 16, 15, 5, 10, 5, 5, 5, 0},
    {T64K, 0x11a, 1280, 1024, 16, 16, 5, 11, 6, 5, 5, 0},
    {T16M, 0x11b, 1280, 1024, 24, 24, 8, 16, 8, 8, 8, 0}
};

int HasVbe = 0;
int HasProtectedModeInterface = 0;
int RestoreOldVGAMode = 0, OldVGAMode;

T_VBE_VbeInfoBlock VBE_VbeInfoBlock;
T_VBE_ModeInfoBlock VBE_ModeInfoBlock;
T_VBE_ProtectedModeInterface *VBE_ProtectedModeInterface;
short VBE_ModeList[512];
unsigned int WinFuncPtrPM;
short *PortsMemory;

T_ModeInternalInfo *InternalModeList = NULL;
T_ModeInternalInfo CurrentInternalMode;
char *LinearVgaBuffer, *AlignedLinearVgaBuffer;

unsigned char PaletteData[768];
_go32_dpmi_seginfo PaletteMem;

int VBE_GetVbeInfoBlock(void);
int VBE_GetModeInfoBlock(int mode);
int VBE_GetProtectedModeInterface(void);
void VBE_ChangeBankRealInterrupt(int BankNumber);
void VBE_ChangeBankRealMode(int BankNumber);
void VBE_ChangeBankProtectMode(int BankNumber);
void NULL_ChangeBank(int BankNumber);
void PutLineLinear(char *addr, int target_y);
void PutLineBanked(char *addr, int target_y);
void PutLineVGA16(char *addr, int target_y);
void PutLineVGAX(char *addr, int target_y);
void InsertMode(T_ModeInfo *ModeInfo, T_ModeInternalInfo *ModeInternalInfo);
void SetModeX(int ModeNumber);
void MapVideoMemory(void);

int VBE_GetVbeInfoBlock(void) {
    _go32_dpmi_registers IntRegs;

    strncpy(VBE_VbeInfoBlock.VbeSignature, "VBE2", 4);
    dosmemput(&VBE_VbeInfoBlock, sizeof(VBE_VbeInfoBlock), __tb);
    IntRegs.x.ax = 0x4F00;
    IntRegs.x.di = __tb & 0x0F;
    IntRegs.x.es = (__tb >> 4) & 0xFFFF;
    IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
    _go32_dpmi_simulate_int(0x10, &IntRegs);
    dosmemget(__tb, sizeof(VBE_VbeInfoBlock), &VBE_VbeInfoBlock);
    return((IntRegs.x.ax == 0x004F) && (VBE_VbeInfoBlock.VbeVersion >= 0x0100));
}

int VBE_GetModeInfoBlock(int mode) {
    _go32_dpmi_registers IntRegs;

    VBE_ModeInfoBlock.ReservedA = 1;
    IntRegs.x.ax = 0x4F01;
    IntRegs.x.cx = mode;
    IntRegs.x.di = __tb & 0x0F;
    IntRegs.x.es = (__tb >> 4) & 0xFFFF;
    IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
    _go32_dpmi_simulate_int(0x10, &IntRegs);
    dosmemget(__tb, sizeof(VBE_ModeInfoBlock), &VBE_ModeInfoBlock);
    return(IntRegs.x.ax == 0x004F);
}

int VBE_GetProtectedModeInterface(void) {
    _go32_dpmi_registers IntRegs;

    IntRegs.x.ax = 0x4F0A;
    IntRegs.x.bx = 0x0000;
    IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
    _go32_dpmi_simulate_int(0x10, &IntRegs);
    VBE_ProtectedModeInterface = (T_VBE_ProtectedModeInterface *) malloc(IntRegs.x.cx);
    if (VBE_ProtectedModeInterface == NULL)
	return(0);
    dosmemget(IntRegs.x.es * 16 + IntRegs.x.di, IntRegs.x.cx, VBE_ProtectedModeInterface);
    WinFuncPtrPM = (unsigned int) VBE_ProtectedModeInterface + VBE_ProtectedModeInterface->SetWindowOffset;
    PortsMemory = (short *) VBE_ProtectedModeInterface + VBE_ProtectedModeInterface->PortsMemoryOffset;
    return(IntRegs.x.ax == 0x004F);
}

void VBE_ChangeBankRealInterrupt(int BankNumber) {
    _go32_dpmi_registers IntRegs;

    IntRegs.x.ax = 0x4F05;
    IntRegs.x.bx = CurrentInternalMode.WriteWindow;
    IntRegs.x.dx = CurrentInternalMode.GranularityFix ? BankNumber * CurrentInternalMode.GranularityFix : BankNumber;
    IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
    _go32_dpmi_simulate_int(0x10, &IntRegs);
    CurrentBankNumber = BankNumber;
}

void VBE_ChangeBankRealMode(int BankNumber) {
    _go32_dpmi_registers IntRegs;

    IntRegs.x.ax = 0x4F05;
    IntRegs.x.bx = CurrentInternalMode.WriteWindow;
    IntRegs.x.dx = CurrentInternalMode.GranularityFix ? BankNumber * CurrentInternalMode.GranularityFix : BankNumber;
    IntRegs.x.cs = CurrentInternalMode.WinFuncPtrSegment;
    IntRegs.x.ip = CurrentInternalMode.WinFuncPtrOffset;
    IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
    _go32_dpmi_simulate_fcall(&IntRegs);
    CurrentBankNumber = BankNumber;
}

void VBE_ChangeBankProtectMode(int BankNumber) {
    int BNumber = CurrentInternalMode.GranularityFix ? BankNumber * CurrentInternalMode.GranularityFix : BankNumber;

    asm __volatile__ ("
	 push %%es
	 movw %0, %%es
	 call %1
	 pop %%es
	 "
	 :
	 : "g" (_dos_ds), "r" (WinFuncPtrPM), "a" (0x4F05), "b" (CurrentInternalMode.WriteWindow), "d" (BNumber)
	 : "%ax", "%bx", "%dx", "cc", "memory"
    );
    CurrentBankNumber = BankNumber;
    _farsetsel(_dos_ds);
}

void NULL_ChangeBank(int BankNumber) {
    CurrentBankNumber = BankNumber;
}

void PutLineLinear(char *addr, int target_y) {
    unsigned int ptr;

    ptr = CurrentMode.LinearAddress + target_y * CurrentMode.LogicalLineLength;
    asm __volatile__ ("
	 push %%es
	 cld
	 movw %0, %%es
	 shrl $2, %%ecx
	 rep
	 movsl
	 pop %%es
	 "
	 :
	 : "g" (_dos_ds), "S" (addr), "D" (ptr), "c" (CurrentMode.LineLength)
	 : "%esi", "%edi", "%ecx", "cc"
    );
}

void PutLineBanked(char *addr, int target_y) {
    unsigned int offs;
    int page, pos, left;
    char *ptr;

    offs = target_y * CurrentMode.LogicalLineLength;
    if (CurrentInternalMode.FastWinCalc) {
	page = offs >> CurrentInternalMode.WinMask1;
	pos = offs & CurrentInternalMode.WinMask2;
    } else {
	page = offs / CurrentMode.WindowSize;
	pos = offs % CurrentMode.WindowSize;
    }
    left = CurrentMode.WindowSize - pos;
    ptr = (char *) CurrentMode.WindowAddress + pos;
    if (page != CurrentBankNumber)
	CurrentMode.ChangeBank(page);
    if (left >= CurrentMode.LineLength)
	asm __volatile__ ("
	     push %%es
	     cld
	     movw %0, %%es
	     shrl $2, %%ecx
	     rep
	     movsl
	     pop %%es
	     "
	     :
	     : "g" (_dos_ds), "S" (addr), "D" (ptr), "c" (CurrentMode.LineLength)
	     : "%esi", "%edi", "%ecx", "cc"
	);
    else {
	asm __volatile__ ("
	     push %%es
	     cld
	     movw %0, %%es
	     shrl $2, %%ecx
	     rep
	     movsl
	     pop %%es
	     "
	     :
	     : "g" (_dos_ds), "S" (addr), "D" (ptr), "c" (left)
	     : "%esi", "%edi", "%ecx", "cc"
	);
	CurrentMode.ChangeBank(page+1);
	asm __volatile__ ("
	     push %%es
	     cld
	     movw %0, %%es
	     addl %1, %%esi
	     subl %1, %%ecx
	     shrl $2, %%ecx
	     rep
	     movsl
	     pop %%es
	     "
	     :
	     : "g" (_dos_ds), "g" (left), "S" (addr), "D" (CurrentMode.WindowAddress), "c" (CurrentMode.LineLength)
	     : "%esi", "%edi", "%ecx", "cc"
	);
    }
}

void PutLineVGA16(char *addr, int target_y) {
    unsigned int start, ptr;
    int i;
    UBYTE c;

#if 0
    ptr = CurrentMode.WindowAddress + target_y * CurrentMode.LogicalLineLength;
    outportw(0x3ce, 0x205);
    outportw(0x3ce, 3);
    for (i=0; i<CurrentMode.ModeWidth; i++) {
	outportw(0x3ce, 1<<(((i&7)^7)+8)|8);
	_farnspeekb(ptr+(i>>3));
	_farnspokeb(ptr+(i>>3), addr[i]);
    }
#else
    start = CurrentMode.WindowAddress + target_y * CurrentMode.LogicalLineLength;
    ptr = start;
    outportw(0x3c4, 0x802);
    for (i=0; i<CurrentMode.ModeWidth; i+=8, ptr++) {
	c = ((addr[i  ]<<4)&128)|((addr[i+1]<<3)&64)|((addr[i+2]<<2)&32)|((addr[i+3]<<1)&16)|
	    ((addr[i+4]   )&8  )|((addr[i+5]>>1)&4 )|((addr[i+6]>>2)&2 )|((addr[i+7]>>3)&1);
	_farnspokeb(ptr, c);
    }
    ptr = start;
    outportw(0x3c4, 0x402);
    for (i=0; i<CurrentMode.ModeWidth; i+=8, ptr++) {
	c = ((addr[i  ]<<5)&128)|((addr[i+1]<<4)&64)|((addr[i+2]<<3)&32)|((addr[i+3]<<2)&16)|
	    ((addr[i+4]<<1)&8  )|((addr[i+5]   )&4 )|((addr[i+6]>>1)&2 )|((addr[i+7]>>2)&1 );
	_farnspokeb(ptr, c);
    }
    ptr = start;
    outportw(0x3c4, 0x202);
    for (i=0; i<CurrentMode.ModeWidth; i+=8, ptr++) {
	c = ((addr[i  ]<<6)&128)|((addr[i+1]<<5)&64)|((addr[i+2]<<4)&32)|((addr[i+3]<<3)&16)|
	    ((addr[i+4]<<2)&8  )|((addr[i+5]<<1)&4 )|((addr[i+6]   )&2 )|((addr[i+7]>>1)&1 );
	_farnspokeb(ptr, c);
    }
    ptr = start;
    outportw(0x3c4, 0x102);
    for (i=0; i<CurrentMode.ModeWidth; i+=8, ptr++) {
	c = ((addr[i  ]<<7)&128)|((addr[i+1]<<6)&64)|((addr[i+2]<<5)&32)|((addr[i+3]<<4)&16)|
	    ((addr[i+4]<<3)&8  )|((addr[i+5]<<2)&4 )|((addr[i+6]<<1)&2 )|((addr[i+7]   )&1 );
	_farnspokeb(ptr, c);
    }
#endif
}

void PutLineVGAX(char *addr, int target_y) {
    unsigned int start, ptr;
    int i;
    UBYTE c;

    start = CurrentMode.WindowAddress + target_y * CurrentMode.LogicalLineLength;
    ptr = start;
    outportw(0x3c4, 0x102);
    for (i=0; i<CurrentMode.ModeWidth; i+=4, ptr++)
	_farnspokeb(ptr, addr[i]);
    ptr = start;
    outportw(0x3c4, 0x202);
    for (i=1; i<CurrentMode.ModeWidth; i+=4, ptr++)
	_farnspokeb(ptr, addr[i]);
    ptr = start;
    outportw(0x3c4, 0x402);
    for (i=2; i<CurrentMode.ModeWidth; i+=4, ptr++)
	_farnspokeb(ptr, addr[i]);
    ptr = start;
    outportw(0x3c4, 0x802);
    for (i=3; i<CurrentMode.ModeWidth; i+=4, ptr++)
	_farnspokeb(ptr, addr[i]);
}

void SetPalette(int Color, int Red, int Green, int Blue) {
    _go32_dpmi_registers IntRegs;

    if (CurrentMode.ModeType == T16) {
	IntRegs.h.ah = 0x10;
	IntRegs.h.al = 0x00;
	IntRegs.h.bh = Color;
	IntRegs.h.bl = Color;
	IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
	_go32_dpmi_simulate_int(0x10, &IntRegs);
    }
    IntRegs.h.ah = 0x10;
    IntRegs.h.al = 0x10;
    IntRegs.x.bx = Color;
    IntRegs.h.ch = Green;
    IntRegs.h.cl = Blue;
    IntRegs.h.dh = Red;
    _go32_dpmi_simulate_int(0x10, &IntRegs);
    DelayedSetPalette(Color, Red, Green, Blue);
}

void DelayedSetPalette(int Color, int Red, int Green, int Blue) {
    PaletteData[Color * 3 + 0] = Red;
    PaletteData[Color * 3 + 1] = Green;
    PaletteData[Color * 3 + 2] = Blue;
}

void ViewPalette(int Color, int *Red, int *Green, int *Blue) {
    *Red = PaletteData[Color * 3 + 0] << 2;
    *Green = PaletteData[Color * 3 + 1] << 2;
    *Blue = PaletteData[Color * 3 + 2] << 2;
}

void LoadPalette(void) {
    _go32_dpmi_registers IntRegs;
    int i;

    dosmemput(PaletteData, 768, PaletteMem.rm_segment*16);
    if (CurrentMode.ModeType == T16) {
	for(i=0; i<16; i++) {
	    IntRegs.h.ah = 0x10;
	    IntRegs.h.al = 0x00;
	    IntRegs.h.bh = i;
	    IntRegs.h.bl = i;
	    IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
	    _go32_dpmi_simulate_int(0x10, &IntRegs);
	}
    }
    IntRegs.h.ah = 0x10;
    IntRegs.h.al = 0x12;
    IntRegs.x.bx = 0;
    if (CurrentMode.ModeType == T16)
	IntRegs.x.cx = 16;
    else
	IntRegs.x.cx = 256;
    IntRegs.x.es = PaletteMem.rm_segment;
    IntRegs.x.dx = 0x0000;
    IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
    _go32_dpmi_simulate_int(0x10, &IntRegs);
}

int InitGfxLib(void) {
    static int GfxInicilized = 0;
    _go32_dpmi_registers IntRegs;
    __dpmi_meminfo MemoryInfo;
    unsigned int DosMemPos;
    int DiscartMode;
    int i, j, TryList=1, CurrentList=0;
    short ModeNumber;
    T_ModeInfo ModeInfo;
    T_ModeInternalInfo ModeInternalInfo;

    if (GfxInicilized)
	return(1);

    /* Save Old VGA Mode */
    IntRegs.x.ax = 0x1C;
    IntRegs.h.ah = 0x0F;
    IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
    _go32_dpmi_simulate_int(0x10, &IntRegs);
    OldVGAMode = IntRegs.h.al;

    /* Allocate Palette Memory */
    PaletteMem.size = 48;
    if (_go32_dpmi_allocate_dos_memory(&PaletteMem)) {
	printf ("Can't allocate real mode memory for palette data.\n");
	return(0);
    }

    /* Check for VBE */
    HasVbe = VBE_GetVbeInfoBlock();

    /* Build ModeInfo Tables */
    if (HasVbe) {
	printf ("VESA BIOS Extension version %d.%d found.\n", VBE_VbeInfoBlock.VbeVersion >> 8, VBE_VbeInfoBlock.VbeVersion & 0xFF);
	if (VBE_VbeInfoBlock.VbeVersion >= 0x0200)
	    HasProtectedModeInterface = VBE_GetProtectedModeInterface();
	DosMemPos = (VBE_VbeInfoBlock.VideoModePtr >> 16) * 16 +
		    (VBE_VbeInfoBlock.VideoModePtr & 0xffff);
	dosmemget(DosMemPos, 1024, VBE_ModeList);
	VBE_ModeList[511] = -1;
	i = 0;
	while(1) {
	    if (TryList) {
		ModeNumber = VBE_Table[CurrentList].OriginalModeNumber;
		CurrentList++;
		if (CurrentList == 25)
		    TryList = 0;
	    } else {
		ModeNumber = VBE_ModeList[i];
		if (ModeNumber == -1)
		    break;
		i++;
		if ((ModeNumber == 0x6a) || ((ModeNumber >= 0x100) && (ModeNumber <= 0x107)) || ((ModeNumber >= 0x10d) && (ModeNumber <= 0x11b)))
		    continue;
	    }
	    if ((ModeNumber != (short)0x81ff) && VBE_GetModeInfoBlock(ModeNumber)) {
		DiscartMode = 0;
		if (!(VBE_ModeInfoBlock.ModeAttributes & 0x1))
		    DiscartMode = 1;
		ModeInternalInfo.OriginalModeNumber = ModeNumber;
		ModeInternalInfo.ControlType = TVBE;
		ModeInfo.RedSize = 0;
		ModeInfo.RedPosition = 0;
		ModeInfo.GreenSize = 0;
		ModeInfo.GreenPosition = 0;
		ModeInfo.BlueSize = 0;
		ModeInfo.BluePosition = 0;
		if (VBE_VbeInfoBlock.VbeVersion < 0x0102) {
		    if (VBE_ModeInfoBlock.ModeAttributes & 0x2) {
			ModeInfo.ModeWidth = VBE_ModeInfoBlock.XResolution;
			ModeInfo.ModeHeight = VBE_ModeInfoBlock.YResolution;
			ModeInfo.BitsPerPixel = VBE_ModeInfoBlock.BitsPerPixel;
			if (ModeInfo.BitsPerPixel == 15)
			    ModeInfo.BitsPerPixel = 16;
			if (ModeInfo.BitsPerPixel == 4) {
			    ModeInfo.ModeType = T16;
			    ModeInfo.BitsPerPixel = 8;
			    ModeInfo.BitsPerColor = 4;
			    if (VBE_ModeInfoBlock.MemoryModel != Planar)
				DiscartMode = 1;
			} else if (ModeInfo.BitsPerPixel == 8) {
			    ModeInfo.ModeType = T256;
			    ModeInfo.BitsPerColor = 8;
			    if (VBE_ModeInfoBlock.MemoryModel != PackedPixel)
				DiscartMode = 1;
			} else if (ModeInfo.BitsPerPixel == 16) {
			    ModeInfo.ModeType = T32K;
			    ModeInfo.BitsPerColor = 15;
			    ModeInfo.RedSize = 5;
			    ModeInfo.RedPosition = 10;
			    ModeInfo.GreenSize = 5;
			    ModeInfo.GreenPosition = 5;
			    ModeInfo.BlueSize = 5;
			    ModeInfo.BluePosition = 0;
			    if (VBE_ModeInfoBlock.MemoryModel != PackedPixel)
				DiscartMode = 1;
			} else if ((ModeInfo.BitsPerPixel == 24) || (ModeInfo.BitsPerPixel == 32)) {
			    ModeInfo.ModeType = T16M;
			    ModeInfo.BitsPerColor = 24;
			    ModeInfo.RedSize = 8;
			    ModeInfo.RedPosition = 16;
			    ModeInfo.GreenSize = 8;
			    ModeInfo.GreenPosition = 8;
			    ModeInfo.BlueSize = 8;
			    ModeInfo.BluePosition = 0;
			    if (VBE_ModeInfoBlock.MemoryModel != PackedPixel)
				DiscartMode = 1;
			} else
			    DiscartMode = 1;
		    } else {
			for (j = 0; j < 24; j++) {
			    if (VBE_Table[j].OriginalModeNumber == ModeInternalInfo.OriginalModeNumber) {
				ModeInfo.ModeType = VBE_Table[j].ModeType;
				ModeInfo.ModeWidth = VBE_Table[j].ModeWidth;
				ModeInfo.ModeHeight = VBE_Table[j].ModeHeight;
				ModeInfo.BitsPerPixel = VBE_Table[j].BitsPerPixel;
				ModeInfo.BitsPerColor = VBE_Table[j].BitsPerColor;
				ModeInfo.RedSize = VBE_Table[j].RedSize;
				ModeInfo.RedPosition = VBE_Table[j].RedPosition;
				ModeInfo.GreenSize = VBE_Table[j].GreenSize;
				ModeInfo.GreenPosition = VBE_Table[j].GreenPosition;
				ModeInfo.BlueSize = VBE_Table[j].BlueSize;
				ModeInfo.BluePosition = VBE_Table[j].BluePosition;
				break;
			    }
			}
			if (j == 24)
			    DiscartMode = 1;
		    }
		} else {
		    ModeInfo.ModeWidth = VBE_ModeInfoBlock.XResolution;
		    ModeInfo.ModeHeight = VBE_ModeInfoBlock.YResolution;
		    ModeInfo.BitsPerPixel = VBE_ModeInfoBlock.BitsPerPixel;
		    if (ModeInfo.BitsPerPixel == 15)
			ModeInfo.BitsPerPixel = 16;
		    if (ModeInfo.BitsPerPixel == 4) {
			ModeInfo.ModeType = T16;
			ModeInfo.BitsPerPixel = 8;
			ModeInfo.BitsPerColor = 4;
			if (VBE_ModeInfoBlock.MemoryModel != Planar)
			    DiscartMode = 1;
		    } else if (ModeInfo.BitsPerPixel == 8) {
			ModeInfo.ModeType = T256;
			ModeInfo.BitsPerColor = 8;
			if (VBE_ModeInfoBlock.MemoryModel != PackedPixel)
			    DiscartMode = 1;
		    } else {
			ModeInfo.RedSize = VBE_ModeInfoBlock.RedMaskSize;
			ModeInfo.RedPosition = VBE_ModeInfoBlock.RedFieldPosition;
			ModeInfo.GreenSize = VBE_ModeInfoBlock.GreenMaskSize;
			ModeInfo.GreenPosition = VBE_ModeInfoBlock.GreenFieldPosition;
			ModeInfo.BlueSize = VBE_ModeInfoBlock.BlueMaskSize;
			ModeInfo.BluePosition = VBE_ModeInfoBlock.BlueFieldPosition;
			ModeInfo.BitsPerColor = ModeInfo.RedSize + ModeInfo.GreenSize + ModeInfo.BlueSize;
			if (ModeInfo.BitsPerColor == 15) {
			    ModeInfo.ModeType = T32K;
			    if (ModeInfo.BitsPerPixel != 16)
				DiscartMode = 1;
			} else if (ModeInfo.BitsPerColor == 16) {
			    ModeInfo.ModeType = T64K;
			    if (ModeInfo.BitsPerPixel != 16)
				DiscartMode = 1;
			} else if (ModeInfo.BitsPerColor == 24) {
			    ModeInfo.ModeType = T16M;
			    if ((ModeInfo.BitsPerPixel != 24) && (ModeInfo.BitsPerPixel != 32))
				DiscartMode = 1;
			} else
			    DiscartMode = 1;
			if (VBE_ModeInfoBlock.MemoryModel != DirectColor)
			    DiscartMode = 1;
		    }
		}
		ModeInfo.LineLength = (ModeInfo.BitsPerPixel >> 3) * ModeInfo.ModeWidth;
		ModeInfo.LogicalLineLength = VBE_ModeInfoBlock.BytesPerScanLine;
		ModeInfo.HasWindow = 0;
		ModeInfo.HasLinear = 0;
		if ((VBE_ModeInfoBlock.MemoryModel == PackedPixel) || (VBE_ModeInfoBlock.MemoryModel == DirectColor)) {
		    if (!(VBE_ModeInfoBlock.ModeAttributes & 0x40)) {
			ModeInfo.HasWindow = 1;
			if (VBE_ModeInfoBlock.WinFuncPtr != 0) {
			    ModeInternalInfo.WinFuncPtrSegment = VBE_ModeInfoBlock.WinFuncPtr >> 16;
			    ModeInternalInfo.WinFuncPtrOffset = VBE_ModeInfoBlock.WinFuncPtr & 0xffff;
			} else {
			    ModeInternalInfo.WinFuncPtrSegment = 0;
			    ModeInternalInfo.WinFuncPtrOffset = 0;
			}
			ModeInfo.WindowSize = VBE_ModeInfoBlock.WinSize * 1024;
			if ((VBE_ModeInfoBlock.WinAAttributes & 1) || (VBE_ModeInfoBlock.WinBAttributes & 1)) {
			    if ((!(VBE_ModeInfoBlock.WinBAttributes & 4)) || (VBE_ModeInfoBlock.WinAAttributes & 4)) {
				ModeInternalInfo.WriteWindow = 0;
				ModeInternalInfo.WindowPosition = ModeInfo.WindowAddress = VBE_ModeInfoBlock.WinASegment * 16;
			    } else {
				ModeInternalInfo.WriteWindow = 1;
				ModeInternalInfo.WindowPosition = ModeInfo.WindowAddress = VBE_ModeInfoBlock.WinBSegment * 16;
			    }
			    if (VBE_ModeInfoBlock.WinGranularity) {
				ModeInternalInfo.GranularityFix = VBE_ModeInfoBlock.WinSize / VBE_ModeInfoBlock.WinGranularity;
				if (ModeInternalInfo.GranularityFix == 1)
				    ModeInternalInfo.GranularityFix = 0;
			    } else
				ModeInternalInfo.GranularityFix = 0;
			} else
			    ModeInfo.WindowSize = ModeInfo.WindowAddress = ModeInternalInfo.GranularityFix = 0;
			/* You never know... */
			if (ModeInfo.WindowSize == 0)
			    ModeInfo.WindowSize = 0x10000;
			if (ModeInfo.WindowAddress == 0)
			    ModeInternalInfo.WindowPosition = ModeInfo.WindowAddress = 0xa0000;
			ModeInternalInfo.FastWinCalc = 0;
			for (j=1; j<32; j++) {
			    if (((unsigned int) 1<<j) == ModeInfo.WindowSize) {
				ModeInternalInfo.FastWinCalc = 1;
				ModeInternalInfo.WinMask1 = j;
				ModeInternalInfo.WinMask2 = ModeInfo.WindowSize-1;
				break;
			    }
			}
			ModeInfo.WindowMappingSize = ModeInfo.WindowSize + 0x2000;
		    }
		    if (VBE_ModeInfoBlock.ModeAttributes & 0x80) {
			ModeInfo.HasLinear = 1;
			ModeInternalInfo.LinearPosition = VBE_ModeInfoBlock.PhysBasePtr;
			ModeInfo.LinearSize = ModeInfo.ModeHeight * ModeInfo.LogicalLineLength;
			MemoryInfo.size = ModeInfo.LinearSize;
			MemoryInfo.address = ModeInternalInfo.LinearPosition;
			if (!__dpmi_physical_address_mapping(&MemoryInfo))
			    ModeInfo.LinearAddress = MemoryInfo.address;
			else
			    ModeInfo.LinearAddress = 0;
			ModeInfo.LinearMappingSize = ModeInfo.LinearSize + 0x2000;
		    }
		}
		ModeInfo.ChangeBank = NULL;
		ModeInfo.PutLine = NULL;
		ModeInfo.MappedAddress = NULL;
		if ((ModeInfo.ModeType == T16) && (ModeInfo.ModeHeight > 600)) //FIXME! not supported yeat
		    DiscartMode = 1;
		if (!DiscartMode)
		    InsertMode(&ModeInfo, &ModeInternalInfo);
	    }
	}
    }
    for (i = 0; i < 7; i++)
	InsertMode(&VGA_InfoTable[i], &VGA_InternalInfoTable[i]);

    /* See if we can map */
    LinearVgaBuffer = malloc(0x11000);
    if (LinearVgaBuffer != NULL) {
	AlignedLinearVgaBuffer = (char *) ALIGN(LinearVgaBuffer);
	if (!__djgpp_map_physical_memory(AlignedLinearVgaBuffer, 0x10000, 0xa0000))
	    CanMapBuffer = 1;
    }

    GfxInicilized = 1;
    return(1);
}

void CloseGfxLib(void) {
    _go32_dpmi_registers IntRegs;

    if (RestoreOldVGAMode) {
	IntRegs.x.ax = 0x1C;
	IntRegs.h.ah = 0x00;
	IntRegs.h.al = OldVGAMode;
	IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
	_go32_dpmi_simulate_int(0x10, &IntRegs);
    }
    _go32_dpmi_free_dos_memory(&PaletteMem);
}

void InsertMode(T_ModeInfo *ModeInfo, T_ModeInternalInfo *ModeInternalInfo) {
    if (NumberOfModes == 0) {
	ModeList = malloc(sizeof(T_ModeInfo));
	InternalModeList = malloc(sizeof(T_ModeInternalInfo));
    } else {
	ModeList = realloc(ModeList, sizeof(T_ModeInfo) * (NumberOfModes + 1));
	InternalModeList = realloc(InternalModeList, sizeof(T_ModeInternalInfo) * (NumberOfModes + 1));
    }
    ModeList[NumberOfModes] = *ModeInfo;
    InternalModeList[NumberOfModes] = *ModeInternalInfo;
    NumberOfModes++;
}

int SetMode(int ModeNumber, int Linear, int Debug) {
    _go32_dpmi_registers IntRegs;

    switch(InternalModeList[ModeNumber].ControlType) {
	case TVBE:
	    if (Debug) printf ("Using VESA BIOS.\n");
	    IntRegs.x.ax = 0x4F02;
	    if (ModeList[ModeNumber].ModeType == T16) {
		if (Linear)
		    return(0);
		IntRegs.x.bx = InternalModeList[ModeNumber].OriginalModeNumber;
		ModeList[ModeNumber].ChangeBank = NULL_ChangeBank;
		ModeList[ModeNumber].PutLine = PutLineVGA16;
	    } else if (Linear) {
		if (!ModeList[ModeNumber].HasLinear)
		    return(0);
		IntRegs.x.bx = InternalModeList[ModeNumber].OriginalModeNumber | (1<<14);
		ModeList[ModeNumber].ChangeBank = NULL_ChangeBank;
		ModeList[ModeNumber].PutLine = PutLineLinear;
	    } else {
		if (!ModeList[ModeNumber].HasWindow)
		    return(0);
		IntRegs.x.bx = InternalModeList[ModeNumber].OriginalModeNumber;
		if (HasProtectedModeInterface) {
		    if (Debug) printf ("Using Protected Mode Bank Switch Function.\n");
		    ModeList[ModeNumber].ChangeBank = VBE_ChangeBankProtectMode;
		} else if ((InternalModeList[ModeNumber].WinFuncPtrSegment != 0) && (InternalModeList[ModeNumber].WinFuncPtrOffset != 0)) {
		    if (Debug) printf ("Using Real Mode Bank Switch Function.\n");
		    ModeList[ModeNumber].ChangeBank = VBE_ChangeBankRealMode;
		} else {
		    if (Debug) printf ("Using Real Mode Bank Switch Interrupt Function.\n");
		    ModeList[ModeNumber].ChangeBank = VBE_ChangeBankRealInterrupt;
		}
		ModeList[ModeNumber].PutLine = PutLineBanked;
	    }
	    IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
	    _go32_dpmi_simulate_int(0x10, &IntRegs);
	    break;
	case TVGA:
	    if (Debug) printf ("Using normal VGA.\n");
	    IntRegs.h.ah = 0x00;
	    IntRegs.h.al = InternalModeList[ModeNumber].OriginalModeNumber;
	    IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
	    _go32_dpmi_simulate_int(0x10, &IntRegs);
	    ModeList[ModeNumber].ChangeBank = NULL_ChangeBank;
	    if (ModeList[ModeNumber].ModeType == T16)
		ModeList[ModeNumber].PutLine = PutLineVGA16;
	    else
		ModeList[ModeNumber].PutLine = PutLineLinear;
	    break;
	case TModeX:
	    if (Debug) printf ("Using VGA Mode X.\n");
	    SetModeX(InternalModeList[ModeNumber].OriginalModeNumber);
	    ModeList[ModeNumber].ChangeBank = NULL_ChangeBank;
	    ModeList[ModeNumber].PutLine = PutLineVGAX;
	    break;
    }
    CurrentMode = ModeList[ModeNumber];
    CurrentInternalMode = InternalModeList[ModeNumber];
    RestoreOldVGAMode = 1;
    MapVideoMemory();
    return(1);
}

void SetModeX(int ModeNumber) {
    _go32_dpmi_registers IntRegs;
    int i;
    char cleared[320];

    IntRegs.h.ah = 0x00;
    IntRegs.h.al = 0x13;
    IntRegs.x.ss = IntRegs.x.sp = IntRegs.x.flags = 0;
    _go32_dpmi_simulate_int(0x10, &IntRegs);
    for (i=0; i<320; i++)
	cleared[i] = 0;
    outportw(0x3c4, 0x604);
    if (ModeNumber == 1) {
	outportw(0x3c4, 0x0100);
	outportb(0x3c2, 0x00e3);
	outportw(0x3c4, 0x0300);
	outportw(0x3d4, 0x2c11);
	outportw(0x3d4, 0x5f00);
	outportw(0x3d4, 0x4f01);
	outportw(0x3d4, 0x5002);
	outportw(0x3d4, 0x8203);
	outportw(0x3d4, 0x5404);
	outportw(0x3d4, 0x8005);
	outportw(0x3d4, 0x0d06);
	outportw(0x3d4, 0x3e07);
	outportw(0x3d4, 0x0008);
	outportw(0x3d4, 0x4109);
	outportw(0x3d4, 0x000a);
	outportw(0x3d4, 0x000b);
	outportw(0x3d4, 0x000c);
	outportw(0x3d4, 0x000d);
	outportw(0x3d4, 0x000e);
	outportw(0x3d4, 0x000f);
	outportw(0x3d4, 0xea10);
	outportw(0x3d4, 0xac11);
	outportw(0x3d4, 0xdf12);
	outportw(0x3d4, 0x2813);
	outportw(0x3d4, 0x0014);
	outportw(0x3d4, 0xe715);
	outportw(0x3d4, 0x0616);
	outportw(0x3d4, 0xe317);
	for (i=0; i<240; i++)
	    PutLineVGAX(cleared, i);
    } else {
	outportw(0x3d4, 0x0e11);
	outportw(0x3d4, 0x5f00);
	outportw(0x3d4, 0x4f01);
	outportw(0x3d4, 0x5002);
	outportw(0x3d4, 0x8203);
	outportw(0x3d4, 0x5404);
	outportw(0x3d4, 0x8005);
	outportw(0x3d4, 0xbf06);
	outportw(0x3d4, 0x1f07);
	outportw(0x3d4, 0x0008);
	outportw(0x3d4, 0x4009);
	outportw(0x3d4, 0x000a);
	outportw(0x3d4, 0x000b);
	outportw(0x3d4, 0x000c);
	outportw(0x3d4, 0x000d);
	outportw(0x3d4, 0x000e);
	outportw(0x3d4, 0x000f);
	outportw(0x3d4, 0x9c10);
	outportw(0x3d4, 0x8e11);
	outportw(0x3d4, 0x8f12);
	outportw(0x3d4, 0x2813);
	outportw(0x3d4, 0x0014);
	outportw(0x3d4, 0x9615);
	outportw(0x3d4, 0xb916);
	outportw(0x3d4, 0xe317);
	for (i=0; i<400; i++)
	    PutLineVGAX(cleared, i);
    }
}

void MapVideoMemory(void) {
    int Amont, AlignedAmont;
    unsigned int PhysicalPosition;
    char *NearPosition, *AlignedBufferPtr;

    if ((!CanMapBuffer) || (CurrentMode.MappedAddress != NULL))
	return;
    if ((CurrentMode.HasLinear) && (CurrentMode.ChangeBank == NULL_ChangeBank)) {
	Amont = CurrentMode.LinearSize;
	PhysicalPosition = CurrentInternalMode.LinearPosition;
	NearPosition = malloc(CurrentMode.LinearMappingSize);
    } else if (CurrentMode.HasWindow) {
	Amont = CurrentMode.WindowSize;
	PhysicalPosition = CurrentInternalMode.WindowPosition;
	NearPosition = malloc(CurrentMode.WindowMappingSize);
    } else
	return;
    if (NearPosition == NULL)
	return;
    AlignedBufferPtr = (char *) ALIGN(NearPosition);
    AlignedAmont = ALIGN(Amont);
    if (__djgpp_map_physical_memory(AlignedBufferPtr, AlignedAmont, PhysicalPosition)) {
	free(NearPosition);
	CurrentMode.MappedAddress = NULL;
    } else
	CurrentMode.MappedAddress = AlignedBufferPtr;
}