DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/ints/int10_vesa.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include <string.h>
00021 #include <stddef.h>
00022 
00023 #include "dosbox.h"
00024 #include "callback.h"
00025 #include "regs.h"
00026 #include "mem.h"
00027 #include "inout.h"
00028 #include "int10.h"
00029 #include "dos_inc.h"
00030 
00031 int hack_lfb_yadjust = 0;
00032 
00033 extern int vesa_mode_width_cap;
00034 extern int vesa_mode_height_cap;
00035 extern bool allow_vesa_lowres_modes;
00036 extern bool vesa12_modes_32bpp;
00037 extern bool allow_vesa_32bpp;
00038 extern bool allow_vesa_24bpp;
00039 extern bool allow_vesa_16bpp;
00040 extern bool allow_vesa_15bpp;
00041 extern bool allow_vesa_8bpp;
00042 extern bool allow_vesa_4bpp;
00043 extern bool allow_vesa_tty;
00044 
00045 #define VESA_SUCCESS          0x00
00046 #define VESA_FAIL             0x01
00047 #define VESA_HW_UNSUPPORTED   0x02
00048 #define VESA_MODE_UNSUPPORTED 0x03
00049 // internal definition to pass to the caller
00050 #define VESA_UNIMPLEMENTED    0xFF
00051 
00052 static struct {
00053         Bitu setwindow;
00054         Bitu pmStart;
00055         Bitu pmWindow;
00056         Bitu pmPalette;
00057 } callback = {0};
00058 
00059 void CALLBACK_DeAllocate(Bitu in);
00060 
00061 static char string_oem[]="S3 Incorporated. Trio64";
00062 static char string_vendorname[]="DOSBox Development Team";
00063 static char string_productname[]="DOSBox - The DOS Emulator";
00064 static char string_productrev[]="DOSBox SVN-X";
00065 
00066 #ifdef _MSC_VER
00067 #pragma pack (1)
00068 #endif
00069 struct MODE_INFO{
00070         Bit16u ModeAttributes;
00071         Bit8u WinAAttributes;
00072         Bit8u WinBAttributes;
00073         Bit16u WinGranularity;
00074         Bit16u WinSize;
00075         Bit16u WinASegment;
00076         Bit16u WinBSegment;
00077         Bit32u WinFuncPtr;
00078         Bit16u BytesPerScanLine;
00079         Bit16u XResolution;
00080         Bit16u YResolution;
00081         Bit8u XCharSize;
00082         Bit8u YCharSize;
00083         Bit8u NumberOfPlanes;
00084         Bit8u BitsPerPixel;
00085         Bit8u NumberOfBanks;
00086         Bit8u MemoryModel;
00087         Bit8u BankSize;
00088         Bit8u NumberOfImagePages;
00089         Bit8u Reserved_page;
00090         Bit8u RedMaskSize;
00091         Bit8u RedMaskPos;
00092         Bit8u GreenMaskSize;
00093         Bit8u GreenMaskPos;
00094         Bit8u BlueMaskSize;
00095         Bit8u BlueMaskPos;
00096         Bit8u ReservedMaskSize;
00097         Bit8u ReservedMaskPos;
00098         Bit8u DirectColorModeInfo;
00099         Bit32u PhysBasePtr;
00100         Bit32u OffScreenMemOffset;
00101         Bit16u OffScreenMemSize;
00102         Bit8u Reserved[206];
00103 } GCC_ATTRIBUTE(packed);
00104 #ifdef _MSC_VER
00105 #pragma pack()
00106 #endif
00107 
00108 void VESA_OnReset_Clear_Callbacks(void) {
00109     if (callback.setwindow != 0) {
00110         CALLBACK_DeAllocate(callback.setwindow);
00111         callback.setwindow = 0;
00112     }
00113     if (callback.pmPalette != 0) {
00114         CALLBACK_DeAllocate(callback.pmPalette);
00115         callback.pmPalette = 0;
00116     }
00117     if (callback.pmStart != 0) {
00118         CALLBACK_DeAllocate(callback.pmStart);
00119         callback.pmStart = 0;
00120     }
00121     if (callback.pmWindow != 0) {
00122         CALLBACK_DeAllocate(callback.pmWindow);
00123         callback.pmWindow = 0;
00124     }
00125 }
00126 
00127 Bit8u VESA_GetSVGAInformation(Bit16u seg,Bit16u off) {
00128         /* Fill 256 byte buffer with VESA information */
00129         PhysPt buffer=PhysMake(seg,off);
00130         Bitu i;
00131         bool vbe2=false;Bit16u vbe2_pos=256+off;
00132         Bitu id=mem_readd(buffer);
00133         if (((id==0x56424532)||(id==0x32454256)) && (!int10.vesa_oldvbe)) vbe2=true;
00134         if (vbe2) {
00135                 for (i=0;i<0x200;i++) mem_writeb(buffer+i,0);           
00136         } else {
00137                 for (i=0;i<0x100;i++) mem_writeb(buffer+i,0);
00138         }
00139         /* Fill common data */
00140         MEM_BlockWrite(buffer,(void *)"VESA",4);                                //Identification
00141         if (!int10.vesa_oldvbe) mem_writew(buffer+0x04,0x200);  //Vesa version 2.0
00142         else mem_writew(buffer+0x04,0x102);                                             //Vesa version 1.2
00143         if (vbe2) {
00144                 mem_writed(buffer+0x06,RealMake(seg,vbe2_pos));
00145                 for (i=0;i<sizeof(string_oem);i++) real_writeb(seg,vbe2_pos++,(Bit8u)string_oem[i]);
00146                 mem_writew(buffer+0x14,0x200);                                  //VBE 2 software revision
00147                 mem_writed(buffer+0x16,RealMake(seg,vbe2_pos));
00148                 for (i=0;i<sizeof(string_vendorname);i++) real_writeb(seg,vbe2_pos++,(Bit8u)string_vendorname[i]);
00149                 mem_writed(buffer+0x1a,RealMake(seg,vbe2_pos));
00150                 for (i=0;i<sizeof(string_productname);i++) real_writeb(seg,vbe2_pos++,(Bit8u)string_productname[i]);
00151                 mem_writed(buffer+0x1e,RealMake(seg,vbe2_pos));
00152                 for (i=0;i<sizeof(string_productrev);i++) real_writeb(seg,vbe2_pos++,(Bit8u)string_productrev[i]);
00153         } else {
00154                 mem_writed(buffer+0x06,int10.rom.oemstring);    //Oemstring
00155         }
00156         mem_writed(buffer+0x0a,0x0);                                    //Capabilities and flags
00157         mem_writed(buffer+0x0e,int10.rom.vesa_modes);   //VESA Mode list
00158         mem_writew(buffer+0x12,(Bit16u)(vga.vmemsize/(64*1024))); // memory size in 64kb blocks
00159         return VESA_SUCCESS;
00160 }
00161 
00162 Bit8u VESA_GetSVGAModeInformation(Bit16u mode,Bit16u seg,Bit16u off) {
00163         MODE_INFO minfo;
00164         memset(&minfo,0,sizeof(minfo));
00165         PhysPt buf=PhysMake(seg,off);
00166         Bitu pageSize;
00167         Bit8u modeAttributes;
00168         Bitu i=0;
00169 
00170         mode&=0x3fff;   // vbe2 compatible, ignore lfb and keep screen content bits
00171         if (mode<0x100) return 0x01;
00172         if (svga.accepts_mode) {
00173                 if (!svga.accepts_mode(mode)) return 0x01;
00174         }
00175         while (ModeList_VGA[i].mode!=0xffff) {
00176                 /* Hack for VBE 1.2 modes and 24/32bpp ambiguity */
00177                 if (ModeList_VGA[i].mode >= 0x100 && ModeList_VGA[i].mode <= 0x11F &&
00178                         ((ModeList_VGA[i].type == M_LIN32 && !vesa12_modes_32bpp) ||
00179                          (ModeList_VGA[i].type == M_LIN24 && vesa12_modes_32bpp))) {
00180                         /* ignore */
00181                         i++;
00182                 }
00183                 else if (mode==ModeList_VGA[i].mode)
00184                         goto foundit;
00185                 else
00186                         i++;
00187         }
00188         return VESA_FAIL;
00189 foundit:
00190         if ((int10.vesa_oldvbe) && (ModeList_VGA[i].mode>=0x120)) return 0x01;
00191         VideoModeBlock * mblock=&ModeList_VGA[i];
00192 
00193         bool allow_res = allow_vesa_lowres_modes ||
00194                 (ModeList_VGA[i].swidth >= 640 && ModeList_VGA[i].sheight >= 400);
00195 
00196         switch (mblock->type) {
00197         case M_LIN4:
00198                 if (!allow_vesa_4bpp) return VESA_FAIL;
00199                 pageSize = mblock->sheight * mblock->swidth/2;
00200                 var_write(&minfo.BytesPerScanLine,((mblock->swidth+15U)/8U)&(~1U)); /* NTS: 4bpp requires even value due to VGA registers, round up */
00201                 var_write(&minfo.NumberOfPlanes,0x4);
00202                 var_write(&minfo.BitsPerPixel,4);
00203                 var_write(&minfo.MemoryModel,3);        //ega planar mode
00204                 modeAttributes = 0x1b;  // Color, graphics, no linear buffer
00205                 break;
00206         case M_LIN8:
00207                 if (!allow_vesa_8bpp || !allow_res) return VESA_FAIL;
00208                 pageSize = mblock->sheight * mblock->swidth;
00209                 var_write(&minfo.BytesPerScanLine,mblock->swidth);
00210                 var_write(&minfo.NumberOfPlanes,0x1);
00211                 var_write(&minfo.BitsPerPixel,8);
00212                 var_write(&minfo.MemoryModel,4);                //packed pixel
00213                 modeAttributes = 0x1b;  // Color, graphics
00214                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00215                 break;
00216         case M_LIN15:
00217                 if (!allow_vesa_15bpp || !allow_res) return VESA_FAIL;
00218                 pageSize = mblock->sheight * mblock->swidth*2;
00219                 var_write(&minfo.BytesPerScanLine,mblock->swidth*2);
00220                 var_write(&minfo.NumberOfPlanes,0x1);
00221                 var_write(&minfo.BitsPerPixel,15);
00222                 var_write(&minfo.MemoryModel,6);        //HiColour
00223                 var_write(&minfo.RedMaskSize,5);
00224                 var_write(&minfo.RedMaskPos,10);
00225                 var_write(&minfo.GreenMaskSize,5);
00226                 var_write(&minfo.GreenMaskPos,5);
00227                 var_write(&minfo.BlueMaskSize,5);
00228                 var_write(&minfo.BlueMaskPos,0);
00229                 var_write(&minfo.ReservedMaskSize,0x01);
00230                 var_write(&minfo.ReservedMaskPos,0x0f);
00231                 modeAttributes = 0x1b;  // Color, graphics
00232                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00233                 break;
00234         case M_LIN16:
00235                 if (!allow_vesa_16bpp || !allow_res) return VESA_FAIL;
00236                 pageSize = mblock->sheight * mblock->swidth*2;
00237                 var_write(&minfo.BytesPerScanLine,mblock->swidth*2);
00238                 var_write(&minfo.NumberOfPlanes,0x1);
00239                 var_write(&minfo.BitsPerPixel,16);
00240                 var_write(&minfo.MemoryModel,6);        //HiColour
00241                 var_write(&minfo.RedMaskSize,5);
00242                 var_write(&minfo.RedMaskPos,11);
00243                 var_write(&minfo.GreenMaskSize,6);
00244                 var_write(&minfo.GreenMaskPos,5);
00245                 var_write(&minfo.BlueMaskSize,5);
00246                 var_write(&minfo.BlueMaskPos,0);
00247                 modeAttributes = 0x1b;  // Color, graphics
00248                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00249                 break;
00250         case M_LIN24:
00251                 if (!allow_vesa_24bpp || !allow_res) return VESA_FAIL;
00252                 pageSize = mblock->sheight * mblock->swidth*3;
00253                 var_write(&minfo.BytesPerScanLine,mblock->swidth*3);
00254                 var_write(&minfo.NumberOfPlanes,0x1);
00255                 var_write(&minfo.BitsPerPixel,24);
00256                 var_write(&minfo.MemoryModel,6);        //HiColour
00257                 var_write(&minfo.RedMaskSize,8);
00258                 var_write(&minfo.RedMaskPos,0x10);
00259                 var_write(&minfo.GreenMaskSize,0x8);
00260                 var_write(&minfo.GreenMaskPos,0x8);
00261                 var_write(&minfo.BlueMaskSize,0x8);
00262                 var_write(&minfo.BlueMaskPos,0x0);
00263                 modeAttributes = 0x1b;  // Color, graphics
00264                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00265                 break;
00266         case M_LIN32:
00267                 if (!allow_vesa_32bpp || !allow_res) return VESA_FAIL;
00268                 pageSize = mblock->sheight * mblock->swidth*4;
00269                 var_write(&minfo.BytesPerScanLine,mblock->swidth*4);
00270                 var_write(&minfo.NumberOfPlanes,0x1);
00271                 var_write(&minfo.BitsPerPixel,32);
00272                 var_write(&minfo.MemoryModel,6);        //HiColour
00273                 var_write(&minfo.RedMaskSize,8);
00274                 var_write(&minfo.RedMaskPos,0x10);
00275                 var_write(&minfo.GreenMaskSize,0x8);
00276                 var_write(&minfo.GreenMaskPos,0x8);
00277                 var_write(&minfo.BlueMaskSize,0x8);
00278                 var_write(&minfo.BlueMaskPos,0x0);
00279                 var_write(&minfo.ReservedMaskSize,0x8);
00280                 var_write(&minfo.ReservedMaskPos,0x18);
00281                 modeAttributes = 0x1b;  // Color, graphics
00282                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00283                 break;
00284         case M_TEXT:
00285                 if (!allow_vesa_tty) return VESA_FAIL;
00286                 pageSize = 0;
00287                 var_write(&minfo.BytesPerScanLine, mblock->twidth * 2);
00288                 var_write(&minfo.NumberOfPlanes,0x4);
00289                 var_write(&minfo.BitsPerPixel,4);
00290                 var_write(&minfo.MemoryModel,0);        // text
00291                 modeAttributes = 0x0f;  //Color, text, bios output
00292                 break;
00293         default:
00294                 return VESA_FAIL;
00295         }
00296         if (pageSize & 0xFFFFu) {
00297                 // It is documented that many applications assume 64k-aligned page sizes
00298                 // VBETEST is one of them
00299                 pageSize +=  0xFFFFu;
00300                 pageSize &= ~0xFFFFu;
00301         }
00302         Bitu pages = 0;
00303         if (pageSize > vga.vmemsize) {
00304                 // mode not supported by current hardware configuration
00305                 modeAttributes &= ~0x1;
00306         } else if (pageSize) {
00307                 pages = (vga.vmemsize / pageSize)-1;
00308         }
00309         var_write(&minfo.NumberOfImagePages, pages);
00310         var_write(&minfo.ModeAttributes, modeAttributes);
00311         var_write(&minfo.WinAAttributes, 0x7);  // Exists/readable/writable
00312 
00313         if (mblock->type==M_TEXT) {
00314                 var_write(&minfo.WinGranularity,32);
00315                 var_write(&minfo.WinSize,32);
00316                 var_write(&minfo.WinASegment,0xb800);
00317                 var_write(&minfo.XResolution,mblock->twidth);
00318                 var_write(&minfo.YResolution,mblock->theight);
00319         } else {
00320                 var_write(&minfo.WinGranularity,64);
00321                 var_write(&minfo.WinSize,64);
00322                 var_write(&minfo.WinASegment,0xa000);
00323                 var_write(&minfo.XResolution,mblock->swidth);
00324                 var_write(&minfo.YResolution,mblock->sheight);
00325         }
00326         var_write(&minfo.WinFuncPtr,CALLBACK_RealPointer(callback.setwindow));
00327         var_write(&minfo.NumberOfBanks,0x1);
00328         var_write(&minfo.Reserved_page,0x1);
00329         var_write(&minfo.XCharSize,mblock->cwidth);
00330         var_write(&minfo.YCharSize,mblock->cheight);
00331         if (!int10.vesa_nolfb) var_write(&minfo.PhysBasePtr,S3_LFB_BASE + (hack_lfb_yadjust*(long)host_readw((HostPt)(&minfo.BytesPerScanLine))));
00332 
00333         MEM_BlockWrite(buf,&minfo,sizeof(MODE_INFO));
00334         return VESA_SUCCESS;
00335 }
00336 
00337 
00338 Bit8u VESA_SetSVGAMode(Bit16u mode) {
00339         if (INT10_SetVideoMode(mode)) {
00340                 int10.vesa_setmode=mode&0x7fff;
00341                 return VESA_SUCCESS;
00342         }
00343         return VESA_FAIL;
00344 }
00345 
00346 Bit8u VESA_GetSVGAMode(Bit16u & mode) {
00347         if (int10.vesa_setmode!=0xffff) mode=int10.vesa_setmode;
00348         else mode=CurMode->mode;
00349         return VESA_SUCCESS;
00350 }
00351 
00352 Bit8u VESA_SetCPUWindow(Bit8u window,Bit8u address) {
00353         if (window) return VESA_FAIL;
00354         if (((Bit32u)(address)*64*1024<vga.vmemsize)) {
00355                 IO_Write(0x3d4,0x6a);
00356                 IO_Write(0x3d5,(Bit8u)address);
00357                 return VESA_SUCCESS;
00358         } else return VESA_FAIL;
00359 }
00360 
00361 Bit8u VESA_GetCPUWindow(Bit8u window,Bit16u & address) {
00362         if (window) return VESA_FAIL;
00363         IO_Write(0x3d4,0x6a);
00364         address=IO_Read(0x3d5);
00365         return VESA_SUCCESS;
00366 }
00367 
00368 
00369 Bit8u VESA_SetPalette(PhysPt data,Bitu index,Bitu count) {
00370 //Structure is (vesa 3.0 doc): blue,green,red,alignment
00371         Bit8u r,g,b;
00372         if (index>255) return VESA_FAIL;
00373         if (index+count>256) return VESA_FAIL;
00374         IO_Write(0x3c8,(Bit8u)index);
00375         while (count) {
00376                 b = mem_readb(data++);
00377                 g = mem_readb(data++);
00378                 r = mem_readb(data++);
00379                 data++;
00380                 IO_Write(0x3c9,r);
00381                 IO_Write(0x3c9,g);
00382                 IO_Write(0x3c9,b);
00383                 count--;
00384         }
00385         return VESA_SUCCESS;
00386 }
00387 
00388 
00389 Bit8u VESA_GetPalette(PhysPt data,Bitu index,Bitu count) {
00390         Bit8u r,g,b;
00391         if (index>255) return VESA_FAIL;
00392         if (index+count>256) return VESA_FAIL;
00393         IO_Write(0x3c7,(Bit8u)index);
00394         while (count) {
00395                 r = IO_Read(0x3c9);
00396                 g = IO_Read(0x3c9);
00397                 b = IO_Read(0x3c9);
00398                 mem_writeb(data++,b);
00399                 mem_writeb(data++,g);
00400                 mem_writeb(data++,r);
00401                 data++;
00402                 count--;
00403         }
00404         return VESA_SUCCESS;
00405 }
00406 
00407 // maximum offset for the S3 Trio64 is 10 bits
00408 #define S3_MAX_OFFSET 0x3ff
00409 
00410 Bit8u VESA_ScanLineLength(Bit8u subcall,Bit16u val, Bit16u & bytes,Bit16u & pixels,Bit16u & lines) {
00411         // offset register: virtual scanline length
00412         Bitu pixels_per_offset;
00413         Bitu bytes_per_offset = 8;
00414         Bitu vmemsize = vga.vmemsize;
00415         Bitu new_offset = vga.config.scan_len;
00416         Bitu screen_height = CurMode->sheight;
00417 
00418         switch (CurMode->type) {
00419         case M_TEXT:
00420                 vmemsize = 0x8000;      // we have only the 32kB window here
00421                 screen_height = CurMode->theight;
00422                 pixels_per_offset = 16; // two characters each 8 pixels wide
00423                 bytes_per_offset = 4;   // 2 characters + 2 attributes
00424                 break;
00425         case M_LIN4:
00426                 pixels_per_offset = 16;
00427                 break;
00428         case M_LIN8:
00429                 pixels_per_offset = 8;
00430                 break;
00431         case M_LIN15:
00432         case M_LIN16:
00433                 pixels_per_offset = 4;
00434                 break;
00435         case M_LIN32:
00436                 pixels_per_offset = 2;
00437                 break;
00438         default:
00439                 return VESA_MODE_UNSUPPORTED;
00440         }
00441         switch (subcall) {
00442         case 0x00: // set scan length in pixels
00443                 new_offset = val / pixels_per_offset;
00444                 if (val % pixels_per_offset) new_offset++;
00445                 
00446                 if (new_offset > S3_MAX_OFFSET)
00447                         return VESA_HW_UNSUPPORTED; // scanline too long
00448                 vga.config.scan_len = new_offset;
00449                 VGA_CheckScanLength();
00450                 break;
00451 
00452         case 0x01: // get current scanline length
00453                 // implemented at the end of this function
00454                 break;
00455 
00456         case 0x02: // set scan length in bytes
00457                 new_offset = val / bytes_per_offset;
00458                 if (val % bytes_per_offset) new_offset++;
00459                 
00460                 if (new_offset > S3_MAX_OFFSET)
00461                         return VESA_HW_UNSUPPORTED; // scanline too long
00462                 vga.config.scan_len = new_offset;
00463                 VGA_CheckScanLength();
00464                 break;
00465 
00466         case 0x03: // get maximum scan line length
00467                 // the smaller of either the hardware maximum scanline length or
00468                 // the limit to get full y resolution of this mode
00469                 new_offset = S3_MAX_OFFSET;
00470                 if ((new_offset * bytes_per_offset * screen_height) > vmemsize)
00471                         new_offset = vmemsize / (bytes_per_offset * screen_height);
00472                 break;
00473 
00474         default:
00475                 return VESA_UNIMPLEMENTED;
00476         }
00477 
00478         // set up the return values
00479         bytes = (Bit16u)(new_offset * bytes_per_offset);
00480         pixels = (Bit16u)(new_offset * pixels_per_offset);
00481         if (!bytes)
00482                 // return failure on division by zero
00483                 // some real VESA BIOS implementations may crash here
00484                 return VESA_FAIL;
00485 
00486         lines = (Bit16u)(vmemsize / bytes);
00487         
00488         if (CurMode->type==M_TEXT)
00489                 lines *= CurMode->cheight;
00490 
00491         return VESA_SUCCESS;
00492 }
00493 
00494 Bit8u VESA_SetDisplayStart(Bit16u x,Bit16u y) {
00495         // TODO wait for retrace in case bl==0x80
00496         Bitu pixels_per_offset;
00497         Bitu panning_factor = 1;
00498 
00499         switch (CurMode->type) {
00500         case M_TEXT:
00501         case M_LIN4:
00502                 pixels_per_offset = 16;
00503                 break;
00504         case M_LIN8:
00505                 panning_factor = 2; // the panning register ignores bit0 in this mode
00506                 pixels_per_offset = 8;
00507                 break;
00508         case M_LIN15:
00509         case M_LIN16:
00510                 panning_factor = 2; // this may be DOSBox specific
00511                 pixels_per_offset = 4;
00512                 break;
00513         case M_LIN32:
00514                 pixels_per_offset = 2;
00515                 break;
00516         default:
00517                 return VESA_MODE_UNSUPPORTED;
00518         }
00519         // We would have to divide y by the character height for text modes and
00520         // write the remainder to the CRTC preset row scan register,
00521         // but VBE2 BIOSes that actually get that far also don't.
00522         // Only a VBE3 BIOS got it right.
00523         Bitu virtual_screen_width = vga.config.scan_len * pixels_per_offset;
00524         Bitu new_start_pixel = virtual_screen_width * y + x;
00525         Bitu new_crtc_start = new_start_pixel / (pixels_per_offset/2);
00526         Bitu new_panning = new_start_pixel % (pixels_per_offset/2);
00527         new_panning *= panning_factor;
00528 
00529         vga.config.display_start = new_crtc_start;
00530         
00531         // Setting the panning register is nice as it allows for super smooth
00532         // scrolling, but if we hit the retrace pulse there may be flicker as
00533         // panning and display start are latched at different times. 
00534 
00535         IO_Read(0x3da);              // reset attribute flipflop
00536         IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on
00537         IO_Write(0x3c0,new_panning);
00538 
00539         return VESA_SUCCESS;
00540 }
00541 
00542 Bit8u VESA_GetDisplayStart(Bit16u & x,Bit16u & y) {
00543         Bitu pixels_per_offset;
00544         Bitu panning_factor = 1;
00545 
00546         switch (CurMode->type) {
00547         case M_TEXT:
00548                 pixels_per_offset = 16;
00549                 break;
00550         case M_LIN4:
00551                 pixels_per_offset = 16;
00552                 break;
00553         case M_LIN8:
00554                 panning_factor = 2;
00555                 pixels_per_offset = 8;
00556                 break;
00557         case M_LIN15:
00558         case M_LIN16:
00559                 panning_factor = 2;
00560                 pixels_per_offset = 4;
00561                 break;
00562         case M_LIN32:
00563                 pixels_per_offset = 2;
00564                 break;
00565         default:
00566                 return VESA_MODE_UNSUPPORTED;
00567         }
00568 
00569         IO_Read(0x3da);              // reset attribute flipflop
00570         IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on
00571         Bit8u panning = IO_Read(0x3c1);
00572 
00573         Bitu virtual_screen_width = vga.config.scan_len * pixels_per_offset;
00574         Bitu start_pixel = vga.config.display_start * (pixels_per_offset/2) 
00575                 + panning / panning_factor;
00576         
00577         y = start_pixel / virtual_screen_width;
00578         x = start_pixel % virtual_screen_width;
00579         return VESA_SUCCESS;
00580 }
00581 
00582 static Bitu VESA_SetWindow(void) {
00583         if (reg_bh) reg_ah=VESA_GetCPUWindow(reg_bl,reg_dx);
00584         else reg_ah=VESA_SetCPUWindow(reg_bl,(Bit8u)reg_dx);
00585         reg_al=0x4f;
00586         return 0;
00587 }
00588 
00589 static Bitu VESA_PMSetWindow(void) {
00590         VESA_SetCPUWindow(0,(Bit8u)reg_dx);
00591         return 0;
00592 }
00593 static Bitu VESA_PMSetPalette(void) {
00594         VESA_SetPalette(SegPhys(es) +  reg_edi, reg_dx, reg_cx );
00595         return 0;
00596 }
00597 static Bitu VESA_PMSetStart(void) {
00598         // This function is from VBE2 and directly sets the VGA
00599         // display start address.
00600 
00601         // TODO wait for retrace in case bl==0x80
00602         Bit32u start = (Bit32u)(((unsigned int)reg_dx << 16u) | (unsigned int)reg_cx);
00603         vga.config.display_start = start;
00604         return 0;
00605 }
00606 
00607 
00608 
00609 extern int vesa_modelist_cap;
00610 
00611 void INT10_SetupVESA(void) {
00612         /* BUGFIX: Generating VESA BIOS data when machine=ega or machine=vgaonly is dumb.
00613          * Stop wasting ROM space! --J.C. */
00614         if (machine != MCH_VGA) return;
00615         if (svgaCard == SVGA_None) return;
00616 
00617         /* Put the mode list somewhere in memory */
00618         Bitu i,modecount=0;
00619         i=0;
00620         int10.rom.vesa_modes=RealMake(0xc000,int10.rom.used);
00621 //TODO Maybe add normal vga modes too, but only seems to complicate things
00622         while (ModeList_VGA[i].mode!=0xffff) {
00623                 bool canuse_mode=false;
00624 
00625                 /* Hack for VBE 1.2 modes and 24/32bpp ambiguity */
00626                 if (ModeList_VGA[i].mode >= 0x100 && ModeList_VGA[i].mode <= 0x11F &&
00627                         ((ModeList_VGA[i].type == M_LIN32 && !vesa12_modes_32bpp) ||
00628                          (ModeList_VGA[i].type == M_LIN24 && vesa12_modes_32bpp))) {
00629                         /* ignore */
00630                 }
00631                 else {
00632                         if (!svga.accepts_mode)
00633                                 canuse_mode=true;
00634                         else if (svga.accepts_mode(ModeList_VGA[i].mode))
00635                                 canuse_mode=true;
00636 
00637                         if (canuse_mode) {
00638                                 if (ModeList_VGA[i].mode >= 0x100) {
00639                                         bool allow_res = allow_vesa_lowres_modes ||
00640                                                 (ModeList_VGA[i].swidth >= 640 && ModeList_VGA[i].sheight >= 400);
00641 
00642                                         switch (ModeList_VGA[i].type) {
00643                                                 case M_LIN32:   canuse_mode=allow_vesa_32bpp && allow_res; break;
00644                                                 case M_LIN24:   canuse_mode=allow_vesa_24bpp && allow_res; break;
00645                                                 case M_LIN16:   canuse_mode=allow_vesa_16bpp && allow_res; break;
00646                                                 case M_LIN15:   canuse_mode=allow_vesa_15bpp && allow_res; break;
00647                                                 case M_LIN8:    canuse_mode=allow_vesa_8bpp && allow_res; break;
00648                                                 case M_LIN4:    canuse_mode=allow_vesa_4bpp; break;
00649                                                 case M_TEXT:    canuse_mode=allow_vesa_tty; break;
00650                                                 default:        break;
00651                                         }
00652                                 }
00653                         }
00654                 }
00655 
00656                 if (canuse_mode && vesa_modelist_cap > 0 && (unsigned int)modecount >= (unsigned int)vesa_modelist_cap)
00657                         canuse_mode = false;
00658 
00659                 if (ModeList_VGA[i].type != M_TEXT) {
00660                         if (canuse_mode && vesa_mode_width_cap > 0 && (unsigned int)ModeList_VGA[i].swidth > (unsigned int)vesa_mode_width_cap)
00661                                 canuse_mode = false;
00662 
00663                         if (canuse_mode && vesa_mode_height_cap > 0 && (unsigned int)ModeList_VGA[i].sheight > (unsigned int)vesa_mode_height_cap)
00664                                 canuse_mode = false;
00665                 }
00666 
00667                 if (ModeList_VGA[i].mode>=0x100 && canuse_mode) {
00668                         if ((!int10.vesa_oldvbe) || (ModeList_VGA[i].mode<0x120)) {
00669                                 phys_writew(PhysMake(0xc000,int10.rom.used),ModeList_VGA[i].mode);
00670                                 int10.rom.used+=2;
00671                                 modecount++;
00672                         }
00673                 }
00674 
00675                 i++;
00676         }
00677         phys_writew(PhysMake(0xc000,int10.rom.used),0xffff);
00678         int10.rom.used+=2;
00679         int10.rom.oemstring=RealMake(0xc000,int10.rom.used);
00680         Bitu len=(Bitu)(strlen(string_oem)+1);
00681         for (i=0;i<len;i++) {
00682                 phys_writeb(0xc0000u+(int10.rom.used++),(Bit8u)string_oem[i]);
00683         }
00684         callback.setwindow=CALLBACK_Allocate();
00685         CALLBACK_Setup(callback.setwindow,VESA_SetWindow,CB_RETF, "VESA Real Set Window");
00686         /* Prepare the pmode interface */
00687         int10.rom.pmode_interface=RealMake(0xc000,int10.rom.used);
00688         int10.rom.used += 8;            //Skip the byte later used for offsets
00689         /* PM Set Window call */
00690         int10.rom.pmode_interface_window = int10.rom.used - RealOff( int10.rom.pmode_interface );
00691         phys_writew( Real2Phys(int10.rom.pmode_interface) + 0, int10.rom.pmode_interface_window );
00692         callback.pmWindow=CALLBACK_Allocate();
00693         int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmWindow, VESA_PMSetWindow, CB_RETN, PhysMake(0xc000,int10.rom.used), "VESA PM Set Window");
00694         /* PM Set start call */
00695         int10.rom.pmode_interface_start = int10.rom.used - RealOff( int10.rom.pmode_interface );
00696         phys_writew( Real2Phys(int10.rom.pmode_interface) + 2, int10.rom.pmode_interface_start);
00697         callback.pmStart=CALLBACK_Allocate();
00698         int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmStart, VESA_PMSetStart, CB_VESA_START, PhysMake(0xc000,int10.rom.used), "VESA PM Set Start");
00699         /* PM Set Palette call */
00700         int10.rom.pmode_interface_palette = int10.rom.used - RealOff( int10.rom.pmode_interface );
00701         phys_writew( Real2Phys(int10.rom.pmode_interface) + 4, int10.rom.pmode_interface_palette);
00702         callback.pmPalette=CALLBACK_Allocate();
00703         int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmPalette, VESA_PMSetPalette, CB_RETN, PhysMake(0xc000,int10.rom.used), "VESA PM Set Palette");
00704         /* Finalize the size and clear the required ports pointer */
00705         phys_writew( Real2Phys(int10.rom.pmode_interface) + 6, 0);
00706         int10.rom.pmode_interface_size=int10.rom.used - RealOff( int10.rom.pmode_interface );
00707 }
00708