quakeforge/libs/video/targets/vid_ext.c
2002-11-05 19:12:51 +00:00

766 lines
19 KiB
C

/*
vid_ext.c
@description@
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:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
static const char rcsid[] =
"$Id$";
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include <dos.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];
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;
/*
================
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");
}
/*
================
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_DoubleTime ();
do {
for (i = 0; i < 100000; i++) {
if ((inportb (0x3DA) & mask) == state)
return true;
}
time = Sys_DoubleTime ();
} 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) {
snprintf (&names[nummodes][0], sizeof (&names[nummodes][0]),
"%4dx%4d", modeinfo.width, modeinfo.height);
names[nummodes][9] = 0;
} else {
snprintf (&names[nummodes][0], sizeof (&names[nummodes][0]),
"%4dx%3d", modeinfo.width, modeinfo.height);
names[nummodes][8] = 0;
}
} else {
if (modeinfo.height > 999) {
snprintf (&names[nummodes][0], sizeof (&names[nummodes][0]),
"%3dx%4d", modeinfo.width, modeinfo.height);
names[nummodes][8] = 0;
} else {
snprintf (&names[nummodes][0], sizeof (&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
Sys_Printf ("VID: (VESA) info for mode 0x%x\n", modeinfo.modenum);
Sys_Printf (" mode attrib = 0x%0x\n", modeinfo.mode_attributes);
Sys_Printf (" win a attrib = 0x%0x\n", *(unsigned char *) (infobuf + 2));
Sys_Printf (" win b attrib = 0x%0x\n", *(unsigned char *) (infobuf + 3));
Sys_Printf (" win a seg 0x%0x\n", (int) modeinfo.winasegment);
Sys_Printf (" win b seg 0x%0x\n", (int) modeinfo.winbsegment);
Sys_Printf (" bytes per scanline = %d\n", modeinfo.bytes_per_scanline);
Sys_Printf (" width = %d, height = %d\n", modeinfo.width, modeinfo.height);
Sys_Printf (" win = %c\n", 'A' + modeinfo.win);
Sys_Printf (" win granularity = %d\n", modeinfo.granularity);
Sys_Printf (" win size = %d\n", modeinfo.win_size);
Sys_Printf (" bits per pixel = %d\n", modeinfo.bits_per_pixel);
Sys_Printf (" bytes per pixel = %d\n", modeinfo.bytes_per_pixel);
Sys_Printf (" memory model = 0x%x\n", modeinfo.memory_model);
Sys_Printf (" num pages = %d\n", modeinfo.num_pages);
Sys_Printf (" red width = %d\n", modeinfo.red_width);
Sys_Printf (" red pos = %d\n", modeinfo.red_pos);
Sys_Printf (" green width = %d\n", modeinfo.green_width);
Sys_Printf (" green pos = %d\n", modeinfo.green_pos);
Sys_Printf (" blue width = %d\n", modeinfo.blue_width);
Sys_Printf (" blue pos = %d\n", modeinfo.blue_pos);
Sys_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->int_val)
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 = vid_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->int_val) {
Cvar_SetValue (vid_wait, (float) VID_WAIT_DISPLAY_ENABLE);
VID_displayedpage = 0;
VID_currentpage = 1;
} else {
if ((lvid->numpages == 1) && !_vid_wait_override->int_val) {
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->int_val != VID_WAIT_VSYNC) {
if ((vid_wait->int_val == 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->int_val == 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;
}
}
}
void
VID_HandlePause (qboolean pause)
{
}