#include <i86.h>

long GetVesaMode();
void SetVesaMode (short Mode);

typedef unsigned char byte;

#define  smBW40   0x0000
#define  smCO40   0x0001      // CGA }
#define  smBW80   0x0002
#define  smCO80   0x0003      // CGA,EGA,VGA }
#define  smMONO   0x0007      // CGA,EGA,VGA }

#define  sm40x12  0x80        // VGA }
#define  sm40x14  0x81        // VGA }
#define  sm40x25  0x82        // CGA,EGA,VGA }
#define  sm40x30  0x83        // VGA }
#define  sm40x34  0x84        // VGA }
#define  sm40x43  0x85        // VGA }
#define  sm40x50  0x86        // VGA }
#define  sm40x60  0x87        // VGA }

#define  sm80x12  0x90        // VGA }
#define  sm80x14  0x91        // VGA }
#define  sm80x25  0x92        // VGA }
#define  sm80x30  0x93        // VGA }
#define  sm80x34  0x94        // VGA }
#define  sm80x43  0x95        // EGA,VGA }
#define  sm80x50  0x96        // VGA }
#define  sm80x60  0x97        // VGA }

#define  sm94x12  0xA0        // VGA }
#define  sm94x14  0xA1        // VGA }
#define  sm94x25  0xA2        // VGA }
#define  sm94x30  0xA3        // VGA }
#define  sm94x34  0xA4        // VGA }
#define  sm94x43  0xA5        // VGA }
#define  sm94x50  0xA6        // VGA }
#define  sm94x60  0xA7        // VGA }
#define  smNonStandard  0x00FF

#define    vga200ScanLines  0x00
#define    vga350ScanLines  0x01
#define    vga400ScanLines  0x02
#define    vga480ScanLines  0x03        // DN specific }

#define    vgaFont16x8      0x0C
#define    vgaFont14x8      0x00
#define    vgaFont8x8       0x04

#define    vga40Cols        0x00
#define    vga80Cols        0x20
#define    vga94Cols        0x30        // DN specific }

#define    EquipmentOfs     0x0010        // Word }
#define    CrtColsOfs       0x044A        // Byte }
#define    CrtRowsOfs       0x0484        // Byte }
#define    CrtCharOfs       0x0485        // Byte }
#define    CrtInfoOfs       0x0087        // Byte }
#define    CrtPSizOfs       0x044C        // Word }


byte vm40x12 = vga200ScanLines | vgaFont16x8 | vga40Cols;
byte vm80x12 = vga200ScanLines | vgaFont16x8 | vga80Cols;
byte vm94x12 = vga200ScanLines | vgaFont16x8 | vga94Cols;
byte vm40x14 = vga200ScanLines | vgaFont14x8 | vga40Cols;
byte vm80x14 = vga200ScanLines | vgaFont14x8 | vga80Cols;
byte vm94x14 = vga200ScanLines | vgaFont14x8 | vga94Cols;
byte vm40x25 = vga400ScanLines | vgaFont16x8 | vga40Cols;
byte vm80x25 = vga400ScanLines | vgaFont16x8 | vga80Cols;
byte vm94x25 = vga400ScanLines | vgaFont16x8 | vga94Cols;
byte vm40x30 = vga480ScanLines | vgaFont16x8 | vga40Cols;
byte vm80x30 = vga480ScanLines | vgaFont16x8 | vga80Cols;
byte vm94x30 = vga480ScanLines | vgaFont16x8 | vga94Cols;
byte vm40x34 = vga480ScanLines | vgaFont14x8 | vga40Cols;
byte vm80x34 = vga480ScanLines | vgaFont14x8 | vga80Cols;
byte vm94x34 = vga480ScanLines | vgaFont14x8 | vga94Cols;
byte vm40x43 = vga350ScanLines | vgaFont8x8  | vga40Cols;
byte vm80x43 = vga350ScanLines | vgaFont8x8  | vga80Cols;
byte vm94x43 = vga350ScanLines | vgaFont8x8  | vga94Cols;
byte vm40x50 = vga400ScanLines | vgaFont8x8  | vga40Cols;
byte vm80x50 = vga400ScanLines | vgaFont8x8  | vga80Cols;
byte vm94x50 = vga400ScanLines | vgaFont8x8  | vga94Cols;
byte vm40x60 = vga480ScanLines | vgaFont8x8  | vga40Cols;
byte vm80x60 = vga480ScanLines | vgaFont8x8  | vga80Cols;
byte vm94x60 = vga480ScanLines | vgaFont8x8  | vga94Cols;

