dfxvideo: fix frameskip issue
[pcsx_rearmed.git] / plugins / dfxvideo / gpu.c
CommitLineData
ef79bbde
P
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
ef79bbde 18#include "gpu.h"
ef79bbde
P
19#include "stdint.h"
20#include "psemu_plugin_defs.h"
ef79bbde
P
21
22////////////////////////////////////////////////////////////////////////
23// memory image of the PSX vram
24////////////////////////////////////////////////////////////////////////
25
26unsigned char *psxVSecure;
27unsigned char *psxVub;
28signed char *psxVsb;
29unsigned short *psxVuw;
30unsigned short *psxVuw_eom;
31signed short *psxVsw;
a96a5eb2 32uint32_t *psxVul;
ef79bbde
P
33int32_t *psxVsl;
34
35////////////////////////////////////////////////////////////////////////
36// GPU globals
37////////////////////////////////////////////////////////////////////////
38
39static long lGPUdataRet;
40long lGPUstatusRet;
a96a5eb2 41uint32_t ulStatusControl[256];
ef79bbde
P
42
43static uint32_t gpuDataM[256];
44static unsigned char gpuCommand = 0;
45static long gpuDataC = 0;
46static long gpuDataP = 0;
47
48VRAMLoad_t VRAMWrite;
49VRAMLoad_t VRAMRead;
50DATAREGISTERMODES DataWriteMode;
51DATAREGISTERMODES DataReadMode;
52
53BOOL bSkipNextFrame = FALSE;
54DWORD dwLaceCnt=0;
ef79bbde
P
55short sDispWidths[8] = {256,320,512,640,368,384,512,640};
56PSXDisplay_t PSXDisplay;
57PSXDisplay_t PreviousPSXDisplay;
58long lSelectedSlot=0;
ef79bbde
P
59BOOL bDoLazyUpdate=FALSE;
60uint32_t lGPUInfoVals[16];
61static int iFakePrimBusy=0;
62
63////////////////////////////////////////////////////////////////////////
64// some misc external display funcs
65////////////////////////////////////////////////////////////////////////
66
67#include <time.h>
ef79bbde 68
a96a5eb2 69// FPS library
70#include "fps.c"
ef79bbde 71
ef79bbde
P
72
73////////////////////////////////////////////////////////////////////////
a96a5eb2 74// sets all kind of act fixes
ef79bbde
P
75////////////////////////////////////////////////////////////////////////
76
a96a5eb2 77static void SetFixes(void)
78 {
79 if(dwActFixes&0x02) sDispWidths[4]=384;
80 else sDispWidths[4]=368;
81 }
ef79bbde
P
82
83////////////////////////////////////////////////////////////////////////
84// INIT, will be called after lib load... well, just do some var init...
85////////////////////////////////////////////////////////////////////////
86
a96a5eb2 87long CALLBACK GPUinit(void) // GPU INIT
ef79bbde
P
88{
89 memset(ulStatusControl,0,256*sizeof(uint32_t)); // init save state scontrol field
90
a96a5eb2 91 psxVSecure = (unsigned char *)malloc((512*2)*1024 + (1024*1024)); // always alloc one extra MB for soft drawing funcs security
ef79bbde
P
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
a96a5eb2 104 psxVuw_eom=psxVuw+1024*512; // pre-calc of end of vram
ef79bbde 105
a96a5eb2 106 memset(psxVSecure,0x00,(512*2)*1024 + (1024*1024));
ef79bbde
P
107 memset(lGPUInfoVals,0x00,16*sizeof(uint32_t));
108
ef79bbde
P
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
146long GPUopen(unsigned long * disp,char * CapText,char * CfgFile)
147{
148 unsigned long d;
a96a5eb2 149
150 SetFixes();
ef79bbde
P
151
152 InitFPS();
153
ef79bbde
P
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
170long CALLBACK GPUclose() // GPU CLOSE
171{
ef79bbde
P
172 CloseDisplay(); // shutdown direct draw
173
174 return 0;
175}
176
177////////////////////////////////////////////////////////////////////////
178// I shot the sheriff
179////////////////////////////////////////////////////////////////////////
180
a96a5eb2 181long CALLBACK GPUshutdown(void) // GPU SHUTDOWN
ef79bbde 182{
a96a5eb2 183 CloseDisplay(); // shutdown direct draw
ef79bbde 184 free(psxVSecure);
ef79bbde
P
185 return 0; // nothinh to do
186}
187
188////////////////////////////////////////////////////////////////////////
189// Update display (swap buffers)
190////////////////////////////////////////////////////////////////////////
191
a96a5eb2 192static void updateDisplay(void) // UPDATE DISPLAY
ef79bbde
P
193{
194 if(PSXDisplay.Disabled) // disable?
195 {
ef79bbde
P
196 return; // -> and bye
197 }
198
199 if(dwActFixes&32) // pc fps calculation fix
200 {
201 if(UseFrameLimit) PCFrameCap(); // -> brake
a96a5eb2 202 if(UseFrameSkip) PCcalcfps();
ef79bbde
P
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 {
0d8f5b89 218 bSkipNextFrame = FALSE;
ef79bbde
P
219 DoBufferSwap(); // -> swap
220 }
f1bad6e1 221
222 bDoVSyncUpdate=FALSE; // vsync done
ef79bbde
P
223}
224
225////////////////////////////////////////////////////////////////////////
226// roughly emulated screen centering bits... not complete !!!
227////////////////////////////////////////////////////////////////////////
228
229void 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
ef79bbde
P
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
ef79bbde
P
278 DoClearScreenBuffer();
279 }
280
281 bDoVSyncUpdate=TRUE;
282}
283
284////////////////////////////////////////////////////////////////////////
285
286void ChangeDispOffsetsY(void) // Y CENTER
287{
288 int iT,iO=PreviousPSXDisplay.Range.y0;
289 int iOldYOffset=PreviousPSXDisplay.DisplayModeNew.y;
290
291// new
292
a96a5eb2 293 if((PreviousPSXDisplay.DisplayModeNew.x+PSXDisplay.DisplayModeNew.y)>512)
ef79bbde 294 {
a96a5eb2 295 int dy1=512-PreviousPSXDisplay.DisplayModeNew.x;
296 int dy2=(PreviousPSXDisplay.DisplayModeNew.x+PSXDisplay.DisplayModeNew.y)-512;
ef79bbde
P
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
a96a5eb2 346static void updateDisplayIfChanged(void) // UPDATE DISPLAY IF CHANGED
ef79bbde
P
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
ef79bbde
P
381////////////////////////////////////////////////////////////////////////
382// update lace is called evry VSync
383////////////////////////////////////////////////////////////////////////
384
385void CALLBACK GPUupdateLace(void) // VSYNC
386{
387 if(!(dwActFixes&1))
388 lGPUstatusRet^=0x80000000; // odd/even bit
389
dc40e780 390 //pcsx-rearmed: removed, this is handled by core
72228559 391 //if(!(dwActFixes&32)) // std fps limitation?
392 // CheckFrameRate();
ef79bbde
P
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 {
f1bad6e1 411 if((bDoVSyncUpdate && !UseFrameSkip) // some primitives drawn?
412 || bDoVSyncUpdate >= 8) // not syned for a while
413 updateDisplay(); // -> update display
ef79bbde
P
414 }
415 }
f1bad6e1 416
417 if(bDoVSyncUpdate) // if display not synced
418 bDoVSyncUpdate++; // count how many times
ef79bbde
P
419}
420
421////////////////////////////////////////////////////////////////////////
422// process read request from GPU status register
423////////////////////////////////////////////////////////////////////////
424
425
426uint32_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
461void 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
ef79bbde 517// new
a96a5eb2 518 PSXDisplay.DisplayPosition.y = (short)((gdata>>10)&0x1ff);
ef79bbde
P
519
520 // store the same val in some helper var, we need it on later compares
521 PreviousPSXDisplay.DisplayModeNew.x=PSXDisplay.DisplayPosition.y;
522
a96a5eb2 523 if((PSXDisplay.DisplayPosition.y+PSXDisplay.DisplayMode.y)>512)
ef79bbde 524 {
a96a5eb2 525 int dy1=512-PSXDisplay.DisplayPosition.y;
526 int dy2=(PSXDisplay.DisplayPosition.y+PSXDisplay.DisplayMode.y)-512;
ef79bbde
P
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:
a96a5eb2 666 lGPUdataRet=0x02; // gpu type
ef79bbde
P
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
a96a5eb2 682static inline void FinishedVRAMWrite(void)
ef79bbde 683{
ef79bbde
P
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
a96a5eb2 695static inline void FinishedVRAMRead(void)
ef79bbde
P
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
715void 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)
a96a5eb2 725 VRAMRead.ImagePtr-=512*1024;
ef79bbde 726 while(VRAMRead.ImagePtr<psxVuw)
a96a5eb2 727 VRAMRead.ImagePtr+=512*1024;
ef79bbde
P
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++;
a96a5eb2 738 if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=512*1024;
ef79bbde
P
739 VRAMRead.RowsRemaining --;
740
741 if(VRAMRead.RowsRemaining<=0)
742 {
743 VRAMRead.RowsRemaining = VRAMRead.Width;
744 VRAMRead.ColsRemaining--;
745 VRAMRead.ImagePtr += 1024 - VRAMRead.Width;
a96a5eb2 746 if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=512*1024;
ef79bbde
P
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++;
a96a5eb2 757 if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=512*1024;
ef79bbde
P
758 VRAMRead.RowsRemaining--;
759 if(VRAMRead.RowsRemaining<=0)
760 {
761 VRAMRead.RowsRemaining = VRAMRead.Width;
762 VRAMRead.ColsRemaining--;
763 VRAMRead.ImagePtr += 1024 - VRAMRead.Width;
a96a5eb2 764 if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=512*1024;
ef79bbde
P
765 }
766 if(VRAMRead.ColsRemaining <= 0)
767 {FinishedVRAMRead();goto ENDREAD;}
768 }
769 else {FinishedVRAMRead();goto ENDREAD;}
770 }
771
772ENDREAD:
773 GPUIsIdle;
774}
775
776
777////////////////////////////////////////////////////////////////////////
778
779uint32_t CALLBACK GPUreadData(void)
780{
781 uint32_t l;
782 GPUreadDataMem(&l,1);
783 return lGPUdataRet;
784}
785
a96a5eb2 786// Software drawing function
787#include "soft.c"
788
789// PSX drawing primitives
790#include "prim.c"
791
ef79bbde
P
792////////////////////////////////////////////////////////////////////////
793// processes data send to GPU data register
794// extra table entries for fixing polyline troubles
795////////////////////////////////////////////////////////////////////////
796
a96a5eb2 797static const unsigned char primTableCX[256] =
ef79bbde
P
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
867void 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
875STARTVRAM:
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)
a96a5eb2 883 VRAMWrite.ImagePtr-=512*1024;
ef79bbde 884 while(VRAMWrite.ImagePtr<psxVuw)
a96a5eb2 885 VRAMWrite.ImagePtr+=512*1024;
ef79bbde
P
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++;
a96a5eb2 898 if(VRAMWrite.ImagePtr>=psxVuw_eom) VRAMWrite.ImagePtr-=512*1024;
ef79bbde
P
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++;
a96a5eb2 916 if(VRAMWrite.ImagePtr>=psxVuw_eom) VRAMWrite.ImagePtr-=512*1024;
ef79bbde
P
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
930ENDVRAM:
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;
0d16cda2 954 PUTLE32_(&gpuDataM[0], gdata);
ef79bbde
P
955 gpuDataP = 1;
956 }
957 else continue;
958 }
959 else
960 {
0d16cda2 961 PUTLE32_(&gpuDataM[gpuDataP], gdata);
ef79bbde
P
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);
a96a5eb2 978 if(dwActFixes&0x0400) // hack for emulating "gpu busy" in some games
979 iFakePrimBusy=4;
980 }
ef79bbde
P
981 }
982 }
983
984 lGPUdataRet=gdata;
985
986 GPUIsReadyForCommands;
987 GPUIsIdle;
988}
989
990////////////////////////////////////////////////////////////////////////
991
992void CALLBACK GPUwriteData(uint32_t gdata)
993{
0d16cda2 994 PUTLE32_(&gdata, gdata);
ef79bbde
P
995 GPUwriteDataMem(&gdata,1);
996}
997
ef79bbde
P
998////////////////////////////////////////////////////////////////////////
999// process gpu commands
1000////////////////////////////////////////////////////////////////////////
1001
1002unsigned long lUsedAddr[3];
1003
a96a5eb2 1004static inline BOOL CheckForEndlessLoop(unsigned long laddr)
ef79bbde
P
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
1015long 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 {
a96a5eb2 1029 addr&=0x1FFFFC;
ef79bbde
P
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
ef79bbde
P
1048////////////////////////////////////////////////////////////////////////
1049// Freeze
1050////////////////////////////////////////////////////////////////////////
1051
1052typedef 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
1062long 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;
ef79bbde
P
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));
a96a5eb2 1081 memcpy(pF->psxVRam, psxVub, 1024*512*2);
ef79bbde
P
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));
a96a5eb2 1090 memcpy(psxVub, pF->psxVRam, 1024*512*2);
ef79bbde
P
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}