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