#include <dos.h>
#include "g_main.h"
#include "m_main.h"

#include <i86.h>
#include <io.h>
#include <conio.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define FALSE 0
#define TRUE  -1




//****************************************************************************

#define MAXMODES 128

typedef
struct tDisplayModesStruct
{
  int  Id;
  int  x;
  int  y;
  int  bits;
  int lfb;                        /* exist lfb for this mody ??*/
  int  NumberOfScreens;            /* How many Buffers could be buffered*/
  int  SizeOfScreen;
  int Vesa;
  char *VRam;
}DisplayModesStruct;

typedef
struct tVesaInfoStruct
{
  char Signature[4];
  unsigned char VHNr;
  unsigned char VLNr;
  unsigned short NameOff,NameSeg;
  unsigned short Dummy1,Dummy2;
  unsigned short VModeOff,VModeSeg;
  unsigned short TotalMemory;
  unsigned short Temp[128];
}VesaInfoStruct;

typedef
struct tVesaModeInfoStruct
{

        short   ModeAttributes;         // Mode attributes
        char    WinAAttributes;         // Window A attributes
        char    WinBAttributes;         // Window B attributes
        short   WinGranularity;         // Window granularity in k
        short   WinSize;                // Window size in k
        short   WinASegment;            // Window A segment
        short   WinBSegment;            // Window B segment
        void    *WinFuncPtr;            // Pointer to window function
        short   BytesPerScanLine;       // Bytes per scanline
        short   XResolution;            // Horizontal resolution
        short   YResolution;            // Vertical resolution
        char    XCharSize;              // Character cell width
        char    YCharSize;              // Character cell height
        char    NumberOfPlanes;         // Number of memory planes
        char    BitsPerPixel;           // Bits per pixel
        char    NumberOfBanks;          // Number of CGA style banks
        char    MemoryModel;            // Memory model type
        char    BankSize;               // Size of CGA style banks
        char    NumberOfImagePages;     // Number of images pages (Max)
        char    res1;                   // Reserved
        char    RedMaskSize;            // Size of direct color red mask
        char    RedFieldPosition;       // Bit posn of lsb of red mask
        char    GreenMaskSize;          // Size of direct color green mask
        char    GreenFieldPosition;     // Bit posn of lsb of green mask
        char    BlueMaskSize;           // Size of direct color blue mask
        char    BlueFieldPosition;      // Bit posn of lsb of blue mask
        char    RsvdMaskSize;           // Size of direct color res mask
        char    RsvdFieldPosition;      // Bit posn of lsb of res mask
        char    DirectColorModeInfo;    // Direct color mode attributes

        // VBE 2.0 extension information
        long   PhysBasePtr;             // Physical address for linear buf
        long   OffScreenMemOffset;      // Pointer to start of offscreen mem
        short   OffScreenMemSize;       // Amount of offscreen mem in 1K's

        // VBE 2.1 extension information
        short   LinBytesPerScanLine;    // Bytes per scanline
        char    BnkNumberOfImagePages;  // Number of images pages (banked)
        char    LinNumberOfImagePages;  // Number of images pages (linear)
        char    LinRedMaskSize;         // Size of direct color red mask
        char    LinRedFieldPosition;    // Bit posn of lsb of red mask
        char    LinGreenMaskSize;       // Size of direct color green mask
        char    LinGreenFieldPosition;  // Bit posn of lsb of green mask
        char    LinBlueMaskSize;        // Size of direct color blue mask
        char    LinBlueFieldPosition;   // Bit posn of lsb of blue mask
        char    LinRsvdMaskSize;        // Size of direct color res mask
        char    LinRsvdFieldPosition;   // Bit posn of lsb of res mask

        char    res2[194];              // Pad to 256 byte block size
}VesaModeInfoStruct;

typedef
struct trminfo
{
  long EDI;
  long ESI;
  long EBP;
  long reserved_by_system;
  long EBX;
  long EDX;
  long ECX;
  long EAX;
  short flags;
  short ES,DS,FS,GS,IP,CS,SP,SS;
}rminfo;

