#include "vesa.h";
#include <conio.h>;

VBE_InfoBlock far *        vinfo;
VBE_ModeInfoBlock far *    minfo;

int *scr;
int dwpl;

// Возвращает версию VESA ####################################################
int GetVBEVersion(void) {
    union REGS              r;
    us                      seg,sel;
    static VBE_RMI          RMI;
    struct SREGS            sr;
    ul                      ver;

    memset (&cm, 0, sizeof(cm));
    r.x.eax=0x0100;
    r.x.ebx=600>>4;
    int386(0x31,&r,&r);
    if (r.x.cflag) {
       puts (" ¦ DOS memory alocate error.");
       return(0);
       }
    seg = r.w.ax;                         // RM segment
    sel = r.w.dx;                         // PM selector
    vinfo = (VBE_InfoBlock far *) MK_FP (sel, 0);
    memset (&sr, 0, sizeof (sr));
    memset (&RMI, 0, sizeof (RMI));
    RMI.EAX = 0x00004f00;   // Get VBE Info
    RMI.ES = seg;
    RMI.EDI = 0;
    r.w.ax = 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 (r.x.cflag) {
       puts (" ¦ DPMI 300h failed.");
       return(0);
       }
    if ((us) RMI.EAX != 0x004f) {
       puts (" ¦ Error Getting Video Controller Info.");
       return(0);
       }
    return(vinfo -> VbeVersion);
    }

void PutVBEVersion(void) {
    int ver=GetVBEVersion();
    printf("%d.%d",(ver & 0xFF00)>>8 ,ver & 0x00FF);
    }

// Включение VESA видео-режима ###########################
// 0 - Ok
// 1 - DPMI 300h failed
// 2 - Error Getting Video Controller Info
// 3 - DOS memory alocate error
// 4 - Video mode no found
// 5 - Error mapping physical to linear
// 6 - Error Init Mode
TMode SetVESAMode(int xmax, int ymax, int bpp) {
    static VBE_RMI          RMI;
    union REGS              r;
    struct SREGS            sr;
    us                      sel,seg,x,y,ic;
    ul                      phys;
    int                     mmode;
    TMode                   cm;

    r.x.eax=0x0100;
    r.x.ebx=600>>4;
    int386(0x31,&r,&r);
    if (r.x.cflag) {
       cm.error=1;
       return(cm);
       }
    seg = r.w.ax;                         // RM segment
    sel = r.w.dx;                         // PM selector
    vinfo = (VBE_InfoBlock far *) MK_FP (sel, 0);
    memset (&sr, 0, sizeof (sr));
    memset (&RMI, 0, sizeof (RMI));
    RMI.EAX = 0x00004f00;   // Get VBE Info
    RMI.ES = seg;
    RMI.EDI = 0;
    r.w.ax = 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 (r.x.cflag) {
        cm.error=1;
        return(cm);
        }
    if ((us) RMI.EAX != 0x004f) {
        cm.error=2;
        return(cm);
        }
    r.x.eax = 0x0100;
    r.x.ebx = 300 >> 4;
    int386 (0x31, &r, &r);
    if (r.x.cflag) {
       cm.error=3;
       return(cm);
       }
    seg = r.w.ax;    // RM segment
    sel = r.w.dx;    // PM selector
    minfo = (VBE_ModeInfoBlock far *) MK_FP (sel, 0);
    memset (&sr, 0, sizeof (sr));
    memset (&RMI, 0, sizeof (RMI));
    mmode=0;
    if (vinfo -> VbeVersion >= 0x0200) {
        for (ic=0x4100;ic<0x4200;ic++) {
            RMI.EAX = 0x00004f01;   // Get VBE Mode Info
            RMI.ECX = ic;
            RMI.ES = seg;
            RMI.EDI = 0;
            r.w.ax = 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 (r.x.cflag) {
               cm.error=1;
               return(cm);
               }
            if ((minfo->BitsPerPixel==bpp) &&
               (minfo->XResolution==xmax) &&
               (minfo->YResolution==ymax) &&
               ((us)RMI.EAX==0x004f)) {
               if (mmode==0) {mmode=ic;}
               }
            }
        if (mmode!=0) cm.banked=1; else cm.banked=0;
        }
    if (mmode==0) {
        for (ic=0x0100;ic<0x0200;ic++) {
            RMI.EAX = 0x00004f01;   // Get VBE Mode Info
            RMI.ECX = ic;
            RMI.ES = seg;
            RMI.EDI = 0;
            r.w.ax = 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 (r.x.cflag) {
               cm.error=1;
               return(cm);
               }
            if ((minfo->BitsPerPixel==bpp)&&
               (minfo->XResolution==xmax)&&
               (minfo->YResolution==ymax)&&
               ((us)RMI.EAX==0x004f)) {
               if (mmode==0) {mmode=ic;}
               }
            }
        }
    if (mmode==0) {
       cm.error=4;
       return(cm);
       }
    RMI.EAX = 0x00004f01;   // Get VBE Mode Info
    RMI.ECX = mmode;
    RMI.ES = seg;
    RMI.EDI = 0;
    r.w.ax = 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);
    phys=minfo->PhysBasePtr;
    cm.bpp=minfo->BitsPerPixel;
    cm.bytespp=cm.bpp/8;
    cm.h=minfo->YResolution;
    cm.w=minfo->XResolution;
    cm.bpl=minfo->BytesPerScanLine;
    r.w.ax = 0x0800;
    r.w.bx = (phys >> 0x10);
    r.w.cx = (phys & 0xffff);
    r.w.si = (long)((minfo->YResolution * minfo->BytesPerScanLine)>>16);
    r.w.di = minfo->YResolution * minfo->BytesPerScanLine;
    int386 (0x31, &r, &r);
    if (r.x.cflag) {
       cm.error=5;
       return(cm);
       }
    cm.LFB = (uc near *) (((ul) r.w.bx << 16) | r.w.cx);
    r.w.ax = 0x4f02;
    r.w.bx = mmode;
    int386 (0x10, &r, &r);
    if (r.w.ax != 0x004f) {
       cm.error=6;
       return(cm);
       }
   cm.error=0;
   return(cm);
   }

