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 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[]="2";
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.mem.memsize/(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         while (ModeList_VGA[i].mode!=0xffff) {
00173                 /* Hack for VBE 1.2 modes and 24/32bpp ambiguity */
00174                 if (ModeList_VGA[i].mode >= 0x100 && ModeList_VGA[i].mode <= 0x11F &&
00175             !(ModeList_VGA[i].special & _USER_MODIFIED) &&
00176                         ((ModeList_VGA[i].type == M_LIN32 && !vesa12_modes_32bpp) ||
00177                          (ModeList_VGA[i].type == M_LIN24 && vesa12_modes_32bpp))) {
00178                         /* ignore */
00179                         i++;
00180                 }
00181         /* ignore deleted modes */
00182         else if (ModeList_VGA[i].type == M_ERROR) {
00183             /* ignore */
00184             i++;
00185         }
00186                 else if (mode==ModeList_VGA[i].mode)
00187                         goto foundit;
00188                 else
00189                         i++;
00190         }
00191         return VESA_FAIL;
00192 foundit:
00193         if ((int10.vesa_oldvbe) && (ModeList_VGA[i].mode>=0x120)) return 0x01;
00194         VideoModeBlock * mblock=&ModeList_VGA[i];
00195 
00196     /* Don't allow querying modes the SVGA card does not accept,
00197      * unless the user modified the mode. */
00198     if (svga.accepts_mode && !(mblock->special & _USER_MODIFIED)) {
00199                 if (!svga.accepts_mode(mode)) return 0x01;
00200         }
00201 
00202     /* do not return information on deleted modes */
00203     if (mblock->type == M_ERROR) return 0x01;
00204 
00205         bool allow_res = allow_vesa_lowres_modes ||
00206                 (ModeList_VGA[i].swidth >= 640 && ModeList_VGA[i].sheight >= 400);
00207 
00208         switch (mblock->type) {
00209         case M_LIN4:
00210                 if (!allow_vesa_4bpp) return VESA_FAIL;
00211                 pageSize = mblock->sheight * mblock->swidth/2;
00212                 var_write(&minfo.BytesPerScanLine,((mblock->swidth+15U)/8U)&(~1U)); /* NTS: 4bpp requires even value due to VGA registers, round up */
00213                 var_write(&minfo.NumberOfPlanes,0x4);
00214                 var_write(&minfo.BitsPerPixel,4);
00215                 var_write(&minfo.MemoryModel,3);        //ega planar mode
00216                 modeAttributes = 0x1b;  // Color, graphics, no linear buffer
00217                 break;
00218         case M_LIN8:
00219                 if (!allow_vesa_8bpp || !allow_res) return VESA_FAIL;
00220                 pageSize = mblock->sheight * mblock->swidth;
00221                 var_write(&minfo.BytesPerScanLine,mblock->swidth);
00222                 var_write(&minfo.NumberOfPlanes,0x1);
00223                 var_write(&minfo.BitsPerPixel,8);
00224                 var_write(&minfo.MemoryModel,4);                //packed pixel
00225                 modeAttributes = 0x1b;  // Color, graphics
00226                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00227                 break;
00228         case M_LIN15:
00229                 if (!allow_vesa_15bpp || !allow_res) return VESA_FAIL;
00230                 pageSize = mblock->sheight * mblock->swidth*2;
00231                 var_write(&minfo.BytesPerScanLine,mblock->swidth*2);
00232                 var_write(&minfo.NumberOfPlanes,0x1);
00233                 var_write(&minfo.BitsPerPixel,15);
00234                 var_write(&minfo.MemoryModel,6);        //HiColour
00235                 var_write(&minfo.RedMaskSize,5);
00236                 var_write(&minfo.RedMaskPos,10);
00237                 var_write(&minfo.GreenMaskSize,5);
00238                 var_write(&minfo.GreenMaskPos,5);
00239                 var_write(&minfo.BlueMaskSize,5);
00240                 var_write(&minfo.BlueMaskPos,0);
00241                 var_write(&minfo.ReservedMaskSize,0x01);
00242                 var_write(&minfo.ReservedMaskPos,0x0f);
00243                 modeAttributes = 0x1b;  // Color, graphics
00244                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00245                 break;
00246         case M_LIN16:
00247                 if (!allow_vesa_16bpp || !allow_res) return VESA_FAIL;
00248                 pageSize = mblock->sheight * mblock->swidth*2;
00249                 var_write(&minfo.BytesPerScanLine,mblock->swidth*2);
00250                 var_write(&minfo.NumberOfPlanes,0x1);
00251                 var_write(&minfo.BitsPerPixel,16);
00252                 var_write(&minfo.MemoryModel,6);        //HiColour
00253                 var_write(&minfo.RedMaskSize,5);
00254                 var_write(&minfo.RedMaskPos,11);
00255                 var_write(&minfo.GreenMaskSize,6);
00256                 var_write(&minfo.GreenMaskPos,5);
00257                 var_write(&minfo.BlueMaskSize,5);
00258                 var_write(&minfo.BlueMaskPos,0);
00259                 modeAttributes = 0x1b;  // Color, graphics
00260                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00261                 break;
00262         case M_LIN24:
00263                 if (!allow_vesa_24bpp || !allow_res) return VESA_FAIL;
00264                 pageSize = mblock->sheight * mblock->swidth*3;
00265                 var_write(&minfo.BytesPerScanLine,mblock->swidth*3);
00266                 var_write(&minfo.NumberOfPlanes,0x1);
00267                 var_write(&minfo.BitsPerPixel,24);
00268                 var_write(&minfo.MemoryModel,6);        //HiColour
00269                 var_write(&minfo.RedMaskSize,8);
00270                 var_write(&minfo.RedMaskPos,0x10);
00271                 var_write(&minfo.GreenMaskSize,0x8);
00272                 var_write(&minfo.GreenMaskPos,0x8);
00273                 var_write(&minfo.BlueMaskSize,0x8);
00274                 var_write(&minfo.BlueMaskPos,0x0);
00275                 modeAttributes = 0x1b;  // Color, graphics
00276                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00277                 break;
00278         case M_LIN32:
00279                 if (!allow_vesa_32bpp || !allow_res) return VESA_FAIL;
00280                 pageSize = mblock->sheight * mblock->swidth*4;
00281                 var_write(&minfo.BytesPerScanLine,mblock->swidth*4);
00282                 var_write(&minfo.NumberOfPlanes,0x1);
00283                 var_write(&minfo.BitsPerPixel,32);
00284                 var_write(&minfo.MemoryModel,6);        //HiColour
00285                 var_write(&minfo.RedMaskSize,8);
00286                 var_write(&minfo.RedMaskPos,0x10);
00287                 var_write(&minfo.GreenMaskSize,0x8);
00288                 var_write(&minfo.GreenMaskPos,0x8);
00289                 var_write(&minfo.BlueMaskSize,0x8);
00290                 var_write(&minfo.BlueMaskPos,0x0);
00291                 var_write(&minfo.ReservedMaskSize,0x8);
00292                 var_write(&minfo.ReservedMaskPos,0x18);
00293                 modeAttributes = 0x1b;  // Color, graphics
00294                 if (!int10.vesa_nolfb) modeAttributes |= 0x80;  // linear framebuffer
00295                 break;
00296         case M_TEXT:
00297                 if (!allow_vesa_tty) return VESA_FAIL;
00298                 pageSize = 0;
00299                 var_write(&minfo.BytesPerScanLine, mblock->twidth * 2);
00300                 var_write(&minfo.NumberOfPlanes,0x4);
00301                 var_write(&minfo.BitsPerPixel,4);
00302                 var_write(&minfo.MemoryModel,0);        // text
00303                 modeAttributes = 0x0f;  //Color, text, bios output
00304                 break;
00305         default:
00306                 return VESA_FAIL;
00307         }
00308         if (pageSize & 0xFFFFu) {
00309                 // It is documented that many applications assume 64k-aligned page sizes
00310                 // VBETEST is one of them
00311                 pageSize +=  0xFFFFu;
00312                 pageSize &= ~0xFFFFu;
00313         }
00314         Bitu pages = 0;
00315         if (pageSize > vga.mem.memsize || (mblock->special & _USER_DISABLED)) {
00316                 // mode not supported by current hardware configuration
00317                 modeAttributes &= ~0x1;
00318         } else if (pageSize) {
00319                 pages = (vga.mem.memsize / pageSize)-1;
00320         }
00321         var_write(&minfo.NumberOfImagePages, pages);
00322         var_write(&minfo.ModeAttributes, modeAttributes);
00323         var_write(&minfo.WinAAttributes, 0x7);  // Exists/readable/writable
00324 
00325         if (mblock->type==M_TEXT) {
00326                 var_write(&minfo.WinGranularity,32);
00327                 var_write(&minfo.WinSize,32);
00328                 var_write(&minfo.WinASegment,0xb800);
00329                 var_write(&minfo.XResolution,mblock->twidth);
00330                 var_write(&minfo.YResolution,mblock->theight);
00331         } else {
00332                 var_write(&minfo.WinGranularity,64);
00333                 var_write(&minfo.WinSize,64);
00334                 var_write(&minfo.WinASegment,0xa000);
00335                 var_write(&minfo.XResolution,mblock->swidth);
00336                 var_write(&minfo.YResolution,mblock->sheight);
00337         }
00338         var_write(&minfo.WinFuncPtr,CALLBACK_RealPointer(callback.setwindow));
00339         var_write(&minfo.NumberOfBanks,0x1);
00340         var_write(&minfo.Reserved_page,0x1);
00341         var_write(&minfo.XCharSize,mblock->cwidth);
00342         var_write(&minfo.YCharSize,mblock->cheight);
00343         if (!int10.vesa_nolfb) var_write(&minfo.PhysBasePtr,S3_LFB_BASE + (hack_lfb_yadjust*(long)host_readw((HostPt)(&minfo.BytesPerScanLine))));
00344 
00345         MEM_BlockWrite(buf,&minfo,sizeof(MODE_INFO));
00346         return VESA_SUCCESS;
00347 }
00348 
00349 
00350 Bit8u VESA_SetSVGAMode(Bit16u mode) {
00351         if (INT10_SetVideoMode(mode)) {
00352                 int10.vesa_setmode=mode&0x7fff;
00353                 return VESA_SUCCESS;
00354         }
00355         return VESA_FAIL;
00356 }
00357 
00358 Bit8u VESA_GetSVGAMode(Bit16u & mode) {
00359         if (int10.vesa_setmode!=0xffff) mode=int10.vesa_setmode;
00360         else mode=CurMode->mode;
00361         return VESA_SUCCESS;
00362 }
00363 
00364 Bit8u VESA_SetCPUWindow(Bit8u window,Bit8u address) {
00365         if (window) return VESA_FAIL;
00366         if (((Bit32u)(address)*64*1024<vga.mem.memsize)) {
00367                 IO_Write(0x3d4,0x6a);
00368                 IO_Write(0x3d5,(Bit8u)address);
00369                 return VESA_SUCCESS;
00370         } else return VESA_FAIL;
00371 }
00372 
00373 Bit8u VESA_GetCPUWindow(Bit8u window,Bit16u & address) {
00374         if (window) return VESA_FAIL;
00375         IO_Write(0x3d4,0x6a);
00376         address=IO_Read(0x3d5);
00377         return VESA_SUCCESS;
00378 }
00379 
00380 
00381 Bit8u VESA_SetPalette(PhysPt data,Bitu index,Bitu count) {
00382 //Structure is (vesa 3.0 doc): blue,green,red,alignment
00383         Bit8u r,g,b;
00384         if (index>255) return VESA_FAIL;
00385         if (index+count>256) return VESA_FAIL;
00386         IO_Write(0x3c8,(Bit8u)index);
00387         while (count) {
00388                 b = mem_readb(data++);
00389                 g = mem_readb(data++);
00390                 r = mem_readb(data++);
00391                 data++;
00392                 IO_Write(0x3c9,r);
00393                 IO_Write(0x3c9,g);
00394                 IO_Write(0x3c9,b);
00395                 count--;
00396         }
00397         return VESA_SUCCESS;
00398 }
00399 
00400 
00401 Bit8u VESA_GetPalette(PhysPt data,Bitu index,Bitu count) {
00402         Bit8u r,g,b;
00403         if (index>255) return VESA_FAIL;
00404         if (index+count>256) return VESA_FAIL;
00405         IO_Write(0x3c7,(Bit8u)index);
00406         while (count) {
00407                 r = IO_Read(0x3c9);
00408                 g = IO_Read(0x3c9);
00409                 b = IO_Read(0x3c9);
00410                 mem_writeb(data++,b);
00411                 mem_writeb(data++,g);
00412                 mem_writeb(data++,r);
00413                 data++;
00414                 count--;
00415         }
00416         return VESA_SUCCESS;
00417 }
00418 
00419 // maximum offset for the S3 Trio64 is 10 bits
00420 #define S3_MAX_OFFSET 0x3ff
00421 
00422 Bit8u VESA_ScanLineLength(Bit8u subcall,Bit16u val, Bit16u & bytes,Bit16u & pixels,Bit16u & lines) {
00423         // offset register: virtual scanline length
00424         Bitu pixels_per_offset;
00425         Bitu bytes_per_offset = 8;
00426         Bitu vmemsize = vga.mem.memsize;
00427         Bitu new_offset = vga.config.scan_len;
00428         Bitu screen_height = CurMode->sheight;
00429 
00430         switch (CurMode->type) {
00431         case M_TEXT:
00432                 vmemsize = 0x8000;      // we have only the 32kB window here
00433                 screen_height = CurMode->theight;
00434                 pixels_per_offset = 16; // two characters each 8 pixels wide
00435                 bytes_per_offset = 4;   // 2 characters + 2 attributes
00436                 break;
00437         case M_LIN4:
00438                 pixels_per_offset = 16;
00439                 break;
00440         case M_LIN8:
00441                 pixels_per_offset = 8;
00442                 break;
00443         case M_LIN15:
00444         case M_LIN16:
00445                 pixels_per_offset = 4;
00446                 break;
00447         case M_LIN32:
00448                 pixels_per_offset = 2;
00449                 break;
00450         default:
00451                 return VESA_MODE_UNSUPPORTED;
00452         }
00453         switch (subcall) {
00454         case 0x00: // set scan length in pixels
00455                 new_offset = val / pixels_per_offset;
00456                 if (val % pixels_per_offset) new_offset++;
00457                 
00458                 if (new_offset > S3_MAX_OFFSET)
00459                         return VESA_HW_UNSUPPORTED; // scanline too long
00460                 vga.config.scan_len = new_offset;
00461                 VGA_CheckScanLength();
00462                 break;
00463 
00464         case 0x01: // get current scanline length
00465                 // implemented at the end of this function
00466                 break;
00467 
00468         case 0x02: // set scan length in bytes
00469                 new_offset = val / bytes_per_offset;
00470                 if (val % bytes_per_offset) new_offset++;
00471                 
00472                 if (new_offset > S3_MAX_OFFSET)
00473                         return VESA_HW_UNSUPPORTED; // scanline too long
00474                 vga.config.scan_len = new_offset;
00475                 VGA_CheckScanLength();
00476                 break;
00477 
00478         case 0x03: // get maximum scan line length
00479                 // the smaller of either the hardware maximum scanline length or
00480                 // the limit to get full y resolution of this mode
00481                 new_offset = S3_MAX_OFFSET;
00482                 if ((new_offset * bytes_per_offset * screen_height) > vmemsize)
00483                         new_offset = vmemsize / (bytes_per_offset * screen_height);
00484                 break;
00485 
00486         default:
00487                 return VESA_UNIMPLEMENTED;
00488         }
00489 
00490         // set up the return values
00491         bytes = (Bit16u)(new_offset * bytes_per_offset);
00492         pixels = (Bit16u)(new_offset * pixels_per_offset);
00493         if (!bytes)
00494                 // return failure on division by zero
00495                 // some real VESA BIOS implementations may crash here
00496                 return VESA_FAIL;
00497 
00498         lines = (Bit16u)(vmemsize / bytes);
00499         
00500         if (CurMode->type==M_TEXT)
00501                 lines *= CurMode->cheight;
00502 
00503         return VESA_SUCCESS;
00504 }
00505 
00506 Bit8u VESA_SetDisplayStart(Bit16u x,Bit16u y) {
00507         // TODO wait for retrace in case bl==0x80
00508         Bitu pixels_per_offset;
00509         Bitu panning_factor = 1;
00510 
00511         switch (CurMode->type) {
00512         case M_TEXT:
00513         case M_LIN4:
00514                 pixels_per_offset = 16;
00515                 break;
00516         case M_LIN8:
00517                 panning_factor = 2; // the panning register ignores bit0 in this mode
00518                 pixels_per_offset = 8;
00519                 break;
00520         case M_LIN15:
00521         case M_LIN16:
00522                 panning_factor = 2; // this may be DOSBox specific
00523                 pixels_per_offset = 4;
00524                 break;
00525         case M_LIN32:
00526                 pixels_per_offset = 2;
00527                 break;
00528         default:
00529                 return VESA_MODE_UNSUPPORTED;
00530         }
00531         // We would have to divide y by the character height for text modes and
00532         // write the remainder to the CRTC preset row scan register,
00533         // but VBE2 BIOSes that actually get that far also don't.
00534         // Only a VBE3 BIOS got it right.
00535         Bitu virtual_screen_width = vga.config.scan_len * pixels_per_offset;
00536         Bitu new_start_pixel = virtual_screen_width * y + x;
00537         Bitu new_crtc_start = new_start_pixel / (pixels_per_offset/2);
00538         Bitu new_panning = new_start_pixel % (pixels_per_offset/2);
00539         new_panning *= panning_factor;
00540 
00541         vga.config.display_start = new_crtc_start;
00542         
00543         // Setting the panning register is nice as it allows for super smooth
00544         // scrolling, but if we hit the retrace pulse there may be flicker as
00545         // panning and display start are latched at different times. 
00546 
00547         IO_Read(0x3da);              // reset attribute flipflop
00548         IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on
00549         IO_Write(0x3c0,new_panning);
00550 
00551         return VESA_SUCCESS;
00552 }
00553 
00554 Bit8u VESA_GetDisplayStart(Bit16u & x,Bit16u & y) {
00555         Bitu pixels_per_offset;
00556         Bitu panning_factor = 1;
00557 
00558         switch (CurMode->type) {
00559         case M_TEXT:
00560                 pixels_per_offset = 16;
00561                 break;
00562         case M_LIN4:
00563                 pixels_per_offset = 16;
00564                 break;
00565         case M_LIN8:
00566                 panning_factor = 2;
00567                 pixels_per_offset = 8;
00568                 break;
00569         case M_LIN15:
00570         case M_LIN16:
00571                 panning_factor = 2;
00572                 pixels_per_offset = 4;
00573                 break;
00574         case M_LIN32:
00575                 pixels_per_offset = 2;
00576                 break;
00577         default:
00578                 return VESA_MODE_UNSUPPORTED;
00579         }
00580 
00581         IO_Read(0x3da);              // reset attribute flipflop
00582         IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on
00583         Bit8u panning = IO_Read(0x3c1);
00584 
00585         Bitu virtual_screen_width = vga.config.scan_len * pixels_per_offset;
00586         Bitu start_pixel = vga.config.display_start * (pixels_per_offset/2) 
00587                 + panning / panning_factor;
00588         
00589         y = start_pixel / virtual_screen_width;
00590         x = start_pixel % virtual_screen_width;
00591         return VESA_SUCCESS;
00592 }
00593 
00594 static Bitu VESA_SetWindow(void) {
00595         if (reg_bh) reg_ah=VESA_GetCPUWindow(reg_bl,reg_dx);
00596         else reg_ah=VESA_SetCPUWindow(reg_bl,(Bit8u)reg_dx);
00597         reg_al=0x4f;
00598         return 0;
00599 }
00600 
00601 static Bitu VESA_PMSetWindow(void) {
00602         VESA_SetCPUWindow(0,(Bit8u)reg_dx);
00603         return 0;
00604 }
00605 static Bitu VESA_PMSetPalette(void) {
00606         VESA_SetPalette(SegPhys(es) +  reg_edi, reg_dx, reg_cx );
00607         return 0;
00608 }
00609 static Bitu VESA_PMSetStart(void) {
00610         // This function is from VBE2 and directly sets the VGA
00611         // display start address.
00612 
00613         // TODO wait for retrace in case bl==0x80
00614         Bit32u start = (Bit32u)(((unsigned int)reg_dx << 16u) | (unsigned int)reg_cx);
00615         vga.config.display_start = start;
00616         return 0;
00617 }
00618 
00619 
00620 
00621 extern int vesa_modelist_cap;
00622 
00623 Bitu INT10_WriteVESAModeList(Bitu max_modes) {
00624     Bitu mode_wptr = int10.rom.vesa_modes;
00625         Bitu i=0,modecount=0;
00626 
00627     //TODO Maybe add normal vga modes too, but only seems to complicate things
00628     while (ModeList_VGA[i].mode!=0xffff) {
00629         bool canuse_mode=false;
00630 
00631         /* Hack for VBE 1.2 modes and 24/32bpp ambiguity */
00632         if (ModeList_VGA[i].mode >= 0x100 && ModeList_VGA[i].mode <= 0x11F &&
00633                 ((ModeList_VGA[i].type == M_LIN32 && !vesa12_modes_32bpp) ||
00634                  (ModeList_VGA[i].type == M_LIN24 && vesa12_modes_32bpp))) {
00635             /* ignore */
00636         }
00637         /* ignore deleted modes */
00638         else if (ModeList_VGA[i].type == M_ERROR) {
00639             /* ignore */
00640         }
00641         else {
00642             /* If there is no "accepts mode" then accept.
00643              *
00644              * If the user modified the mode, then accept.
00645              * If the mode exceeds video memory, then the mode will be reported as not supported by VESA BIOS functions.
00646              *
00647              * If the SVGA card would accept the mode (generally it's a memsize check), then accept. */
00648             if (!svga.accepts_mode)
00649                 canuse_mode=true;
00650             else if (ModeList_VGA[i].special & _USER_MODIFIED)
00651                 canuse_mode=true;
00652             else if (svga.accepts_mode(ModeList_VGA[i].mode))
00653                 canuse_mode=true;
00654 
00655             if (canuse_mode) {
00656                 if (ModeList_VGA[i].mode >= 0x100) {
00657                     bool allow_res = allow_vesa_lowres_modes ||
00658                         (ModeList_VGA[i].swidth >= 640 && ModeList_VGA[i].sheight >= 400);
00659 
00660                     switch (ModeList_VGA[i].type) {
00661                         case M_LIN32:   canuse_mode=allow_vesa_32bpp && allow_res; break;
00662                         case M_LIN24:   canuse_mode=allow_vesa_24bpp && allow_res; break;
00663                         case M_LIN16:   canuse_mode=allow_vesa_16bpp && allow_res; break;
00664                         case M_LIN15:   canuse_mode=allow_vesa_15bpp && allow_res; break;
00665                         case M_LIN8:    canuse_mode=allow_vesa_8bpp && allow_res; break;
00666                         case M_LIN4:    canuse_mode=allow_vesa_4bpp; break;
00667                         case M_TEXT:    canuse_mode=allow_vesa_tty; break;
00668                         default:        break;
00669                     }
00670                 }
00671             }
00672         }
00673 
00674         if (canuse_mode && vesa_modelist_cap > 0 && (unsigned int)modecount >= (unsigned int)vesa_modelist_cap)
00675             canuse_mode = false;
00676         if (canuse_mode && (unsigned int)modecount >= (unsigned int)max_modes)
00677             canuse_mode = false;
00678 
00679         if (ModeList_VGA[i].type != M_TEXT) {
00680             if (canuse_mode && vesa_mode_width_cap > 0 && (unsigned int)ModeList_VGA[i].swidth > (unsigned int)vesa_mode_width_cap)
00681                 canuse_mode = false;
00682 
00683             if (canuse_mode && vesa_mode_height_cap > 0 && (unsigned int)ModeList_VGA[i].sheight > (unsigned int)vesa_mode_height_cap)
00684                 canuse_mode = false;
00685         }
00686 
00687         if (ModeList_VGA[i].mode>=0x100 && canuse_mode) {
00688             if ((!int10.vesa_oldvbe) || (ModeList_VGA[i].mode<0x120)) {
00689                 phys_writew(PhysMake(0xc000,mode_wptr),ModeList_VGA[i].mode);
00690                 mode_wptr+=2;
00691                 modecount++;
00692             }
00693         }
00694 
00695         i++;
00696     }
00697 
00698     assert(modecount <= int10.rom.vesa_alloc_modes); /* do not overrun the buffer */
00699 
00700     /* after the buffer, is 0xFFFF */
00701     phys_writew(PhysMake(0xc000,mode_wptr),0xFFFF);
00702     mode_wptr+=2;
00703 
00704     return modecount;
00705 }
00706 
00707 void INT10_SetupVESA(void) {
00708         Bitu i,modecount=0;
00709 
00710         /* BUGFIX: Generating VESA BIOS data when machine=ega or machine=vgaonly is dumb.
00711          * Stop wasting ROM space! --J.C. */
00712         if (machine != MCH_VGA) return;
00713         if (svgaCard == SVGA_None) return;
00714 
00715         /* Put the mode list somewhere in memory */
00716     int10.rom.vesa_alloc_modes = ~0;
00717     int10.rom.vesa_modes = RealMake(0xc000,int10.rom.used);
00718     modecount = INT10_WriteVESAModeList(0xFFFF/*max mode count*/);
00719     int10.rom.vesa_alloc_modes = (Bit16u)modecount;
00720     int10.rom.used += modecount * 2u;
00721         phys_writew(PhysMake(0xc000,int10.rom.used),0xffff);
00722         int10.rom.used+=2;
00723         int10.rom.oemstring=RealMake(0xc000,int10.rom.used);
00724         Bitu len=(Bitu)(strlen(string_oem)+1);
00725         for (i=0;i<len;i++) {
00726                 phys_writeb(0xc0000u+(int10.rom.used++),(Bit8u)string_oem[i]);
00727         }
00728         callback.setwindow=CALLBACK_Allocate();
00729         CALLBACK_Setup(callback.setwindow,VESA_SetWindow,CB_RETF, "VESA Real Set Window");
00730         /* Prepare the pmode interface */
00731         int10.rom.pmode_interface=RealMake(0xc000,int10.rom.used);
00732         int10.rom.used += 8;            //Skip the byte later used for offsets
00733         /* PM Set Window call */
00734         int10.rom.pmode_interface_window = int10.rom.used - RealOff( int10.rom.pmode_interface );
00735         phys_writew( Real2Phys(int10.rom.pmode_interface) + 0, int10.rom.pmode_interface_window );
00736         callback.pmWindow=CALLBACK_Allocate();
00737         int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmWindow, VESA_PMSetWindow, CB_RETN, PhysMake(0xc000,int10.rom.used), "VESA PM Set Window");
00738         /* PM Set start call */
00739         int10.rom.pmode_interface_start = int10.rom.used - RealOff( int10.rom.pmode_interface );
00740         phys_writew( Real2Phys(int10.rom.pmode_interface) + 2, int10.rom.pmode_interface_start);
00741         callback.pmStart=CALLBACK_Allocate();
00742         int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmStart, VESA_PMSetStart, CB_VESA_START, PhysMake(0xc000,int10.rom.used), "VESA PM Set Start");
00743         /* PM Set Palette call */
00744         int10.rom.pmode_interface_palette = int10.rom.used - RealOff( int10.rom.pmode_interface );
00745         phys_writew( Real2Phys(int10.rom.pmode_interface) + 4, int10.rom.pmode_interface_palette);
00746         callback.pmPalette=CALLBACK_Allocate();
00747         int10.rom.used += (Bit16u)CALLBACK_Setup(callback.pmPalette, VESA_PMSetPalette, CB_RETN, PhysMake(0xc000,int10.rom.used), "VESA PM Set Palette");
00748         /* Finalize the size and clear the required ports pointer */
00749         phys_writew( Real2Phys(int10.rom.pmode_interface) + 6, 0);
00750         int10.rom.pmode_interface_size=int10.rom.used - RealOff( int10.rom.pmode_interface );
00751 }
00752