diff options
| author | Travis Bradshaw <travis.bradshaw@idsoftware.com> | 2012-01-31 14:32:09 -0600 |
|---|---|---|
| committer | Travis Bradshaw <travis.bradshaw@idsoftware.com> | 2012-01-31 14:32:09 -0600 |
| commit | 0023db327bc1db00068284b70e1db45857aeee35 (patch) | |
| tree | 01dd8366d20bd674741cfe8ceb069d82a2e33973 /WinQuake/vid_ext.c | |
| download | quake-pum-0023db327bc1db00068284b70e1db45857aeee35.tar.gz | |
The Quake sources as originally release under the GPL license on December 21, 1999
Diffstat (limited to 'WinQuake/vid_ext.c')
| -rw-r--r-- | WinQuake/vid_ext.c | 795 |
1 files changed, 795 insertions, 0 deletions
diff --git a/WinQuake/vid_ext.c b/WinQuake/vid_ext.c new file mode 100644 index 0000000..4188e2b --- /dev/null +++ b/WinQuake/vid_ext.c @@ -0,0 +1,795 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// +// vid_ext.c: extended video modes +// in this implementation, VESA-specific DOS video stuff +// + +// TODO: make dependencies on vid_vga.c explicit or eliminate them + +#include <stdlib.h> +#include <dos.h> + +#include "quakedef.h" +#include "d_local.h" +#include "dosisms.h" +#include "vid_dos.h" +#include <dpmi.h> + +#define MODE_SUPPORTED_IN_HW 0x0001 +#define COLOR_MODE 0x0008 +#define GRAPHICS_MODE 0x0010 +#define VGA_INCOMPATIBLE 0x0020 +#define LINEAR_FRAME_BUFFER 0x0080 + +#define LINEAR_MODE 0x4000 + +#define VESA_DONT_WAIT_VSYNC 0 // when page flipping +#define VESA_WAIT_VSYNC 0x80 + +#define MAX_VESA_MODES 30 // we'll just take the first 30 if there + // are more +typedef struct { + int pages[3]; // either 2 or 3 is valid + int vesamode; // LINEAR_MODE set if linear mode + void *plinearmem; // linear address of start of frame buffer + qboolean vga_incompatible; +} vesa_extra_t; + +static vmode_t vesa_modes[MAX_VESA_MODES] = + {{NULL, NULL, " ********* VESA modes ********* "}}; +static vesa_extra_t vesa_extra[MAX_VESA_MODES]; +static char names[MAX_VESA_MODES][10]; + +extern regs_t regs; + +static int VID_currentpage; +static int VID_displayedpage; +static int *VID_pagelist; +static byte *VID_membase; +static int VID_banked; + +typedef struct +{ + int modenum; + int mode_attributes; + int winasegment; + int winbsegment; + int bytes_per_scanline; // bytes per logical scanline (+16) + int win; // window number (A=0, B=1) + int win_size; // window size (+6) + int granularity; // how finely i can set the window in vid mem (+4) + int width, height; // displayed width and height (+18, +20) + int bits_per_pixel; // er, better be 8, 15, 16, 24, or 32 (+25) + int bytes_per_pixel; // er, better be 1, 2, or 4 + int memory_model; // and better be 4 or 6, packed or direct color (+27) + int num_pages; // number of complete frame buffer pages (+29) + int red_width; // the # of bits in the red component (+31) + int red_pos; // the bit position of the red component (+32) + int green_width; // etc.. (+33) + int green_pos; // (+34) + int blue_width; // (+35) + int blue_pos; // (+36) + int pptr; + int pagesize; + int numpages; +} modeinfo_t; + +static modeinfo_t modeinfo; + +// all bytes to avoid problems with compiler field packing +typedef struct vbeinfoblock_s { + byte VbeSignature[4]; + byte VbeVersion[2]; + byte OemStringPtr[4]; + byte Capabilities[4]; + byte VideoModePtr[4]; + byte TotalMemory[2]; + byte OemSoftwareRev[2]; + byte OemVendorNamePtr[4]; + byte OemProductNamePtr[4]; + byte OemProductRevPtr[4]; + byte Reserved[222]; + byte OemData[256]; +} vbeinfoblock_t; + +static int totalvidmem; +static byte *ppal; +qboolean vsync_exists, de_exists; + +qboolean VID_ExtraGetModeInfo(int modenum); +int VID_ExtraInitMode (viddef_t *vid, vmode_t *pcurrentmode); +void VID_ExtraSwapBuffers (viddef_t *vid, vmode_t *pcurrentmode, + vrect_t *rects); + + +/* +================ +VGA_BankedBeginDirectRect +================ +*/ +void VGA_BankedBeginDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, + int x, int y, byte *pbitmap, int width, int height) +{ + + if (!lvid->direct) + return; + + regs.x.ax = 0x4f05; + regs.x.bx = 0; + regs.x.dx = VID_displayedpage; + dos_int86(0x10); + + VGA_BeginDirectRect (lvid, pcurrentmode, x, y, pbitmap, width, height); + + regs.x.ax = 0x4f05; + regs.x.bx = 0; + regs.x.dx = VID_currentpage; + dos_int86(0x10); +} + + +/* +================ +VGA_BankedEndDirectRect +================ +*/ +void VGA_BankedEndDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, + int x, int y, int width, int height) +{ + + if (!lvid->direct) + return; + + regs.x.ax = 0x4f05; + regs.x.bx = 0; + regs.x.dx = VID_displayedpage; + dos_int86(0x10); + + VGA_EndDirectRect (lvid, pcurrentmode, x, y, width, height); + + regs.x.ax = 0x4f05; + regs.x.bx = 0; + regs.x.dx = VID_currentpage; + dos_int86(0x10); +} + + +/* +================ +VID_SetVESAPalette +================ +*/ +void VID_SetVESAPalette (viddef_t *lvid, vmode_t *pcurrentmode, + unsigned char *pal) +{ + int i; + byte *pp; + + UNUSED(lvid); + UNUSED(pcurrentmode); + + pp = ppal; + + for (i=0 ; i<256 ; i++) + { + pp[2] = pal[0] >> 2; + pp[1] = pal[1] >> 2; + pp[0] = pal[2] >> 2; + pp += 4; + pal += 3; + } + + regs.x.ax = 0x4F09; + regs.x.bx = 0; + regs.x.cx = 256; + regs.x.dx = 0; + regs.x.es = ptr2real(ppal) >> 4; + regs.x.di = ptr2real(ppal) & 0xf; + dos_int86(0x10); + + if (regs.x.ax != 0x4f) + Sys_Error ("Unable to load VESA palette\n"); +} + + + + +/* +================ +VID_ExtraFarToLinear +================ +*/ +void *VID_ExtraFarToLinear (void *ptr) +{ + int temp; + + temp = (int)ptr; + return real2ptr(((temp & 0xFFFF0000) >> 12) + (temp & 0xFFFF)); +} + + +/* +================ +VID_ExtraWaitDisplayEnable +================ +*/ +void VID_ExtraWaitDisplayEnable () +{ + while ((inportb (0x3DA) & 0x01) == 1) + ; +} + + +/* +================ +VID_ExtraVidLookForState +================ +*/ +qboolean VID_ExtraVidLookForState (unsigned state, unsigned mask) +{ + int i; + double starttime, time; + + starttime = Sys_FloatTime (); + + do + { + for (i=0 ; i<100000 ; i++) + { + if ((inportb (0x3DA) & mask) == state) + return true; + } + + time = Sys_FloatTime (); + } while ((time - starttime) < 0.1); + + return false; +} + + +/* +================ +VID_ExtraStateFound +================ +*/ +qboolean VID_ExtraStateFound (unsigned state) +{ + int i, workingstate; + + workingstate = 0; + + for (i=0 ; i<10 ; i++) + { + if (!VID_ExtraVidLookForState(workingstate, state)) + { + return false; + } + + workingstate ^= state; + } + + return true; +} + + +/* +================ +VID_InitExtra +================ +*/ +void VID_InitExtra (void) +{ + int nummodes; + short *pmodenums; + vbeinfoblock_t *pinfoblock; + __dpmi_meminfo phys_mem_info; + + pinfoblock = dos_getmemory(sizeof(vbeinfoblock_t)); + + *(long *)pinfoblock->VbeSignature = 'V' + ('B'<<8) + ('E'<<16) + ('2'<<24); + +// see if VESA support is available + regs.x.ax = 0x4f00; + regs.x.es = ptr2real(pinfoblock) >> 4; + regs.x.di = ptr2real(pinfoblock) & 0xf; + dos_int86(0x10); + + if (regs.x.ax != 0x4f) + return; // no VESA support + + if (pinfoblock->VbeVersion[1] < 0x02) + return; // not VESA 2.0 or greater + + Con_Printf ("VESA 2.0 compliant adapter:\n%s\n", + VID_ExtraFarToLinear (*(byte **)&pinfoblock->OemStringPtr[0])); + + totalvidmem = *(unsigned short *)&pinfoblock->TotalMemory[0] << 16; + + pmodenums = (short *) + VID_ExtraFarToLinear (*(byte **)&pinfoblock->VideoModePtr[0]); + +// find 8 bit modes until we either run out of space or run out of modes + nummodes = 0; + + while ((*pmodenums != -1) && (nummodes < MAX_VESA_MODES)) + { + if (VID_ExtraGetModeInfo (*pmodenums)) + { + vesa_modes[nummodes].pnext = &vesa_modes[nummodes+1]; + if (modeinfo.width > 999) + { + if (modeinfo.height > 999) + { + sprintf (&names[nummodes][0], "%4dx%4d", modeinfo.width, + modeinfo.height); + names[nummodes][9] = 0; + } + else + { + sprintf (&names[nummodes][0], "%4dx%3d", modeinfo.width, + modeinfo.height); + names[nummodes][8] = 0; + } + } + else + { + if (modeinfo.height > 999) + { + sprintf (&names[nummodes][0], "%3dx%4d", modeinfo.width, + modeinfo.height); + names[nummodes][8] = 0; + } + else + { + sprintf (&names[nummodes][0], "%3dx%3d", modeinfo.width, + modeinfo.height); + names[nummodes][7] = 0; + } + } + + vesa_modes[nummodes].name = &names[nummodes][0]; + vesa_modes[nummodes].width = modeinfo.width; + vesa_modes[nummodes].height = modeinfo.height; + vesa_modes[nummodes].aspect = + ((float)modeinfo.height / (float)modeinfo.width) * + (320.0 / 240.0); + vesa_modes[nummodes].rowbytes = modeinfo.bytes_per_scanline; + vesa_modes[nummodes].planar = 0; + vesa_modes[nummodes].pextradata = &vesa_extra[nummodes]; + vesa_modes[nummodes].setmode = VID_ExtraInitMode; + vesa_modes[nummodes].swapbuffers = VID_ExtraSwapBuffers; + vesa_modes[nummodes].setpalette = VID_SetVESAPalette; + + if (modeinfo.mode_attributes & LINEAR_FRAME_BUFFER) + { + // add linear bit to mode for linear modes + vesa_extra[nummodes].vesamode = modeinfo.modenum | LINEAR_MODE; + vesa_extra[nummodes].pages[0] = 0; + vesa_extra[nummodes].pages[1] = modeinfo.pagesize; + vesa_extra[nummodes].pages[2] = modeinfo.pagesize * 2; + vesa_modes[nummodes].numpages = modeinfo.numpages; + + vesa_modes[nummodes].begindirectrect = VGA_BeginDirectRect; + vesa_modes[nummodes].enddirectrect = VGA_EndDirectRect; + + phys_mem_info.address = (int)modeinfo.pptr; + phys_mem_info.size = 0x400000; + + if (__dpmi_physical_address_mapping(&phys_mem_info)) + goto NextMode; + + vesa_extra[nummodes].plinearmem = + real2ptr (phys_mem_info.address); + } + else + { + // banked at 0xA0000 + vesa_extra[nummodes].vesamode = modeinfo.modenum; + vesa_extra[nummodes].pages[0] = 0; + vesa_extra[nummodes].plinearmem = + real2ptr(modeinfo.winasegment<<4); + + vesa_modes[nummodes].begindirectrect = + VGA_BankedBeginDirectRect; + vesa_modes[nummodes].enddirectrect = VGA_BankedEndDirectRect; + vesa_extra[nummodes].pages[1] = modeinfo.pagesize; + vesa_extra[nummodes].pages[2] = modeinfo.pagesize * 2; + vesa_modes[nummodes].numpages = modeinfo.numpages; + } + + vesa_extra[nummodes].vga_incompatible = + modeinfo.mode_attributes & VGA_INCOMPATIBLE; + + nummodes++; + } +NextMode: + pmodenums++; + } + +// add the VESA modes at the start of the mode list (if there are any) + if (nummodes) + { + vesa_modes[nummodes-1].pnext = pvidmodes; + pvidmodes = &vesa_modes[0]; + numvidmodes += nummodes; + ppal = dos_getmemory(256*4); + } + + dos_freememory(pinfoblock); +} + + +/* +================ +VID_ExtraGetModeInfo +================ +*/ +qboolean VID_ExtraGetModeInfo(int modenum) +{ + char *infobuf; + int numimagepages; + + infobuf = dos_getmemory(256); + + regs.x.ax = 0x4f01; + regs.x.cx = modenum; + regs.x.es = ptr2real(infobuf) >> 4; + regs.x.di = ptr2real(infobuf) & 0xf; + dos_int86(0x10); + if (regs.x.ax != 0x4f) + { + return false; + } + else + { + modeinfo.modenum = modenum; + modeinfo.bits_per_pixel = *(char*)(infobuf+25); + modeinfo.bytes_per_pixel = (modeinfo.bits_per_pixel+1)/8; + modeinfo.width = *(short*)(infobuf+18); + modeinfo.height = *(short*)(infobuf+20); + + // we do only 8-bpp in software + if ((modeinfo.bits_per_pixel != 8) || + (modeinfo.bytes_per_pixel != 1) || + (modeinfo.width > MAXWIDTH) || + (modeinfo.height > MAXHEIGHT)) + { + return false; + } + + modeinfo.mode_attributes = *(short*)infobuf; + + // we only want color graphics modes that are supported by the hardware + if ((modeinfo.mode_attributes & + (MODE_SUPPORTED_IN_HW | COLOR_MODE | GRAPHICS_MODE)) != + (MODE_SUPPORTED_IN_HW | COLOR_MODE | GRAPHICS_MODE)) + { + return false; + } + + // we only work with linear frame buffers, except for 320x200, which can + // effectively be linear when banked at 0xA000 + if (!(modeinfo.mode_attributes & LINEAR_FRAME_BUFFER)) + { + if ((modeinfo.width != 320) || (modeinfo.height != 200)) + return false; + } + + modeinfo.bytes_per_scanline = *(short*)(infobuf+16); + + modeinfo.pagesize = modeinfo.bytes_per_scanline * modeinfo.height; + + if (modeinfo.pagesize > totalvidmem) + return false; + + // force to one page if the adapter reports it doesn't support more pages + // than that, no matter how much memory it has--it may not have hardware + // support for page flipping + numimagepages = *(unsigned char *)(infobuf+29); + + if (numimagepages <= 0) + { + // wrong, but there seems to be an ATI VESA driver that reports 0 + modeinfo.numpages = 1; + } + else if (numimagepages < 3) + { + modeinfo.numpages = numimagepages; + } + else + { + modeinfo.numpages = 3; + } + + if (*(char*)(infobuf+2) & 5) + { + modeinfo.winasegment = *(unsigned short*)(infobuf+8); + modeinfo.win = 0; + } + else if (*(char*)(infobuf+3) & 5) + { + modeinfo.winbsegment = *(unsigned short*)(infobuf+8); + modeinfo.win = 1; + } + modeinfo.granularity = *(short*)(infobuf+4) * 1024; + modeinfo.win_size = *(short*)(infobuf+6) * 1024; + modeinfo.bits_per_pixel = *(char*)(infobuf+25); + modeinfo.bytes_per_pixel = (modeinfo.bits_per_pixel+1)/8; + modeinfo.memory_model = *(unsigned char*)(infobuf+27); + modeinfo.num_pages = *(char*)(infobuf+29) + 1; + + modeinfo.red_width = *(char*)(infobuf+31); + modeinfo.red_pos = *(char*)(infobuf+32); + modeinfo.green_width = *(char*)(infobuf+33); + modeinfo.green_pos = *(char*)(infobuf+34); + modeinfo.blue_width = *(char*)(infobuf+35); + modeinfo.blue_pos = *(char*)(infobuf+36); + + modeinfo.pptr = *(long *)(infobuf+40); + +#if 0 + printf("VID: (VESA) info for mode 0x%x\n", modeinfo.modenum); + printf(" mode attrib = 0x%0x\n", modeinfo.mode_attributes); + printf(" win a attrib = 0x%0x\n", *(unsigned char*)(infobuf+2)); + printf(" win b attrib = 0x%0x\n", *(unsigned char*)(infobuf+3)); + printf(" win a seg 0x%0x\n", (int) modeinfo.winasegment); + printf(" win b seg 0x%0x\n", (int) modeinfo.winbsegment); + printf(" bytes per scanline = %d\n", + modeinfo.bytes_per_scanline); + printf(" width = %d, height = %d\n", modeinfo.width, + modeinfo.height); + printf(" win = %c\n", 'A' + modeinfo.win); + printf(" win granularity = %d\n", modeinfo.granularity); + printf(" win size = %d\n", modeinfo.win_size); + printf(" bits per pixel = %d\n", modeinfo.bits_per_pixel); + printf(" bytes per pixel = %d\n", modeinfo.bytes_per_pixel); + printf(" memory model = 0x%x\n", modeinfo.memory_model); + printf(" num pages = %d\n", modeinfo.num_pages); + printf(" red width = %d\n", modeinfo.red_width); + printf(" red pos = %d\n", modeinfo.red_pos); + printf(" green width = %d\n", modeinfo.green_width); + printf(" green pos = %d\n", modeinfo.green_pos); + printf(" blue width = %d\n", modeinfo.blue_width); + printf(" blue pos = %d\n", modeinfo.blue_pos); + printf(" phys mem = %x\n", modeinfo.pptr); +#endif + } + + dos_freememory(infobuf); + + return true; +} + + +/* +================ +VID_ExtraInitMode +================ +*/ +int VID_ExtraInitMode (viddef_t *lvid, vmode_t *pcurrentmode) +{ + vesa_extra_t *pextra; + int pageoffset; + + pextra = pcurrentmode->pextradata; + + if (vid_nopageflip.value) + lvid->numpages = 1; + else + lvid->numpages = pcurrentmode->numpages; + +// clean up any old vid buffer lying around, alloc new if needed + if (!VGA_FreeAndAllocVidbuffer (lvid, lvid->numpages == 1)) + return -1; // memory alloc failed + +// clear the screen and wait for the next frame. VGA_pcurmode, which +// VGA_ClearVideoMem relies on, is guaranteed to be set because mode 0 is +// always the first mode set in a session + if (VGA_pcurmode) + VGA_ClearVideoMem (VGA_pcurmode->planar); + +// set the mode + regs.x.ax = 0x4f02; + regs.x.bx = pextra->vesamode; + dos_int86(0x10); + + if (regs.x.ax != 0x4f) + return 0; + + VID_banked = !(pextra->vesamode & LINEAR_MODE); + VID_membase = pextra->plinearmem; + VGA_width = lvid->width; + VGA_height = lvid->height; + VGA_rowbytes = lvid->rowbytes; + + lvid->colormap = host_colormap; + + VID_pagelist = &pextra->pages[0]; + +// wait for display enable by default only when triple-buffering on a VGA- +// compatible machine that actually has a functioning display enable status + vsync_exists = VID_ExtraStateFound (0x08); + de_exists = VID_ExtraStateFound (0x01); + + if (!pextra->vga_incompatible && + (lvid->numpages == 3) && + de_exists && + (_vid_wait_override.value == 0.0)) + { + Cvar_SetValue ("vid_wait", (float)VID_WAIT_DISPLAY_ENABLE); + + VID_displayedpage = 0; + VID_currentpage = 1; + } + else + { + if ((lvid->numpages == 1) && (_vid_wait_override.value == 0.0)) + { + Cvar_SetValue ("vid_wait", (float)VID_WAIT_NONE); + VID_displayedpage = VID_currentpage = 0; + } + else + { + Cvar_SetValue ("vid_wait", (float)VID_WAIT_VSYNC); + + VID_displayedpage = 0; + + if (lvid->numpages > 1) + VID_currentpage = 1; + else + VID_currentpage = 0; + } + } + +// TODO: really should be a call to a function + pageoffset = VID_pagelist[VID_displayedpage]; + + regs.x.ax = 0x4f07; + regs.x.bx = 0x80; // wait for vsync so we know page 0 is visible + regs.x.cx = pageoffset % VGA_rowbytes; + regs.x.dx = pageoffset / VGA_rowbytes; + dos_int86(0x10); + + if (VID_banked) + { + regs.x.ax = 0x4f05; + regs.x.bx = 0; + regs.x.dx = VID_currentpage; + dos_int86(0x10); + + VGA_pagebase = VID_membase; + } + else + { + VGA_pagebase = VID_membase + VID_pagelist[VID_currentpage]; + } + + if (lvid->numpages > 1) + { + lvid->buffer = VGA_pagebase; + lvid->conbuffer = lvid->buffer; + } + else + { + lvid->rowbytes = lvid->width; + } + + lvid->direct = VGA_pagebase; + lvid->conrowbytes = lvid->rowbytes; + lvid->conwidth = lvid->width; + lvid->conheight = lvid->height; + + lvid->maxwarpwidth = WARP_WIDTH; + lvid->maxwarpheight = WARP_HEIGHT; + + VGA_pcurmode = pcurrentmode; + + D_InitCaches (vid_surfcache, vid_surfcachesize); + + return 1; +} + + +/* +================ +VID_ExtraSwapBuffers +================ +*/ +void VID_ExtraSwapBuffers (viddef_t *lvid, vmode_t *pcurrentmode, + vrect_t *rects) +{ + int pageoffset; + + UNUSED(rects); + UNUSED(pcurrentmode); + + pageoffset = VID_pagelist[VID_currentpage]; + +// display the newly finished page + if (lvid->numpages > 1) + { + // page flipped + regs.x.ax = 0x4f07; + + if (vid_wait.value != VID_WAIT_VSYNC) + { + if ((vid_wait.value == VID_WAIT_DISPLAY_ENABLE) && de_exists) + VID_ExtraWaitDisplayEnable (); + + regs.x.bx = VESA_DONT_WAIT_VSYNC; + } + else + { + regs.x.bx = VESA_WAIT_VSYNC; // double buffered has to wait + } + + regs.x.cx = pageoffset % VGA_rowbytes; + regs.x.dx = pageoffset / VGA_rowbytes; + dos_int86(0x10); + + VID_displayedpage = VID_currentpage; + if (++VID_currentpage >= lvid->numpages) + VID_currentpage = 0; + + // + // set the new write window if this is a banked mode; otherwise, set the + // new address to which to write + // + if (VID_banked) + { + regs.x.ax = 0x4f05; + regs.x.bx = 0; + regs.x.dx = VID_currentpage; + dos_int86(0x10); + } + else + { + lvid->direct = lvid->buffer; // direct drawing goes to the + // currently displayed page + lvid->buffer = VID_membase + VID_pagelist[VID_currentpage]; + lvid->conbuffer = lvid->buffer; + } + + VGA_pagebase = lvid->buffer; + } + else + { + // non-page-flipped + if (vsync_exists && (vid_wait.value == VID_WAIT_VSYNC)) + { + VGA_WaitVsync (); + } + + while (rects) + { + VGA_UpdateLinearScreen ( + lvid->buffer + rects->x + (rects->y * lvid->rowbytes), + VGA_pagebase + rects->x + (rects->y * VGA_rowbytes), + rects->width, + rects->height, + lvid->rowbytes, + VGA_rowbytes); + + rects = rects->pnext; + } + } +} + |
