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