DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/ints/int10_vesa.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  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 along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "render.h"
00030 #include "dos_inc.h"
00031 
00032 int hack_lfb_yadjust = 0;
00033 
00034 bool vesa_zero_on_get_information = true;
00035 
00036 extern int vesa_mode_width_cap;
00037 extern int vesa_mode_height_cap;
00038 extern bool enable_vga_8bit_dac;
00039 extern bool allow_hd_vesa_modes;
00040 extern bool allow_unusual_vesa_modes;
00041 extern bool allow_explicit_vesa_24bpp;
00042 extern bool allow_vesa_lowres_modes;
00043 extern bool allow_vesa_4bpp_packed;
00044 extern bool vesa12_modes_32bpp;
00045 extern bool allow_vesa_32bpp;
00046 extern bool allow_vesa_24bpp;
00047 extern bool allow_vesa_16bpp;
00048 extern bool allow_vesa_15bpp;
00049 extern bool allow_vesa_8bpp;
00050 extern bool allow_vesa_4bpp;
00051 extern bool allow_vesa_tty;
00052 extern bool vga_8bit_dac;
00053 
00054 #define VESA_SUCCESS          0x00
00055 #define VESA_FAIL             0x01
00056 #define VESA_HW_UNSUPPORTED   0x02
00057 #define VESA_MODE_UNSUPPORTED 0x03
00058 // internal definition to pass to the caller
00059 #define VESA_UNIMPLEMENTED    0xFF
00060 
00061 static struct {
00062         Bitu rmWindow;
00063         Bitu pmStart;
00064         Bitu pmWindow;
00065         Bitu pmPalette;
00066 } callback = {0};
00067 
00068 void CALLBACK_DeAllocate(Bitu in);
00069 
00070 static char string_oem[]="S3 Incorporated. Trio64";
00071 static char string_vendorname[]="DOSBox Development Team";
00072 static char string_productname[]="DOSBox - The DOS Emulator";
00073 static char string_productrev[]="2";
00074 
00075 #ifdef _MSC_VER
00076 #pragma pack (1)
00077 #endif
00078 struct MODE_INFO{
00079         Bit16u ModeAttributes;
00080         Bit8u WinAAttributes;
00081         Bit8u WinBAttributes;
00082         Bit16u WinGranularity;
00083         Bit16u WinSize;
00084         Bit16u WinASegment;
00085         Bit16u WinBSegment;
00086         Bit32u WinFuncPtr;
00087         Bit16u BytesPerScanLine;
00088         Bit16u XResolution;
00089         Bit16u YResolution;
00090         Bit8u XCharSize;
00091         Bit8u YCharSize;
00092         Bit8u NumberOfPlanes;
00093         Bit8u BitsPerPixel;
00094         Bit8u NumberOfBanks;
00095         Bit8u MemoryModel;
00096         Bit8u BankSize;
00097         Bit8u NumberOfImagePages;
00098         Bit8u Reserved_page;
00099         Bit8u RedMaskSize;
00100         Bit8u RedMaskPos;
00101         Bit8u GreenMaskSize;
00102         Bit8u GreenMaskPos;
00103         Bit8u BlueMaskSize;
00104         Bit8u BlueMaskPos;
00105         Bit8u ReservedMaskSize;
00106         Bit8u ReservedMaskPos;
00107         Bit8u DirectColorModeInfo;
00108         Bit32u PhysBasePtr;
00109         Bit32u OffScreenMemOffset;
00110         Bit16u OffScreenMemSize;
00111         Bit8u Reserved[206];
00112 } GCC_ATTRIBUTE(packed);
00113 #ifdef _MSC_VER
00114 #pragma pack()
00115 #endif
00116 
00117 void VESA_OnReset_Clear_Callbacks(void) {
00118     if (callback.rmWindow != 0) {
00119         CALLBACK_DeAllocate(callback.rmWindow);
00120         callback.rmWindow = 0;
00121     }
00122     if (callback.pmPalette != 0) {
00123         CALLBACK_DeAllocate(callback.pmPalette);
00124         callback.pmPalette = 0;
00125     }
00126     if (callback.pmStart != 0) {
00127         CALLBACK_DeAllocate(callback.pmStart);
00128         callback.pmStart = 0;
00129     }
00130     if (callback.pmWindow != 0) {
00131         CALLBACK_DeAllocate(callback.pmWindow);
00132         callback.pmWindow = 0;
00133     }
00134 }
00135 
00136 extern bool vesa_bios_modelist_in_info;
00137 
00138 Bit8u VESA_GetSVGAInformation(Bit16u seg,Bit16u off) {
00139         /* Fill 256 byte buffer with VESA information */
00140         PhysPt buffer=PhysMake(seg,off);
00141         Bitu i;
00142         bool vbe2=false;Bit16u vbe2_pos;
00143         Bitu id=mem_readd(buffer);
00144         if (((id==0x56424532)||(id==0x32454256)) && (!int10.vesa_oldvbe)) vbe2=true;
00145 
00146     /* The reason this is an option is that there are some old DOS games that assume the BIOS
00147      * fills in only the structure members. These games do not provide enough room for the
00148      * full 256-byte block. If we zero the entire block, unrelated data next to the buffer
00149      * gets wiped and the game crashes. */
00150     if (vesa_zero_on_get_information) {
00151         if (vbe2) {
00152             for (i=0;i<0x200;i++) mem_writeb((PhysPt)(buffer+i),0);             
00153         } else {
00154             for (i=0;i<0x100;i++) mem_writeb((PhysPt)(buffer+i),0);
00155         }
00156     }
00157 
00158         /* Fill common data */
00159         MEM_BlockWrite(buffer,(void *)"VESA",4);                                //Identification
00160         if (!int10.vesa_oldvbe) mem_writew(buffer+0x04,0x200);  //Vesa version 2.0
00161         else mem_writew(buffer+0x04,0x102);                                             //Vesa version 1.2
00162         if (vbe2) {
00163         vbe2_pos=256+off;
00164 
00165                 mem_writed(buffer+0x06,RealMake(seg,vbe2_pos));
00166                 for (i=0;i<sizeof(string_oem);i++) real_writeb(seg,vbe2_pos++,(Bit8u)string_oem[i]);
00167                 mem_writew(buffer+0x14,0x200);                                  //VBE 2 software revision
00168                 mem_writed(buffer+0x16,RealMake(seg,vbe2_pos));
00169                 for (i=0;i<sizeof(string_vendorname);i++) real_writeb(seg,vbe2_pos++,(Bit8u)string_vendorname[i]);
00170                 mem_writed(buffer+0x1a,RealMake(seg,vbe2_pos));
00171                 for (i=0;i<sizeof(string_productname);i++) real_writeb(seg,vbe2_pos++,(Bit8u)string_productname[i]);
00172                 mem_writed(buffer+0x1e,RealMake(seg,vbe2_pos));
00173                 for (i=0;i<sizeof(string_productrev);i++) real_writeb(seg,vbe2_pos++,(Bit8u)string_productrev[i]);
00174     } else {
00175         vbe2_pos=0x20+off;
00176 
00177         mem_writed(buffer+0x06,int10.rom.oemstring);    //Oemstring
00178         }
00179 
00180     if (vesa_bios_modelist_in_info) {
00181         /* put the modelist into the VBE struct itself, as modern BIOSes like to do.
00182          * NOTICE: This limits the modelist to what is able to fit! Extended modes may not fit, which is why the option is OFF by default. */
00183         uint16_t modesg = int10.rom.vesa_modes >> 16;
00184         uint16_t modoff = int10.rom.vesa_modes & 0xFFFF;
00185 
00186         mem_writed(buffer+0x0e,RealMake(seg,vbe2_pos)); //VESA Mode list
00187 
00188         do {
00189             if (vbe2) {
00190                 if (vbe2_pos >= (509+off)) break;
00191             }
00192             else {
00193                 if (vbe2_pos >= (253+off)) break;
00194             }
00195             uint16_t m = real_readw(modesg,modoff);
00196             if (m == 0xFFFF) break;
00197             real_writew(seg,vbe2_pos,m);
00198             vbe2_pos += 2;
00199             modoff += 2;
00200         } while (1);
00201         real_writew(seg,vbe2_pos,0xFFFF);
00202     }
00203     else {
00204         mem_writed(buffer+0x0e,int10.rom.vesa_modes);   //VESA Mode list
00205     }
00206 
00207         mem_writed(buffer+0x0a,(enable_vga_8bit_dac ? 1 : 0));          //Capabilities and flags
00208         mem_writew(buffer+0x12,(Bit16u)(vga.mem.memsize/(64*1024))); // memory size in 64kb blocks
00209         return VESA_SUCCESS;
00210 }
00211 
00212 Bit8u VESA_GetSVGAModeInformation(Bit16u mode,Bit16u seg,Bit16u off) {
00213         MODE_INFO minfo;
00214         memset(&minfo,0,sizeof(minfo));
00215         PhysPt buf=PhysMake(seg,off);
00216         Bitu pageSize;
00217         Bit8u modeAttributes;
00218         Bitu i=0;
00219 
00220         mode&=0x3fff;   // vbe2 compatible, ignore lfb and keep screen content bits
00221         if (mode<0x100) return 0x01;
00222         while (ModeList_VGA[i].mode!=0xffff) {
00223                 /* Hack for VBE 1.2 modes and 24/32bpp ambiguity */
00224                 if (ModeList_VGA[i].mode >= 0x100 && ModeList_VGA[i].mode <= 0x11F &&
00225             !(ModeList_VGA[i].special & _USER_MODIFIED) &&
00226                         ((ModeList_VGA[i].type == M_LIN32 && !vesa12_modes_32bpp) ||
00227                          (ModeList_VGA[i].type == M_LIN24 && vesa12_modes_32bpp))) {
00228                         /* ignore */
00229                         i++;
00230                 }
00231         /* ignore deleted modes */
00232         else if (ModeList_VGA[i].type == M_ERROR) {
00233             /* ignore */
00234             i++;
00235         }
00236                 else if (mode==ModeList_VGA[i].mode)
00237                         goto foundit;
00238                 else
00239                         i++;
00240         }
00241         return VESA_FAIL;
00242 foundit:
00243         if ((int10.vesa_oldvbe) && (ModeList_VGA[i].mode>=0x120)) return 0x01;
00244         VideoModeBlock * mblock=&ModeList_VGA[i];
00245 
00246     /* Don't allow querying modes the SVGA card does not accept,
00247      * unless the user modified the mode. */
00248     if (svga.accepts_mode && !(mblock->special & _USER_MODIFIED)) {
00249                 if (!svga.accepts_mode(mode)) return 0x01;
00250         }
00251 
00252     /* do not return information on deleted modes */
00253     if (mblock->type == M_ERROR) return 0x01;
00254 
00255         bool allow_res = allow_vesa_lowres_modes ||
00256                 (ModeList_VGA[i].swidth >= 640 && ModeList_VGA[i].sheight >= 400);
00257 
00258         switch (mblock->type) {
00259         case M_PACKED4:
00260                 if (!allow_vesa_4bpp_packed) return VESA_FAIL;//TODO: New option to disable
00261                 pageSize = mblock->sheight * mblock->swidth/2;
00262                 var_write(&minfo.BytesPerScanLine,(Bit16u)((((mblock->swidth+15U)/8U)&(~1U))*4)); /* NTS: 4bpp requires even value due to VGA registers, round up */
00263                 var_write(&minfo.NumberOfPlanes,0x1);
00264                 var_write(&minfo.BitsPerPixel,4);
00265                 var_write(&minfo.MemoryModel,4);        //packed pixel
00266                 modeAttributes = 0x1b;  // Color, graphics
00267                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00268                 break;
00269         case M_LIN4:
00270                 if (!allow_vesa_4bpp) return VESA_FAIL;
00271                 pageSize = mblock->sheight * mblock->swidth/2;
00272                 var_write(&minfo.BytesPerScanLine,(Bit16u)(((mblock->swidth+15U)/8U)&(~1U))); /* NTS: 4bpp requires even value due to VGA registers, round up */
00273                 var_write(&minfo.NumberOfPlanes,0x4);
00274                 var_write(&minfo.BitsPerPixel,4);//FIXME: Shouldn't this say 4 planes, 1 bit per pixel??
00275                 var_write(&minfo.MemoryModel,3);        //ega planar mode
00276                 modeAttributes = 0x1b;  // Color, graphics, no linear buffer
00277                 break;
00278         case M_LIN8:
00279                 if (!allow_vesa_8bpp || !allow_res) return VESA_FAIL;
00280                 pageSize = mblock->sheight * mblock->swidth;
00281                 var_write(&minfo.BytesPerScanLine,(Bit16u)mblock->swidth);
00282                 var_write(&minfo.NumberOfPlanes,0x1);
00283                 var_write(&minfo.BitsPerPixel,8);
00284                 var_write(&minfo.MemoryModel,4);                //packed pixel
00285                 modeAttributes = 0x1b;  // Color, graphics
00286                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00287                 break;
00288         case M_LIN15:
00289                 if (!allow_vesa_15bpp || !allow_res) return VESA_FAIL;
00290                 pageSize = mblock->sheight * mblock->swidth*2;
00291                 var_write(&minfo.BytesPerScanLine,(Bit16u)(mblock->swidth*2));
00292                 var_write(&minfo.NumberOfPlanes,0x1);
00293                 var_write(&minfo.BitsPerPixel,15);
00294                 var_write(&minfo.MemoryModel,6);        //HiColour
00295                 var_write(&minfo.RedMaskSize,5);
00296                 var_write(&minfo.RedMaskPos,10);
00297                 var_write(&minfo.GreenMaskSize,5);
00298                 var_write(&minfo.GreenMaskPos,5);
00299                 var_write(&minfo.BlueMaskSize,5);
00300                 var_write(&minfo.BlueMaskPos,0);
00301                 var_write(&minfo.ReservedMaskSize,0x01);
00302                 var_write(&minfo.ReservedMaskPos,0x0f);
00303                 modeAttributes = 0x1b;  // Color, graphics
00304                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00305                 break;
00306         case M_LIN16:
00307                 if (!allow_vesa_16bpp || !allow_res) return VESA_FAIL;
00308                 pageSize = mblock->sheight * mblock->swidth*2;
00309                 var_write(&minfo.BytesPerScanLine,(Bit16u)(mblock->swidth*2));
00310                 var_write(&minfo.NumberOfPlanes,0x1);
00311                 var_write(&minfo.BitsPerPixel,16);
00312                 var_write(&minfo.MemoryModel,6);        //HiColour
00313                 var_write(&minfo.RedMaskSize,5);
00314                 var_write(&minfo.RedMaskPos,11);
00315                 var_write(&minfo.GreenMaskSize,6);
00316                 var_write(&minfo.GreenMaskPos,5);
00317                 var_write(&minfo.BlueMaskSize,5);
00318                 var_write(&minfo.BlueMaskPos,0);
00319                 modeAttributes = 0x1b;  // Color, graphics
00320                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00321                 break;
00322         case M_LIN24:
00323                 if (!allow_vesa_24bpp || !allow_res) return VESA_FAIL;
00324         if (mode >= 0x120 && !allow_explicit_vesa_24bpp) return VESA_FAIL;
00325                 pageSize = mblock->sheight * mblock->swidth*3;
00326                 var_write(&minfo.BytesPerScanLine,(Bit16u)(mblock->swidth*3));
00327                 var_write(&minfo.NumberOfPlanes,0x1);
00328                 var_write(&minfo.BitsPerPixel,24);
00329                 var_write(&minfo.MemoryModel,6);        //HiColour
00330                 var_write(&minfo.RedMaskSize,8);
00331                 var_write(&minfo.RedMaskPos,0x10);
00332                 var_write(&minfo.GreenMaskSize,0x8);
00333                 var_write(&minfo.GreenMaskPos,0x8);
00334                 var_write(&minfo.BlueMaskSize,0x8);
00335                 var_write(&minfo.BlueMaskPos,0x0);
00336                 modeAttributes = 0x1b;  // Color, graphics
00337                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00338                 break;
00339         case M_LIN32:
00340                 if (!allow_vesa_32bpp || !allow_res) return VESA_FAIL;
00341                 pageSize = mblock->sheight * mblock->swidth*4;
00342                 var_write(&minfo.BytesPerScanLine,(Bit16u)(mblock->swidth*4));
00343                 var_write(&minfo.NumberOfPlanes,0x1);
00344                 var_write(&minfo.BitsPerPixel,32);
00345                 var_write(&minfo.MemoryModel,6);        //HiColour
00346                 var_write(&minfo.RedMaskSize,8);
00347                 var_write(&minfo.RedMaskPos,0x10);
00348                 var_write(&minfo.GreenMaskSize,0x8);
00349                 var_write(&minfo.GreenMaskPos,0x8);
00350                 var_write(&minfo.BlueMaskSize,0x8);
00351                 var_write(&minfo.BlueMaskPos,0x0);
00352                 var_write(&minfo.ReservedMaskSize,0x8);
00353                 var_write(&minfo.ReservedMaskPos,0x18);
00354                 modeAttributes = 0x1b;  // Color, graphics
00355                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00356                 break;
00357         case M_TEXT:
00358                 if (!allow_vesa_tty) return VESA_FAIL;
00359                 pageSize = 0;
00360                 var_write(&minfo.BytesPerScanLine, (Bit16u)(mblock->twidth * 2));
00361                 var_write(&minfo.NumberOfPlanes,0x4);
00362                 var_write(&minfo.BitsPerPixel,4);
00363                 var_write(&minfo.MemoryModel,0);        // text
00364                 modeAttributes = 0x0f;  //Color, text, bios output
00365                 break;
00366         default:
00367                 return VESA_FAIL;
00368         }
00369         if (pageSize & 0xFFFFu) {
00370                 // It is documented that many applications assume 64k-aligned page sizes
00371                 // VBETEST is one of them
00372                 pageSize +=  0xFFFFu;
00373                 pageSize &= ~0xFFFFu;
00374         }
00375         Bitu pages = 0;
00376         if (pageSize > vga.mem.memsize || (mblock->special & _USER_DISABLED)) {
00377                 // mode not supported by current hardware configuration
00378                 modeAttributes &= ~0x1;
00379         } else if (pageSize) {
00380                 pages = (vga.mem.memsize / pageSize)-1;
00381         }
00382         var_write(&minfo.NumberOfImagePages, (Bit8u)pages);
00383         var_write(&minfo.ModeAttributes, modeAttributes);
00384         var_write(&minfo.WinAAttributes, 0x7);  // Exists/readable/writable
00385 
00386         if (mblock->type==M_TEXT) {
00387                 var_write(&minfo.WinGranularity,32);
00388                 var_write(&minfo.WinSize,32);
00389                 var_write(&minfo.WinASegment,(Bit16u)0xb800);
00390                 var_write(&minfo.XResolution,(Bit16u)mblock->twidth);
00391                 var_write(&minfo.YResolution,(Bit16u)mblock->theight);
00392         } else {
00393                 var_write(&minfo.WinGranularity,64);
00394                 var_write(&minfo.WinSize,64);
00395                 var_write(&minfo.WinASegment,(Bit16u)0xa000);
00396                 var_write(&minfo.XResolution,(Bit16u)mblock->swidth);
00397                 var_write(&minfo.YResolution,(Bit16u)mblock->sheight);
00398         }
00399         var_write(&minfo.WinFuncPtr,int10.rom.set_window);
00400         var_write(&minfo.NumberOfBanks,0x1);
00401         var_write(&minfo.Reserved_page,0x1);
00402         var_write(&minfo.XCharSize,(Bit8u)mblock->cwidth);
00403         var_write(&minfo.YCharSize,(Bit8u)mblock->cheight);
00404         if (!int10.vesa_nolfb) var_write(&minfo.PhysBasePtr,S3_LFB_BASE + (hack_lfb_yadjust*(long)host_readw((HostPt)(&minfo.BytesPerScanLine))));
00405 
00406         MEM_BlockWrite(buf,&minfo,sizeof(MODE_INFO));
00407         return VESA_SUCCESS;
00408 }
00409 
00410 
00411 Bit8u VESA_SetSVGAMode(Bit16u mode) {
00412         if (INT10_SetVideoMode(mode)) {
00413                 int10.vesa_setmode=mode&0x7fff;
00414                 return VESA_SUCCESS;
00415         }
00416         return VESA_FAIL;
00417 }
00418 
00419 Bit8u VESA_GetSVGAMode(Bit16u & mode) {
00420         if (int10.vesa_setmode!=0xffff) mode=int10.vesa_setmode;
00421         else mode=CurMode->mode;
00422         return VESA_SUCCESS;
00423 }
00424 
00425 Bit8u VESA_SetCPUWindow(Bit8u window,Bit8u address) {
00426         if (window) return VESA_FAIL;
00427         if ((Bit32u)(address)*64*1024<vga.mem.memsize) {
00428                 IO_Write(0x3d4,0x6a);
00429                 IO_Write(0x3d5,(Bit8u)address);
00430                 return VESA_SUCCESS;
00431         } else return VESA_FAIL;
00432 }
00433 
00434 Bit8u VESA_GetCPUWindow(Bit8u window,Bit16u & address) {
00435         if (window) return VESA_FAIL;
00436         IO_Write(0x3d4,0x6a);
00437         address=IO_Read(0x3d5);
00438         return VESA_SUCCESS;
00439 }
00440 
00441 
00442 Bit8u VESA_SetPalette(PhysPt data,Bitu index,Bitu count,bool wait) {
00443 //Structure is (vesa 3.0 doc): blue,green,red,alignment
00444         if (index>255) return VESA_FAIL;
00445         if (index+count>256) return VESA_FAIL;
00446         
00447         // Wait for retrace if requested
00448         if (wait) CALLBACK_RunRealFar(RealSeg(int10.rom.wait_retrace),RealOff(int10.rom.wait_retrace));
00449         
00450         IO_Write(0x3c8,(Bit8u)index);
00451         while (count) {
00452                 Bit8u b = mem_readb(data++);
00453                 Bit8u g = mem_readb(data++);
00454                 Bit8u r = mem_readb(data++);
00455                 data++;
00456                 IO_Write(0x3c9,r);
00457                 IO_Write(0x3c9,g);
00458                 IO_Write(0x3c9,b);
00459                 count--;
00460         }
00461         return VESA_SUCCESS;
00462 }
00463 
00464 
00465 Bit8u VESA_GetPalette(PhysPt data,Bitu index,Bitu count) {
00466         if (index>255) return VESA_FAIL;
00467         if (index+count>256) return VESA_FAIL;
00468         IO_Write(0x3c7,(Bit8u)index);
00469         while (count) {
00470                 Bit8u r = IO_Read(0x3c9);
00471                 Bit8u g = IO_Read(0x3c9);
00472                 Bit8u b = IO_Read(0x3c9);
00473                 mem_writeb(data++,b);
00474                 mem_writeb(data++,g);
00475                 mem_writeb(data++,r);
00476                 data++;
00477                 count--;
00478         }
00479         return VESA_SUCCESS;
00480 }
00481 
00482 // maximum offset for the S3 Trio64 is 10 bits
00483 #define S3_MAX_OFFSET 0x3ff
00484 
00485 Bit8u VESA_ScanLineLength(Bit8u subcall,Bit16u val, Bit16u & bytes,Bit16u & pixels,Bit16u & lines) {
00486         // offset register: virtual scanline length
00487         Bitu pixels_per_offset;
00488         Bitu bytes_per_offset = 8;
00489         Bitu vmemsize = vga.mem.memsize;
00490         Bitu new_offset = vga.config.scan_len;
00491         Bitu screen_height = CurMode->sheight;
00492 
00493         switch (CurMode->type) {
00494         case M_TEXT:
00495                 vmemsize = 0x8000;      // we have only the 32kB window here
00496                 screen_height = CurMode->theight;
00497                 pixels_per_offset = 16; // two characters each 8 pixels wide
00498                 bytes_per_offset = 4;   // 2 characters + 2 attributes
00499                 break;
00500         case M_LIN4:
00501         case M_PACKED4:
00502                 pixels_per_offset = 16;
00503                 break;
00504         case M_LIN8:
00505                 pixels_per_offset = 8;
00506                 break;
00507         case M_LIN15:
00508         case M_LIN16:
00509                 pixels_per_offset = 4;
00510                 break;
00511         case M_LIN32:
00512                 pixels_per_offset = 2;
00513                 break;
00514         default:
00515                 return VESA_MODE_UNSUPPORTED;
00516         }
00517         switch (subcall) {
00518         case 0x00: // set scan length in pixels
00519                 new_offset = val / pixels_per_offset;
00520                 if (val % pixels_per_offset) new_offset++;
00521                 
00522                 if (new_offset > S3_MAX_OFFSET)
00523                         return VESA_HW_UNSUPPORTED; // scanline too long
00524                 vga.config.scan_len = new_offset;
00525                 VGA_CheckScanLength();
00526                 break;
00527 
00528         case 0x01: // get current scanline length
00529                 // implemented at the end of this function
00530                 break;
00531 
00532         case 0x02: // set scan length in bytes
00533                 new_offset = val / bytes_per_offset;
00534                 if (val % bytes_per_offset) new_offset++;
00535                 
00536                 if (new_offset > S3_MAX_OFFSET)
00537                         return VESA_HW_UNSUPPORTED; // scanline too long
00538                 vga.config.scan_len = new_offset;
00539                 VGA_CheckScanLength();
00540                 break;
00541 
00542         case 0x03: // get maximum scan line length
00543                 // the smaller of either the hardware maximum scanline length or
00544                 // the limit to get full y resolution of this mode
00545                 new_offset = S3_MAX_OFFSET;
00546                 if ((new_offset * bytes_per_offset * screen_height) > vmemsize)
00547                         new_offset = vmemsize / (bytes_per_offset * screen_height);
00548                 break;
00549 
00550         default:
00551                 return VESA_UNIMPLEMENTED;
00552         }
00553 
00554         // set up the return values
00555         bytes = (Bit16u)(new_offset * bytes_per_offset);
00556         pixels = (Bit16u)(new_offset * pixels_per_offset);
00557         if (!bytes)
00558                 // return failure on division by zero
00559                 // some real VESA BIOS implementations may crash here
00560                 return VESA_FAIL;
00561 
00562         lines = (Bit16u)(vmemsize / bytes);
00563         
00564         if (CurMode->type==M_TEXT)
00565                 lines *= (Bit16u)(CurMode->cheight);
00566 
00567         return VESA_SUCCESS;
00568 }
00569 
00570 Bit8u VESA_SetDisplayStart(Bit16u x,Bit16u y,bool wait) {
00571         Bitu pixels_per_offset;
00572         Bitu panning_factor = 1;
00573 
00574         switch (CurMode->type) {
00575         case M_TEXT:
00576         case M_LIN4:
00577         case M_PACKED4:
00578                 pixels_per_offset = 16;
00579                 break;
00580         case M_LIN8:
00581                 panning_factor = 2; // the panning register ignores bit0 in this mode
00582                 pixels_per_offset = 8;
00583                 break;
00584         case M_LIN15:
00585         case M_LIN16:
00586                 panning_factor = 2; // this may be DOSBox specific
00587                 pixels_per_offset = 4;
00588                 break;
00589         case M_LIN32:
00590                 pixels_per_offset = 2;
00591                 break;
00592         default:
00593                 return VESA_MODE_UNSUPPORTED;
00594         }
00595         // We would have to divide y by the character height for text modes and
00596         // write the remainder to the CRTC preset row scan register,
00597         // but VBE2 BIOSes that actually get that far also don't.
00598         // Only a VBE3 BIOS got it right.
00599         Bitu virtual_screen_width = vga.config.scan_len * pixels_per_offset;
00600         Bitu new_start_pixel = virtual_screen_width * y + x;
00601         Bitu new_crtc_start = new_start_pixel / (pixels_per_offset/2);
00602         Bitu new_panning = new_start_pixel % (pixels_per_offset/2);
00603         new_panning *= panning_factor;
00604 
00605         vga.config.display_start = new_crtc_start;
00606         
00607         // Setting the panning register is nice as it allows for super smooth
00608         // scrolling, but if we hit the retrace pulse there may be flicker as
00609         // panning and display start are latched at different times. 
00610 
00611         IO_Read(0x3da);              // reset attribute flipflop
00612         IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on
00613         IO_Write(0x3c0,(Bit8u)new_panning);
00614         
00615         // Wait for retrace if requested
00616         if (wait) CALLBACK_RunRealFar(RealSeg(int10.rom.wait_retrace),RealOff(int10.rom.wait_retrace));
00617 
00618         return VESA_SUCCESS;
00619 }
00620 
00621 Bit8u VESA_GetDisplayStart(Bit16u & x,Bit16u & y) {
00622         Bitu pixels_per_offset;
00623         Bitu panning_factor = 1;
00624 
00625         switch (CurMode->type) {
00626         case M_TEXT:
00627                 pixels_per_offset = 16;
00628                 break;
00629         case M_LIN4:
00630         case M_PACKED4:
00631                 pixels_per_offset = 16;
00632                 break;
00633         case M_LIN8:
00634                 panning_factor = 2;
00635                 pixels_per_offset = 8;
00636                 break;
00637         case M_LIN15:
00638         case M_LIN16:
00639                 panning_factor = 2;
00640                 pixels_per_offset = 4;
00641                 break;
00642         case M_LIN32:
00643                 pixels_per_offset = 2;
00644                 break;
00645         default:
00646                 return VESA_MODE_UNSUPPORTED;
00647         }
00648 
00649         IO_Read(0x3da);              // reset attribute flipflop
00650         IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on
00651         Bit8u panning = IO_Read(0x3c1);
00652 
00653         Bitu virtual_screen_width = vga.config.scan_len * pixels_per_offset;
00654         Bitu start_pixel = vga.config.display_start * (pixels_per_offset/2) 
00655                 + panning / panning_factor;
00656         
00657         y = (Bit16u)(start_pixel / virtual_screen_width);
00658         x = (Bit16u)(start_pixel % virtual_screen_width);
00659         return VESA_SUCCESS;
00660 }
00661 
00662 static Bitu VESA_SetWindow(void) {
00663         if (reg_bh) reg_ah=VESA_GetCPUWindow(reg_bl,reg_dx);
00664         else reg_ah=VESA_SetCPUWindow(reg_bl,(Bit8u)reg_dx);
00665         reg_al=0x4f;
00666         return CBRET_NONE;
00667 }
00668 
00669 static Bitu VESA_PMSetWindow(void) {
00670         IO_Write(0x3d4,0x6a);
00671         IO_Write(0x3d5,reg_dl);
00672         return CBRET_NONE;
00673 }
00674 static Bitu VESA_PMSetPalette(void) {
00675         PhysPt data=SegPhys(es)+reg_edi;
00676         Bit32u count=reg_cx;
00677         IO_Write(0x3c8,reg_dl);
00678         do {
00679                 IO_Write(0x3c9,mem_readb(data+2));
00680                 IO_Write(0x3c9,mem_readb(data+1));
00681                 IO_Write(0x3c9,mem_readb(data));
00682                 data+=4;
00683         } while (--count);
00684         return CBRET_NONE;
00685 }
00686 static Bitu VESA_PMSetStart(void) {
00687         Bit32u start = (Bit32u)(((unsigned int)reg_dx << 16u) | (unsigned int)reg_cx);
00688         vga.config.display_start = start;
00689         return CBRET_NONE;
00690 }
00691 
00692 
00693 
00694 extern int vesa_modelist_cap;
00695 
00696 Bitu INT10_WriteVESAModeList(Bitu max_modes) {
00697     Bitu mode_wptr = int10.rom.vesa_modes;
00698         Bitu i=0,modecount=0;
00699 
00700     //TODO Maybe add normal vga modes too, but only seems to complicate things
00701     while (ModeList_VGA[i].mode!=0xffff) {
00702         bool canuse_mode=false;
00703 
00704         /* Hack for VBE 1.2 modes and 24/32bpp ambiguity */
00705         if (ModeList_VGA[i].mode >= 0x100 && ModeList_VGA[i].mode <= 0x11F &&
00706                 ((ModeList_VGA[i].type == M_LIN32 && !vesa12_modes_32bpp) ||
00707                  (ModeList_VGA[i].type == M_LIN24 && vesa12_modes_32bpp))) {
00708             /* ignore */
00709         }
00710         /* ignore deleted modes */
00711         else if (ModeList_VGA[i].type == M_ERROR) {
00712             /* ignore */
00713         }
00714         else {
00715             /* If there is no "accepts mode" then accept.
00716              *
00717              * If the user modified the mode, then accept.
00718              * If the mode exceeds video memory, then the mode will be reported as not supported by VESA BIOS functions.
00719              *
00720              * If the SVGA card would accept the mode (generally it's a memsize check), then accept. */
00721             if (!svga.accepts_mode)
00722                 canuse_mode=true;
00723             else if (ModeList_VGA[i].special & _USER_MODIFIED)
00724                 canuse_mode=true;
00725             else if (svga.accepts_mode(ModeList_VGA[i].mode))
00726                 canuse_mode=true;
00727 
00728             if (canuse_mode) {
00729                 if (ModeList_VGA[i].mode >= 0x100) {
00730                     bool allow1 = allow_vesa_lowres_modes ||
00731                         (ModeList_VGA[i].swidth >= 640 && ModeList_VGA[i].sheight >= 400);
00732                     bool allow2 =
00733                         allow_explicit_vesa_24bpp || ModeList_VGA[i].type != M_LIN24 ||
00734                         (ModeList_VGA[i].type == M_LIN24 && ModeList_VGA[i].mode < 0x120);
00735                     bool allow3 =
00736                         allow_hd_vesa_modes ||
00737                         !(ModeList_VGA[i].special & _HIGH_DEFINITION);
00738                     bool allow4 =
00739                         allow_unusual_vesa_modes ||
00740                         !(ModeList_VGA[i].special & _UNUSUAL_MODE);
00741                     bool allow5 = /* either user modified or within the limits of the render scaler architecture */
00742                         (ModeList_VGA[i].special & _USER_MODIFIED) ||
00743                         (ModeList_VGA[i].swidth <= SCALER_MAXWIDTH && ModeList_VGA[i].sheight <= SCALER_MAXHEIGHT);
00744                     bool allow_res = allow1 && allow2 && allow3 && allow4 && allow5;
00745 
00746                     switch (ModeList_VGA[i].type) {
00747                         case M_LIN32:   canuse_mode=allow_vesa_32bpp && allow_res; break;
00748                         case M_LIN24:   canuse_mode=allow_vesa_24bpp && allow_res; break;
00749                         case M_LIN16:   canuse_mode=allow_vesa_16bpp && allow_res; break;
00750                         case M_LIN15:   canuse_mode=allow_vesa_15bpp && allow_res; break;
00751                         case M_LIN8:    canuse_mode=allow_vesa_8bpp && allow_res; break;
00752                         case M_LIN4:    canuse_mode=allow_vesa_4bpp && allow_res; break;
00753                         case M_PACKED4: canuse_mode=allow_vesa_4bpp_packed && allow_res; break;
00754                         case M_TEXT:    canuse_mode=allow_vesa_tty && allow_res; break;
00755                         default:        break;
00756                     }
00757                 }
00758             }
00759         }
00760 
00761         if (canuse_mode && vesa_modelist_cap > 0 && (unsigned int)modecount >= (unsigned int)vesa_modelist_cap)
00762             canuse_mode = false;
00763         if (canuse_mode && (unsigned int)modecount >= (unsigned int)max_modes)
00764             canuse_mode = false;
00765 
00766         if (ModeList_VGA[i].type != M_TEXT) {
00767             if (canuse_mode && vesa_mode_width_cap > 0 && (unsigned int)ModeList_VGA[i].swidth > (unsigned int)vesa_mode_width_cap)
00768                 canuse_mode = false;
00769 
00770             if (canuse_mode && vesa_mode_height_cap > 0 && (unsigned int)ModeList_VGA[i].sheight > (unsigned int)vesa_mode_height_cap)
00771                 canuse_mode = false;
00772         }
00773 
00774         if (ModeList_VGA[i].mode>=0x100 && canuse_mode) {
00775             if ((!int10.vesa_oldvbe) || (ModeList_VGA[i].mode<0x120)) {
00776                 phys_writew(PhysMake((Bit16u)0xc000,(Bit16u)mode_wptr),(Bit16u)ModeList_VGA[i].mode);
00777                 mode_wptr+=2;
00778                 modecount++;
00779             }
00780         }
00781 
00782         i++;
00783     }
00784 
00785     assert(modecount <= int10.rom.vesa_alloc_modes); /* do not overrun the buffer */
00786 
00787     /* after the buffer, is 0xFFFF */
00788     phys_writew(PhysMake((Bit16u)0xc000,(Bit16u)mode_wptr),(Bit16u)0xFFFF);
00789     mode_wptr+=2;
00790 
00791     return modecount;
00792 }
00793 
00794 void INT10_SetupVESA(void) {
00795         Bitu i,modecount=0;
00796 
00797         /* BUGFIX: Generating VESA BIOS data when machine=ega or machine=vgaonly is dumb.
00798          * Stop wasting ROM space! --J.C. */
00799         if (machine != MCH_VGA) return;
00800         if (svgaCard == SVGA_None) return;
00801 
00802         /* Put the mode list somewhere in memory */
00803     int10.rom.vesa_alloc_modes = (Bit16u)(~0u);
00804     int10.rom.vesa_modes = RealMake(0xc000,int10.rom.used);
00805     modecount = INT10_WriteVESAModeList(0xFFFF/*max mode count*/);
00806     int10.rom.vesa_alloc_modes = (Bit16u)modecount;
00807     int10.rom.used += (Bit16u)(modecount * 2u);
00808         phys_writew(PhysMake(0xc000,int10.rom.used),0xffff);
00809         int10.rom.used+=2;
00810         int10.rom.oemstring=RealMake(0xc000,int10.rom.used);
00811         Bitu len=(Bitu)(strlen(string_oem)+1);
00812         for (i=0;i<len;i++) {
00813                 phys_writeb(0xc0000u+(int10.rom.used++),(Bit8u)string_oem[i]);
00814         }
00815         /* Prepare the real mode interface */
00816         int10.rom.wait_retrace=RealMake(0xc000,int10.rom.used);
00817         int10.rom.used += (Bit16u)CALLBACK_Setup(0, NULL, CB_VESA_WAIT, PhysMake(0xc000,int10.rom.used), "");
00818         callback.rmWindow=CALLBACK_Allocate();
00819         int10.rom.set_window=RealMake(0xc000,int10.rom.used);
00820         int10.rom.used += (Bit16u)CALLBACK_Setup(callback.rmWindow, VESA_SetWindow, CB_RETF, PhysMake(0xc000,int10.rom.used), "VESA Real Set Window");
00821         /* Prepare the pmode interface */
00822         int10.rom.pmode_interface=RealMake(0xc000,int10.rom.used);
00823         int10.rom.used += 8;            //Skip the byte later used for offsets
00824         /* PM Set Window call */
00825         int10.rom.pmode_interface_window = int10.rom.used - RealOff( int10.rom.pmode_interface );
00826         phys_writew( Real2Phys(int10.rom.pmode_interface) + 0, int10.rom.pmode_interface_window );
00827         callback.pmWindow=CALLBACK_Allocate();
00828         int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmWindow, VESA_PMSetWindow, CB_RETN, PhysMake(0xc000,int10.rom.used), "VESA PM Set Window");
00829         /* PM Set start call */
00830         int10.rom.pmode_interface_start = int10.rom.used - RealOff( int10.rom.pmode_interface );
00831         phys_writew( Real2Phys(int10.rom.pmode_interface) + 2, int10.rom.pmode_interface_start);
00832         callback.pmStart=CALLBACK_Allocate();
00833         int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmStart, VESA_PMSetStart, CB_VESA_PM, PhysMake(0xc000,int10.rom.used), "VESA PM Set Start");
00834         /* PM Set Palette call */
00835         int10.rom.pmode_interface_palette = int10.rom.used - RealOff( int10.rom.pmode_interface );
00836         phys_writew( Real2Phys(int10.rom.pmode_interface) + 4, int10.rom.pmode_interface_palette);
00837         callback.pmPalette=CALLBACK_Allocate();
00838         int10.rom.used += (Bit16u)CALLBACK_Setup(0, NULL, CB_VESA_PM, PhysMake(0xc000,int10.rom.used), "");
00839         int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmPalette, VESA_PMSetPalette, CB_RETN, PhysMake(0xc000,int10.rom.used), "VESA PM Set Palette");
00840         /* Finalize the size and clear the required ports pointer */
00841         phys_writew( Real2Phys(int10.rom.pmode_interface) + 6, 0);
00842         int10.rom.pmode_interface_size=int10.rom.used - RealOff( int10.rom.pmode_interface );
00843 }
00844