psxmem: Add support for Lightrec's custom mem init sequence
[pcsx_rearmed.git] / plugins / dfxvideo / gpu.c
1 /***************************************************************************
2                           gpu.c  -  description
3                              -------------------
4     begin                : Sun Oct 28 2001
5     copyright            : (C) 2001 by Pete Bernert
6     email                : BlackDove@addcom.de
7  ***************************************************************************/
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version. See also the license.txt file for *
14  *   additional informations.                                              *
15  *                                                                         *
16  ***************************************************************************/
17
18 #include "gpu.h"
19 #include "stdint.h"
20 #include "psemu_plugin_defs.h"
21
22 ////////////////////////////////////////////////////////////////////////
23 // memory image of the PSX vram 
24 ////////////////////////////////////////////////////////////////////////
25
26 unsigned char  *psxVub;
27 signed   char  *psxVsb;
28 unsigned short *psxVuw;
29 unsigned short *psxVuw_eom;
30 signed   short *psxVsw;
31 uint32_t *psxVul;
32 int32_t  *psxVsl;
33
34 ////////////////////////////////////////////////////////////////////////
35 // GPU globals
36 ////////////////////////////////////////////////////////////////////////
37
38 static long       lGPUdataRet;
39 long              lGPUstatusRet;
40 uint32_t          ulStatusControl[256];
41
42 static uint32_t gpuDataM[256];
43 static unsigned   char gpuCommand = 0;
44 static long       gpuDataC = 0;
45 static long       gpuDataP = 0;
46
47 VRAMLoad_t        VRAMWrite;
48 VRAMLoad_t        VRAMRead;
49 DATAREGISTERMODES DataWriteMode;
50 DATAREGISTERMODES DataReadMode;
51
52 BOOL              bSkipNextFrame = FALSE;
53 BOOL              fskip_frameReady;
54 DWORD             lace_count_since_flip;
55 DWORD             dwLaceCnt=0;
56 short             sDispWidths[8] = {256,320,512,640,368,384,512,640};
57 PSXDisplay_t      PSXDisplay;
58 PSXDisplay_t      PreviousPSXDisplay;
59 long              lSelectedSlot=0;
60 BOOL              bDoLazyUpdate=FALSE;
61 uint32_t          lGPUInfoVals[16];
62 static int        iFakePrimBusy=0;
63 static const int  *skip_advice;
64
65 ////////////////////////////////////////////////////////////////////////
66 // some misc external display funcs
67 ////////////////////////////////////////////////////////////////////////
68
69 #include <time.h>
70
71 // FPS library
72 #include "fps.c"
73
74
75 ////////////////////////////////////////////////////////////////////////
76 // sets all kind of act fixes
77 ////////////////////////////////////////////////////////////////////////
78
79 static void SetFixes(void)
80  {
81   if(dwActFixes&0x02) sDispWidths[4]=384;
82   else                sDispWidths[4]=368;
83  }
84
85 ////////////////////////////////////////////////////////////////////////
86 // INIT, will be called after lib load... well, just do some var init...
87 ////////////////////////////////////////////////////////////////////////
88
89 // one extra MB for soft drawing funcs security
90 static unsigned char vram[1024*512*2 + 1024*1024] __attribute__((aligned(2048)));
91
92 long CALLBACK GPUinit(void)                                // GPU INIT
93 {
94  memset(ulStatusControl,0,256*sizeof(uint32_t));  // init save state scontrol field
95
96  //!!! ATTENTION !!!
97  psxVub=vram + 512 * 1024;                           // security offset into double sized psx vram!
98
99  psxVsb=(signed char *)psxVub;                         // different ways of accessing PSX VRAM
100  psxVsw=(signed short *)psxVub;
101  psxVsl=(int32_t *)psxVub;
102  psxVuw=(unsigned short *)psxVub;
103  psxVul=(uint32_t *)psxVub;
104
105  psxVuw_eom=psxVuw+1024*512;                    // pre-calc of end of vram
106
107  memset(vram,0x00,(512*2)*1024 + (1024*1024));
108  memset(lGPUInfoVals,0x00,16*sizeof(uint32_t));
109
110  PSXDisplay.RGB24        = FALSE;                      // init some stuff
111  PSXDisplay.Interlaced   = FALSE;
112  PSXDisplay.DrawOffset.x = 0;
113  PSXDisplay.DrawOffset.y = 0;
114  PSXDisplay.DisplayMode.x= 320;
115  PSXDisplay.DisplayMode.y= 240;
116  PreviousPSXDisplay.DisplayMode.x= 320;
117  PreviousPSXDisplay.DisplayMode.y= 240;
118  PSXDisplay.Disabled     = FALSE;
119  PreviousPSXDisplay.Range.x0 =0;
120  PreviousPSXDisplay.Range.y0 =0;
121  PSXDisplay.Range.x0=0;
122  PSXDisplay.Range.x1=0;
123  PreviousPSXDisplay.DisplayModeNew.y=0;
124  PSXDisplay.Double = 1;
125  lGPUdataRet = 0x400;
126
127  DataWriteMode = DR_NORMAL;
128
129  // Reset transfer values, to prevent mis-transfer of data
130  memset(&VRAMWrite, 0, sizeof(VRAMLoad_t));
131  memset(&VRAMRead, 0, sizeof(VRAMLoad_t));
132  
133  // device initialised already !
134  lGPUstatusRet = 0x14802000;
135  GPUIsIdle;
136  GPUIsReadyForCommands;
137  bDoVSyncUpdate = TRUE;
138
139  return 0;
140 }
141
142 ////////////////////////////////////////////////////////////////////////
143 // Here starts all...
144 ////////////////////////////////////////////////////////////////////////
145
146
147 long GPUopen(unsigned long * disp,char * CapText,char * CfgFile)
148 {
149  unsigned long d;
150  
151  SetFixes();
152
153  InitFPS();
154
155  bDoVSyncUpdate = TRUE;
156
157  d=ulInitDisplay();                                    // setup x
158
159  if(disp)
160         *disp=d;                                     // wanna x pointer? ok
161
162  if(d) return 0;
163  return -1;
164 }
165
166
167 ////////////////////////////////////////////////////////////////////////
168 // time to leave...
169 ////////////////////////////////////////////////////////////////////////
170
171 long CALLBACK GPUclose()                               // GPU CLOSE
172 {
173  CloseDisplay();                                       // shutdown direct draw
174
175  return 0;
176 }
177
178 ////////////////////////////////////////////////////////////////////////
179 // I shot the sheriff
180 ////////////////////////////////////////////////////////////////////////
181
182 long CALLBACK GPUshutdown(void)                            // GPU SHUTDOWN
183 {
184  CloseDisplay();                                       // shutdown direct draw
185  return 0;                                             // nothinh to do
186 }
187
188 ////////////////////////////////////////////////////////////////////////
189 // Update display (swap buffers)
190 ////////////////////////////////////////////////////////////////////////
191
192 static void updateDisplay(void)                               // UPDATE DISPLAY
193 {
194  if(PSXDisplay.Disabled)                               // disable?
195   {
196    return;                                             // -> and bye
197   }
198
199  if(dwActFixes&32)                                     // pc fps calculation fix
200   {
201    if(UseFrameLimit) PCFrameCap();                     // -> brake
202    if(UseFrameSkip) PCcalcfps();         
203   }
204
205  if(UseFrameSkip)                                      // skip ?
206   {
207    if(fskip_frameReady)
208     {
209      DoBufferSwap();                                   // -> to skip or not to skip
210      fskip_frameReady=FALSE;
211      bDoVSyncUpdate=FALSE;                             // vsync done
212     }
213   }
214  else                                                  // no skip ?
215   {
216    bSkipNextFrame = FALSE;
217    DoBufferSwap();                                     // -> swap
218    bDoVSyncUpdate=FALSE;                               // vsync done
219   }
220 }
221
222 static void decideSkip(void)
223 {
224  if(!bDoVSyncUpdate)
225    return;
226
227  lace_count_since_flip=0;
228  fskip_frameReady=!bSkipNextFrame;
229
230  if(dwActFixes&0xa0)                                   // -> pc fps calculation fix/old skipping fix
231   {
232    int skip = (skip_advice && *skip_advice) || UseFrameSkip == 1 || fps_skip < fFrameRateHz;
233    if(skip && !bSkipNextFrame)                         // -> skip max one in a row
234        {bSkipNextFrame = TRUE; fps_skip=fFrameRateHz;}
235    else bSkipNextFrame = FALSE;
236   }
237  else FrameSkip();
238 }
239
240 ////////////////////////////////////////////////////////////////////////
241 // roughly emulated screen centering bits... not complete !!!
242 ////////////////////////////////////////////////////////////////////////
243
244 void ChangeDispOffsetsX(void)                          // X CENTER
245 {
246  long lx,l;
247
248  if(!PSXDisplay.Range.x1) return;
249
250  l=PreviousPSXDisplay.DisplayMode.x;
251
252  l*=(long)PSXDisplay.Range.x1;
253  l/=2560;lx=l;l&=0xfffffff8;
254
255  if(l==PreviousPSXDisplay.Range.y1) return;            // abusing range.y1 for
256  PreviousPSXDisplay.Range.y1=(short)l;                 // storing last x range and test
257
258  if(lx>=PreviousPSXDisplay.DisplayMode.x)
259   {
260    PreviousPSXDisplay.Range.x1=
261     (short)PreviousPSXDisplay.DisplayMode.x;
262    PreviousPSXDisplay.Range.x0=0;
263   }
264  else
265   {
266    PreviousPSXDisplay.Range.x1=(short)l;
267
268    PreviousPSXDisplay.Range.x0=
269     (PSXDisplay.Range.x0-500)/8;
270
271    if(PreviousPSXDisplay.Range.x0<0)
272     PreviousPSXDisplay.Range.x0=0;
273
274    if((PreviousPSXDisplay.Range.x0+lx)>
275       PreviousPSXDisplay.DisplayMode.x)
276     {
277      PreviousPSXDisplay.Range.x0=
278       (short)(PreviousPSXDisplay.DisplayMode.x-lx);
279      PreviousPSXDisplay.Range.x0+=2; //???
280
281      PreviousPSXDisplay.Range.x1+=(short)(lx-l);
282
283      PreviousPSXDisplay.Range.x1-=2; // makes linux stretching easier
284
285     }
286
287    // some linux alignment security
288    PreviousPSXDisplay.Range.x0=PreviousPSXDisplay.Range.x0>>1;
289    PreviousPSXDisplay.Range.x0=PreviousPSXDisplay.Range.x0<<1;
290    PreviousPSXDisplay.Range.x1=PreviousPSXDisplay.Range.x1>>1;
291    PreviousPSXDisplay.Range.x1=PreviousPSXDisplay.Range.x1<<1;
292
293    DoClearScreenBuffer();
294   }
295
296  bDoVSyncUpdate=TRUE;
297 }
298
299 ////////////////////////////////////////////////////////////////////////
300
301 void ChangeDispOffsetsY(void)                          // Y CENTER
302 {
303  int iT,iO=PreviousPSXDisplay.Range.y0;
304  int iOldYOffset=PreviousPSXDisplay.DisplayModeNew.y;
305
306 // new
307
308  if((PreviousPSXDisplay.DisplayModeNew.x+PSXDisplay.DisplayModeNew.y)>512)
309   {
310    int dy1=512-PreviousPSXDisplay.DisplayModeNew.x;
311    int dy2=(PreviousPSXDisplay.DisplayModeNew.x+PSXDisplay.DisplayModeNew.y)-512;
312
313    if(dy1>=dy2)
314     {
315      PreviousPSXDisplay.DisplayModeNew.y=-dy2;
316     }
317    else
318     {
319      PSXDisplay.DisplayPosition.y=0;
320      PreviousPSXDisplay.DisplayModeNew.y=-dy1;
321     }
322   }
323  else PreviousPSXDisplay.DisplayModeNew.y=0;
324
325 // eon
326
327  if(PreviousPSXDisplay.DisplayModeNew.y!=iOldYOffset) // if old offset!=new offset: recalc height
328   {
329    PSXDisplay.Height = PSXDisplay.Range.y1 - 
330                        PSXDisplay.Range.y0 +
331                        PreviousPSXDisplay.DisplayModeNew.y;
332    PSXDisplay.DisplayModeNew.y=PSXDisplay.Height*PSXDisplay.Double;
333   }
334
335 //
336
337  if(PSXDisplay.PAL) iT=48; else iT=28;
338
339  if(PSXDisplay.Range.y0>=iT)
340   {
341    PreviousPSXDisplay.Range.y0=
342     (short)((PSXDisplay.Range.y0-iT-4)*PSXDisplay.Double);
343    if(PreviousPSXDisplay.Range.y0<0)
344     PreviousPSXDisplay.Range.y0=0;
345    PSXDisplay.DisplayModeNew.y+=
346     PreviousPSXDisplay.Range.y0;
347   }
348  else 
349   PreviousPSXDisplay.Range.y0=0;
350
351  if(iO!=PreviousPSXDisplay.Range.y0)
352   {
353    DoClearScreenBuffer();
354  }
355 }
356
357 ////////////////////////////////////////////////////////////////////////
358 // check if update needed
359 ////////////////////////////////////////////////////////////////////////
360
361 static void updateDisplayIfChanged(void)                      // UPDATE DISPLAY IF CHANGED
362 {
363  if ((PSXDisplay.DisplayMode.y == PSXDisplay.DisplayModeNew.y) && 
364      (PSXDisplay.DisplayMode.x == PSXDisplay.DisplayModeNew.x))
365   {
366    if((PSXDisplay.RGB24      == PSXDisplay.RGB24New) && 
367       (PSXDisplay.Interlaced == PSXDisplay.InterlacedNew)) return;
368   }
369
370  PSXDisplay.RGB24         = PSXDisplay.RGB24New;       // get new infos
371
372  PSXDisplay.DisplayMode.y = PSXDisplay.DisplayModeNew.y;
373  PSXDisplay.DisplayMode.x = PSXDisplay.DisplayModeNew.x;
374  PreviousPSXDisplay.DisplayMode.x=                     // previous will hold
375   min(640,PSXDisplay.DisplayMode.x);                   // max 640x512... that's
376  PreviousPSXDisplay.DisplayMode.y=                     // the size of my 
377   min(512,PSXDisplay.DisplayMode.y);                   // back buffer surface
378  PSXDisplay.Interlaced    = PSXDisplay.InterlacedNew;
379     
380  PSXDisplay.DisplayEnd.x=                              // calc end of display
381   PSXDisplay.DisplayPosition.x+ PSXDisplay.DisplayMode.x;
382  PSXDisplay.DisplayEnd.y=
383   PSXDisplay.DisplayPosition.y+ PSXDisplay.DisplayMode.y+PreviousPSXDisplay.DisplayModeNew.y;
384  PreviousPSXDisplay.DisplayEnd.x=
385   PreviousPSXDisplay.DisplayPosition.x+ PSXDisplay.DisplayMode.x;
386  PreviousPSXDisplay.DisplayEnd.y=
387   PreviousPSXDisplay.DisplayPosition.y+ PSXDisplay.DisplayMode.y+PreviousPSXDisplay.DisplayModeNew.y;
388
389  ChangeDispOffsetsX();
390
391  if(iFrameLimit==2) SetAutoFrameCap();                 // -> set it
392
393  if(UseFrameSkip) decideSkip();                        // stupid stuff when frame skipping enabled
394 }
395
396 ////////////////////////////////////////////////////////////////////////
397 // update lace is called evry VSync
398 ////////////////////////////////////////////////////////////////////////
399
400 void CALLBACK GPUupdateLace(void)                      // VSYNC
401 {
402  //if(!(dwActFixes&1))
403  // lGPUstatusRet^=0x80000000;                           // odd/even bit
404
405  //pcsx-rearmed: removed, this is handled by core
406  //if(!(dwActFixes&32))                                  // std fps limitation?
407  // CheckFrameRate();
408
409  if(PSXDisplay.Interlaced)                             // interlaced mode?
410   {
411    lGPUstatusRet^=0x80000000;                          // odd/even bit?
412
413    if(bDoVSyncUpdate && PSXDisplay.DisplayMode.x>0 && PSXDisplay.DisplayMode.y>0)
414     {
415      updateDisplay();
416     }
417   }
418  else                                                  // non-interlaced?
419   {
420    if(dwActFixes&64)                                   // lazy screen update fix
421     {
422      if(bDoLazyUpdate)
423       updateDisplay(); 
424      bDoLazyUpdate=FALSE;
425     }
426    else
427     {
428      if(bDoVSyncUpdate)                                // some primitives drawn?
429        updateDisplay();                                // -> update display
430     }
431   }
432
433  if(UseFrameSkip) {                                    // frame over-skip guard
434   lace_count_since_flip++;
435   if(lace_count_since_flip > 8) {
436    bSkipNextFrame=FALSE;
437    fskip_frameReady=TRUE;
438   }
439  }
440 }
441
442 ////////////////////////////////////////////////////////////////////////
443 // process read request from GPU status register
444 ////////////////////////////////////////////////////////////////////////
445
446
447 uint32_t CALLBACK GPUreadStatus(void)             // READ STATUS
448 {
449  if(dwActFixes&1)
450   {
451    static int iNumRead=0;                         // odd/even hack
452    if((iNumRead++)==2)
453     {
454      iNumRead=0;
455      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)
456     }
457   }
458
459  if(iFakePrimBusy)                                // 27.10.2007 - PETE : emulating some 'busy' while drawing... pfff
460   {
461    iFakePrimBusy--;
462
463    if(iFakePrimBusy&1)                            // we do a busy-idle-busy-idle sequence after/while drawing prims
464     {
465      GPUIsBusy;
466      GPUIsNotReadyForCommands;
467     }
468    else
469     {
470      GPUIsIdle;
471      GPUIsReadyForCommands;
472     }
473   }
474  return lGPUstatusRet;
475 }
476
477 ////////////////////////////////////////////////////////////////////////
478 // processes data send to GPU status register
479 // these are always single packet commands.
480 ////////////////////////////////////////////////////////////////////////
481
482 void CALLBACK GPUwriteStatus(uint32_t gdata)      // WRITE STATUS
483 {
484  uint32_t lCommand=(gdata>>24)&0xff;
485
486  ulStatusControl[lCommand]=gdata;                      // store command for freezing
487
488  switch(lCommand)
489   {
490    //--------------------------------------------------//
491    // reset gpu
492    case 0x00:
493     memset(lGPUInfoVals,0x00,16*sizeof(uint32_t));
494     lGPUstatusRet=0x14802000;
495     PSXDisplay.Disabled=1;
496     DataWriteMode=DataReadMode=DR_NORMAL;
497     PSXDisplay.DrawOffset.x=PSXDisplay.DrawOffset.y=0;
498     drawX=drawY=0;drawW=drawH=0;
499     sSetMask=0;lSetMask=0;bCheckMask=FALSE;
500     usMirror=0;
501     GlobalTextAddrX=0;GlobalTextAddrY=0;
502     GlobalTextTP=0;GlobalTextABR=0;
503     PSXDisplay.RGB24=FALSE;
504     PSXDisplay.Interlaced=FALSE;
505     bUsingTWin = FALSE;
506     return;
507    //--------------------------------------------------//
508    // dis/enable display 
509    case 0x03:  
510
511     PreviousPSXDisplay.Disabled = PSXDisplay.Disabled;
512     PSXDisplay.Disabled = (gdata & 1);
513
514     if(PSXDisplay.Disabled) 
515          lGPUstatusRet|=GPUSTATUS_DISPLAYDISABLED;
516     else lGPUstatusRet&=~GPUSTATUS_DISPLAYDISABLED;
517     return;
518
519    //--------------------------------------------------//
520    // setting transfer mode
521    case 0x04:
522     gdata &= 0x03;                                     // Only want the lower two bits
523
524     DataWriteMode=DataReadMode=DR_NORMAL;
525     if(gdata==0x02) DataWriteMode=DR_VRAMTRANSFER;
526     if(gdata==0x03) DataReadMode =DR_VRAMTRANSFER;
527     lGPUstatusRet&=~GPUSTATUS_DMABITS;                 // Clear the current settings of the DMA bits
528     lGPUstatusRet|=(gdata << 29);                      // Set the DMA bits according to the received data
529
530     return;
531    //--------------------------------------------------//
532    // setting display position
533    case 0x05: 
534     {
535      PreviousPSXDisplay.DisplayPosition.x = PSXDisplay.DisplayPosition.x;
536      PreviousPSXDisplay.DisplayPosition.y = PSXDisplay.DisplayPosition.y;
537
538 // new
539      PSXDisplay.DisplayPosition.y = (short)((gdata>>10)&0x1ff);
540
541      // store the same val in some helper var, we need it on later compares
542      PreviousPSXDisplay.DisplayModeNew.x=PSXDisplay.DisplayPosition.y;
543
544      if((PSXDisplay.DisplayPosition.y+PSXDisplay.DisplayMode.y)>512)
545       {
546        int dy1=512-PSXDisplay.DisplayPosition.y;
547        int dy2=(PSXDisplay.DisplayPosition.y+PSXDisplay.DisplayMode.y)-512;
548
549        if(dy1>=dy2)
550         {
551          PreviousPSXDisplay.DisplayModeNew.y=-dy2;
552         }
553        else
554         {
555          PSXDisplay.DisplayPosition.y=0;
556          PreviousPSXDisplay.DisplayModeNew.y=-dy1;
557         }
558       }
559      else PreviousPSXDisplay.DisplayModeNew.y=0;
560 // eon
561
562      PSXDisplay.DisplayPosition.x = (short)(gdata & 0x3ff);
563      PSXDisplay.DisplayEnd.x=
564       PSXDisplay.DisplayPosition.x+ PSXDisplay.DisplayMode.x;
565      PSXDisplay.DisplayEnd.y=
566       PSXDisplay.DisplayPosition.y+ PSXDisplay.DisplayMode.y + PreviousPSXDisplay.DisplayModeNew.y;
567      PreviousPSXDisplay.DisplayEnd.x=
568       PreviousPSXDisplay.DisplayPosition.x+ PSXDisplay.DisplayMode.x;
569      PreviousPSXDisplay.DisplayEnd.y=
570       PreviousPSXDisplay.DisplayPosition.y+ PSXDisplay.DisplayMode.y + PreviousPSXDisplay.DisplayModeNew.y;
571  
572      bDoVSyncUpdate=TRUE;
573
574      if (!(PSXDisplay.Interlaced))                      // stupid frame skipping option
575       {
576        if(dwActFixes&64) bDoLazyUpdate=TRUE;
577       }
578      if(UseFrameSkip)  decideSkip();
579     }return;
580    //--------------------------------------------------//
581    // setting width
582    case 0x06:
583
584     PSXDisplay.Range.x0=(short)(gdata & 0x7ff);
585     PSXDisplay.Range.x1=(short)((gdata>>12) & 0xfff);
586
587     PSXDisplay.Range.x1-=PSXDisplay.Range.x0;
588
589     ChangeDispOffsetsX();
590
591     return;
592    //--------------------------------------------------//
593    // setting height
594    case 0x07:
595     {
596
597      PSXDisplay.Range.y0=(short)(gdata & 0x3ff);
598      PSXDisplay.Range.y1=(short)((gdata>>10) & 0x3ff);
599                                       
600      PreviousPSXDisplay.Height = PSXDisplay.Height;
601
602      PSXDisplay.Height = PSXDisplay.Range.y1 - 
603                          PSXDisplay.Range.y0 +
604                          PreviousPSXDisplay.DisplayModeNew.y;
605
606      if(PreviousPSXDisplay.Height!=PSXDisplay.Height)
607       {
608        PSXDisplay.DisplayModeNew.y=PSXDisplay.Height*PSXDisplay.Double;
609
610        ChangeDispOffsetsY();
611
612        updateDisplayIfChanged();
613       }
614      return;
615     }
616    //--------------------------------------------------//
617    // setting display infos
618    case 0x08:
619
620     PSXDisplay.DisplayModeNew.x =
621      sDispWidths[(gdata & 0x03) | ((gdata & 0x40) >> 4)];
622
623     if (gdata&0x04) PSXDisplay.Double=2;
624     else            PSXDisplay.Double=1;
625
626     PSXDisplay.DisplayModeNew.y = PSXDisplay.Height*PSXDisplay.Double;
627
628     ChangeDispOffsetsY();
629
630     PSXDisplay.PAL           = (gdata & 0x08)?TRUE:FALSE; // if 1 - PAL mode, else NTSC
631     PSXDisplay.RGB24New      = (gdata & 0x10)?TRUE:FALSE; // if 1 - TrueColor
632     PSXDisplay.InterlacedNew = (gdata & 0x20)?TRUE:FALSE; // if 1 - Interlace
633
634     lGPUstatusRet&=~GPUSTATUS_WIDTHBITS;                   // Clear the width bits
635     lGPUstatusRet|=
636                (((gdata & 0x03) << 17) | 
637                ((gdata & 0x40) << 10));                // Set the width bits
638
639     if(PSXDisplay.InterlacedNew)
640      {
641       if(!PSXDisplay.Interlaced)
642        {
643         PreviousPSXDisplay.DisplayPosition.x = PSXDisplay.DisplayPosition.x;
644         PreviousPSXDisplay.DisplayPosition.y = PSXDisplay.DisplayPosition.y;
645        }
646       lGPUstatusRet|=GPUSTATUS_INTERLACED;
647      }
648     else lGPUstatusRet&=~(GPUSTATUS_INTERLACED|0x80000000);
649
650     if (PSXDisplay.PAL)
651          lGPUstatusRet|=GPUSTATUS_PAL;
652     else lGPUstatusRet&=~GPUSTATUS_PAL;
653
654     if (PSXDisplay.Double==2)
655          lGPUstatusRet|=GPUSTATUS_DOUBLEHEIGHT;
656     else lGPUstatusRet&=~GPUSTATUS_DOUBLEHEIGHT;
657
658     if (PSXDisplay.RGB24New)
659          lGPUstatusRet|=GPUSTATUS_RGB24;
660     else lGPUstatusRet&=~GPUSTATUS_RGB24;
661
662     updateDisplayIfChanged();
663
664     return;
665    //--------------------------------------------------//
666    // ask about GPU version and other stuff
667    case 0x10: 
668
669     gdata&=0xff;
670
671     switch(gdata) 
672      {
673       case 0x02:
674        lGPUdataRet=lGPUInfoVals[INFO_TW];              // tw infos
675        return;
676       case 0x03:
677        lGPUdataRet=lGPUInfoVals[INFO_DRAWSTART];       // draw start
678        return;
679       case 0x04:
680        lGPUdataRet=lGPUInfoVals[INFO_DRAWEND];         // draw end
681        return;
682       case 0x05:
683       case 0x06:
684        lGPUdataRet=lGPUInfoVals[INFO_DRAWOFF];         // draw offset
685        return;
686       case 0x07:
687        lGPUdataRet=0x02;                               // gpu type
688        return;
689       case 0x08:
690       case 0x0F:                                       // some bios addr?
691        lGPUdataRet=0xBFC03720;
692        return;
693      }
694     return;
695    //--------------------------------------------------//
696   }   
697 }
698
699 ////////////////////////////////////////////////////////////////////////
700 // vram read/write helpers, needed by LEWPY's optimized vram read/write :)
701 ////////////////////////////////////////////////////////////////////////
702
703 static inline void FinishedVRAMWrite(void)
704 {
705  // Set register to NORMAL operation
706  DataWriteMode = DR_NORMAL;
707  // Reset transfer values, to prevent mis-transfer of data
708  VRAMWrite.x = 0;
709  VRAMWrite.y = 0;
710  VRAMWrite.Width = 0;
711  VRAMWrite.Height = 0;
712  VRAMWrite.ColsRemaining = 0;
713  VRAMWrite.RowsRemaining = 0;
714 }
715
716 static inline void FinishedVRAMRead(void)
717 {
718  // Set register to NORMAL operation
719  DataReadMode = DR_NORMAL;
720  // Reset transfer values, to prevent mis-transfer of data
721  VRAMRead.x = 0;
722  VRAMRead.y = 0;
723  VRAMRead.Width = 0;
724  VRAMRead.Height = 0;
725  VRAMRead.ColsRemaining = 0;
726  VRAMRead.RowsRemaining = 0;
727
728  // Indicate GPU is no longer ready for VRAM data in the STATUS REGISTER
729  lGPUstatusRet&=~GPUSTATUS_READYFORVRAM;
730 }
731
732 ////////////////////////////////////////////////////////////////////////
733 // core read from vram
734 ////////////////////////////////////////////////////////////////////////
735
736 void CALLBACK GPUreadDataMem(uint32_t * pMem, int iSize)
737 {
738  int i;
739
740  if(DataReadMode!=DR_VRAMTRANSFER) return;
741
742  GPUIsBusy;
743
744  // adjust read ptr, if necessary
745  while(VRAMRead.ImagePtr>=psxVuw_eom)
746   VRAMRead.ImagePtr-=512*1024;
747  while(VRAMRead.ImagePtr<psxVuw)
748   VRAMRead.ImagePtr+=512*1024;
749
750  for(i=0;i<iSize;i++)
751   {
752    // do 2 seperate 16bit reads for compatibility (wrap issues)
753    if ((VRAMRead.ColsRemaining > 0) && (VRAMRead.RowsRemaining > 0))
754     {
755      // lower 16 bit
756      lGPUdataRet=(uint32_t)GETLE16(VRAMRead.ImagePtr);
757
758      VRAMRead.ImagePtr++;
759      if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=512*1024;
760      VRAMRead.RowsRemaining --;
761
762      if(VRAMRead.RowsRemaining<=0)
763       {
764        VRAMRead.RowsRemaining = VRAMRead.Width;
765        VRAMRead.ColsRemaining--;
766        VRAMRead.ImagePtr += 1024 - VRAMRead.Width;
767        if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=512*1024;
768       }
769
770      // higher 16 bit (always, even if it's an odd width)
771      lGPUdataRet|=(uint32_t)GETLE16(VRAMRead.ImagePtr)<<16;
772      PUTLE32(pMem, lGPUdataRet); pMem++;
773
774      if(VRAMRead.ColsRemaining <= 0)
775       {FinishedVRAMRead();goto ENDREAD;}
776
777      VRAMRead.ImagePtr++;
778      if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=512*1024;
779      VRAMRead.RowsRemaining--;
780      if(VRAMRead.RowsRemaining<=0)
781       {
782        VRAMRead.RowsRemaining = VRAMRead.Width;
783        VRAMRead.ColsRemaining--;
784        VRAMRead.ImagePtr += 1024 - VRAMRead.Width;
785        if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=512*1024;
786       }
787      if(VRAMRead.ColsRemaining <= 0)
788       {FinishedVRAMRead();goto ENDREAD;}
789     }
790    else {FinishedVRAMRead();goto ENDREAD;}
791   }
792
793 ENDREAD:
794  GPUIsIdle;
795 }
796
797
798 ////////////////////////////////////////////////////////////////////////
799
800 uint32_t CALLBACK GPUreadData(void)
801 {
802  uint32_t l;
803  GPUreadDataMem(&l,1);
804  return lGPUdataRet;
805 }
806
807 // Software drawing function
808 #include "soft.c"
809
810 // PSX drawing primitives
811 #include "prim.c"
812
813 ////////////////////////////////////////////////////////////////////////
814 // processes data send to GPU data register
815 // extra table entries for fixing polyline troubles
816 ////////////////////////////////////////////////////////////////////////
817
818 static const unsigned char primTableCX[256] =
819 {
820     // 00
821     0,0,3,0,0,0,0,0,
822     // 08
823     0,0,0,0,0,0,0,0,
824     // 10
825     0,0,0,0,0,0,0,0,
826     // 18
827     0,0,0,0,0,0,0,0,
828     // 20
829     4,4,4,4,7,7,7,7,
830     // 28
831     5,5,5,5,9,9,9,9,
832     // 30
833     6,6,6,6,9,9,9,9,
834     // 38
835     8,8,8,8,12,12,12,12,
836     // 40
837     3,3,3,3,0,0,0,0,
838     // 48
839 //  5,5,5,5,6,6,6,6,    // FLINE
840     254,254,254,254,254,254,254,254,
841     // 50
842     4,4,4,4,0,0,0,0,
843     // 58
844 //  7,7,7,7,9,9,9,9,    // GLINE
845     255,255,255,255,255,255,255,255,
846     // 60
847     3,3,3,3,4,4,4,4,    
848     // 68
849     2,2,2,2,3,3,3,3,    // 3=SPRITE1???
850     // 70
851     2,2,2,2,3,3,3,3,
852     // 78
853     2,2,2,2,3,3,3,3,
854     // 80
855     4,0,0,0,0,0,0,0,
856     // 88
857     0,0,0,0,0,0,0,0,
858     // 90
859     0,0,0,0,0,0,0,0,
860     // 98
861     0,0,0,0,0,0,0,0,
862     // a0
863     3,0,0,0,0,0,0,0,
864     // a8
865     0,0,0,0,0,0,0,0,
866     // b0
867     0,0,0,0,0,0,0,0,
868     // b8
869     0,0,0,0,0,0,0,0,
870     // c0
871     3,0,0,0,0,0,0,0,
872     // c8
873     0,0,0,0,0,0,0,0,
874     // d0
875     0,0,0,0,0,0,0,0,
876     // d8
877     0,0,0,0,0,0,0,0,
878     // e0
879     0,1,1,1,1,1,1,0,
880     // e8
881     0,0,0,0,0,0,0,0,
882     // f0
883     0,0,0,0,0,0,0,0,
884     // f8
885     0,0,0,0,0,0,0,0
886 };
887
888 void CALLBACK GPUwriteDataMem(uint32_t * pMem, int iSize)
889 {
890  unsigned char command;
891  uint32_t gdata=0;
892  int i=0;
893  GPUIsBusy;
894  GPUIsNotReadyForCommands;
895
896 STARTVRAM:
897
898  if(DataWriteMode==DR_VRAMTRANSFER)
899   {
900    BOOL bFinished=FALSE;
901
902    // make sure we are in vram
903    while(VRAMWrite.ImagePtr>=psxVuw_eom)
904     VRAMWrite.ImagePtr-=512*1024;
905    while(VRAMWrite.ImagePtr<psxVuw)
906     VRAMWrite.ImagePtr+=512*1024;
907
908    // now do the loop
909    while(VRAMWrite.ColsRemaining>0)
910     {
911      while(VRAMWrite.RowsRemaining>0)
912       {
913        if(i>=iSize) {goto ENDVRAM;}
914        i++;
915
916        gdata=GETLE32(pMem); pMem++;
917
918        PUTLE16(VRAMWrite.ImagePtr, (unsigned short)gdata); VRAMWrite.ImagePtr++;
919        if(VRAMWrite.ImagePtr>=psxVuw_eom) VRAMWrite.ImagePtr-=512*1024;
920        VRAMWrite.RowsRemaining --;
921
922        if(VRAMWrite.RowsRemaining <= 0)
923         {
924          VRAMWrite.ColsRemaining--;
925          if (VRAMWrite.ColsRemaining <= 0)             // last pixel is odd width
926           {
927            gdata=(gdata&0xFFFF)|(((uint32_t)GETLE16(VRAMWrite.ImagePtr))<<16);
928            FinishedVRAMWrite();
929            bDoVSyncUpdate=TRUE;
930            goto ENDVRAM;
931           }
932          VRAMWrite.RowsRemaining = VRAMWrite.Width;
933          VRAMWrite.ImagePtr += 1024 - VRAMWrite.Width;
934         }
935
936        PUTLE16(VRAMWrite.ImagePtr, (unsigned short)(gdata>>16)); VRAMWrite.ImagePtr++;
937        if(VRAMWrite.ImagePtr>=psxVuw_eom) VRAMWrite.ImagePtr-=512*1024;
938        VRAMWrite.RowsRemaining --;
939       }
940
941      VRAMWrite.RowsRemaining = VRAMWrite.Width;
942      VRAMWrite.ColsRemaining--;
943      VRAMWrite.ImagePtr += 1024 - VRAMWrite.Width;
944      bFinished=TRUE;
945     }
946
947    FinishedVRAMWrite();
948    if(bFinished) bDoVSyncUpdate=TRUE;
949   }
950
951 ENDVRAM:
952
953  if(DataWriteMode==DR_NORMAL)
954   {
955    void (* *primFunc)(unsigned char *);
956    if(bSkipNextFrame) primFunc=primTableSkip;
957    else               primFunc=primTableJ;
958
959    for(;i<iSize;)
960     {
961      if(DataWriteMode==DR_VRAMTRANSFER) goto STARTVRAM;
962
963      gdata=GETLE32(pMem); pMem++; i++;
964  
965      if(gpuDataC == 0)
966       {
967        command = (unsigned char)((gdata>>24) & 0xff);
968  
969 //if(command>=0xb0 && command<0xc0) auxprintf("b0 %x!!!!!!!!!\n",command);
970
971        if(primTableCX[command])
972         {
973          gpuDataC = primTableCX[command];
974          gpuCommand = command;
975          PUTLE32_(&gpuDataM[0], gdata);
976          gpuDataP = 1;
977         }
978        else continue;
979       }
980      else
981       {
982        PUTLE32_(&gpuDataM[gpuDataP], gdata);
983        if(gpuDataC>128)
984         {
985          if((gpuDataC==254 && gpuDataP>=3) ||
986             (gpuDataC==255 && gpuDataP>=4 && !(gpuDataP&1)))
987           {
988            if((gpuDataM[gpuDataP] & 0xF000F000) == 0x50005000)
989             gpuDataP=gpuDataC-1;
990           }
991         }
992        gpuDataP++;
993       }
994  
995      if(gpuDataP == gpuDataC)
996       {
997        gpuDataC=gpuDataP=0;
998        primFunc[gpuCommand]((unsigned char *)gpuDataM);
999        if(dwActFixes&0x0400)      // hack for emulating "gpu busy" in some games
1000         iFakePrimBusy=4;
1001       }
1002     } 
1003   }
1004
1005  lGPUdataRet=gdata;
1006
1007  GPUIsReadyForCommands;
1008  GPUIsIdle;                
1009 }
1010
1011 ////////////////////////////////////////////////////////////////////////
1012
1013 void CALLBACK GPUwriteData(uint32_t gdata)
1014 {
1015  PUTLE32_(&gdata, gdata);
1016  GPUwriteDataMem(&gdata,1);
1017 }
1018
1019 ////////////////////////////////////////////////////////////////////////
1020 // process gpu commands
1021 ////////////////////////////////////////////////////////////////////////
1022
1023 unsigned long lUsedAddr[3];
1024
1025 static inline BOOL CheckForEndlessLoop(unsigned long laddr)
1026 {
1027  if(laddr==lUsedAddr[1]) return TRUE;
1028  if(laddr==lUsedAddr[2]) return TRUE;
1029
1030  if(laddr<lUsedAddr[0]) lUsedAddr[1]=laddr;
1031  else                   lUsedAddr[2]=laddr;
1032  lUsedAddr[0]=laddr;
1033  return FALSE;
1034 }
1035
1036 long CALLBACK GPUdmaChain(uint32_t * baseAddrL, uint32_t addr)
1037 {
1038  uint32_t dmaMem;
1039  unsigned char * baseAddrB;
1040  short count;unsigned int DMACommandCounter = 0;
1041  long dmaWords = 0;
1042
1043  GPUIsBusy;
1044
1045  lUsedAddr[0]=lUsedAddr[1]=lUsedAddr[2]=0xffffff;
1046
1047  baseAddrB = (unsigned char*) baseAddrL;
1048
1049  do
1050   {
1051    addr&=0x1FFFFC;
1052    if(DMACommandCounter++ > 2000000) break;
1053    if(CheckForEndlessLoop(addr)) break;
1054
1055    count = baseAddrB[addr+3];
1056    dmaWords += 1 + count;
1057
1058    dmaMem=addr+4;
1059
1060    if(count>0) GPUwriteDataMem(&baseAddrL[dmaMem>>2],count);
1061
1062    addr = GETLE32(&baseAddrL[addr>>2])&0xffffff;
1063   }
1064  while (addr != 0xffffff);
1065
1066  GPUIsIdle;
1067
1068  return dmaWords;
1069 }
1070
1071 ////////////////////////////////////////////////////////////////////////
1072 // Freeze
1073 ////////////////////////////////////////////////////////////////////////
1074
1075 typedef struct GPUFREEZETAG
1076 {
1077  uint32_t ulFreezeVersion;      // should be always 1 for now (set by main emu)
1078  uint32_t ulStatus;             // current gpu status
1079  uint32_t ulControl[256];       // latest control register values
1080  unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
1081 } GPUFreeze_t;
1082
1083 ////////////////////////////////////////////////////////////////////////
1084
1085 long CALLBACK GPUfreeze(uint32_t ulGetFreezeData,GPUFreeze_t * pF)
1086 {
1087  //----------------------------------------------------//
1088  if(ulGetFreezeData==2)                                // 2: info, which save slot is selected? (just for display)
1089   {
1090    long lSlotNum=*((long *)pF);
1091    if(lSlotNum<0) return 0;
1092    if(lSlotNum>8) return 0;
1093    lSelectedSlot=lSlotNum+1;
1094    return 1;
1095   }
1096  //----------------------------------------------------//
1097  if(!pF)                    return 0;                  // some checks
1098  if(pF->ulFreezeVersion!=1) return 0;
1099
1100  if(ulGetFreezeData==1)                                // 1: get data
1101   {
1102    pF->ulStatus=lGPUstatusRet;
1103    memcpy(pF->ulControl,ulStatusControl,256*sizeof(uint32_t));
1104    memcpy(pF->psxVRam,  psxVub,         1024*512*2);
1105
1106    return 1;
1107   }
1108
1109  if(ulGetFreezeData!=0) return 0;                      // 0: set data
1110
1111  lGPUstatusRet=pF->ulStatus;
1112  memcpy(ulStatusControl,pF->ulControl,256*sizeof(uint32_t));
1113  memcpy(psxVub,         pF->psxVRam,  1024*512*2);
1114
1115 // RESET TEXTURE STORE HERE, IF YOU USE SOMETHING LIKE THAT
1116
1117  PreviousPSXDisplay.Height = 0;
1118  GPUwriteStatus(ulStatusControl[0]);
1119  GPUwriteStatus(ulStatusControl[1]);
1120  GPUwriteStatus(ulStatusControl[2]);
1121  GPUwriteStatus(ulStatusControl[3]);
1122  GPUwriteStatus(ulStatusControl[8]);                   // try to repair things
1123  GPUwriteStatus(ulStatusControl[6]);
1124  GPUwriteStatus(ulStatusControl[7]);
1125  GPUwriteStatus(ulStatusControl[5]);
1126  GPUwriteStatus(ulStatusControl[4]);
1127
1128  return 1;
1129 }
1130
1131 // rearmed thing
1132 #include "../../frontend/plugin_lib.h"
1133
1134 const struct rearmed_cbs *rcbs;
1135
1136 void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
1137 {
1138  // sync config
1139  UseFrameSkip = cbs->frameskip;
1140  iUseDither = cbs->gpu_peops.iUseDither;
1141  dwActFixes = cbs->gpu_peops.dwActFixes;
1142  fFrameRateHz = cbs->gpu_peops.fFrameRateHz;
1143  dwFrameRateTicks = cbs->gpu_peops.dwFrameRateTicks;
1144  if (cbs->pl_vout_set_raw_vram)
1145   cbs->pl_vout_set_raw_vram(psxVub);
1146  if (cbs->pl_set_gpu_caps)
1147   cbs->pl_set_gpu_caps(0);
1148
1149  skip_advice = &cbs->fskip_advice;
1150  fps_skip = 100.0f;
1151  rcbs = cbs;
1152 }