enum TVideoType
{
 vtUnknown=0,
 vtMONO,
 vtCGA,
 vtEGA,
 vtVGA,
 vtXGA,
 vtSVGA,
 vtVBE2
};

static struct rminfo {
    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;
} RMI;

typedef struct
{
 byte   ID[4];
 short  XAttr;
 byte   res[512];
}VideoInfo;

byte   VideoType=vtUnknown, ButtonCount = 0;
short  ScreenMode, ScreenWidth, ScreenHeight,CursorLines;
byte   HiResScreen, CheckSnow;
byte   *ScreenBuffer;



void SetDNMode(byte Mode)
{
 union REGS r;

 if(VideoType<vtVGA)
 {
  switch(Mode)
  {
   case sm40x25:
        r.w.ax = 0x0001; int386(0x10,&r,&r);
        r.w.ax = 0x1111; r.h.bl = 0x00; int386(0x10,&r,&r);
        break;
   case sm80x25:
        r.w.ax = 0x0003; int386(0x10,&r,&r);
        r.w.ax = 0x1111; r.h.bl = 0x00; int386(0x10,&r,&r);
        break;
   case sm80x43:
        if(VideoType==vtEGA)
        {
         r.w.ax = 0x0003; int386(0x10,&r,&r);
         r.w.ax = 0x1112; r.h.bl = 0x00; int386(0x10,&r,&r);
        }
        break;
  }
 }
 else  // VideoType >= vtVGA }
 {
  switch(Mode)
  {
   case sm40x12: SetVgaMode ( vm40x12,  40*256+12 ); break;
   case sm40x14: SetVgaMode ( vm40x14,  40*256+14 ); break;
   case sm40x25: SetVgaMode ( vm40x25,  40*256+25 ); break;
   case sm40x30: SetVgaMode ( vm40x30,  40*256+30 ); break;
   case sm40x34: SetVgaMode ( vm40x34,  40*256+34 ); break;
   case sm40x43: SetVgaMode ( vm40x43,  40*256+43 ); break;
   case sm40x50: SetVgaMode ( vm40x50,  40*256+50 ); break;
   case sm40x60: SetVgaMode ( vm40x60,  40*256+60 ); break;
   case sm80x12: SetVgaMode ( vm80x12,  80*256+12 ); break;
   case sm80x14: SetVgaMode ( vm80x14,  80*256+14 ); break;
   case sm80x25: SetVgaMode ( vm80x25,  80*256+25 ); break;
   case sm80x30: SetVgaMode ( vm80x30,  80*256+30 ); break;
   case sm80x34: SetVgaMode ( vm80x34,  80*256+34 ); break;
   case sm80x43: SetVgaMode ( vm80x43,  80*256+43 ); break;
   case sm80x50: SetVgaMode ( vm80x50,  80*256+50 ); break;
   case sm80x60: SetVgaMode ( vm80x60,  80*256+60 ); break;
   case sm94x12: SetVgaMode ( vm94x12,  94*256+12 ); break;
   case sm94x14: SetVgaMode ( vm94x14,  94*256+14 ); break;
   case sm94x25: SetVgaMode ( vm94x25,  94*256+25 ); break;
   case sm94x30: SetVgaMode ( vm94x30,  94*256+30 ); break;
   case sm94x34: SetVgaMode ( vm94x34,  94*256+34 ); break;
   case sm94x43: SetVgaMode ( vm94x43,  94*256+43 ); break;
   case sm94x50: SetVgaMode ( vm94x50,  94*256+50 ); break;
   case sm94x60: SetVgaMode ( vm94x60,  94*256+60 ); break;
  }
 }
}

