1
0
Fork 0

VESA patch:

- implement text mode support, add modes 108h through 10Ch
- adjust the S3 text modes to the values found on real hardware
- added defines for VESA return values
- rewrite get/set scanlength and set display start for completeness
- catch a possible division by zero, part of SF patch 3154782, thanks Daniel Richard G.
- align display page sizes to 64k for compatibility

Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@3684
This commit is contained in:
Ralf Grillenberger 2011-03-06 16:34:11 +00:00
parent d97aade01a
commit 5d865dafc8
2 changed files with 193 additions and 107 deletions

View file

@ -54,8 +54,8 @@ VideoModeBlock ModeList_VGA[]={
{ 0x012 ,M_EGA ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0xA000 ,100 ,525 ,80 ,480 ,0 },
{ 0x013 ,M_VGA ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xA0000 ,0x2000 ,100 ,449 ,80 ,400 ,0 },
{ 0x054 ,M_TEXT ,1056,688, 132,43, 8, 16, 1 ,0xB8000 ,0x4000, 192, 800, 132,688, 0 },
{ 0x055 ,M_TEXT ,1056,400, 132,25, 8, 16, 1 ,0xB8000 ,0x2000, 192, 449, 132,400, 0 },
{ 0x054 ,M_TEXT ,1056,344, 132,43, 8, 8, 1 ,0xB8000 ,0x4000, 160, 449, 132,344, 0 },
{ 0x055 ,M_TEXT ,1056,400, 132,25, 8, 16, 1 ,0xB8000 ,0x2000, 160, 449, 132,400, 0 },
/* Alias of mode 101 */
{ 0x069 ,M_LIN8 ,640 ,480 ,80 ,30 ,8 ,16 ,1 ,0xA0000 ,0x10000,100 ,525 ,80 ,480 ,0 },
@ -72,6 +72,14 @@ VideoModeBlock ModeList_VGA[]={
{ 0x106 ,M_LIN4 ,1280,1024,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,212 ,1066,160,1024,0 },
{ 0x107 ,M_LIN8 ,1280,1024,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,212 ,1066,160,1024,0 },
/* VESA text modes */
{ 0x108 ,M_TEXT ,640 ,480, 80,60, 8, 8 ,2 ,0xB8000 ,0x4000, 100 ,525 ,80 ,480 ,0 },
{ 0x109 ,M_TEXT ,1056,400, 132,25, 8, 16, 1 ,0xB8000 ,0x2000, 160, 449, 132,400, 0 },
{ 0x10A ,M_TEXT ,1056,688, 132,43, 8, 8, 1 ,0xB8000 ,0x4000, 160, 449, 132,344, 0 },
{ 0x10B ,M_TEXT ,1056,400, 132,50, 8, 8, 1 ,0xB8000 ,0x4000, 160, 449, 132,400, 0 },
{ 0x10C ,M_TEXT ,1056,480, 132,60, 8, 8, 2 ,0xB8000 ,0x4000, 160, 531, 132,480, 0 },
/* VESA higher color modes */
{ 0x10D ,M_LIN15 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,449 ,80 ,400 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
{ 0x10E ,M_LIN16 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xA0000 ,0x10000,100 ,449 ,80 ,400 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
{ 0x10F ,M_LIN32 ,320 ,200 ,40 ,25 ,8 ,8 ,1 ,0xA0000 ,0x10000,50 ,449 ,40 ,400 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
@ -81,10 +89,10 @@ VideoModeBlock ModeList_VGA[]={
{ 0x113 ,M_LIN15 ,800 ,600 ,100,37 ,8 ,16 ,1 ,0xA0000 ,0x10000,264 ,628 ,200,600 ,0 },
{ 0x114 ,M_LIN16 ,800 ,600 ,100,37 ,8 ,16 ,1 ,0xA0000 ,0x10000,264 ,628 ,200,600 ,0 },
{ 0x115 ,M_LIN32 ,800 ,600 ,100,37 ,8 ,16 ,1 ,0xA0000 ,0x10000,132 ,628 ,100,600 ,0 },
{ 0x116 ,M_LIN15 ,1024,768 ,128,48 ,8 ,16 ,1 ,0xA0000 ,0x10000,336 ,806 ,256,768 ,0 },
{ 0x117 ,M_LIN16 ,1024,768 ,128,48 ,8 ,16 ,1 ,0xA0000 ,0x10000,336 ,806 ,256,768 ,0 },
{ 0x118 ,M_LIN32 ,1024,768 ,128,48 ,8 ,16 ,1 ,0xA0000 ,0x10000,168 ,806 ,128,768 ,0 },
/* those should be interlaced but ok */
//{ 0x119 ,M_LIN15 ,1280,1024,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,424 ,1066,320,1024,0 },
//{ 0x11A ,M_LIN16 ,1280,1024,160,64 ,8 ,16 ,1 ,0xA0000 ,0x10000,424 ,1066,320,1024,0 },

View file

@ -29,6 +29,13 @@
#include "int10.h"
#include "dos_inc.h"
#define VESA_SUCCESS 0x00
#define VESA_FAIL 0x01
#define VESA_HW_UNSUPPORTED 0x02
#define VESA_MODE_UNSUPPORTED 0x03
// internal definition to pass to the caller
#define VESA_UNIMPLEMENTED 0xFF
static struct {
Bitu setwindow;
Bitu pmStart;
@ -117,7 +124,7 @@ Bit8u VESA_GetSVGAInformation(Bit16u seg,Bit16u off) {
mem_writed(buffer+0x0a,0x0); //Capabilities and flags
mem_writed(buffer+0x0e,int10.rom.vesa_modes); //VESA Mode list
mem_writew(buffer+0x12,(Bit16u)(vga.vmemsize/(64*1024))); // memory size in 64kb blocks
return 0x00;
return VESA_SUCCESS;
}
Bit8u VESA_GetSVGAModeInformation(Bit16u mode,Bit16u seg,Bit16u off) {
@ -136,14 +143,13 @@ Bit8u VESA_GetSVGAModeInformation(Bit16u mode,Bit16u seg,Bit16u off) {
while (ModeList_VGA[i].mode!=0xffff) {
if (mode==ModeList_VGA[i].mode) goto foundit; else i++;
}
return 0x01;
return VESA_FAIL;
foundit:
if ((int10.vesa_oldvbe) && (ModeList_VGA[i].mode>=0x120)) return 0x01;
VideoModeBlock * mblock=&ModeList_VGA[i];
switch (mblock->type) {
case M_LIN4:
pageSize = mblock->sheight * mblock->swidth/2;
pageSize = (pageSize | 15) & ~ 15;
var_write(&minfo.BytesPerScanLine,mblock->swidth/8);
var_write(&minfo.NumberOfPlanes,0x4);
var_write(&minfo.BitsPerPixel,4);
@ -152,7 +158,6 @@ foundit:
break;
case M_LIN8:
pageSize = mblock->sheight * mblock->swidth;
pageSize = (pageSize | 15) & ~ 15;
var_write(&minfo.BytesPerScanLine,mblock->swidth);
var_write(&minfo.NumberOfPlanes,0x1);
var_write(&minfo.BitsPerPixel,8);
@ -162,7 +167,6 @@ foundit:
break;
case M_LIN15:
pageSize = mblock->sheight * mblock->swidth*2;
pageSize = (pageSize | 15) & ~ 15;
var_write(&minfo.BytesPerScanLine,mblock->swidth*2);
var_write(&minfo.NumberOfPlanes,0x1);
var_write(&minfo.BitsPerPixel,15);
@ -180,7 +184,6 @@ foundit:
break;
case M_LIN16:
pageSize = mblock->sheight * mblock->swidth*2;
pageSize = (pageSize | 15) & ~ 15;
var_write(&minfo.BytesPerScanLine,mblock->swidth*2);
var_write(&minfo.NumberOfPlanes,0x1);
var_write(&minfo.BitsPerPixel,16);
@ -196,7 +199,6 @@ foundit:
break;
case M_LIN32:
pageSize = mblock->sheight * mblock->swidth*4;
pageSize = (pageSize | 15) & ~ 15;
var_write(&minfo.BytesPerScanLine,mblock->swidth*4);
var_write(&minfo.NumberOfPlanes,0x1);
var_write(&minfo.BitsPerPixel,32);
@ -212,36 +214,40 @@ foundit:
modeAttributes = 0x1b; // Color, graphics
if (!int10.vesa_nolfb) modeAttributes |= 0x80; // linear framebuffer
break;
/* case M_TEXT:
pageSize = mblock->sheight/8 * mblock->swidth*2/8;
pageSize = (pageSize | 15) & ~ 15;
var_write(&minfo.BytesPerScanLine,mblock->swidth*2/8);
case M_TEXT:
pageSize = 0;
var_write(&minfo.BytesPerScanLine, mblock->twidth * 2);
var_write(&minfo.NumberOfPlanes,0x4);
var_write(&minfo.BitsPerPixel,4);
var_write(&minfo.MemoryModel,0); //Text
var_write(&minfo.MemoryModel,0); // text
modeAttributes = 0x0f; //Color, text, bios output
break; */
break;
default:
return 0x1;
return VESA_FAIL;
}
var_write(&minfo.WinAAttributes,0x7); // Exists/readable/writable
if(pageSize > vga.vmemsize) {
// Mode not supported by current hardware configuration
var_write(&minfo.ModeAttributes, modeAttributes & ~0x1);
var_write(&minfo.NumberOfImagePages,0);
} else {
var_write(&minfo.ModeAttributes, modeAttributes);
Bitu pages = (vga.vmemsize / pageSize)-1;
var_write(&minfo.NumberOfImagePages,pages);
if (pageSize & 0xFFFF) {
// It is documented that many applications assume 64k-aligned page sizes
// VBETEST is one of them
pageSize += 0x10000;
pageSize &= ~0xFFFF;
}
Bitu pages = 0;
if (pageSize > vga.vmemsize) {
// mode not supported by current hardware configuration
modeAttributes &= ~0x1;
} else if (pageSize) {
pages = (vga.vmemsize / pageSize)-1;
}
var_write(&minfo.NumberOfImagePages, pages);
var_write(&minfo.ModeAttributes, modeAttributes);
var_write(&minfo.WinAAttributes, 0x7); // Exists/readable/writable
if (mblock->type==M_TEXT) {
var_write(&minfo.WinGranularity,32);
var_write(&minfo.WinSize,32);
var_write(&minfo.WinASegment,0xb800);
var_write(&minfo.XResolution,mblock->swidth/8);
var_write(&minfo.YResolution,mblock->sheight/8);
var_write(&minfo.XResolution,mblock->twidth);
var_write(&minfo.YResolution,mblock->theight);
} else {
var_write(&minfo.WinGranularity,64);
var_write(&minfo.WinSize,64);
@ -257,46 +263,46 @@ foundit:
if (!int10.vesa_nolfb) var_write(&minfo.PhysBasePtr,S3_LFB_BASE);
MEM_BlockWrite(buf,&minfo,sizeof(MODE_INFO));
return 0x00;
return VESA_SUCCESS;
}
Bit8u VESA_SetSVGAMode(Bit16u mode) {
if (INT10_SetVideoMode(mode)) {
int10.vesa_setmode=mode&0x7fff;
return 0x00;
return VESA_SUCCESS;
}
return 0x01;
return VESA_FAIL;
}
Bit8u VESA_GetSVGAMode(Bit16u & mode) {
if (int10.vesa_setmode!=0xffff) mode=int10.vesa_setmode;
else mode=CurMode->mode;
return 0x00;
return VESA_SUCCESS;
}
Bit8u VESA_SetCPUWindow(Bit8u window,Bit8u address) {
if (window) return 0x1;
if (window) return VESA_FAIL;
if (((Bit32u)(address)*64*1024<vga.vmemsize)) {
IO_Write(0x3d4,0x6a);
IO_Write(0x3d5,(Bit8u)address);
return 0x0;
} else return 0x1;
return VESA_SUCCESS;
} else return VESA_FAIL;
}
Bit8u VESA_GetCPUWindow(Bit8u window,Bit16u & address) {
if (window) return 0x1;
if (window) return VESA_FAIL;
IO_Write(0x3d4,0x6a);
address=IO_Read(0x3d5);
return 0x0;
return VESA_SUCCESS;
}
Bit8u VESA_SetPalette(PhysPt data,Bitu index,Bitu count) {
//Structure is (vesa 3.0 doc): blue,green,red,alignment
Bit8u r,g,b;
if (index>255) return 0x1;
if (index+count>256) return 0x1;
if (index>255) return VESA_FAIL;
if (index+count>256) return VESA_FAIL;
IO_Write(0x3c8,(Bit8u)index);
while (count) {
b = mem_readb(data++);
@ -308,14 +314,14 @@ Bit8u VESA_SetPalette(PhysPt data,Bitu index,Bitu count) {
IO_Write(0x3c9,b);
count--;
}
return 0x00;
return VESA_SUCCESS;
}
Bit8u VESA_GetPalette(PhysPt data,Bitu index,Bitu count) {
Bit8u r,g,b;
if (index>255) return 0x1;
if (index+count>256) return 0x1;
if (index>255) return VESA_FAIL;
if (index+count>256) return VESA_FAIL;
IO_Write(0x3c7,(Bit8u)index);
while (count) {
r = IO_Read(0x3c9);
@ -327,114 +333,182 @@ Bit8u VESA_GetPalette(PhysPt data,Bitu index,Bitu count) {
data++;
count--;
}
return 0x00;
return VESA_SUCCESS;
}
// maximum offset for the S3 Trio64 is 10 bits
#define S3_MAX_OFFSET 0x3ff
Bit8u VESA_ScanLineLength(Bit8u subcall,Bit16u val, Bit16u & bytes,Bit16u & pixels,Bit16u & lines) {
Bit8u bpp;
// offset register: virtual scanline length
Bitu pixels_per_offset;
Bitu bytes_per_offset = 8;
Bitu vmemsize = vga.vmemsize;
Bitu new_offset = vga.config.scan_len;
Bitu screen_height = CurMode->sheight;
switch (CurMode->type) {
case M_TEXT:
vmemsize = 0x8000; // we have only the 32kB window here
screen_height = CurMode->theight;
pixels_per_offset = 16; // two characters each 8 pixels wide
bytes_per_offset = 4; // 2 characters + 2 attributes
break;
case M_LIN4:
bpp = 1;
pixels_per_offset = 16;
break;
case M_LIN8:
bpp=1;
pixels_per_offset = 8;
break;
case M_LIN15:
case M_LIN16:
bpp=2;
pixels_per_offset = 4;
break;
case M_LIN32:
bpp=4;
pixels_per_offset = 2;
break;
default:
return 0x1;
return VESA_MODE_UNSUPPORTED;
}
switch (subcall) {
case 0x00: /* Set in pixels */
if(CurMode->type==M_LIN4) vga.config.scan_len=val/2;
else vga.config.scan_len = (val * bpp);
case 0x00: // set scan length in pixels
new_offset = val / pixels_per_offset;
if (val % pixels_per_offset) new_offset++;
if (new_offset > S3_MAX_OFFSET)
return VESA_HW_UNSUPPORTED; // scanline too long
vga.config.scan_len = new_offset;
VGA_CheckScanLength();
break;
case 0x02: /* Set in bytes */
if(CurMode->type==M_LIN4) vga.config.scan_len = val*4;
else vga.config.scan_len = val;
case 0x01: // get current scanline length
// implemented at the end of this function
break;
case 0x03: /* Get maximum */
bytes=0x400*4;
pixels=bytes/bpp;
lines = (Bit16u)(vga.vmemsize / bytes);
return 0x00;
case 0x01: /* Get lengths */
case 0x02: // set scan length in bytes
new_offset = val / bytes_per_offset;
if (val % bytes_per_offset) new_offset++;
if (new_offset > S3_MAX_OFFSET)
return VESA_HW_UNSUPPORTED; // scanline too long
vga.config.scan_len = new_offset;
VGA_CheckScanLength();
break;
case 0x03: // get maximum scan line length
// the smaller of either the hardware maximum scanline length or
// the limit to get full y resolution of this mode
new_offset = S3_MAX_OFFSET;
if ((new_offset * bytes_per_offset * screen_height) > vmemsize)
new_offset = vmemsize / (bytes_per_offset * screen_height);
break;
default:
return 0x1; //Illegal call
return VESA_UNIMPLEMENTED;
}
if (subcall!=0x01) {
/* Write the scan line to video card the simple way */
if (vga.config.scan_len & 7)
vga.config.scan_len += 8;
vga.config.scan_len /= 8;
}
if(CurMode->type==M_LIN4) {
pixels=(vga.config.scan_len*16)/bpp;
bytes=vga.config.scan_len*2;
lines = (Bit16u)(vga.vmemsize /( bytes*4));
}
else {
pixels=(vga.config.scan_len*8)/bpp;
bytes=vga.config.scan_len*8;
lines = (Bit16u)(vga.vmemsize / bytes);
}
VGA_StartResize();
return 0x0;
// set up the return values
bytes = (Bit16u)(new_offset * bytes_per_offset);
pixels = (Bit16u)(new_offset * pixels_per_offset);
if (!bytes)
// return failure on division by zero
// some real VESA BIOS implementations may crash here
return VESA_FAIL;
lines = (Bit16u)(vmemsize / bytes);
if (CurMode->type==M_TEXT)
lines *= CurMode->cheight;
return VESA_SUCCESS;
}
Bit8u VESA_SetDisplayStart(Bit16u x,Bit16u y) {
//TODO Maybe do things differently with lowres double line modes?
Bitu start;
// TODO wait for retrace in case bl==0x80
Bitu pixels_per_offset;
Bitu panning_factor = 1;
switch (CurMode->type) {
case M_TEXT:
case M_LIN4:
start=vga.config.scan_len*16*y+x;
vga.config.display_start=start/8;
IO_Read(0x3da);
IO_Write(0x3c0,0x13+32);
IO_Write(0x3c0,start % 8);
pixels_per_offset = 16;
break;
case M_LIN8:
start=vga.config.scan_len*8*y+x;
vga.config.display_start=start/4;
IO_Read(0x3da);
IO_Write(0x3c0,0x13+32);
IO_Write(0x3c0,(start % 4)*2);
panning_factor = 2; // the panning register ignores bit0 in this mode
pixels_per_offset = 8;
break;
case M_LIN16:
case M_LIN15:
start=vga.config.scan_len*8*y+x*2;
vga.config.display_start=start/4;
case M_LIN16:
panning_factor = 2; // this may be DOSBox specific
pixels_per_offset = 4;
break;
case M_LIN32:
start=vga.config.scan_len*8*y+x*4;
vga.config.display_start=start/4;
pixels_per_offset = 2;
break;
default:
return 0x1;
return VESA_MODE_UNSUPPORTED;
}
return 0x00;
// We would have to divide y by the character height for text modes and
// write the remainder to the CRTC preset row scan register,
// but VBE2 BIOSes that actually get that far also don't.
// Only a VBE3 BIOS got it right.
Bitu virtual_screen_width = vga.config.scan_len * pixels_per_offset;
Bitu new_start_pixel = virtual_screen_width * y + x;
Bitu new_crtc_start = new_start_pixel / (pixels_per_offset/2);
Bitu new_panning = new_start_pixel % (pixels_per_offset/2);
new_panning *= panning_factor;
vga.config.display_start = new_crtc_start;
// Setting the panning register is nice as it allows for super smooth
// scrolling, but if we hit the retrace pulse there may be flicker as
// panning and display start are latched at different times.
IO_Read(0x3da); // reset attribute flipflop
IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on
IO_Write(0x3c0,new_panning);
return VESA_SUCCESS;
}
Bit8u VESA_GetDisplayStart(Bit16u & x,Bit16u & y) {
Bitu times=(vga.config.display_start*4)/(vga.config.scan_len*8);
Bitu rem=(vga.config.display_start*4) % (vga.config.scan_len*8);
Bitu pan=vga.config.pel_panning;
Bitu pixels_per_offset;
Bitu panning_factor = 1;
switch (CurMode->type) {
case M_TEXT:
pixels_per_offset = 16;
break;
case M_LIN4:
pixels_per_offset = 16;
break;
case M_LIN8:
y=(Bit16u)times;
x=(Bit16u)(rem+pan);
panning_factor = 2;
pixels_per_offset = 8;
break;
case M_LIN15:
case M_LIN16:
panning_factor = 2;
pixels_per_offset = 4;
break;
case M_LIN32:
pixels_per_offset = 2;
break;
default:
return 0x1;
return VESA_MODE_UNSUPPORTED;
}
return 0x00;
IO_Read(0x3da); // reset attribute flipflop
IO_Write(0x3c0,0x13 | 0x20); // panning register, screen on
Bit8u panning = IO_Read(0x3c1);
Bitu virtual_screen_width = vga.config.scan_len * pixels_per_offset;
Bitu start_pixel = vga.config.display_start * (pixels_per_offset/2)
+ panning / panning_factor;
y = start_pixel / virtual_screen_width;
x = start_pixel % virtual_screen_width;
return VESA_SUCCESS;
}
static Bitu VESA_SetWindow(void) {
@ -453,6 +527,10 @@ static Bitu VESA_PMSetPalette(void) {
return 0;
}
static Bitu VESA_PMSetStart(void) {
// This function is from VBE2 and directly sets the VGA
// display start address.
// TODO wait for retrace in case bl==0x80
Bit32u start = (reg_dx << 16) | reg_cx;
vga.config.display_start = start;
return 0;