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