void SetVgaMode (byte Flags,short Size)
{
 byte temp,temp1,temp2;
 union REGS r;

 r.h.ah = 0x12; r.h.al = Flags&vga480ScanLines;
 if(r.h.al>=vga480ScanLines) r.h.al--;
 r.h.bl = 0x30; int386(0x10,&r,&r);
 r.h.ah = 0x00; r.h.al = Flags&vga94Cols; r.h.al>>=4;
 if(r.h.al!=(vga94Cols>>4))   r.h.al++;
 int386(0x10,&r,&r);
 r.h.ah = 0x11; r.h.al = Flags&vgaFont16x8; r.h.al>>=2;
 r.h.al+=r.h.ah; r.h.bl = 0x00;
 int386(0x10,&r,&r); _asm{CLI};
//----- VGA OFF ------------------------
 outp(0x3C4,0); outp(0x3C5,1);
 outp(0x3D4,23); outp(0x3D5,0x7F&inp(0x3D5));
 outp(0x3D4,17); outp(0x3D5,0x7F&inp(0x3D5));
//--------------------------------------
 if((vga94Cols&Flags) == vga94Cols)
 {
  outp(0x3C2,(0xF3&inp(0x3CC))|0x04);
  outp(0x3C4,0x01); outp(0x3C5,0x01|inp(0x3C5));
  outp(0x3D4,0x00); outp(0x3D5,0x6C);
  outp(0x3D4,0x01); outp(0x3D5,0x5D);
  outp(0x3D4,0x02); outp(0x3D5,0x5E);
  outp(0x3D4,0x03); outp(0x3D5,0x8F);
  outp(0x3D4,0x04); outp(0x3D5,0x62);
  outp(0x3D4,0x05); outp(0x3D5,0x8E);
  outp(0x3D4,0x13); outp(0x3D5,0x2F);
  inp(0x3DA);
  outp(0x3C0,0x13); outp(0x3C0,0x00); outp(0x3C0,0x20); outp(0x3C0,0x20);
 }
 if((vga480ScanLines&Flags) == vga480ScanLines)
 {
  outp(0x3C2,0xC0|inp(0x3CC));
  outp(0x3D4,6); outp(0x3D5,11);
  outp(0x3D4,7); outp(0x3D5,62);
  outp(0x3D4,9); outp(0x3D5,79);
  outp(0x3D4,16); outp(0x3D5,234);
  outp(0x3D4,17); outp(0x3D5,140);
  outp(0x3D4,18); outp(0x3D5,223);
  outp(0x3D4,21); outp(0x3D5,231);
  outp(0x3D4,22); outp(0x3D5,4);
 }
 if((vgaFont16x8&Flags)==vgaFont8x8)  temp2=temp= 8;
 if((vgaFont16x8&Flags)==vgaFont14x8) temp2=temp=14;
 if((vgaFont16x8&Flags)==vgaFont16x8) temp2=temp=16;
//-----Char Height----------------------
 outp(0x3D4,0x09); temp1=0xE0&inp(0x3D5); outp(0x3D5,(--temp)|temp1);
 outp(0x3D4,0x0A); if((--temp)>10) --temp; outp(0x3D5,temp);
 outp(0x3D4,0x0B); outp(0x3D5,++temp);
//----- VGA ON -------------------------
 outp(0x3D4,17); outp(0x3D5,0x80|inp(0x3D5));
 outp(0x3D4,23); outp(0x3D5,0x80|inp(0x3D5));
 outp(0x3C4,0); outp(0x3C5,3);
//--------------------------------------
 _asm{sti};
 *(byte *)(CrtCharOfs)=temp2;
 temp=Size>>8; temp1=Size;
 *(byte *)(CrtColsOfs)=temp;
 *(byte *)(CrtRowsOfs)=temp1-1;
 *(short *)(CrtPSizOfs)=(temp*temp1)<<1;
}

short FixCrtMode(short Mode)
{
 byte modes[]={smMONO,smCO40,smCO80,sm40x25,sm80x25,sm80x43,sm40x12,sm40x14,sm40x30,sm40x34,sm40x43,
               sm40x50,sm40x60,sm80x12,sm80x14,sm80x30,sm80x34,sm80x50,sm80x60,sm94x12,sm94x14,sm94x25,
               sm94x30,sm94x34,sm94x43,sm94x50,sm94x60};
 byte i,i1;

 if(Mode<256)
 {
  if(VideoType>=vtVGA)i1=27;
  else if(VideoType>=vtEGA)i1=6;
  else i1=5;
  for(i=0;i<i1;i++) if(Mode==modes[i]) return(Mode);
 }
// if(Mode==CheckVesaMode()) return(Mode);
 return(smCO80);
}

