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