/* | |
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_vga.c: VGA-specific DOS video stuff | |
// | |
// TODO: proper handling of page-swap failure | |
#include <dos.h> | |
#include "quakedef.h" | |
#include "d_local.h" | |
#include "dosisms.h" | |
#include "vid_dos.h" | |
#include <dpmi.h> | |
extern regs_t regs; | |
int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes; | |
byte *VGA_pagebase; | |
vmode_t *VGA_pcurmode; | |
static int VGA_planar; | |
static int VGA_numpages; | |
static int VGA_buffersize; | |
void *vid_surfcache; | |
int vid_surfcachesize; | |
int VGA_highhunkmark; | |
#include "vgamodes.h" | |
#define NUMVIDMODES (sizeof(vgavidmodes) / sizeof(vgavidmodes[0])) | |
void VGA_UpdatePlanarScreen (void *srcbuffer); | |
static byte backingbuf[48*24]; | |
/* | |
================ | |
VGA_BeginDirectRect | |
================ | |
*/ | |
void VGA_BeginDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x, | |
int y, byte *pbitmap, int width, int height) | |
{ | |
int i, j, k, plane, reps, repshift; | |
if (!lvid->direct) | |
return; | |
if (lvid->aspect > 1.5) | |
{ | |
reps = 2; | |
repshift = 1; | |
} | |
else | |
{ | |
reps = 1; | |
repshift = 0; | |
} | |
if (pcurrentmode->planar) | |
{ | |
for (plane=0 ; plane<4 ; plane++) | |
{ | |
// select the correct plane for reading and writing | |
outportb (SC_INDEX, MAP_MASK); | |
outportb (SC_DATA, 1 << plane); | |
outportb (GC_INDEX, READ_MAP); | |
outportb (GC_DATA, plane); | |
for (i=0 ; i<(height << repshift) ; i += reps) | |
{ | |
for (k=0 ; k<reps ; k++) | |
{ | |
for (j=0 ; j<(width >> 2) ; j++) | |
{ | |
backingbuf[(i + k) * 24 + (j << 2) + plane] = | |
lvid->direct[(y + i + k) * VGA_rowbytes + | |
(x >> 2) + j]; | |
lvid->direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = | |
pbitmap[(i >> repshift) * 24 + | |
(j << 2) + plane]; | |
} | |
} | |
} | |
} | |
} | |
else | |
{ | |
for (i=0 ; i<(height << repshift) ; i += reps) | |
{ | |
for (j=0 ; j<reps ; j++) | |
{ | |
memcpy (&backingbuf[(i + j) * 24], | |
lvid->direct + x + ((y << repshift) + i + j) * | |
VGA_rowbytes, | |
width); | |
memcpy (lvid->direct + x + ((y << repshift) + i + j) * | |
VGA_rowbytes, | |
&pbitmap[(i >> repshift) * width], | |
width); | |
} | |
} | |
} | |
} | |
/* | |
================ | |
VGA_EndDirectRect | |
================ | |
*/ | |
void VGA_EndDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x, | |
int y, int width, int height) | |
{ | |
int i, j, k, plane, reps, repshift; | |
if (!lvid->direct) | |
return; | |
if (lvid->aspect > 1.5) | |
{ | |
reps = 2; | |
repshift = 1; | |
} | |
else | |
{ | |
reps = 1; | |
repshift = 0; | |
} | |
if (pcurrentmode->planar) | |
{ | |
for (plane=0 ; plane<4 ; plane++) | |
{ | |
// select the correct plane for writing | |
outportb (SC_INDEX, MAP_MASK); | |
outportb (SC_DATA, 1 << plane); | |
for (i=0 ; i<(height << repshift) ; i += reps) | |
{ | |
for (k=0 ; k<reps ; k++) | |
{ | |
for (j=0 ; j<(width >> 2) ; j++) | |
{ | |
lvid->direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = | |
backingbuf[(i + k) * 24 + (j << 2) + plane]; | |
} | |
} | |
} | |
} | |
} | |
else | |
{ | |
for (i=0 ; i<(height << repshift) ; i += reps) | |
{ | |
for (j=0 ; j<reps ; j++) | |
{ | |
memcpy (lvid->direct + x + ((y << repshift) + i + j) * | |
VGA_rowbytes, | |
&backingbuf[(i + j) * 24], | |
width); | |
} | |
} | |
} | |
} | |
/* | |
================ | |
VGA_Init | |
================ | |
*/ | |
void VGA_Init (void) | |
{ | |
int i; | |
// link together all the VGA modes | |
for (i=0 ; i<(NUMVIDMODES - 1) ; i++) | |
{ | |
vgavidmodes[i].pnext = &vgavidmodes[i+1]; | |
} | |
// add the VGA modes at the start of the mode list | |
vgavidmodes[NUMVIDMODES-1].pnext = pvidmodes; | |
pvidmodes = &vgavidmodes[0]; | |
numvidmodes += NUMVIDMODES; | |
} | |
/* | |
================ | |
VGA_WaitVsync | |
================ | |
*/ | |
void VGA_WaitVsync (void) | |
{ | |
while ((inportb (0x3DA) & 0x08) == 0) | |
; | |
} | |
/* | |
================ | |
VGA_ClearVideoMem | |
================ | |
*/ | |
void VGA_ClearVideoMem (int planar) | |
{ | |
if (planar) | |
{ | |
// enable all planes for writing | |
outportb (SC_INDEX, MAP_MASK); | |
outportb (SC_DATA, 0x0F); | |
} | |
Q_memset (VGA_pagebase, 0, VGA_rowbytes * VGA_height); | |
} | |
/* | |
================ | |
VGA_FreeAndAllocVidbuffer | |
================ | |
*/ | |
qboolean VGA_FreeAndAllocVidbuffer (viddef_t *lvid, int allocnewbuffer) | |
{ | |
int tsize, tbuffersize; | |
if (allocnewbuffer) | |
{ | |
// alloc an extra line in case we want to wrap, and allocate the z-buffer | |
tbuffersize = (lvid->rowbytes * (lvid->height + 1)) + | |
(lvid->width * lvid->height * sizeof (*d_pzbuffer)); | |
} | |
else | |
{ | |
// just allocate the z-buffer | |
tbuffersize = lvid->width * lvid->height * sizeof (*d_pzbuffer); | |
} | |
tsize = D_SurfaceCacheForRes (lvid->width, lvid->height); | |
tbuffersize += tsize; | |
// see if there's enough memory, allowing for the normal mode 0x13 pixel, | |
// z, and surface buffers | |
if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + | |
0x10000 * 3) < minimum_memory) | |
{ | |
Con_Printf ("Not enough memory for video mode\n"); | |
VGA_pcurmode = NULL; // so no further accesses to the buffer are | |
// attempted, particularly when clearing | |
return false; // not enough memory for mode | |
} | |
VGA_buffersize = tbuffersize; | |
vid_surfcachesize = tsize; | |
if (d_pzbuffer) | |
{ | |
D_FlushCaches (); | |
Hunk_FreeToHighMark (VGA_highhunkmark); | |
d_pzbuffer = NULL; | |
} | |
VGA_highhunkmark = Hunk_HighMark (); | |
d_pzbuffer = Hunk_HighAllocName (VGA_buffersize, "video"); | |
vid_surfcache = (byte *)d_pzbuffer | |
+ lvid->width * lvid->height * sizeof (*d_pzbuffer); | |
if (allocnewbuffer) | |
{ | |
lvid->buffer = (void *)( (byte *)vid_surfcache + vid_surfcachesize); | |
lvid->conbuffer = lvid->buffer; | |
} | |
return true; | |
} | |
/* | |
================ | |
VGA_CheckAdequateMem | |
================ | |
*/ | |
qboolean VGA_CheckAdequateMem (int width, int height, int rowbytes, | |
int allocnewbuffer) | |
{ | |
int tbuffersize; | |
tbuffersize = width * height * sizeof (*d_pzbuffer); | |
if (allocnewbuffer) | |
{ | |
// alloc an extra line in case we want to wrap, and allocate the z-buffer | |
tbuffersize += (rowbytes * (height + 1)); | |
} | |
tbuffersize += D_SurfaceCacheForRes (width, height); | |
// see if there's enough memory, allowing for the normal mode 0x13 pixel, | |
// z, and surface buffers | |
if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + | |
0x10000 * 3) < minimum_memory) | |
{ | |
return false; // not enough memory for mode | |
} | |
return true; | |
} | |
/* | |
================ | |
VGA_InitMode | |
================ | |
*/ | |
int VGA_InitMode (viddef_t *lvid, vmode_t *pcurrentmode) | |
{ | |
vextra_t *pextra; | |
pextra = pcurrentmode->pextradata; | |
if (!VGA_FreeAndAllocVidbuffer (lvid, pextra->vidbuffer)) | |
return -1; // memory alloc failed | |
if (VGA_pcurmode) | |
VGA_ClearVideoMem (VGA_pcurmode->planar); | |
// mode 0x13 is the base for all the Mode X-class mode sets | |
regs.h.ah = 0; | |
regs.h.al = 0x13; | |
dos_int86(0x10); | |
VGA_pagebase = (void *)real2ptr(0xa0000); | |
lvid->direct = (pixel_t *)VGA_pagebase; | |
// set additional registers as needed | |
VideoRegisterSet (pextra->pregset); | |
VGA_numpages = 1; | |
lvid->numpages = VGA_numpages; | |
VGA_width = (lvid->width + 0x1F) & ~0x1F; | |
VGA_height = lvid->height; | |
VGA_planar = pcurrentmode->planar; | |
if (VGA_planar) | |
VGA_rowbytes = lvid->rowbytes / 4; | |
else | |
VGA_rowbytes = lvid->rowbytes; | |
VGA_bufferrowbytes = lvid->rowbytes; | |
lvid->colormap = host_colormap; | |
lvid->fullbright = 256 - LittleLong (*((int *)lvid->colormap + 2048)); | |
lvid->maxwarpwidth = WARP_WIDTH; | |
lvid->maxwarpheight = WARP_HEIGHT; | |
lvid->conbuffer = lvid->buffer; | |
lvid->conrowbytes = lvid->rowbytes; | |
lvid->conwidth = lvid->width; | |
lvid->conheight = lvid->height; | |
VGA_pcurmode = pcurrentmode; | |
VGA_ClearVideoMem (pcurrentmode->planar); | |
if (_vid_wait_override.value) | |
{ | |
Cvar_SetValue ("vid_wait", (float)VID_WAIT_VSYNC); | |
} | |
else | |
{ | |
Cvar_SetValue ("vid_wait", (float)VID_WAIT_NONE); | |
} | |
D_InitCaches (vid_surfcache, vid_surfcachesize); | |
return 1; | |
} | |
/* | |
================ | |
VGA_SetPalette | |
================ | |
*/ | |
void VGA_SetPalette(viddef_t *lvid, vmode_t *pcurrentmode, unsigned char *pal) | |
{ | |
int shiftcomponents=2; | |
int i; | |
UNUSED(lvid); | |
UNUSED(pcurrentmode); | |
dos_outportb(0x3c8, 0); | |
for (i=0 ; i<768 ; i++) | |
outportb(0x3c9, pal[i]>>shiftcomponents); | |
} | |
/* | |
================ | |
VGA_SwapBuffersCopy | |
================ | |
*/ | |
void VGA_SwapBuffersCopy (viddef_t *lvid, vmode_t *pcurrentmode, | |
vrect_t *rects) | |
{ | |
UNUSED(pcurrentmode); | |
// TODO: can write a dword at a time | |
// TODO: put in ASM | |
// TODO: copy only specified rectangles | |
if (VGA_planar) | |
{ | |
// TODO: copy only specified rectangles | |
VGA_UpdatePlanarScreen (lvid->buffer); | |
} | |
else | |
{ | |
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; | |
} | |
} | |
} | |
/* | |
================ | |
VGA_SwapBuffers | |
================ | |
*/ | |
void VGA_SwapBuffers (viddef_t *lvid, vmode_t *pcurrentmode, vrect_t *rects) | |
{ | |
UNUSED(lvid); | |
if (vid_wait.value == VID_WAIT_VSYNC) | |
VGA_WaitVsync (); | |
VGA_SwapBuffersCopy (lvid, pcurrentmode, rects); | |
} | |