void SetVideoMode (short Mode)
{
 byte Mode1;
 union REGS r;

 if(Mode>0x7F)
 {
  Mode1=FixCrtMode(Mode);
  if(Mode1>0x7F) if(Mode1<0x100) SetDNMode(Mode1); else SetVesaMode(Mode1);
  else {r.h.ah = 0; r.h.al = Mode1; int386(0x10,&r,&r);}
  SetCrtData();
 }
 printf("2: x - %d, y - %d\n",ScreenWidth,ScreenHeight);
}

void DetectVideoType()
{
 union REGS r;

 if(VideoType!=vtUnknown) return;
 VideoType=vtMONO;
 r.x.eax = 0x0; int386(0x11,&r,&r);
 if((r.h.al&0x30)==0x30) return;
 VideoType=vtCGA;
 r.h.ah = 0x12; r.w.bx=0xFF10; int386(0x10,&r,&r);
 if(r.h.bh == 0xFF) return;
 VideoType=vtEGA;
 r.w.ax = 0x1C00; r.w.cx=0x7; int386(0x10,&r,&r);
 if(r.h.al!=0x1C)
 {
  r.w.ax = 0x1200; r.h.bl=0x32; int386(0x10,&r,&r); // VGA, MCGA, enable video addressing
  if(r.h.al!=0x12) return;
 }
 VideoType=vtVGA; DetectVesaType();
 return;
}

typedef struct
{
 byte row;
 byte col;
 byte mode;
} infmod;

infmod modes_vid[]=
{
 {12,40,sm40x12}, {14,40,sm40x14}, {25,40,sm40x25}, {30,40,sm40x30},
 {34,40,sm40x34}, {43,40,sm40x43}, {50,40,sm40x50}, {60,40,sm40x60},
 {12,80,sm80x12}, {14,80,sm80x14}, {25,80,sm80x25}, {30,80,sm80x30},
 {34,80,sm80x34}, {43,80,sm80x43}, {50,80,sm80x50}, {60,80,sm80x60},
 {12,94,sm94x12}, {14,94,sm94x14}, {25,94,sm94x25}, {30,94,sm94x30},
 {34,94,sm94x34}, {43,94,sm94x43}, {50,94,sm94x50}, {60,94,sm94x60},
};

byte GetCrtMode()
{
 int i;
 long temp;
 union REGS r;
 byte ch_of,row_of,col_of;

 DetectVideoType();
 if((temp=GetVesaMode())<0x100) temp&=0x7F;
 if(temp==0xFFFF){ r.h.ah = 0x0F; int386(0x10,&r,&r); temp=r.w.ax&0x7F;}
 ch_of = *(byte *)(CrtCharOfs);
 row_of= *(byte *)(CrtRowsOfs);
 col_of= *(byte *)(CrtColsOfs);
 if(temp<0x100) row_of++;
 for(i=0;i<sizeof(modes_vid)/sizeof(infmod);i++)
  if(modes_vid[i].row==row_of&&modes_vid[i].col==col_of) return(modes_vid[i].mode);
 return(smNonStandard);
};

void SetCrtData()
{
 int i;
 union REGS r;

 ScreenMode=GetCrtMode();
 for(i=0;i<sizeof(modes_vid)/sizeof(infmod);i++) if(modes_vid[i].mode==ScreenMode) break;
 if(modes_vid[i].row<=24) HiResScreen=0; else HiResScreen=1;
 ScreenWidth=modes_vid[i].col; ScreenHeight=modes_vid[i].row;
 if(ScreenMode==smMONO) ScreenBuffer= 0xB8000; else ScreenBuffer= 0xB0000;
 r.h.ah = 0x03; r.h.bh = 0; int386(0x10,&r,&r); CursorLines=r.w.cx;
 r.h.ah = 0x01; r.w.cx = 0x2000; int386(0x10,&r,&r);
 if(!ButtonCount) return;
 r.w.ax = 7; r.w.dx = (ScreenWidth<<3)-1; int386(0x33,&r,&r);
 r.w.ax = 8; r.w.dx = (ScreenHeight<<3)-1; int386(0x33,&r,&r);
}

