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