/*************************************************************************** gpu.c - description ------------------- begin : Sun Oct 28 2001 copyright : (C) 2001 by Pete Bernert email : BlackDove@addcom.de ***************************************************************************/ /*************************************************************************** * * * 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. See also the license.txt file for * * additional informations. * * * ***************************************************************************/ #ifndef _MACGL #include "config.h" #endif #define _IN_GPU #include "externals.h" #include "gpu.h" #include "draw.h" #include "cfg.h" #include "prim.h" #include "stdint.h" #include "psemu_plugin_defs.h" #include "menu.h" #include "key.h" #include "fps.h" #include "swap.h" #ifdef ENABLE_NLS #include #include #define _(x) gettext(x) #define N_(x) (x) #else #define _(x) (x) #define N_(x) (x) #endif //////////////////////////////////////////////////////////////////////// // PPDK developer must change libraryName field and can change revision and build //////////////////////////////////////////////////////////////////////// const unsigned char version = 1; // do not touch - library for PSEmu 1.x const unsigned char revision = 1; const unsigned char build = 17; // increase that with each version #ifdef _MACGL static char *libraryName = N_("SoftGL Driver"); static char *libraryInfo = N_("P.E.Op.S. SoftGL Driver V1.17\nCoded by Pete Bernert and the P.E.Op.S. team\n"); #else static char *libraryName = N_("XVideo Driver"); static char *libraryInfo = N_("P.E.Op.S. Xvideo Driver V1.17\nCoded by Pete Bernert and the P.E.Op.S. team\n"); #endif static char *PluginAuthor = N_("Pete Bernert and the P.E.Op.S. team"); //////////////////////////////////////////////////////////////////////// // memory image of the PSX vram //////////////////////////////////////////////////////////////////////// unsigned char *psxVSecure; unsigned char *psxVub; signed char *psxVsb; unsigned short *psxVuw; unsigned short *psxVuw_eom; signed short *psxVsw; uint32_t *psxVul; int32_t *psxVsl; //////////////////////////////////////////////////////////////////////// // GPU globals //////////////////////////////////////////////////////////////////////// static long lGPUdataRet; long lGPUstatusRet; char szDispBuf[64]; char szMenuBuf[36]; char szDebugText[512]; uint32_t ulStatusControl[256]; static uint32_t gpuDataM[256]; static unsigned char gpuCommand = 0; static long gpuDataC = 0; static long gpuDataP = 0; VRAMLoad_t VRAMWrite; VRAMLoad_t VRAMRead; DATAREGISTERMODES DataWriteMode; DATAREGISTERMODES DataReadMode; BOOL bSkipNextFrame = FALSE; DWORD dwLaceCnt=0; int iColDepth; int iWindowMode; short sDispWidths[8] = {256,320,512,640,368,384,512,640}; PSXDisplay_t PSXDisplay; PSXDisplay_t PreviousPSXDisplay; long lSelectedSlot=0; BOOL bChangeWinMode=FALSE; BOOL bDoLazyUpdate=FALSE; uint32_t lGPUInfoVals[16]; static int iFakePrimBusy=0; //////////////////////////////////////////////////////////////////////// // some misc external display funcs //////////////////////////////////////////////////////////////////////// #include time_t tStart; void CALLBACK GPUdisplayText(char * pText) // some debug func { if(!pText) {szDebugText[0]=0;return;} if(strlen(pText)>511) return; time(&tStart); strcpy(szDebugText,pText); } //////////////////////////////////////////////////////////////////////// void CALLBACK GPUdisplayFlags(unsigned long dwFlags) // some info func { dwCoreFlags=dwFlags; BuildDispMenu(0); } //////////////////////////////////////////////////////////////////////// // stuff to make this a true PDK module //////////////////////////////////////////////////////////////////////// /* char * CALLBACK PSEgetLibName(void) { return _(libraryName); } unsigned long CALLBACK PSEgetLibType(void) { return PSE_LT_GPU; } unsigned long CALLBACK PSEgetLibVersion(void) { return version<<16|revision<<8|build; } char * GPUgetLibInfos(void) { return _(libraryInfo); } */ //////////////////////////////////////////////////////////////////////// // Snapshot func //////////////////////////////////////////////////////////////////////// static char * pGetConfigInfos(int iCfg) { char szO[2][4]={"off","on "}; char szTxt[256]; char * pB = (char *)malloc(32767); if (!pB) return NULL; *pB = 0; //----------------------------------------------------// sprintf(szTxt,"Plugin: %s %d.%d.%d\r\n",libraryName,version,revision,build); strcat(pB,szTxt); sprintf(szTxt,"Author: %s\r\n\r\n",PluginAuthor); strcat(pB,szTxt); //----------------------------------------------------// if(iCfg && iWindowMode) sprintf(szTxt,"Resolution/Color:\r\n- %dx%d ",LOWORD(iWinSize),HIWORD(iWinSize)); else sprintf(szTxt,"Resolution/Color:\r\n- %dx%d ",iResX,iResY); strcat(pB,szTxt); if(iWindowMode && iCfg) strcpy(szTxt,"Window mode\r\n"); else if(iWindowMode) sprintf(szTxt,"Window mode - [%d Bit]\r\n",iDesktopCol); else sprintf(szTxt,"Fullscreen - [%d Bit]\r\n",iColDepth); strcat(pB,szTxt); sprintf(szTxt,"Stretch mode: %d\r\n",iUseNoStretchBlt); strcat(pB,szTxt); sprintf(szTxt,"Dither mode: %d\r\n\r\n",iUseDither); strcat(pB,szTxt); //----------------------------------------------------// sprintf(szTxt,"Framerate:\r\n- FPS limit: %s\r\n",szO[UseFrameLimit]); strcat(pB,szTxt); sprintf(szTxt,"- Frame skipping: %s",szO[UseFrameSkip]); strcat(pB,szTxt); if(iFastFwd) strcat(pB," (fast forward)"); strcat(pB,"\r\n"); if(iFrameLimit==2) strcpy(szTxt,"- FPS limit: Auto\r\n\r\n"); else sprintf(szTxt,"- FPS limit: %.1f\r\n\r\n",fFrameRate); strcat(pB,szTxt); //----------------------------------------------------// #ifndef _MACGL strcpy(szTxt,"Misc:\r\n- MaintainAspect: "); if(iMaintainAspect == 0) strcat(szTxt,"disabled"); else if(iMaintainAspect == 1) strcat(szTxt,"enabled"); strcat(szTxt,"\r\n"); strcat(pB,szTxt); #endif sprintf(szTxt,"- Game fixes: %s [%08x]\r\n",szO[iUseFixes],dwCfgFixes); strcat(pB,szTxt); //----------------------------------------------------// return pB; } static void DoTextSnapShot(int iNum) { FILE *txtfile; char szTxt[256]; char *pB; sprintf(szTxt,"%s/pcsx%04d.txt",getenv("HOME"),iNum); if ((txtfile = fopen(szTxt, "wb")) == NULL) return; pB = pGetConfigInfos(0); if (pB) { fwrite(pB, strlen(pB), 1, txtfile); free(pB); } fclose(txtfile); } void CALLBACK GPUmakeSnapshot(void) { FILE *bmpfile; char filename[256]; unsigned char header[0x36]; long size, height; unsigned char line[1024 * 3]; short i, j; unsigned char empty[2] = {0,0}; unsigned short color; unsigned long snapshotnr = 0; unsigned char *pD; height = PreviousPSXDisplay.DisplayMode.y; size = height * PreviousPSXDisplay.Range.x1 * 3 + 0x38; // fill in proper values for BMP // hardcoded BMP header memset(header, 0, 0x36); header[0] = 'B'; header[1] = 'M'; header[2] = size & 0xff; header[3] = (size >> 8) & 0xff; header[4] = (size >> 16) & 0xff; header[5] = (size >> 24) & 0xff; header[0x0a] = 0x36; header[0x0e] = 0x28; header[0x12] = PreviousPSXDisplay.Range.x1 % 256; header[0x13] = PreviousPSXDisplay.Range.x1 / 256; header[0x16] = height % 256; header[0x17] = height / 256; header[0x1a] = 0x01; header[0x1c] = 0x18; header[0x26] = 0x12; header[0x27] = 0x0B; header[0x2A] = 0x12; header[0x2B] = 0x0B; // increment snapshot value & try to get filename do { snapshotnr++; sprintf(filename, "%s/pcsx%04ld.bmp", getenv("HOME"), snapshotnr); bmpfile = fopen(filename,"rb"); if (bmpfile == NULL) break; fclose(bmpfile); } while(TRUE); // try opening new snapshot file if ((bmpfile = fopen(filename,"wb")) == NULL) return; fwrite(header, 0x36, 1, bmpfile); for (i = height + PSXDisplay.DisplayPosition.y - 1; i >= PSXDisplay.DisplayPosition.y; i--) { pD = (unsigned char *)&psxVuw[i * 1024 + PSXDisplay.DisplayPosition.x]; for (j = 0; j < PreviousPSXDisplay.Range.x1; j++) { if (PSXDisplay.RGB24) { uint32_t lu = *(uint32_t *)pD; line[j * 3 + 2] = RED(lu); line[j * 3 + 1] = GREEN(lu); line[j * 3 + 0] = BLUE(lu); pD += 3; } else { color = GETLE16(pD); line[j * 3 + 2] = (color << 3) & 0xf1; line[j * 3 + 1] = (color >> 2) & 0xf1; line[j * 3 + 0] = (color >> 7) & 0xf1; pD += 2; } } fwrite(line, PreviousPSXDisplay.Range.x1 * 3, 1, bmpfile); } fwrite(empty, 0x2, 1, bmpfile); fclose(bmpfile); DoTextSnapShot(snapshotnr); } //////////////////////////////////////////////////////////////////////// // INIT, will be called after lib load... well, just do some var init... //////////////////////////////////////////////////////////////////////// long CALLBACK GPUinit() // GPU INIT { memset(ulStatusControl,0,256*sizeof(uint32_t)); // init save state scontrol field szDebugText[0] = 0; // init debug text buffer psxVSecure = (unsigned char *)malloc((iGPUHeight*2)*1024 + (1024*1024)); // always alloc one extra MB for soft drawing funcs security if (!psxVSecure) return -1; //!!! ATTENTION !!! psxVub=psxVSecure + 512 * 1024; // security offset into double sized psx vram! psxVsb=(signed char *)psxVub; // different ways of accessing PSX VRAM psxVsw=(signed short *)psxVub; psxVsl=(int32_t *)psxVub; psxVuw=(unsigned short *)psxVub; psxVul=(uint32_t *)psxVub; psxVuw_eom=psxVuw+1024*iGPUHeight; // pre-calc of end of vram memset(psxVSecure,0x00,(iGPUHeight*2)*1024 + (1024*1024)); memset(lGPUInfoVals,0x00,16*sizeof(uint32_t)); SetFPSHandler(); PSXDisplay.RGB24 = FALSE; // init some stuff PSXDisplay.Interlaced = FALSE; PSXDisplay.DrawOffset.x = 0; PSXDisplay.DrawOffset.y = 0; PSXDisplay.DisplayMode.x= 320; PSXDisplay.DisplayMode.y= 240; PreviousPSXDisplay.DisplayMode.x= 320; PreviousPSXDisplay.DisplayMode.y= 240; PSXDisplay.Disabled = FALSE; PreviousPSXDisplay.Range.x0 =0; PreviousPSXDisplay.Range.y0 =0; PSXDisplay.Range.x0=0; PSXDisplay.Range.x1=0; PreviousPSXDisplay.DisplayModeNew.y=0; PSXDisplay.Double = 1; lGPUdataRet = 0x400; DataWriteMode = DR_NORMAL; // Reset transfer values, to prevent mis-transfer of data memset(&VRAMWrite, 0, sizeof(VRAMLoad_t)); memset(&VRAMRead, 0, sizeof(VRAMLoad_t)); // device initialised already ! lGPUstatusRet = 0x14802000; GPUIsIdle; GPUIsReadyForCommands; bDoVSyncUpdate = TRUE; return 0; } //////////////////////////////////////////////////////////////////////// // Here starts all... //////////////////////////////////////////////////////////////////////// long GPUopen(unsigned long * disp,char * CapText,char * CfgFile) { unsigned long d; pCaptionText=CapText; ReadConfigGPU(); // read registry InitFPS(); bIsFirstFrame = TRUE; // we have to init later bDoVSyncUpdate = TRUE; d=ulInitDisplay(); // setup x if(disp) *disp=d; // wanna x pointer? ok if(d) return 0; return -1; } //////////////////////////////////////////////////////////////////////// // time to leave... //////////////////////////////////////////////////////////////////////// long CALLBACK GPUclose() // GPU CLOSE { ReleaseKeyHandler(); // de-subclass window CloseDisplay(); // shutdown direct draw return 0; } //////////////////////////////////////////////////////////////////////// // I shot the sheriff //////////////////////////////////////////////////////////////////////// long CALLBACK GPUshutdown() // GPU SHUTDOWN { free(psxVSecure); return 0; // nothinh to do } //////////////////////////////////////////////////////////////////////// // Update display (swap buffers) //////////////////////////////////////////////////////////////////////// void updateDisplay(void) // UPDATE DISPLAY { if(PSXDisplay.Disabled) // disable? { DoClearFrontBuffer(); // -> clear frontbuffer return; // -> and bye } if(dwActFixes&32) // pc fps calculation fix { if(UseFrameLimit) PCFrameCap(); // -> brake if(UseFrameSkip || ulKeybits&KEY_SHOWFPS) PCcalcfps(); } if(ulKeybits&KEY_SHOWFPS) // make fps display buf { sprintf(szDispBuf,"FPS %06.1f",fps_cur); } if(iFastFwd) // fastfwd ? { static int fpscount; UseFrameSkip=1; if(!bSkipNextFrame) DoBufferSwap(); // -> to skip or not to skip if(fpscount%6) // -> skip 6/7 frames bSkipNextFrame = TRUE; else bSkipNextFrame = FALSE; fpscount++; if(fpscount >= (int)fFrameRateHz) fpscount = 0; return; } if(UseFrameSkip) // skip ? { if(!bSkipNextFrame) DoBufferSwap(); // -> to skip or not to skip if(dwActFixes&0xa0) // -> pc fps calculation fix/old skipping fix { if((fps_skip < fFrameRateHz) && !(bSkipNextFrame)) // -> skip max one in a row {bSkipNextFrame = TRUE; fps_skip=fFrameRateHz;} else bSkipNextFrame = FALSE; } else FrameSkip(); } else // no skip ? { DoBufferSwap(); // -> swap } } //////////////////////////////////////////////////////////////////////// // roughly emulated screen centering bits... not complete !!! //////////////////////////////////////////////////////////////////////// void ChangeDispOffsetsX(void) // X CENTER { long lx,l; if(!PSXDisplay.Range.x1) return; l=PreviousPSXDisplay.DisplayMode.x; l*=(long)PSXDisplay.Range.x1; l/=2560;lx=l;l&=0xfffffff8; if(l==PreviousPSXDisplay.Range.y1) return; // abusing range.y1 for PreviousPSXDisplay.Range.y1=(short)l; // storing last x range and test if(lx>=PreviousPSXDisplay.DisplayMode.x) { PreviousPSXDisplay.Range.x1= (short)PreviousPSXDisplay.DisplayMode.x; PreviousPSXDisplay.Range.x0=0; } else { PreviousPSXDisplay.Range.x1=(short)l; PreviousPSXDisplay.Range.x0= (PSXDisplay.Range.x0-500)/8; if(PreviousPSXDisplay.Range.x0<0) PreviousPSXDisplay.Range.x0=0; if((PreviousPSXDisplay.Range.x0+lx)> PreviousPSXDisplay.DisplayMode.x) { PreviousPSXDisplay.Range.x0= (short)(PreviousPSXDisplay.DisplayMode.x-lx); PreviousPSXDisplay.Range.x0+=2; //??? PreviousPSXDisplay.Range.x1+=(short)(lx-l); PreviousPSXDisplay.Range.x1-=2; // makes linux stretching easier } // some linux alignment security PreviousPSXDisplay.Range.x0=PreviousPSXDisplay.Range.x0>>1; PreviousPSXDisplay.Range.x0=PreviousPSXDisplay.Range.x0<<1; PreviousPSXDisplay.Range.x1=PreviousPSXDisplay.Range.x1>>1; PreviousPSXDisplay.Range.x1=PreviousPSXDisplay.Range.x1<<1; DoClearScreenBuffer(); } bDoVSyncUpdate=TRUE; } //////////////////////////////////////////////////////////////////////// void ChangeDispOffsetsY(void) // Y CENTER { int iT,iO=PreviousPSXDisplay.Range.y0; int iOldYOffset=PreviousPSXDisplay.DisplayModeNew.y; // new if((PreviousPSXDisplay.DisplayModeNew.x+PSXDisplay.DisplayModeNew.y)>iGPUHeight) { int dy1=iGPUHeight-PreviousPSXDisplay.DisplayModeNew.x; int dy2=(PreviousPSXDisplay.DisplayModeNew.x+PSXDisplay.DisplayModeNew.y)-iGPUHeight; if(dy1>=dy2) { PreviousPSXDisplay.DisplayModeNew.y=-dy2; } else { PSXDisplay.DisplayPosition.y=0; PreviousPSXDisplay.DisplayModeNew.y=-dy1; } } else PreviousPSXDisplay.DisplayModeNew.y=0; // eon if(PreviousPSXDisplay.DisplayModeNew.y!=iOldYOffset) // if old offset!=new offset: recalc height { PSXDisplay.Height = PSXDisplay.Range.y1 - PSXDisplay.Range.y0 + PreviousPSXDisplay.DisplayModeNew.y; PSXDisplay.DisplayModeNew.y=PSXDisplay.Height*PSXDisplay.Double; } // if(PSXDisplay.PAL) iT=48; else iT=28; if(PSXDisplay.Range.y0>=iT) { PreviousPSXDisplay.Range.y0= (short)((PSXDisplay.Range.y0-iT-4)*PSXDisplay.Double); if(PreviousPSXDisplay.Range.y0<0) PreviousPSXDisplay.Range.y0=0; PSXDisplay.DisplayModeNew.y+= PreviousPSXDisplay.Range.y0; } else PreviousPSXDisplay.Range.y0=0; if(iO!=PreviousPSXDisplay.Range.y0) { DoClearScreenBuffer(); } } //////////////////////////////////////////////////////////////////////// // check if update needed //////////////////////////////////////////////////////////////////////// void updateDisplayIfChanged(void) // UPDATE DISPLAY IF CHANGED { if ((PSXDisplay.DisplayMode.y == PSXDisplay.DisplayModeNew.y) && (PSXDisplay.DisplayMode.x == PSXDisplay.DisplayModeNew.x)) { if((PSXDisplay.RGB24 == PSXDisplay.RGB24New) && (PSXDisplay.Interlaced == PSXDisplay.InterlacedNew)) return; } PSXDisplay.RGB24 = PSXDisplay.RGB24New; // get new infos PSXDisplay.DisplayMode.y = PSXDisplay.DisplayModeNew.y; PSXDisplay.DisplayMode.x = PSXDisplay.DisplayModeNew.x; PreviousPSXDisplay.DisplayMode.x= // previous will hold min(640,PSXDisplay.DisplayMode.x); // max 640x512... that's PreviousPSXDisplay.DisplayMode.y= // the size of my min(512,PSXDisplay.DisplayMode.y); // back buffer surface PSXDisplay.Interlaced = PSXDisplay.InterlacedNew; PSXDisplay.DisplayEnd.x= // calc end of display PSXDisplay.DisplayPosition.x+ PSXDisplay.DisplayMode.x; PSXDisplay.DisplayEnd.y= PSXDisplay.DisplayPosition.y+ PSXDisplay.DisplayMode.y+PreviousPSXDisplay.DisplayModeNew.y; PreviousPSXDisplay.DisplayEnd.x= PreviousPSXDisplay.DisplayPosition.x+ PSXDisplay.DisplayMode.x; PreviousPSXDisplay.DisplayEnd.y= PreviousPSXDisplay.DisplayPosition.y+ PSXDisplay.DisplayMode.y+PreviousPSXDisplay.DisplayModeNew.y; ChangeDispOffsetsX(); if(iFrameLimit==2) SetAutoFrameCap(); // -> set it if(UseFrameSkip) updateDisplay(); // stupid stuff when frame skipping enabled } //////////////////////////////////////////////////////////////////////// #ifndef _MACGL #include "draw.h" void ChangeWindowMode(void) // TOGGLE FULLSCREEN - WINDOW { extern Display *display; extern Window window; extern int root_window_id; Screen *screen; XSizeHints hints; MotifWmHints mwmhints; Atom mwmatom; screen=DefaultScreenOfDisplay(display); iWindowMode=!iWindowMode; if(!iWindowMode) // fullscreen { mwmhints.flags=MWM_HINTS_DECORATIONS; mwmhints.functions=0; mwmhints.decorations=0; mwmhints.input_mode=0; mwmatom=XInternAtom(display,"_MOTIF_WM_HINTS",0); XChangeProperty(display,window,mwmatom,mwmatom,32, PropModeReplace,(unsigned char *)&mwmhints,5); XResizeWindow(display,window,screen->width,screen->height); hints.min_width = hints.max_width = hints.base_width = screen->width; hints.min_height= hints.max_height = hints.base_height = screen->height; XSetWMNormalHints(display,window,&hints); { XEvent xev; memset(&xev, 0, sizeof(xev)); xev.xclient.type = ClientMessage; xev.xclient.serial = 0; xev.xclient.send_event = 1; xev.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", 0); xev.xclient.window = window; xev.xclient.format = 32; xev.xclient.data.l[0] = 1; xev.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", 0); xev.xclient.data.l[2] = 0; xev.xclient.data.l[3] = 0; xev.xclient.data.l[4] = 0; XSendEvent(display, root_window_id, 0, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } } else { { XEvent xev; memset(&xev, 0, sizeof(xev)); xev.xclient.type = ClientMessage; xev.xclient.serial = 0; xev.xclient.send_event = 1; xev.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", 0); xev.xclient.window = window; xev.xclient.format = 32; xev.xclient.data.l[0] = 0; xev.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", 0); xev.xclient.data.l[2] = 0; xev.xclient.data.l[3] = 0; xev.xclient.data.l[4] = 0; XSendEvent(display, root_window_id, 0, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } mwmhints.flags=MWM_HINTS_DECORATIONS; mwmhints.functions=0; mwmhints.decorations=1; mwmhints.input_mode=0; mwmatom=XInternAtom(display,"_MOTIF_WM_HINTS",0); //This shouldn't work on 64 bit longs, but it does...in fact, it breaks when I change all the mwmhints to int. //I don't pretend to understand it. XChangeProperty(display,window,mwmatom,mwmatom,32, PropModeReplace,(unsigned char *)&mwmhints,5); hints.flags=USPosition|USSize; hints.base_width = iResX; hints.base_height = iResY; XSetWMNormalHints(display,window,&hints); XResizeWindow(display,window,iResX,iResY); } DoClearScreenBuffer(); bChangeWinMode=FALSE; bDoVSyncUpdate=TRUE; } #endif //////////////////////////////////////////////////////////////////////// // gun cursor func: player=0-7, x=0-511, y=0-255 //////////////////////////////////////////////////////////////////////// void CALLBACK GPUcursor(int iPlayer,int x,int y) { if(iPlayer<0) return; if(iPlayer>7) return; usCursorActive|=(1<511) x=511; if(y<0) y=0; if(y>255) y=255; ptCursorPoint[iPlayer].x=x; ptCursorPoint[iPlayer].y=y; } //////////////////////////////////////////////////////////////////////// // update lace is called evry VSync //////////////////////////////////////////////////////////////////////// void CALLBACK GPUupdateLace(void) // VSYNC { if(!(dwActFixes&1)) lGPUstatusRet^=0x80000000; // odd/even bit //if(!(dwActFixes&32)) // std fps limitation? // CheckFrameRate(); pl_frame_limit(); if(PSXDisplay.Interlaced) // interlaced mode? { if(bDoVSyncUpdate && PSXDisplay.DisplayMode.x>0 && PSXDisplay.DisplayMode.y>0) { updateDisplay(); } } else // non-interlaced? { if(dwActFixes&64) // lazy screen update fix { if(bDoLazyUpdate && !UseFrameSkip) updateDisplay(); bDoLazyUpdate=FALSE; } else { if(bDoVSyncUpdate && !UseFrameSkip) // some primitives drawn? updateDisplay(); // -> update display } } #ifndef _MACGL if(bChangeWinMode) ChangeWindowMode(); // toggle full - window mode #endif bDoVSyncUpdate=FALSE; // vsync done } //////////////////////////////////////////////////////////////////////// // process read request from GPU status register //////////////////////////////////////////////////////////////////////// uint32_t CALLBACK GPUreadStatus(void) // READ STATUS { if(dwActFixes&1) { static int iNumRead=0; // odd/even hack if((iNumRead++)==2) { iNumRead=0; lGPUstatusRet^=0x80000000; // interlaced bit toggle... we do it on every 3 read status... needed by some games (like ChronoCross) with old epsxe versions (1.5.2 and older) } } if(iFakePrimBusy) // 27.10.2007 - PETE : emulating some 'busy' while drawing... pfff { iFakePrimBusy--; if(iFakePrimBusy&1) // we do a busy-idle-busy-idle sequence after/while drawing prims { GPUIsBusy; GPUIsNotReadyForCommands; } else { GPUIsIdle; GPUIsReadyForCommands; } } return lGPUstatusRet; } //////////////////////////////////////////////////////////////////////// // processes data send to GPU status register // these are always single packet commands. //////////////////////////////////////////////////////////////////////// void CALLBACK GPUwriteStatus(uint32_t gdata) // WRITE STATUS { uint32_t lCommand=(gdata>>24)&0xff; ulStatusControl[lCommand]=gdata; // store command for freezing switch(lCommand) { //--------------------------------------------------// // reset gpu case 0x00: memset(lGPUInfoVals,0x00,16*sizeof(uint32_t)); lGPUstatusRet=0x14802000; PSXDisplay.Disabled=1; DataWriteMode=DataReadMode=DR_NORMAL; PSXDisplay.DrawOffset.x=PSXDisplay.DrawOffset.y=0; drawX=drawY=0;drawW=drawH=0; sSetMask=0;lSetMask=0;bCheckMask=FALSE; usMirror=0; GlobalTextAddrX=0;GlobalTextAddrY=0; GlobalTextTP=0;GlobalTextABR=0; PSXDisplay.RGB24=FALSE; PSXDisplay.Interlaced=FALSE; bUsingTWin = FALSE; return; //--------------------------------------------------// // dis/enable display case 0x03: PreviousPSXDisplay.Disabled = PSXDisplay.Disabled; PSXDisplay.Disabled = (gdata & 1); if(PSXDisplay.Disabled) lGPUstatusRet|=GPUSTATUS_DISPLAYDISABLED; else lGPUstatusRet&=~GPUSTATUS_DISPLAYDISABLED; return; //--------------------------------------------------// // setting transfer mode case 0x04: gdata &= 0x03; // Only want the lower two bits DataWriteMode=DataReadMode=DR_NORMAL; if(gdata==0x02) DataWriteMode=DR_VRAMTRANSFER; if(gdata==0x03) DataReadMode =DR_VRAMTRANSFER; lGPUstatusRet&=~GPUSTATUS_DMABITS; // Clear the current settings of the DMA bits lGPUstatusRet|=(gdata << 29); // Set the DMA bits according to the received data return; //--------------------------------------------------// // setting display position case 0x05: { PreviousPSXDisplay.DisplayPosition.x = PSXDisplay.DisplayPosition.x; PreviousPSXDisplay.DisplayPosition.y = PSXDisplay.DisplayPosition.y; //////// /* PSXDisplay.DisplayPosition.y = (short)((gdata>>10)&0x3ff); if (PSXDisplay.DisplayPosition.y & 0x200) PSXDisplay.DisplayPosition.y |= 0xfffffc00; if(PSXDisplay.DisplayPosition.y<0) { PreviousPSXDisplay.DisplayModeNew.y=PSXDisplay.DisplayPosition.y/PSXDisplay.Double; PSXDisplay.DisplayPosition.y=0; } else PreviousPSXDisplay.DisplayModeNew.y=0; */ // new if(iGPUHeight==1024) { if(dwGPUVersion==2) PSXDisplay.DisplayPosition.y = (short)((gdata>>12)&0x3ff); else PSXDisplay.DisplayPosition.y = (short)((gdata>>10)&0x3ff); } else PSXDisplay.DisplayPosition.y = (short)((gdata>>10)&0x1ff); // store the same val in some helper var, we need it on later compares PreviousPSXDisplay.DisplayModeNew.x=PSXDisplay.DisplayPosition.y; if((PSXDisplay.DisplayPosition.y+PSXDisplay.DisplayMode.y)>iGPUHeight) { int dy1=iGPUHeight-PSXDisplay.DisplayPosition.y; int dy2=(PSXDisplay.DisplayPosition.y+PSXDisplay.DisplayMode.y)-iGPUHeight; if(dy1>=dy2) { PreviousPSXDisplay.DisplayModeNew.y=-dy2; } else { PSXDisplay.DisplayPosition.y=0; PreviousPSXDisplay.DisplayModeNew.y=-dy1; } } else PreviousPSXDisplay.DisplayModeNew.y=0; // eon PSXDisplay.DisplayPosition.x = (short)(gdata & 0x3ff); PSXDisplay.DisplayEnd.x= PSXDisplay.DisplayPosition.x+ PSXDisplay.DisplayMode.x; PSXDisplay.DisplayEnd.y= PSXDisplay.DisplayPosition.y+ PSXDisplay.DisplayMode.y + PreviousPSXDisplay.DisplayModeNew.y; PreviousPSXDisplay.DisplayEnd.x= PreviousPSXDisplay.DisplayPosition.x+ PSXDisplay.DisplayMode.x; PreviousPSXDisplay.DisplayEnd.y= PreviousPSXDisplay.DisplayPosition.y+ PSXDisplay.DisplayMode.y + PreviousPSXDisplay.DisplayModeNew.y; bDoVSyncUpdate=TRUE; if (!(PSXDisplay.Interlaced)) // stupid frame skipping option { if(UseFrameSkip) updateDisplay(); if(dwActFixes&64) bDoLazyUpdate=TRUE; } }return; //--------------------------------------------------// // setting width case 0x06: PSXDisplay.Range.x0=(short)(gdata & 0x7ff); PSXDisplay.Range.x1=(short)((gdata>>12) & 0xfff); PSXDisplay.Range.x1-=PSXDisplay.Range.x0; ChangeDispOffsetsX(); return; //--------------------------------------------------// // setting height case 0x07: { PSXDisplay.Range.y0=(short)(gdata & 0x3ff); PSXDisplay.Range.y1=(short)((gdata>>10) & 0x3ff); PreviousPSXDisplay.Height = PSXDisplay.Height; PSXDisplay.Height = PSXDisplay.Range.y1 - PSXDisplay.Range.y0 + PreviousPSXDisplay.DisplayModeNew.y; if(PreviousPSXDisplay.Height!=PSXDisplay.Height) { PSXDisplay.DisplayModeNew.y=PSXDisplay.Height*PSXDisplay.Double; ChangeDispOffsetsY(); updateDisplayIfChanged(); } return; } //--------------------------------------------------// // setting display infos case 0x08: PSXDisplay.DisplayModeNew.x = sDispWidths[(gdata & 0x03) | ((gdata & 0x40) >> 4)]; if (gdata&0x04) PSXDisplay.Double=2; else PSXDisplay.Double=1; PSXDisplay.DisplayModeNew.y = PSXDisplay.Height*PSXDisplay.Double; ChangeDispOffsetsY(); PSXDisplay.PAL = (gdata & 0x08)?TRUE:FALSE; // if 1 - PAL mode, else NTSC PSXDisplay.RGB24New = (gdata & 0x10)?TRUE:FALSE; // if 1 - TrueColor PSXDisplay.InterlacedNew = (gdata & 0x20)?TRUE:FALSE; // if 1 - Interlace lGPUstatusRet&=~GPUSTATUS_WIDTHBITS; // Clear the width bits lGPUstatusRet|= (((gdata & 0x03) << 17) | ((gdata & 0x40) << 10)); // Set the width bits if(PSXDisplay.InterlacedNew) { if(!PSXDisplay.Interlaced) { PreviousPSXDisplay.DisplayPosition.x = PSXDisplay.DisplayPosition.x; PreviousPSXDisplay.DisplayPosition.y = PSXDisplay.DisplayPosition.y; } lGPUstatusRet|=GPUSTATUS_INTERLACED; } else lGPUstatusRet&=~GPUSTATUS_INTERLACED; if (PSXDisplay.PAL) lGPUstatusRet|=GPUSTATUS_PAL; else lGPUstatusRet&=~GPUSTATUS_PAL; if (PSXDisplay.Double==2) lGPUstatusRet|=GPUSTATUS_DOUBLEHEIGHT; else lGPUstatusRet&=~GPUSTATUS_DOUBLEHEIGHT; if (PSXDisplay.RGB24New) lGPUstatusRet|=GPUSTATUS_RGB24; else lGPUstatusRet&=~GPUSTATUS_RGB24; updateDisplayIfChanged(); return; //--------------------------------------------------// // ask about GPU version and other stuff case 0x10: gdata&=0xff; switch(gdata) { case 0x02: lGPUdataRet=lGPUInfoVals[INFO_TW]; // tw infos return; case 0x03: lGPUdataRet=lGPUInfoVals[INFO_DRAWSTART]; // draw start return; case 0x04: lGPUdataRet=lGPUInfoVals[INFO_DRAWEND]; // draw end return; case 0x05: case 0x06: lGPUdataRet=lGPUInfoVals[INFO_DRAWOFF]; // draw offset return; case 0x07: if(dwGPUVersion==2) lGPUdataRet=0x01; else lGPUdataRet=0x02; // gpu type return; case 0x08: case 0x0F: // some bios addr? lGPUdataRet=0xBFC03720; return; } return; //--------------------------------------------------// } } //////////////////////////////////////////////////////////////////////// // vram read/write helpers, needed by LEWPY's optimized vram read/write :) //////////////////////////////////////////////////////////////////////// __inline void FinishedVRAMWrite(void) { /* // NEWX if(!PSXDisplay.Interlaced && UseFrameSkip) // stupid frame skipping { VRAMWrite.Width +=VRAMWrite.x; VRAMWrite.Height+=VRAMWrite.y; if(VRAMWrite.x=PSXDisplay.DisplayPosition.x && VRAMWrite.y=PSXDisplay.DisplayPosition.y) updateDisplay(); } */ // Set register to NORMAL operation DataWriteMode = DR_NORMAL; // Reset transfer values, to prevent mis-transfer of data VRAMWrite.x = 0; VRAMWrite.y = 0; VRAMWrite.Width = 0; VRAMWrite.Height = 0; VRAMWrite.ColsRemaining = 0; VRAMWrite.RowsRemaining = 0; } __inline void FinishedVRAMRead(void) { // Set register to NORMAL operation DataReadMode = DR_NORMAL; // Reset transfer values, to prevent mis-transfer of data VRAMRead.x = 0; VRAMRead.y = 0; VRAMRead.Width = 0; VRAMRead.Height = 0; VRAMRead.ColsRemaining = 0; VRAMRead.RowsRemaining = 0; // Indicate GPU is no longer ready for VRAM data in the STATUS REGISTER lGPUstatusRet&=~GPUSTATUS_READYFORVRAM; } //////////////////////////////////////////////////////////////////////// // core read from vram //////////////////////////////////////////////////////////////////////// void CALLBACK GPUreadDataMem(uint32_t * pMem, int iSize) { int i; if(DataReadMode!=DR_VRAMTRANSFER) return; GPUIsBusy; // adjust read ptr, if necessary while(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=iGPUHeight*1024; while(VRAMRead.ImagePtr 0) && (VRAMRead.RowsRemaining > 0)) { // lower 16 bit lGPUdataRet=(uint32_t)GETLE16(VRAMRead.ImagePtr); VRAMRead.ImagePtr++; if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=iGPUHeight*1024; VRAMRead.RowsRemaining --; if(VRAMRead.RowsRemaining<=0) { VRAMRead.RowsRemaining = VRAMRead.Width; VRAMRead.ColsRemaining--; VRAMRead.ImagePtr += 1024 - VRAMRead.Width; if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=iGPUHeight*1024; } // higher 16 bit (always, even if it's an odd width) lGPUdataRet|=(uint32_t)GETLE16(VRAMRead.ImagePtr)<<16; PUTLE32(pMem, lGPUdataRet); pMem++; if(VRAMRead.ColsRemaining <= 0) {FinishedVRAMRead();goto ENDREAD;} VRAMRead.ImagePtr++; if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=iGPUHeight*1024; VRAMRead.RowsRemaining--; if(VRAMRead.RowsRemaining<=0) { VRAMRead.RowsRemaining = VRAMRead.Width; VRAMRead.ColsRemaining--; VRAMRead.ImagePtr += 1024 - VRAMRead.Width; if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=iGPUHeight*1024; } if(VRAMRead.ColsRemaining <= 0) {FinishedVRAMRead();goto ENDREAD;} } else {FinishedVRAMRead();goto ENDREAD;} } ENDREAD: GPUIsIdle; } //////////////////////////////////////////////////////////////////////// uint32_t CALLBACK GPUreadData(void) { uint32_t l; GPUreadDataMem(&l,1); return lGPUdataRet; } //////////////////////////////////////////////////////////////////////// // processes data send to GPU data register // extra table entries for fixing polyline troubles //////////////////////////////////////////////////////////////////////// const unsigned char primTableCX[256] = { // 00 0,0,3,0,0,0,0,0, // 08 0,0,0,0,0,0,0,0, // 10 0,0,0,0,0,0,0,0, // 18 0,0,0,0,0,0,0,0, // 20 4,4,4,4,7,7,7,7, // 28 5,5,5,5,9,9,9,9, // 30 6,6,6,6,9,9,9,9, // 38 8,8,8,8,12,12,12,12, // 40 3,3,3,3,0,0,0,0, // 48 // 5,5,5,5,6,6,6,6, // FLINE 254,254,254,254,254,254,254,254, // 50 4,4,4,4,0,0,0,0, // 58 // 7,7,7,7,9,9,9,9, // GLINE 255,255,255,255,255,255,255,255, // 60 3,3,3,3,4,4,4,4, // 68 2,2,2,2,3,3,3,3, // 3=SPRITE1??? // 70 2,2,2,2,3,3,3,3, // 78 2,2,2,2,3,3,3,3, // 80 4,0,0,0,0,0,0,0, // 88 0,0,0,0,0,0,0,0, // 90 0,0,0,0,0,0,0,0, // 98 0,0,0,0,0,0,0,0, // a0 3,0,0,0,0,0,0,0, // a8 0,0,0,0,0,0,0,0, // b0 0,0,0,0,0,0,0,0, // b8 0,0,0,0,0,0,0,0, // c0 3,0,0,0,0,0,0,0, // c8 0,0,0,0,0,0,0,0, // d0 0,0,0,0,0,0,0,0, // d8 0,0,0,0,0,0,0,0, // e0 0,1,1,1,1,1,1,0, // e8 0,0,0,0,0,0,0,0, // f0 0,0,0,0,0,0,0,0, // f8 0,0,0,0,0,0,0,0 }; void CALLBACK GPUwriteDataMem(uint32_t * pMem, int iSize) { unsigned char command; uint32_t gdata=0; int i=0; GPUIsBusy; GPUIsNotReadyForCommands; STARTVRAM: if(DataWriteMode==DR_VRAMTRANSFER) { BOOL bFinished=FALSE; // make sure we are in vram while(VRAMWrite.ImagePtr>=psxVuw_eom) VRAMWrite.ImagePtr-=iGPUHeight*1024; while(VRAMWrite.ImagePtr0) { while(VRAMWrite.RowsRemaining>0) { if(i>=iSize) {goto ENDVRAM;} i++; gdata=GETLE32(pMem); pMem++; PUTLE16(VRAMWrite.ImagePtr, (unsigned short)gdata); VRAMWrite.ImagePtr++; if(VRAMWrite.ImagePtr>=psxVuw_eom) VRAMWrite.ImagePtr-=iGPUHeight*1024; VRAMWrite.RowsRemaining --; if(VRAMWrite.RowsRemaining <= 0) { VRAMWrite.ColsRemaining--; if (VRAMWrite.ColsRemaining <= 0) // last pixel is odd width { gdata=(gdata&0xFFFF)|(((uint32_t)GETLE16(VRAMWrite.ImagePtr))<<16); FinishedVRAMWrite(); bDoVSyncUpdate=TRUE; goto ENDVRAM; } VRAMWrite.RowsRemaining = VRAMWrite.Width; VRAMWrite.ImagePtr += 1024 - VRAMWrite.Width; } PUTLE16(VRAMWrite.ImagePtr, (unsigned short)(gdata>>16)); VRAMWrite.ImagePtr++; if(VRAMWrite.ImagePtr>=psxVuw_eom) VRAMWrite.ImagePtr-=iGPUHeight*1024; VRAMWrite.RowsRemaining --; } VRAMWrite.RowsRemaining = VRAMWrite.Width; VRAMWrite.ColsRemaining--; VRAMWrite.ImagePtr += 1024 - VRAMWrite.Width; bFinished=TRUE; } FinishedVRAMWrite(); if(bFinished) bDoVSyncUpdate=TRUE; } ENDVRAM: if(DataWriteMode==DR_NORMAL) { void (* *primFunc)(unsigned char *); if(bSkipNextFrame) primFunc=primTableSkip; else primFunc=primTableJ; for(;i>24) & 0xff); //if(command>=0xb0 && command<0xc0) auxprintf("b0 %x!!!!!!!!!\n",command); if(primTableCX[command]) { gpuDataC = primTableCX[command]; gpuCommand = command; PUTLE32(&gpuDataM[0], gdata); gpuDataP = 1; } else continue; } else { PUTLE32(&gpuDataM[gpuDataP], gdata); if(gpuDataC>128) { if((gpuDataC==254 && gpuDataP>=3) || (gpuDataC==255 && gpuDataP>=4 && !(gpuDataP&1))) { if((gpuDataM[gpuDataP] & 0xF000F000) == 0x50005000) gpuDataP=gpuDataC-1; } } gpuDataP++; } if(gpuDataP == gpuDataC) { gpuDataC=gpuDataP=0; primFunc[gpuCommand]((unsigned char *)gpuDataM); if(dwEmuFixes&0x0001 || dwActFixes&0x0400) // hack for emulating "gpu busy" in some games iFakePrimBusy=4; } } } lGPUdataRet=gdata; GPUIsReadyForCommands; GPUIsIdle; } //////////////////////////////////////////////////////////////////////// void CALLBACK GPUwriteData(uint32_t gdata) { PUTLE32(&gdata, gdata); GPUwriteDataMem(&gdata,1); } //////////////////////////////////////////////////////////////////////// // this functions will be removed soon (or 'soonish')... not really needed, but some emus want them //////////////////////////////////////////////////////////////////////// void CALLBACK GPUsetMode(unsigned long gdata) { // Peops does nothing here... // DataWriteMode=(gdata&1)?DR_VRAMTRANSFER:DR_NORMAL; // DataReadMode =(gdata&2)?DR_VRAMTRANSFER:DR_NORMAL; } long CALLBACK GPUgetMode(void) { long iT=0; if(DataWriteMode==DR_VRAMTRANSFER) iT|=0x1; if(DataReadMode ==DR_VRAMTRANSFER) iT|=0x2; return iT; } //////////////////////////////////////////////////////////////////////// // call config dlg //////////////////////////////////////////////////////////////////////// long CALLBACK GPUconfigure(void) { SoftDlgProc(); return 0; } //////////////////////////////////////////////////////////////////////// // sets all kind of act fixes //////////////////////////////////////////////////////////////////////// void SetFixes(void) { if(dwActFixes&0x02) sDispWidths[4]=384; else sDispWidths[4]=368; } //////////////////////////////////////////////////////////////////////// // process gpu commands //////////////////////////////////////////////////////////////////////// unsigned long lUsedAddr[3]; __inline BOOL CheckForEndlessLoop(unsigned long laddr) { if(laddr==lUsedAddr[1]) return TRUE; if(laddr==lUsedAddr[2]) return TRUE; if(laddr 2000000) break; if(CheckForEndlessLoop(addr)) break; count = baseAddrB[addr+3]; dmaMem=addr+4; if(count>0) GPUwriteDataMem(&baseAddrL[dmaMem>>2],count); addr = GETLE32(&baseAddrL[addr>>2])&0xffffff; } while (addr != 0xffffff); GPUIsIdle; return 0; } //////////////////////////////////////////////////////////////////////// // show about dlg //////////////////////////////////////////////////////////////////////// void CALLBACK GPUabout(void) // ABOUT { AboutDlgProc(); return; } //////////////////////////////////////////////////////////////////////// // We are ever fine ;) //////////////////////////////////////////////////////////////////////// long CALLBACK GPUtest(void) { // if test fails this function should return negative value for error (unable to continue) // and positive value for warning (can continue but output might be crappy) return 0; } //////////////////////////////////////////////////////////////////////// // Freeze //////////////////////////////////////////////////////////////////////// typedef struct GPUFREEZETAG { uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu) uint32_t ulStatus; // current gpu status uint32_t ulControl[256]; // latest control register values unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN) } GPUFreeze_t; //////////////////////////////////////////////////////////////////////// long CALLBACK GPUfreeze(uint32_t ulGetFreezeData,GPUFreeze_t * pF) { //----------------------------------------------------// if(ulGetFreezeData==2) // 2: info, which save slot is selected? (just for display) { long lSlotNum=*((long *)pF); if(lSlotNum<0) return 0; if(lSlotNum>8) return 0; lSelectedSlot=lSlotNum+1; BuildDispMenu(0); return 1; } //----------------------------------------------------// if(!pF) return 0; // some checks if(pF->ulFreezeVersion!=1) return 0; if(ulGetFreezeData==1) // 1: get data { pF->ulStatus=lGPUstatusRet; memcpy(pF->ulControl,ulStatusControl,256*sizeof(uint32_t)); memcpy(pF->psxVRam, psxVub, 1024*iGPUHeight*2); return 1; } if(ulGetFreezeData!=0) return 0; // 0: set data lGPUstatusRet=pF->ulStatus; memcpy(ulStatusControl,pF->ulControl,256*sizeof(uint32_t)); memcpy(psxVub, pF->psxVRam, 1024*iGPUHeight*2); // RESET TEXTURE STORE HERE, IF YOU USE SOMETHING LIKE THAT GPUwriteStatus(ulStatusControl[0]); GPUwriteStatus(ulStatusControl[1]); GPUwriteStatus(ulStatusControl[2]); GPUwriteStatus(ulStatusControl[3]); GPUwriteStatus(ulStatusControl[8]); // try to repair things GPUwriteStatus(ulStatusControl[6]); GPUwriteStatus(ulStatusControl[7]); GPUwriteStatus(ulStatusControl[5]); GPUwriteStatus(ulStatusControl[4]); return 1; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // SAVE STATE DISPLAY STUFF //////////////////////////////////////////////////////////////////////// // font 0-9, 24x20 pixels, 1 byte = 4 dots // 00 = black // 01 = white // 10 = red // 11 = transparent unsigned char cFont[10][120]= { // 0 {0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x05,0x54,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x05,0x54,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa }, // 1 {0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x50,0x00,0x00, 0x80,0x00,0x05,0x50,0x00,0x00, 0x80,0x00,0x00,0x50,0x00,0x00, 0x80,0x00,0x00,0x50,0x00,0x00, 0x80,0x00,0x00,0x50,0x00,0x00, 0x80,0x00,0x00,0x50,0x00,0x00, 0x80,0x00,0x00,0x50,0x00,0x00, 0x80,0x00,0x00,0x50,0x00,0x00, 0x80,0x00,0x00,0x50,0x00,0x00, 0x80,0x00,0x05,0x55,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa }, // 2 {0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x05,0x54,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x00,0x05,0x00,0x00, 0x80,0x00,0x00,0x05,0x00,0x00, 0x80,0x00,0x00,0x14,0x00,0x00, 0x80,0x00,0x00,0x50,0x00,0x00, 0x80,0x00,0x01,0x40,0x00,0x00, 0x80,0x00,0x05,0x00,0x00,0x00, 0x80,0x00,0x14,0x00,0x00,0x00, 0x80,0x00,0x15,0x55,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa }, // 3 {0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x05,0x54,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x00,0x05,0x00,0x00, 0x80,0x00,0x00,0x05,0x00,0x00, 0x80,0x00,0x01,0x54,0x00,0x00, 0x80,0x00,0x00,0x05,0x00,0x00, 0x80,0x00,0x00,0x05,0x00,0x00, 0x80,0x00,0x00,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x05,0x54,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa }, // 4 {0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x14,0x00,0x00, 0x80,0x00,0x00,0x54,0x00,0x00, 0x80,0x00,0x01,0x54,0x00,0x00, 0x80,0x00,0x01,0x54,0x00,0x00, 0x80,0x00,0x05,0x14,0x00,0x00, 0x80,0x00,0x14,0x14,0x00,0x00, 0x80,0x00,0x15,0x55,0x00,0x00, 0x80,0x00,0x00,0x14,0x00,0x00, 0x80,0x00,0x00,0x14,0x00,0x00, 0x80,0x00,0x00,0x55,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa }, // 5 {0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x15,0x55,0x00,0x00, 0x80,0x00,0x14,0x00,0x00,0x00, 0x80,0x00,0x14,0x00,0x00,0x00, 0x80,0x00,0x14,0x00,0x00,0x00, 0x80,0x00,0x15,0x54,0x00,0x00, 0x80,0x00,0x00,0x05,0x00,0x00, 0x80,0x00,0x00,0x05,0x00,0x00, 0x80,0x00,0x00,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x05,0x54,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa }, // 6 {0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x01,0x54,0x00,0x00, 0x80,0x00,0x05,0x00,0x00,0x00, 0x80,0x00,0x14,0x00,0x00,0x00, 0x80,0x00,0x14,0x00,0x00,0x00, 0x80,0x00,0x15,0x54,0x00,0x00, 0x80,0x00,0x15,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x05,0x54,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa }, // 7 {0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x15,0x55,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x00,0x14,0x00,0x00, 0x80,0x00,0x00,0x14,0x00,0x00, 0x80,0x00,0x00,0x50,0x00,0x00, 0x80,0x00,0x00,0x50,0x00,0x00, 0x80,0x00,0x01,0x40,0x00,0x00, 0x80,0x00,0x01,0x40,0x00,0x00, 0x80,0x00,0x05,0x00,0x00,0x00, 0x80,0x00,0x05,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa }, // 8 {0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x05,0x54,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x05,0x54,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x05,0x54,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa }, // 9 {0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x05,0x54,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x05,0x00,0x00, 0x80,0x00,0x14,0x15,0x00,0x00, 0x80,0x00,0x05,0x55,0x00,0x00, 0x80,0x00,0x00,0x05,0x00,0x00, 0x80,0x00,0x00,0x05,0x00,0x00, 0x80,0x00,0x00,0x14,0x00,0x00, 0x80,0x00,0x05,0x50,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00, 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa } }; //////////////////////////////////////////////////////////////////////// void PaintPicDot(unsigned char * p,unsigned char c) { if(c==0) {*p++=0x00;*p++=0x00;*p=0x00;return;} // black if(c==1) {*p++=0xff;*p++=0xff;*p=0xff;return;} // white if(c==2) {*p++=0x00;*p++=0x00;*p=0xff;return;} // red // transparent } //////////////////////////////////////////////////////////////////////// // the main emu allocs 128x96x3 bytes, and passes a ptr // to it in pMem... the plugin has to fill it with // 8-8-8 bit BGR screen data (Win 24 bit BMP format // without header). // Beware: the func can be called at any time, // so you have to use the frontbuffer to get a fully // rendered picture // LINUX version: extern char * Xpixels; void GPUgetScreenPic(unsigned char * pMem) { /* unsigned short c;unsigned char * pf;int x,y; float XS=(float)iResX/128; float YS=(float)iResY/96; pf=pMem; memset(pMem, 0, 128*96*3); if(Xpixels) { unsigned char * ps=(unsigned char *)Xpixels; { long lPitch=iResX<<2; uint32_t sx; for(y=0;y<96;y++) { for(x=0;x<128;x++) { sx=*((uint32_t *)((ps)+ (((int)((float)y*YS))*lPitch)+ ((int)((float)x*XS))*4)); *(pf+0)=(sx&0xff); *(pf+1)=(sx&0xff00)>>8; *(pf+2)=(sx&0xff0000)>>16; pf+=3; } } } } ///////////////////////////////////////////////////////////////////// // generic number/border painter pf=pMem+(103*3); // offset to number rect for(y=0;y<20;y++) // loop the number rect pixel { for(x=0;x<6;x++) { c=cFont[lSelectedSlot][x+y*6]; // get 4 char dot infos at once (number depends on selected slot) PaintPicDot(pf,(c&0xc0)>>6);pf+=3; // paint the dots into the rect PaintPicDot(pf,(c&0x30)>>4);pf+=3; PaintPicDot(pf,(c&0x0c)>>2);pf+=3; PaintPicDot(pf,(c&0x03)); pf+=3; } pf+=104*3; // next rect y line } pf=pMem; // ptr to first pos in 128x96 pic for(x=0;x<128;x++) // loop top/bottom line { *(pf+(95*128*3))=0x00;*pf++=0x00; *(pf+(95*128*3))=0x00;*pf++=0x00; // paint it red *(pf+(95*128*3))=0xff;*pf++=0xff; } pf=pMem; // ptr to first pos for(y=0;y<96;y++) // loop left/right line { *(pf+(127*3))=0x00;*pf++=0x00; *(pf+(127*3))=0x00;*pf++=0x00; // paint it red *(pf+(127*3))=0xff;*pf++=0xff; pf+=127*3; // offset to next line } */ } //////////////////////////////////////////////////////////////////////// // func will be called with 128x96x3 BGR data. // the plugin has to store the data and display // it in the upper right corner. // If the func is called with a NULL ptr, you can // release your picture data and stop displaying // the screen pic void CALLBACK GPUshowScreenPic(unsigned char * pMem) { DestroyPic(); // destroy old pic data if(pMem==0) return; // done CreatePic(pMem); // create new pic... don't free pMem or something like that... just read from it } void CALLBACK GPUsetfix(uint32_t dwFixBits) { dwEmuFixes=dwFixBits; }