Home | History | Annotate | Download | only in WinQuake
      1 /*
      2 Copyright (C) 1996-1997 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 //
     21 // vid_vga.c: VGA-specific DOS video stuff
     22 //
     23 
     24 // TODO: proper handling of page-swap failure
     25 
     26 #include <dos.h>
     27 
     28 #include "quakedef.h"
     29 #include "d_local.h"
     30 #include "dosisms.h"
     31 #include "vid_dos.h"
     32 #include <dpmi.h>
     33 
     34 extern regs_t regs;
     35 
     36 int		VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes;
     37 byte	*VGA_pagebase;
     38 vmode_t	*VGA_pcurmode;
     39 
     40 static int		VGA_planar;
     41 static int		VGA_numpages;
     42 static int		VGA_buffersize;
     43 
     44 void	*vid_surfcache;
     45 int		vid_surfcachesize;
     46 
     47 int		VGA_highhunkmark;
     48 
     49 #include "vgamodes.h"
     50 
     51 #define NUMVIDMODES		(sizeof(vgavidmodes) / sizeof(vgavidmodes[0]))
     52 
     53 void VGA_UpdatePlanarScreen (void *srcbuffer);
     54 
     55 static byte	backingbuf[48*24];
     56 
     57 /*
     58 ================
     59 VGA_BeginDirectRect
     60 ================
     61 */
     62 void VGA_BeginDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x,
     63 	int y, byte *pbitmap, int width, int height)
     64 {
     65 	int		i, j, k, plane, reps, repshift;
     66 
     67 	if (!lvid->direct)
     68 		return;
     69 
     70 	if (lvid->aspect > 1.5)
     71 	{
     72 		reps = 2;
     73 		repshift = 1;
     74 	}
     75 	else
     76 	{
     77 		reps = 1;
     78 		repshift = 0;
     79 	}
     80 
     81 	if (pcurrentmode->planar)
     82 	{
     83 		for (plane=0 ; plane<4 ; plane++)
     84 		{
     85 		// select the correct plane for reading and writing
     86 			outportb (SC_INDEX, MAP_MASK);
     87 			outportb (SC_DATA, 1 << plane);
     88 			outportb (GC_INDEX, READ_MAP);
     89 			outportb (GC_DATA, plane);
     90 
     91 			for (i=0 ; i<(height << repshift) ; i += reps)
     92 			{
     93 				for (k=0 ; k<reps ; k++)
     94 				{
     95 					for (j=0 ; j<(width >> 2) ; j++)
     96 					{
     97 						backingbuf[(i + k) * 24 + (j << 2) + plane] =
     98 								lvid->direct[(y + i + k) * VGA_rowbytes +
     99 								(x >> 2) + j];
    100 						lvid->direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] =
    101 								pbitmap[(i >> repshift) * 24 +
    102 								(j << 2) + plane];
    103 					}
    104 				}
    105 			}
    106 		}
    107 	}
    108 	else
    109 	{
    110 		for (i=0 ; i<(height << repshift) ; i += reps)
    111 		{
    112 			for (j=0 ; j<reps ; j++)
    113 			{
    114 				memcpy (&backingbuf[(i + j) * 24],
    115 						lvid->direct + x + ((y << repshift) + i + j) *
    116 						 VGA_rowbytes,
    117 						width);
    118 				memcpy (lvid->direct + x + ((y << repshift) + i + j) *
    119 						 VGA_rowbytes,
    120 						&pbitmap[(i >> repshift) * width],
    121 						width);
    122 			}
    123 		}
    124 	}
    125 }
    126 
    127 
    128 /*
    129 ================
    130 VGA_EndDirectRect
    131 ================
    132 */
    133 void VGA_EndDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x,
    134 	int y, int width, int height)
    135 {
    136 	int		i, j, k, plane, reps, repshift;
    137 
    138 	if (!lvid->direct)
    139 		return;
    140 
    141 	if (lvid->aspect > 1.5)
    142 	{
    143 		reps = 2;
    144 		repshift = 1;
    145 	}
    146 	else
    147 	{
    148 		reps = 1;
    149 		repshift = 0;
    150 	}
    151 
    152 	if (pcurrentmode->planar)
    153 	{
    154 		for (plane=0 ; plane<4 ; plane++)
    155 		{
    156 		// select the correct plane for writing
    157 			outportb (SC_INDEX, MAP_MASK);
    158 			outportb (SC_DATA, 1 << plane);
    159 
    160 			for (i=0 ; i<(height << repshift) ; i += reps)
    161 			{
    162 				for (k=0 ; k<reps ; k++)
    163 				{
    164 					for (j=0 ; j<(width >> 2) ; j++)
    165 					{
    166 						lvid->direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] =
    167 								backingbuf[(i + k) * 24 + (j << 2) + plane];
    168 					}
    169 				}
    170 			}
    171 		}
    172 	}
    173 	else
    174 	{
    175 		for (i=0 ; i<(height << repshift) ; i += reps)
    176 		{
    177 			for (j=0 ; j<reps ; j++)
    178 			{
    179 				memcpy (lvid->direct + x + ((y << repshift) + i + j) *
    180 						 VGA_rowbytes,
    181 						&backingbuf[(i + j) * 24],
    182 						width);
    183 			}
    184 		}
    185 	}
    186 }
    187 
    188 
    189 /*
    190 ================
    191 VGA_Init
    192 ================
    193 */
    194 void VGA_Init (void)
    195 {
    196 	int		i;
    197 
    198 // link together all the VGA modes
    199 	for (i=0 ; i<(NUMVIDMODES - 1) ; i++)
    200 	{
    201 		vgavidmodes[i].pnext = &vgavidmodes[i+1];
    202 	}
    203 
    204 // add the VGA modes at the start of the mode list
    205 	vgavidmodes[NUMVIDMODES-1].pnext = pvidmodes;
    206 	pvidmodes = &vgavidmodes[0];
    207 
    208 	numvidmodes += NUMVIDMODES;
    209 }
    210 
    211 
    212 /*
    213 ================
    214 VGA_WaitVsync
    215 ================
    216 */
    217 void VGA_WaitVsync (void)
    218 {
    219 	while ((inportb (0x3DA) & 0x08) == 0)
    220 		;
    221 }
    222 
    223 
    224 /*
    225 ================
    226 VGA_ClearVideoMem
    227 ================
    228 */
    229 void VGA_ClearVideoMem (int planar)
    230 {
    231 
    232 	if (planar)
    233 	{
    234 	// enable all planes for writing
    235 		outportb (SC_INDEX, MAP_MASK);
    236 		outportb (SC_DATA, 0x0F);
    237 	}
    238 
    239 	Q_memset (VGA_pagebase, 0, VGA_rowbytes * VGA_height);
    240 }
    241 
    242 /*
    243 ================
    244 VGA_FreeAndAllocVidbuffer
    245 ================
    246 */
    247 qboolean VGA_FreeAndAllocVidbuffer (viddef_t *lvid, int allocnewbuffer)
    248 {
    249 	int		tsize, tbuffersize;
    250 
    251 	if (allocnewbuffer)
    252 	{
    253 	// alloc an extra line in case we want to wrap, and allocate the z-buffer
    254 		tbuffersize = (lvid->rowbytes * (lvid->height + 1)) +
    255 				(lvid->width * lvid->height * sizeof (*d_pzbuffer));
    256 	}
    257 	else
    258 	{
    259 	// just allocate the z-buffer
    260 		tbuffersize = lvid->width * lvid->height * sizeof (*d_pzbuffer);
    261 	}
    262 
    263 	tsize = D_SurfaceCacheForRes (lvid->width, lvid->height);
    264 
    265 	tbuffersize += tsize;
    266 
    267 // see if there's enough memory, allowing for the normal mode 0x13 pixel,
    268 // z, and surface buffers
    269 	if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 +
    270 		 0x10000 * 3) < minimum_memory)
    271 	{
    272 		Con_Printf ("Not enough memory for video mode\n");
    273 		VGA_pcurmode = NULL;	// so no further accesses to the buffer are
    274 								//  attempted, particularly when clearing
    275 		return false;		// not enough memory for mode
    276 	}
    277 
    278 	VGA_buffersize = tbuffersize;
    279 	vid_surfcachesize = tsize;
    280 
    281 	if (d_pzbuffer)
    282 	{
    283 		D_FlushCaches ();
    284 		Hunk_FreeToHighMark (VGA_highhunkmark);
    285 		d_pzbuffer = NULL;
    286 	}
    287 
    288 	VGA_highhunkmark = Hunk_HighMark ();
    289 
    290 	d_pzbuffer = Hunk_HighAllocName (VGA_buffersize, "video");
    291 
    292 	vid_surfcache = (byte *)d_pzbuffer
    293 		+ lvid->width * lvid->height * sizeof (*d_pzbuffer);
    294 
    295 	if (allocnewbuffer)
    296 	{
    297 		lvid->buffer = (void *)( (byte *)vid_surfcache + vid_surfcachesize);
    298 		lvid->conbuffer = lvid->buffer;
    299 	}
    300 
    301 	return true;
    302 }
    303 
    304 
    305 /*
    306 ================
    307 VGA_CheckAdequateMem
    308 ================
    309 */
    310 qboolean VGA_CheckAdequateMem (int width, int height, int rowbytes,
    311 	int allocnewbuffer)
    312 {
    313 	int		tbuffersize;
    314 
    315 	tbuffersize = width * height * sizeof (*d_pzbuffer);
    316 
    317 	if (allocnewbuffer)
    318 	{
    319 	// alloc an extra line in case we want to wrap, and allocate the z-buffer
    320 		tbuffersize += (rowbytes * (height + 1));
    321 	}
    322 
    323 	tbuffersize += D_SurfaceCacheForRes (width, height);
    324 
    325 // see if there's enough memory, allowing for the normal mode 0x13 pixel,
    326 // z, and surface buffers
    327 	if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 +
    328 		 0x10000 * 3) < minimum_memory)
    329 	{
    330 		return false;		// not enough memory for mode
    331 	}
    332 
    333 	return true;
    334 }
    335 
    336 
    337 /*
    338 ================
    339 VGA_InitMode
    340 ================
    341 */
    342 int VGA_InitMode (viddef_t *lvid, vmode_t *pcurrentmode)
    343 {
    344 	vextra_t		*pextra;
    345 
    346 	pextra = pcurrentmode->pextradata;
    347 
    348 	if (!VGA_FreeAndAllocVidbuffer (lvid, pextra->vidbuffer))
    349 		return -1;	// memory alloc failed
    350 
    351 	if (VGA_pcurmode)
    352 		VGA_ClearVideoMem (VGA_pcurmode->planar);
    353 
    354 // mode 0x13 is the base for all the Mode X-class mode sets
    355 	regs.h.ah = 0;
    356 	regs.h.al = 0x13;
    357 	dos_int86(0x10);
    358 
    359 	VGA_pagebase = (void *)real2ptr(0xa0000);
    360 	lvid->direct = (pixel_t *)VGA_pagebase;
    361 
    362 // set additional registers as needed
    363 	VideoRegisterSet (pextra->pregset);
    364 
    365 	VGA_numpages = 1;
    366 	lvid->numpages = VGA_numpages;
    367 
    368 	VGA_width = (lvid->width + 0x1F) & ~0x1F;
    369 	VGA_height = lvid->height;
    370 	VGA_planar = pcurrentmode->planar;
    371 	if (VGA_planar)
    372 		VGA_rowbytes = lvid->rowbytes / 4;
    373 	else
    374 		VGA_rowbytes = lvid->rowbytes;
    375 	VGA_bufferrowbytes = lvid->rowbytes;
    376 	lvid->colormap = host_colormap;
    377 	lvid->fullbright = 256 - LittleLong (*((int *)lvid->colormap + 2048));
    378 
    379 	lvid->maxwarpwidth = WARP_WIDTH;
    380 	lvid->maxwarpheight = WARP_HEIGHT;
    381 
    382 	lvid->conbuffer = lvid->buffer;
    383 	lvid->conrowbytes = lvid->rowbytes;
    384 	lvid->conwidth = lvid->width;
    385 	lvid->conheight = lvid->height;
    386 
    387 	VGA_pcurmode = pcurrentmode;
    388 
    389 	VGA_ClearVideoMem (pcurrentmode->planar);
    390 
    391 	if (_vid_wait_override.value)
    392 	{
    393 		Cvar_SetValue ("vid_wait", (float)VID_WAIT_VSYNC);
    394 	}
    395 	else
    396 	{
    397 		Cvar_SetValue ("vid_wait", (float)VID_WAIT_NONE);
    398 	}
    399 
    400 	D_InitCaches (vid_surfcache, vid_surfcachesize);
    401 
    402 	return 1;
    403 }
    404 
    405 
    406 /*
    407 ================
    408 VGA_SetPalette
    409 ================
    410 */
    411 void VGA_SetPalette(viddef_t *lvid, vmode_t *pcurrentmode, unsigned char *pal)
    412 {
    413 	int shiftcomponents=2;
    414 	int i;
    415 
    416 	UNUSED(lvid);
    417 	UNUSED(pcurrentmode);
    418 
    419 	dos_outportb(0x3c8, 0);
    420 	for (i=0 ; i<768 ; i++)
    421 		outportb(0x3c9, pal[i]>>shiftcomponents);
    422 }
    423 
    424 
    425 /*
    426 ================
    427 VGA_SwapBuffersCopy
    428 ================
    429 */
    430 void VGA_SwapBuffersCopy (viddef_t *lvid, vmode_t *pcurrentmode,
    431 	vrect_t *rects)
    432 {
    433 
    434 	UNUSED(pcurrentmode);
    435 
    436 // TODO: can write a dword at a time
    437 // TODO: put in ASM
    438 // TODO: copy only specified rectangles
    439 	if (VGA_planar)
    440 	{
    441 
    442 	// TODO: copy only specified rectangles
    443 
    444 		VGA_UpdatePlanarScreen (lvid->buffer);
    445 	}
    446 	else
    447 	{
    448 		while (rects)
    449 		{
    450 			VGA_UpdateLinearScreen (
    451 					lvid->buffer + rects->x + (rects->y * lvid->rowbytes),
    452 		 			VGA_pagebase + rects->x + (rects->y * VGA_rowbytes),
    453 					rects->width,
    454 					rects->height,
    455 					lvid->rowbytes,
    456 					VGA_rowbytes);
    457 
    458 			rects = rects->pnext;
    459 		}
    460 	}
    461 }
    462 
    463 
    464 /*
    465 ================
    466 VGA_SwapBuffers
    467 ================
    468 */
    469 void VGA_SwapBuffers (viddef_t *lvid, vmode_t *pcurrentmode, vrect_t *rects)
    470 {
    471 	UNUSED(lvid);
    472 
    473 	if (vid_wait.value == VID_WAIT_VSYNC)
    474 		VGA_WaitVsync ();
    475 
    476 	VGA_SwapBuffersCopy (lvid, pcurrentmode, rects);
    477 }
    478 
    479