diff options
Diffstat (limited to 'src/fake86/render.c')
-rwxr-xr-x | src/fake86/render.c | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/src/fake86/render.c b/src/fake86/render.c new file mode 100755 index 0000000..4f51639 --- /dev/null +++ b/src/fake86/render.c @@ -0,0 +1,570 @@ +/* + Fake86: A portable, open-source 8086 PC emulator. + Copyright (C)2010-2013 Mike Chambers + + 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 the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* render.c: functions for SDL initialization, as well as video scaling/rendering. + it is a bit messy. i plan to rework much of this in the future. i am also + going to add hardware accelerated scaling soon. */ + +#include <SDL/SDL.h> +#include <stdint.h> +#include <stdio.h> +#include "mutex.h" + +#ifdef _WIN32 +CRITICAL_SECTION screenmutex; +#else +pthread_t vidthread; +pthread_mutex_t screenmutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +SDL_Surface *screen = NULL; +uint32_t *scalemap = NULL; +uint8_t regenscalemap = 1; + +extern uint8_t RAM[0x100000], portram[0x10000]; +extern uint8_t VRAM[262144], vidmode, cgabg, blankattr, vidgfxmode, vidcolor, running; +extern uint16_t cursx, cursy, cols, rows, vgapage, cursorposition, cursorvisible; +extern uint8_t updatedscreen, clocksafe, port3da, port6, portout16; +extern uint16_t VGA_SC[0x100], VGA_CRTC[0x100], VGA_ATTR[0x100], VGA_GC[0x100]; +extern uint32_t videobase, textbase, x, y; +extern uint8_t fontcga[32768]; +extern uint32_t palettecga[16], palettevga[256]; +extern uint32_t usefullscreen, usegrabmode; + +uint64_t totalframes = 0; +uint32_t framedelay = 20; +uint8_t scrmodechange = 0, noscale = 0, nosmooth = 1, renderbenchmark = 0, doaudio = 1; +char windowtitle[128]; + +void initcga(); +#ifdef _WIN32 +void VideoThread (void *dummy); +#else +void *VideoThread (void *dummy); +#endif + +void setwindowtitle (uint8_t *extra) { + char temptext[128]; + sprintf (temptext, "%s%s", windowtitle, extra); + SDL_WM_SetCaption ( (const char *) temptext, NULL); +} + +uint8_t initscreen (uint8_t *ver) { + if (doaudio) { + if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) ) return (0); + } + else { + if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_TIMER) ) return (0); + } + screen = SDL_SetVideoMode (640, 400, 32, SDL_HWSURFACE); + if (screen == NULL) return (0); + sprintf (windowtitle, "%s", ver); + setwindowtitle (""); + initcga(); +#ifdef _WIN32 + InitializeCriticalSection (&screenmutex); + _beginthread (VideoThread, 0, NULL); +#else + pthread_create (&vidthread, NULL, (void *) VideoThread, NULL); +#endif + + return (1); +} + +uint32_t prestretch[1024][1024]; +uint32_t nw, nh; //native width and height, pre-stretching (i.e. 320x200 for mode 13h) +void createscalemap() { + uint32_t srcx, srcy, dstx, dsty, scalemapptr; + double xscale, yscale; + + xscale = (double) nw / (double) screen->w; + yscale = (double) nh / (double) screen->h; + if (scalemap != NULL) free(scalemap); + scalemap = (void *)malloc( ((uint32_t)screen->w + 1) * (uint32_t)screen->h * 4); + if (scalemap == NULL) { + printf("\nFATAL: Unable to allocate memory for scalemap!\n"); + exit(1); + } + scalemapptr = 0; + for (dsty=0; dsty<(uint32_t)screen->h; dsty++) { + srcy = (uint32_t) ( (double) dsty * yscale); + scalemap[scalemapptr++] = srcy; + for (dstx=0; dstx<(uint32_t)screen->w; dstx++) { + srcx = (uint32_t) ( (double) dstx * xscale); + scalemap[scalemapptr++] = srcx; + } + } + + regenscalemap = 0; +} + +extern uint16_t oldw, oldh, constantw, constanth; +void draw(); +extern void handleinput(); +#ifdef _WIN32 +void VideoThread (void *dummy) { +#else +void *VideoThread (void *dummy) { +#endif + uint32_t cursorprevtick, cursorcurtick, delaycalc; + cursorprevtick = SDL_GetTicks(); + cursorvisible = 0; + + while (running) { + cursorcurtick = SDL_GetTicks(); + if ( (cursorcurtick - cursorprevtick) >= 250) { + updatedscreen = 1; + cursorvisible = ~cursorvisible & 1; + cursorprevtick = cursorcurtick; + } + + if (updatedscreen || renderbenchmark) { + updatedscreen = 0; + if (screen != NULL) { + MutexLock (screenmutex); + if (regenscalemap) createscalemap(); + draw(); + MutexUnlock (screenmutex); + } + totalframes++; + } + if (!renderbenchmark) { + delaycalc = framedelay - (SDL_GetTicks() - cursorcurtick); + if (delaycalc > framedelay) delaycalc = framedelay; + SDL_Delay (delaycalc); + } + } +} + +#ifdef _WIN32 +void ShowMenu(); +void HideMenu(); +#endif + +void doscrmodechange() { + MutexLock (screenmutex); + if (scrmodechange) { + if (screen != NULL) SDL_FreeSurface (screen); +#ifdef _WIN32 + if (usefullscreen) HideMenu(); else ShowMenu(); +#endif + if (constantw && constanth) screen = SDL_SetVideoMode (constantw, constanth, 32, SDL_HWSURFACE | usefullscreen); + else if (noscale) screen = SDL_SetVideoMode (nw, nh, 32, SDL_HWSURFACE | usefullscreen); + else { + if ( (nw >= 640) || (nh >= 400) ) screen = SDL_SetVideoMode (nw, nh, 32, SDL_HWSURFACE | usefullscreen); + else screen = SDL_SetVideoMode (640, 400, 32, SDL_HWSURFACE | usefullscreen); + } + if (usefullscreen) SDL_WM_GrabInput (SDL_GRAB_ON); //always have mouse grab turned on for full screen mode + else SDL_WM_GrabInput (usegrabmode); + SDL_ShowCursor (SDL_DISABLE); + if (!usefullscreen) { + if (usegrabmode == SDL_GRAB_ON) setwindowtitle (" (press Ctrl + Alt to release mouse)"); + else setwindowtitle (""); + } + regenscalemap = 1; + createscalemap(); + } + MutexUnlock (screenmutex); + scrmodechange = 0; +} + +void stretchblit (SDL_Surface *target) { + uint32_t srcx, srcy, dstx, dsty, lastx, lasty, r, g, b; + uint32_t consecutivex, consecutivey = 0, limitx, limity, scalemapptr; + uint32_t ofs; + uint8_t *pixelrgb; + + limitx = (uint32_t)((double) nw / (double) target->w); + limity = (uint32_t)((double) nh / (double) target->h); + + if (SDL_MUSTLOCK (target) ) + if (SDL_LockSurface (target) < 0) + return; + + lasty = 0; + scalemapptr = 0; + for (dsty=0; dsty<(uint32_t)target->h; dsty++) { + srcy = scalemap[scalemapptr++]; + ofs = dsty*target->w; + consecutivex = 0; + lastx = 0; + if (srcy == lasty) consecutivey++; + else consecutivey = 0; + for (dstx=0; dstx<(uint32_t)target->w; dstx++) { + srcx = scalemap[scalemapptr++]; + pixelrgb = (uint8_t *) &prestretch[srcy][srcx]; + r = pixelrgb[0]; + g = pixelrgb[1]; + b = pixelrgb[2]; + if (srcx == lastx) consecutivex++; + else consecutivex = 0; + if ( (consecutivex > limitx) && (consecutivey > limity) ) { + pixelrgb = (uint8_t *) &prestretch[srcy][srcx+1]; + r += pixelrgb[0]; + g += pixelrgb[1]; + b += pixelrgb[2]; + pixelrgb = (uint8_t *) &prestretch[srcy+1][srcx]; + r += pixelrgb[0]; + g += pixelrgb[1]; + b += pixelrgb[2]; + pixelrgb = (uint8_t *) &prestretch[srcy+1][srcx+1]; + r += pixelrgb[0]; + g += pixelrgb[1]; + b += pixelrgb[2]; + r = r >> 2; + g = g >> 2; + b = b >> 2; + //r = 255; g = 0; b = 0; + } + else if (consecutivex > limitx) { + pixelrgb = (uint8_t *) &prestretch[srcy][srcx+1]; + r += pixelrgb[0]; + r = r >> 1; + g += pixelrgb[1]; + g = g >> 1; + b += pixelrgb[2]; + b = b >> 1; + //r = 0; g = 255; b = 0; + } + else if (consecutivey > limity) { + pixelrgb = (uint8_t *) &prestretch[srcy+1][srcx]; + r += pixelrgb[0]; + r = r >> 1; + g += pixelrgb[1]; + g = g >> 1; + b += pixelrgb[2]; + b = b >> 1; + //r = 0; g = 0; b = 255; + } + ( (uint32_t *) target->pixels) [ofs++] = SDL_MapRGB (target->format, (uint8_t) r, (uint8_t) g, (uint8_t) b); + lastx = srcx; + } + lasty = srcy; + } + + if (SDL_MUSTLOCK (target) ) + SDL_UnlockSurface (target); + SDL_UpdateRect (target, 0, 0, target->w, target->h); +} + +void roughblit (SDL_Surface *target) { + uint32_t srcx, srcy, dstx, dsty, scalemapptr; + int32_t ofs; + uint8_t *pixelrgb; + + if (SDL_MUSTLOCK (target) ) + if (SDL_LockSurface (target) < 0) + return; + + scalemapptr = 0; + for (dsty=0; dsty<(uint32_t)target->h; dsty++) { + srcy = scalemap[scalemapptr++]; + ofs = dsty*target->w; + for (dstx=0; dstx<(uint32_t)target->w; dstx++) { + srcx = scalemap[scalemapptr++]; + pixelrgb = (uint8_t *) &prestretch[srcy][srcx]; + ( (uint32_t *) target->pixels) [ofs++] = SDL_MapRGB (target->format, pixelrgb[0], pixelrgb[1], pixelrgb[2]); + } + } + + if (SDL_MUSTLOCK (target) ) + SDL_UnlockSurface (target); + SDL_UpdateRect (target, 0, 0, target->w, target->h); +} + +/* NOTE: doubleblit is only used when smoothing is not enabled, and the SDL window size + is exactly double of native resolution for the current video mode. we can take + advantage of the fact that every pixel is simply doubled both horizontally and + vertically. this way, we do not need to waste mountains of CPU time doing + floating point multiplication for each and every on-screen pixel. it makes the + difference between games being smooth and playable, and being jerky on my old + 400 MHz PowerPC G3 iMac. +*/ +void doubleblit (SDL_Surface *target) { + uint32_t srcx, srcy, dstx, dsty, curcolor; + int32_t ofs, startofs; + uint8_t *pixelrgb; + + if (SDL_MUSTLOCK (target) ) + if (SDL_LockSurface (target) < 0) + return; + + for (dsty=0; dsty<(uint32_t)target->h; dsty += 2) { + srcy = (uint32_t) (dsty >> 1); + startofs = ofs = dsty*target->w; + for (dstx=0; dstx<(uint32_t)target->w; dstx += 2) { + srcx = (uint32_t) (dstx >> 1); + pixelrgb = (uint8_t *) &prestretch[srcy][srcx]; + curcolor = SDL_MapRGB (target->format, pixelrgb[0], pixelrgb[1], pixelrgb[2]); + ( (uint32_t *) target->pixels) [ofs+target->w] = curcolor; + ( (uint32_t *) target->pixels) [ofs++] = curcolor; + ( (uint32_t *) target->pixels) [ofs+target->w] = curcolor; + ( (uint32_t *) target->pixels) [ofs++] = curcolor; + } + } + + if (SDL_MUSTLOCK (target) ) + SDL_UnlockSurface (target); + SDL_UpdateRect (target, 0, 0, target->w, target->h); +} + +extern uint16_t vtotal; +void draw () { + uint32_t planemode, vgapage, color, chary, charx, vidptr, divx, divy, curchar, curpixel, usepal, intensity, blockw, curheight, x1, y1; + switch (vidmode) { + case 0: + case 1: + case 2: //text modes + case 3: + case 7: + case 0x82: + nw = 640; + nh = 400; + vgapage = ( (uint32_t) VGA_CRTC[0xC]<<8) + (uint32_t) VGA_CRTC[0xD]; + for (y=0; y<400; y++) + for (x=0; x<640; x++) { + if (cols==80) { + charx = x/8; + divx = 1; + } + else { + charx = x/16; + divx = 2; + } + if ( (portram[0x3D8]==9) && (portram[0x3D4]==9) ) { + chary = y/4; + vidptr = vgapage + videobase + chary*cols*2 + charx*2; + curchar = RAM[vidptr]; + color = fontcga[curchar*128 + (y%4) *8 + ( (x/divx) %8) ]; + } + else { + chary = y/16; + vidptr = videobase + chary*cols*2 + charx*2; + curchar = RAM[vidptr]; + color = fontcga[curchar*128 + (y%16) *8 + ( (x/divx) %8) ]; + } + if (vidcolor) { + /*if (!color) if (portram[0x3D8]&128) color = palettecga[ (RAM[vidptr+1]/16) &7]; + else*/ if (!color) color = palettecga[RAM[vidptr+1]/16]; //high intensity background + else color = palettecga[RAM[vidptr+1]&15]; + } + else { + if ( (RAM[vidptr+1] & 0x70) ) { + if (!color) color = palettecga[7]; + else color = palettecga[0]; + } + else { + if (!color) color = palettecga[0]; + else color = palettecga[7]; + } + } + prestretch[y][x] = color; + } + break; + case 4: + case 5: + nw = 320; + nh = 200; + usepal = (portram[0x3D9]>>5) & 1; + intensity = ( (portram[0x3D9]>>4) & 1) << 3; + for (y=0; y<200; y++) { + for (x=0; x<320; x++) { + charx = x; + chary = y; + vidptr = videobase + ( (chary>>1) * 80) + ( (chary & 1) * 8192) + (charx >> 2); + curpixel = RAM[vidptr]; + switch (charx & 3) { + case 3: + curpixel = curpixel & 3; + break; + case 2: + curpixel = (curpixel>>2) & 3; + break; + case 1: + curpixel = (curpixel>>4) & 3; + break; + case 0: + curpixel = (curpixel>>6) & 3; + break; + } + if (vidmode==4) { + curpixel = curpixel * 2 + usepal + intensity; + if (curpixel == (usepal + intensity) ) curpixel = cgabg; + color = palettecga[curpixel]; + prestretch[y][x] = color; + } + else { + curpixel = curpixel * 63; + color = palettecga[curpixel]; + prestretch[y][x] = color; + } + } + } + break; + case 6: + nw = 640; + nh = 200; + for (y=0; y<400; y+=2) { + for (x=0; x<640; x++) { + charx = x; + chary = y >> 1; + vidptr = videobase + ( (chary>>1) * 80) + ( (chary&1) * 8192) + (charx>>3); + curpixel = (RAM[vidptr]>> (7- (charx&7) ) ) &1; + color = palettecga[curpixel*15]; + prestretch[y][x] = color; + prestretch[y+1][x] = color; + } + } + break; + case 127: + nw = 720; + nh = 348; + for (y=0; y<348; y++) { + for (x=0; x<720; x++) { + charx = x; + chary = y>>1; + vidptr = videobase + ( (y & 3) << 13) + (y >> 2) *90 + (x >> 3); + curpixel = (RAM[vidptr]>> (7- (charx&7) ) ) &1; +#ifdef __BIG_ENDIAN__ + if (curpixel) color = 0xFFFFFF00; +#else + if (curpixel) color = 0x00FFFFFF; +#endif + else color = 0x00000000; + prestretch[y][x] = color; + } + } + break; + case 0x8: //160x200 16-color (PCjr) + nw = 640; //fix this + nh = 400; //part later + for (y=0; y<400; y++) + for (x=0; x<640; x++) { + vidptr = 0xB8000 + (y>>2) *80 + (x>>3) + ( (y>>1) &1) *8192; + if ( ( (x>>1) &1) ==0) color = palettecga[RAM[vidptr] >> 4]; + else color = palettecga[RAM[vidptr] & 15]; + prestretch[y][x] = color; + } + break; + case 0x9: //320x200 16-color (Tandy/PCjr) + nw = 640; //fix this + nh = 400; //part later + for (y=0; y<400; y++) + for (x=0; x<640; x++) { + vidptr = 0xB8000 + (y>>3) *160 + (x>>2) + ( (y>>1) &3) *8192; + if ( ( (x>>1) &1) ==0) color = palettecga[RAM[vidptr] >> 4]; + else color = palettecga[RAM[vidptr] & 15]; + prestretch[y][x] = color; + } + break; + case 0xD: + case 0xE: + nw = 640; //fix this + nh = 400; //part later + for (y=0; y<400; y++) + for (x=0; x<640; x++) { + divx = x>>1; + divy = y>>1; + vidptr = divy*40 + (divx>>3); + x1 = 7 - (divx & 7); + color = (VRAM[vidptr] >> x1) & 1; + color += ( ( (VRAM[0x10000 + vidptr] >> x1) & 1) << 1); + color += ( ( (VRAM[0x20000 + vidptr] >> x1) & 1) << 2); + color += ( ( (VRAM[0x30000 + vidptr] >> x1) & 1) << 3); + color = palettevga[color]; + prestretch[y][x] = color; + } + break; + case 0x10: + nw = 640; + nh = 350; + for (y=0; y<350; y++) + for (x=0; x<640; x++) { + vidptr = y*80 + (x>>3); + x1 = 7 - (x & 7); + color = (VRAM[vidptr] >> x1) & 1; + color |= ( ( (VRAM[0x10000 + vidptr] >> x1) & 1) << 1); + color |= ( ( (VRAM[0x20000 + vidptr] >> x1) & 1) << 2); + color |= ( ( (VRAM[0x30000 + vidptr] >> x1) & 1) << 3); + color = palettevga[color]; + prestretch[y][x] = color; + } + break; + case 0x12: + nw = 640; + nh = 480; + vgapage = ( (uint32_t) VGA_CRTC[0xC]<<8) + (uint32_t) VGA_CRTC[0xD]; + for (y=0; y<nh; y++) + for (x=0; x<nw; x++) { + vidptr = y*80 + (x/8); + color = (VRAM[vidptr] >> (~x & 7) ) & 1; + color |= ( (VRAM[vidptr+0x10000] >> (~x & 7) ) & 1) << 1; + color |= ( (VRAM[vidptr+0x20000] >> (~x & 7) ) & 1) << 2; + color |= ( (VRAM[vidptr+0x30000] >> (~x & 7) ) & 1) << 3; + prestretch[y][x] = palettevga[color]; + } + break; + case 0x13: + if (vtotal == 11) { //ugly hack to show Flashback at the proper resolution + nw = 256; + nh = 224; + } + else { + nw = 320; + nh = 200; + } + if (VGA_SC[4] & 6) planemode = 1; + else planemode = 0; + //vgapage = ( (uint32_t) VGA_CRTC[0xC]<<8) + (uint32_t) VGA_CRTC[0xD]; + vgapage = (( (uint32_t) VGA_CRTC[0xC]<<8) + (uint32_t) VGA_CRTC[0xD]) << 2; + for (y=0; y<nh; y++) + for (x=0; x<nw; x++) { + if (!planemode) color = palettevga[RAM[videobase + ((vgapage + y*nw + x) & 0xFFFF) ]]; + //if (!planemode) color = palettevga[RAM[videobase + y*nw + x]]; + else { + vidptr = y*nw + x; + vidptr = vidptr/4 + (x & 3) *0x10000; + vidptr = vidptr + vgapage - (VGA_ATTR[0x13] & 15); + color = palettevga[VRAM[vidptr]]; + } + prestretch[y][x] = color; + } + } + + if (vidgfxmode==0) { + if (cursorvisible) { + curheight = 2; + if (cols==80) blockw = 8; + else blockw = 16; + x1 = cursx * blockw; + y1 = cursy * 8 + 8 - curheight; + for (y=y1*2; y<=y1*2+curheight-1; y++) + for (x=x1; x<=x1+blockw-1; x++) { + color = palettecga[RAM[videobase+cursy*cols*2+cursx*2+1]&15]; + prestretch[y&1023][x&1023] = color; + } + } + } + if (nosmooth) { + if ( ((nw << 1) == screen->w) && ((nh << 1) == screen->h) ) doubleblit (screen); + else roughblit (screen); + } + else stretchblit (screen); +} + |