//****************************************************************************

void FlipScreen(unsigned long, unsigned long);
#pragma aux FlipScreen = \
"        mov     al,07h         " \
"        mov     ah,4fh         " \
"        mov     ebx,00h        " \
"        int     10h            " \
parm [ecx] [edx] modify[eax ebx ecx edx esi edi ebp] ;

// ebx,80h doing FrameSwap with Rastertiming


unsigned long SetVesaMode(unsigned long);
#pragma aux SetVesaMode = \
"        mov     al,02h         " \
"        mov     ah,4fh         " \
"        int     10h            " \
parm [ebx] modify[eax ebx ecx edx esi edi ebp] VALUE [eax];

unsigned char GetDosMode();
#pragma aux GetDosMode = \
"        mov     al,00h         " \
"        mov     ah,0fh         " \
"        int     10h            " \
modify[eax ebx ecx edx esi edi ebp] VALUE [al];

void SetDosMode(int);
#pragma aux SetDosMode = \
"        mov     ah,00h         " \
"        int     10h            " \
parm [eax] modify[eax ebx ecx edx esi edi ebp];


void * PhysAdrMap(void *, unsigned long);
#pragma aux PhysAdrMap = \
"        mov    cx,bx           " \
"        shr    ebx,16          " \
"        mov    si,si           " \
"        shr    esi,16          " \
"        mov     eax,0800h      " \
"        int    31h             " \
"        shl    ebx,16          " \
"        mov    bx,cx           " \
parm [ebx] [esi] modify[eax ebx ecx edx esi edi ebp] VALUE [ebx];

void SetVesaBank(unsigned long);
#pragma aux SetVesaBank = \
"        mov     al,05h         " \
"        mov     ah,4fh         " \
"        mov     ebx,0h         " \
"        int     10h            " \
parm [edx] modify[eax ebx ecx edx esi edi ebp] ;


short *ModesList;
DisplayModesStruct DisplayModesList[128];
VesaInfoStruct far *VesaInfo;
VesaModeInfoStruct far *VesaModeInfo;
unsigned long *FrameBufferAdr=0;


int oldmode;
int nbmodes=0;
int currentmode=0;
int currentbits=0;
int currenty=0;

/****************************************************************************/

static cFile *border;

int borderflag=0;


void    g_freebitmap (cbitmap *b)
{
  if (b->pal) m_free ((char *)b->pal);
  if (b->data) m_free (b->data);
}


void    g_init(void)
{
  if (!GetVesaInfo())
    m_fatalerror ("No Vesa found!!!");

  if (!FindVesaMode ( 320, 200, 24))
    if (!FindVesaMode ( 320, 200, 32))
      if (!FindVesaMode ( 320, 400, 24))
        m_fatalerror ("Can not find right Vesamode please install univbe..");
  if (!FrameBufferAdr)
    m_fatalerror ("Needed Framebuffer.... (install vesa2+)");

  SetVesaMode(currentmode);

  border=m_loadfile("gfx/border.chk");

}

void    g_shutdown (void)
{
   m_freefile(border);

  SetDosMode (oldmode);
}


