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