// Установка VGA режима ######################################################
void SetVGAMode(int mode) {
    static VBE_RMI          RMI;
    union REGS              r;
    struct SREGS            sr;

    memset (&sr, 0, sizeof (sr));
    memset (&RMI, 0, sizeof (RMI));
    memset (&r, 0, sizeof (r));
    RMI.EAX = mode;   // Get VBE Mode Info
    r.w.ax = 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);
    }

void blt(TMode m) {
     if (m.bpp==32 && m.banked==1) { // Если включен 32 битный LFB режим
        memcpy((void *)m.LFB, (void *)m.VLFB, m.w * m.h * m.bytespp);
        }
     }

void InitGraph(void) {
     dwpl=cm.bpl>>2;
     scr=(int *)cm.VLFB;
     }

void PutPixel(int x,int y,int color) {
     scr[y*dwpl+x]=color;
     }

int sgn(int i) {
    if (i==0) return(0);
    if (i>0) return(1);
    return(-1);
    }

void Line(int x1,int y1,int x2,int y2,int color) {
    int dx,dy,r,dr1,dr2,x,y,sx,sy;

    dx=abs(x2-x1);
    dy=abs(y2-y1);
    sx=sgn(x2-x1);
    sy=sgn(y2-y1);
    if (dx>dy) {

        r=dx;
        dr1=-dy<<1;
        dr2=(dx-dy)<<1;
        y=y1;
        x=x1;
        x2=x2+sx;
        do {
            PutPixel(x,y,color);
            if (r>0) r=r+dr1;
            else {
                r=r+dr2;
                y=y+sy;
                PutPixel(x,y,color);
                }
            x=x+sx;
            } while (x!=x2);
        }
    else {
        r=dy;
        dr1=-dx<<1;
        dr2=(dy-dx)<<1;
        y=y1;
        x=x1;
        y2=y2+sy;
        do {
            PutPixel(x,y,color);
            if (r>0) r=r+dr1;
            else {
                r=r+dr2;
                x=x+sx;
                PutPixel(x,y,color);
                }
            y=y+sy;
            } while (y!=y2);
        }
    }