void    g_update (rgb18 *buf)
{
  long x,y;
  unsigned char *pic = (unsigned char *) FrameBufferAdr;




  if (currentbits == 24)
  {
    if (currenty == 200)
    {

      if (!borderflag)
      {
        char *tmp=pic;
        char *p=(char *) border->data;
        for (x=0;x<320*200;x++)
        {
          *tmp++=p[3];
          *tmp++=p[2];
          *tmp++=p[1];
          p+=4;

        }
        borderflag=-1;
      }

      pic+=(3*36*320);
      for ( y=0;y<128;y++)
      {
        for (x=0;x<160;x++)
        {
          *pic++ = buf[0].b<<2;
          *pic++ = buf[0].g<<2;
          *pic++ = buf[0].r<<2;

          *pic++ = ((unsigned long)buf[0].b+buf[1].b)<<1;
          *pic++ = ((unsigned long)buf[0].g+buf[1].g)<<1;
          *pic++ = ((unsigned long)buf[0].r+buf[1].r)<<1;
          buf++;
        }
      }
    }
    else
    {

      if (!borderflag)
      {
        char *tmp=pic;
        char *p=(char *) border->data;
        for (y=0;y<200;y++)
        {
          for (x=0;x<320;x++)
          {

            *tmp++=p[3];
            *tmp++=p[2];
            *tmp++=p[1];
            p+=4;

          }
          tmp+=320*3;
        }
        borderflag=-1;
      }


      pic+=(3*36*2*320);
      for ( y=0;y<128;y++)
      {
        for (x=0;x<160;x++)
        {
          *pic++ = buf[0].b<<2;
          *pic++ = buf[0].g<<2;
          *pic++ = buf[0].r<<2;

          *pic++ = ((unsigned long)buf[0].b+buf[1].b)<<1;
          *pic++ = ((unsigned long)buf[0].g+buf[1].g)<<1;
          *pic++ = ((unsigned long)buf[0].r+buf[1].r)<<1;
          buf++;
        }
        pic+=320*3;
      }
    }

  }
  else
  {
      if (!borderflag)
      {
        char *tmp=pic;
        char *p=(char *) border->data;
        for (x=0;x<320*200;x++)
        {
          *tmp++=p[3];
          *tmp++=p[2];
          *tmp++=p[1];
          tmp++;
          p+=4;


        }
        borderflag=-1;
      }


    pic+=(4*36*320);

    for ( y=0;y<128;y++)
    {
      for (x=0;x<160;x++)
      {
        *pic++ = buf[0].b<<2;
        *pic++ = buf[0].g<<2;
        *pic++ = buf[0].r<<2;
         pic++;


        *pic++ = ((unsigned long)buf[0].b+buf[1].b)<<1;
        *pic++ = ((unsigned long)buf[0].g+buf[1].g)<<1;
        *pic++ = ((unsigned long)buf[0].r+buf[1].r)<<1;
         pic++;


        buf++;
      }
    }
  }


}




int FindVesaMode (int x, int y, int bits)
{
  int i;
  for ( i=0;i<nbmodes;i++ )
  {
    if ( DisplayModesList[i].x == x &&  DisplayModesList[i].y == y && DisplayModesList[i].bits == bits )
      {
        currentmode=DisplayModesList[i].Id;      //Vesamodenr.
        currenty=DisplayModesList[i].y;      //Vesamodenr.

        currentbits=bits;
        return -1;
      }
    i++;
  }
  return 0;
}