long GetVesaMode()
{
 union REGS r;
 struct SREGS sr;

 if(VideoType==vtXGA) {RMI.EAX = 0x4E04; RMI.EDX = 0x4E;}
 else if(VideoType >= vtSVGA) {RMI.EAX = 0x4F03; RMI.EDX = 0x4E;}
 else return(0);
 r.x.eax = 0x0300; r.h.bl = 0x10;
 r.h.bh = 0; r.w.cx = 0; sr.ds=sr.es=FP_SEG(&RMI); r.x.edi = FP_OFF(&RMI);
 int386x(0x31, &r, &r, &sr);
 if(RMI.EAX == RMI.EDX) return(RMI.EBX);
 return 0;
};

void DetectVesaType()
{
 union REGS r;
 struct SREGS sr;
 short selector, segment;
 VideoInfo *P;

 r.w.ax=0x0100; r.w.bx=(sizeof(VideoInfo)+15)>>4;
 int386(0x31,&r,&r); segment=r.w.ax; selector=r.w.dx;
 memset(&RMI,0,sizeof(RMI));
 RMI.ES=segment; RMI.EAX=0x4E00;
 P=(VideoInfo *)(segment<<4); strcpy(P->ID,"QQQQ");

 r.x.eax = 0x0300; r.h.bl = 0x10;
 r.h.bh = 0; r.w.cx = 0; sr.ds=sr.es=FP_SEG(&RMI); r.x.edi = FP_OFF(&RMI);
 int386x(0x31,&r,&r,&sr);
 if(!strncmp(P->ID,"VESA",4)) VideoType=vtXGA;
 else
 {
  RMI.ES=segment; RMI.EAX=0x4F00; r.x.eax = 0x0300; r.h.bl = 0x10;
  r.h.bh = 0; r.w.cx = 0; sr.es = FP_SEG(&RMI); r.x.edi = FP_OFF(&RMI);
  int386x(0x31, &r, &r, &sr);
  if(!strncmp(P->ID,"VESA",4))
  {
   VideoType=vtSVGA;
   strcpy(P->ID,"VBE2");
   RMI.ES=segment; RMI.EAX=0x4F00; r.x.eax = 0x0300; r.h.bl = 0x10;
   r.h.bh = 0; r.w.cx = 0; sr.es = FP_SEG(&RMI); r.x.edi = FP_OFF(&RMI);
   int386x(0x31, &r, &r, &sr);
   if(!strncmp(P->ID,"VESA",4)) VideoType=vtVBE2;
  }
 }
 r.w.ax=0x0101; r.w.dx=selector; int386(0x31,&r,&r);
}

void SetVesaMode (short Mode)
{
 VideoInfo *P;
 union REGS r;
 struct SREGS sr;
 short selector, segment;

 if(VideoType==vtXGA)
 {
  r.w.ax=0x0100; r.w.bx=(sizeof(VideoInfo)+15)>>4;
  int386(0x31,&r,&r); segment=r.w.ax; selector=r.w.dx;
  memset(&RMI,0,sizeof(RMI));
  P=(VideoInfo *)(segment<<4);
  RMI.ES=segment; RMI.EAX=0x4E02; RMI.EBX=Mode;
  r.x.eax = 0x0300; r.h.bl = 0x10;
  r.h.bh = 0; r.w.cx = 0; sr.ds=sr.es=FP_SEG(&RMI); r.x.edi=FP_OFF(&RMI);
  int386x(0x31,&r,&r,&sr);
  r.w.ax=0x0101; r.w.dx=selector; int386(0x31,&r,&r);
 }
 else if(VideoType==vtSVGA)
 {
  RMI.EAX=0x4F02; RMI.EBX=Mode;
  r.x.eax=0x0300; r.h.bl=0x10;
  r.h.bh=0; r.w.cx=0; sr.ds=sr.es=FP_SEG(&RMI); r.x.edi=FP_OFF(&RMI);
  int386x(0x31,&r,&r,&sr);
 }
}