/* vid_vga.c VGA-specific DOS video stuff Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 1999,2000 contributors of the QuakeForge project Please see the file "AUTHORS" for a list of contributors 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$ */ // TODO: proper handling of page-swap failure #include #include #include #include #include #include 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 #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> 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 ; jdirect + 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> 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 ; jdirect + 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) { vid_wait->value = (float)VID_WAIT_VSYNC; } else { vid_wait->value = (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); }