int GetVesaInfo (void)
{
  union REGS regs;
  struct SREGS sregs;
  short selector;
  short segment;
  rminfo RMI;
  short *ML;
  unsigned long GraphikMemory;
  long i=0;

  oldmode = (int) GetDosMode ();

  memset(&sregs,0,sizeof(sregs));           // Alloc dos memory
  regs.w.ax=0x0100;
  regs.w.bx=0x0020;
  int386x( 0x31, &regs, &regs, &sregs);
  segment=regs.w.ax;
  selector=regs.w.dx;

  VesaInfo=(VesaInfoStruct far*) MK_FP(selector,0);      // create vesainfo ptr

  VesaInfo->Signature[0]='V';      // create vesainfo ptr
  VesaInfo->Signature[1]='B';
  VesaInfo->Signature[2]='E';
  VesaInfo->Signature[3]='2';

  memset(&RMI,0,sizeof(RMI));                   // Get vesa info
  RMI.EAX=0x00003900;
  RMI.EAX = 0x4f00;
  RMI.EBX = 0x0000;
  RMI.ES=segment;
  RMI.EDI=0;
  regs.w.ax = 0x0300;
  regs.h.bl = 0x10;
  regs.h.bh = 0;
  regs.w.cx = 0;
  sregs.es = FP_SEG(&RMI);
  regs.x.edi = FP_OFF(&RMI);
  int386x( 0x31, &regs, &regs, &sregs );

  if ( RMI.EAX!=0x004f )
  {
    return (0);
  }

  ModesList = (short *) (VesaInfo->VModeSeg*16+VesaInfo->VModeOff);

  memset(&sregs,0,sizeof(sregs));           // Alloc dos memory
  regs.w.ax=0x0100;
  regs.w.bx=0x0020;
  int386x( 0x31, &regs, &regs, &sregs);
  segment=regs.w.ax;
  selector=regs.w.dx;

  VesaModeInfo=(VesaModeInfoStruct far*) MK_FP(selector,0);      // create vesamodeinfo ptr

  DisplayModesList[i].Id=0x13;
  DisplayModesList[i].x=320;
  DisplayModesList[i].y=200;
  DisplayModesList[i].bits=8;
  DisplayModesList[i].Vesa=FALSE;
  DisplayModesList[i].lfb =TRUE;
  DisplayModesList[i].NumberOfScreens=1;
  DisplayModesList[i].SizeOfScreen=DisplayModesList[i].x*DisplayModesList[i].y*DisplayModesList[i].bits/8;
  DisplayModesList[i].VRam=(unsigned char *)0xa0000;
  i++;

  ML=ModesList;
  GraphikMemory = VesaInfo->TotalMemory*0x10000;

  while ( *ML!=-1)
  {
    memset(&RMI,0,sizeof(RMI));                   // Get vesa info
    RMI.EAX = 0x4f01;
    RMI.EBX = 0x0000;
    RMI.ECX = *ML;
    RMI.ES=segment;
    RMI.EDI=0;
    regs.w.ax = 0x0300;
    regs.h.bl = 0x10;
    regs.h.bh = 0;
    regs.w.cx = 0;
    sregs.es = FP_SEG(&RMI);
    regs.x.edi = FP_OFF(&RMI);
    int386x( 0x31, &regs, &regs, &sregs );

    if ( (VesaModeInfo->RedMaskSize+VesaModeInfo->BlueMaskSize+VesaModeInfo->GreenMaskSize) != 15 && (*ML>256) && (VesaModeInfo->BitsPerPixel>=8) && (VesaModeInfo->BitsPerPixel<=32) )
    {
      DisplayModesList[i].Id=*ML;
      DisplayModesList[i].x=VesaModeInfo->XResolution;
      DisplayModesList[i].y=VesaModeInfo->YResolution;
      DisplayModesList[i].bits=VesaModeInfo->BitsPerPixel;
      DisplayModesList[i].Vesa=TRUE;
      DisplayModesList[i].SizeOfScreen=DisplayModesList[i].x*DisplayModesList[i].y*DisplayModesList[i].bits/8;



      if ( VesaModeInfo->ModeAttributes&0x80 && VesaInfo->VLNr==2)
      {
        DisplayModesList[i].lfb = TRUE;
        DisplayModesList[i].NumberOfScreens=GraphikMemory/DisplayModesList[i].SizeOfScreen;
        DisplayModesList[i].Id|=0x4000;
        DisplayModesList[i].VRam=0;
      }
      else
      {
        DisplayModesList[i].lfb = FALSE;
        DisplayModesList[i].NumberOfScreens=1;
        DisplayModesList[i].VRam=(unsigned char *)0xa0000;
      }
      if (DisplayModesList[i].NumberOfScreens>3) DisplayModesList[i].NumberOfScreens=3;
      if (DisplayModesList[i].NumberOfScreens>0) i++;
    }
    nbmodes++;
    ML++;
  }

  DisplayModesList[i].bits=0;
  FrameBufferAdr=(unsigned long *) PhysAdrMap ((void *) VesaModeInfo->PhysBasePtr,4096*1024);

  i=0;
  while(DisplayModesList[i].bits!=0)
  {
    if(DisplayModesList[i].lfb==TRUE && DisplayModesList[i].VRam==0)
      DisplayModesList[i].VRam=(unsigned char *)FrameBufferAdr;
    i++;
  }
  return -1;
}
/****************************************************************************/