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