quakeforge/libs/video/targets/vid_vga.c

456 lines
9.5 KiB
C
Raw Normal View History

/*
vid_vga.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
$Id$
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <dos.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 = (byte *) 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 = vid_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->int_val) {
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->int_val == VID_WAIT_VSYNC)
VGA_WaitVsync ();
VGA_SwapBuffersCopy (lvid, pcurrentmode, rects);
}
void
VID_HandlePause (qboolean pause)
{
}