1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 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
34extern regs_t regs;
35
36int		VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes;
37byte	*VGA_pagebase;
38vmode_t	*VGA_pcurmode;
39
40static int		VGA_planar;
41static int		VGA_numpages;
42static int		VGA_buffersize;
43
44void	*vid_surfcache;
45int		vid_surfcachesize;
46
47int		VGA_highhunkmark;
48
49#include "vgamodes.h"
50
51#define NUMVIDMODES		(sizeof(vgavidmodes) / sizeof(vgavidmodes[0]))
52
53void VGA_UpdatePlanarScreen (void *srcbuffer);
54
55static byte	backingbuf[48*24];
56
57/*
58================
59VGA_BeginDirectRect
60================
61*/
62void 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================
130VGA_EndDirectRect
131================
132*/
133void 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================
191VGA_Init
192================
193*/
194void 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================
214VGA_WaitVsync
215================
216*/
217void VGA_WaitVsync (void)
218{
219	while ((inportb (0x3DA) & 0x08) == 0)
220		;
221}
222
223
224/*
225================
226VGA_ClearVideoMem
227================
228*/
229void 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================
244VGA_FreeAndAllocVidbuffer
245================
246*/
247qboolean 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================
307VGA_CheckAdequateMem
308================
309*/
310qboolean 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================
339VGA_InitMode
340================
341*/
342int 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================
408VGA_SetPalette
409================
410*/
411void 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================
427VGA_SwapBuffersCopy
428================
429*/
430void 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================
466VGA_SwapBuffers
467================
468*/
469void 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