1 /******************************************************************************/
2 /* Mednafen Virtual Boy Emulation Module */
3 /******************************************************************************/
4 /* vip.cpp:
5 ** Copyright (C) 2010-2017 Mednafen Team
6 **
7 ** This program is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU General Public License
9 ** as published by the Free Software Foundation; either version 2
10 ** of the License, or (at your option) any later version.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software Foundation, Inc.,
19 ** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 #include "vb.h"
23 #include "vip.h"
24
25 #define VIP_DBGMSG(...) { }
26 //#define VIP_DBGMSG(...) printf(__VA_ARGS__)
27
28 namespace MDFN_IEN_VB
29 {
30
31 static uint8 FB[2][2][0x6000];
32 static uint16 CHR_RAM[0x8000 / sizeof(uint16)];
33 static uint16 DRAM[0x20000 / sizeof(uint16)];
34
35 #define INT_SCAN_ERR 0x0001
36 #define INT_LFB_END 0x0002
37 #define INT_RFB_END 0x0004
38 #define INT_GAME_START 0x0008
39 #define INT_FRAME_START 0x0010
40
41 #define INT_SB_HIT 0x2000
42 #define INT_XP_END 0x4000
43 #define INT_TIME_ERR 0x8000
44
45 static uint16 InterruptPending;
46 static uint16 InterruptEnable;
47
48 static uint8 BRTA, BRTB, BRTC, REST;
49 static uint8 Repeat;
50
51 static NO_INLINE void CopyFBColumnToTarget_Anaglyph(void);
52 static NO_INLINE void CopyFBColumnToTarget_AnaglyphSlow(void);
53 static NO_INLINE void CopyFBColumnToTarget_CScope(void);
54 static NO_INLINE void CopyFBColumnToTarget_SideBySide(void);
55 static NO_INLINE void CopyFBColumnToTarget_VLI(void);
56 static NO_INLINE void CopyFBColumnToTarget_HLI(void);
57 static void (*CopyFBColumnToTarget)(void) = NULL;
58 static float VBLEDOnScale;
59 static uint32 VB3DMode;
60 static uint32 VB3DReverse;
61 static uint32 VBPrescale;
62 static uint32 VBSBS_Separation;
63 static uint32 HLILUT[256];
64 static uint32 ColorLUT[2][256];
65 static int32 BrightnessCache[4];
66 static uint32 BrightCLUT[2][4];
67
68 static float ColorLUTNoGC[2][256][3];
69 static uint32 AnaSlowColorLUT[256][256];
70
71 // A few settings:
72 static bool InstantDisplayHack;
73 static bool AllowDrawSkip;
74
75 static bool VidSettingsDirty;
76 static bool ParallaxDisabled;
77 static uint32 Anaglyph_Colors[2];
78 static uint32 Default_Color;
79
80 const CustomPalette_Spec VIP_CPInfo[] =
81 {
82 { gettext_noop("VB LED Active Time; 256 left, 256 right. If only 256 triplets are present, then they will be used for both left and right views. When the custom palette's right view colors are the same as the left view colors(either explicitly, or when using a 256-entry custom palette), and anaglyph 3D mode is active, then the custom palette will not be used."), NULL, { 256, 256 * 2, 0 } },
83
84 { NULL, NULL }
85 };
86
MakeColorLUT(const MDFN_PixelFormat & format,const uint8 * const CustomPalette,const uint32 CustomPaletteNumEntries)87 static void MakeColorLUT(const MDFN_PixelFormat& format, const uint8* const CustomPalette, const uint32 CustomPaletteNumEntries)
88 {
89 //
90 // TODO: Use correct CRT/sRGB gamma curve, instead of approximation.
91 //
92 const bool cprunique = CustomPalette && (CustomPaletteNumEntries == 512) && memcmp(CustomPalette + 0 * 3, CustomPalette + 256 * 3, 256 * 3);
93
94 for(int lr = 0; lr < 2; lr++)
95 {
96 for(int i = 0; i < 256; i++)
97 {
98 float r, g, b;
99 uint32 modcolor_prime;
100
101 if(VB3DMode == VB3DMODE_ANAGLYPH)
102 modcolor_prime = Anaglyph_Colors[lr ^ VB3DReverse];
103 else
104 modcolor_prime = Default_Color;
105
106 if(CustomPalette && (VB3DMode != VB3DMODE_ANAGLYPH || cprunique))
107 {
108 r = g = b = 1.0;
109 // Don't ^ VB3DReverse here.
110 modcolor_prime = MDFN_de24msb(CustomPalette + ((lr & cprunique) * 256 + i) * 3);
111 }
112 else
113 r = g = b = std::min<float>(1.0, i * VBLEDOnScale / 255.0);
114
115 // Modulate.
116 r = r * pow(((modcolor_prime >> 16) & 0xFF) / 255.0, 2.2 / 1.0);
117 g = g * pow(((modcolor_prime >> 8) & 0xFF) / 255.0, 2.2 / 1.0);
118 b = b * pow(((modcolor_prime >> 0) & 0xFF) / 255.0, 2.2 / 1.0);
119
120 ColorLUTNoGC[lr][i][0] = r;
121 ColorLUTNoGC[lr][i][1] = g;
122 ColorLUTNoGC[lr][i][2] = b;
123
124 // Apply gamma correction
125 const float r_prime = pow(r, 1.0 / 2.2);
126 const float g_prime = pow(g, 1.0 / 2.2);
127 const float b_prime = pow(b, 1.0 / 2.2);
128
129 ColorLUT[lr][i] = format.MakeColor((int)(r_prime * 255), (int)(g_prime * 255), (int)(b_prime * 255), 0);
130 }
131 }
132
133 // Anaglyph slow-mode LUT calculation
134 for(int l_b = 0; l_b < 256; l_b++)
135 {
136 for(int r_b = 0; r_b < 256; r_b++)
137 {
138 float r, g, b;
139 float r_prime, g_prime, b_prime;
140
141 r = ColorLUTNoGC[0][l_b][0] + ColorLUTNoGC[1][r_b][0];
142 g = ColorLUTNoGC[0][l_b][1] + ColorLUTNoGC[1][r_b][1];
143 b = ColorLUTNoGC[0][l_b][2] + ColorLUTNoGC[1][r_b][2];
144
145 if(r > 1.0)
146 r = 1.0;
147 if(g > 1.0)
148 g = 1.0;
149 if(b > 1.0)
150 b = 1.0;
151
152 r_prime = pow(r, 1.0 / 2.2);
153 g_prime = pow(g, 1.0 / 2.2);
154 b_prime = pow(b, 1.0 / 2.2);
155
156 AnaSlowColorLUT[l_b][r_b] = format.MakeColor((int)(r_prime * 255), (int)(g_prime * 255), (int)(b_prime * 255), 0);
157 }
158 }
159 }
160
RecalcBrightnessCache(void)161 static void RecalcBrightnessCache(void)
162 {
163 static const int32 MaxTime = 255;
164 int32 CumulativeTime = (BRTA + 1 + BRTB + 1 + BRTC + 1 + REST + 1) + 1;
165
166 //printf("BRTA: %d, BRTB: %d, BRTC: %d, Rest: %d --- %d\n", BRTA, BRTB, BRTC, REST, BRTA + 1 + BRTB + 1 + BRTC);
167
168 BrightnessCache[0] = 0;
169 BrightnessCache[1] = 0;
170 BrightnessCache[2] = 0;
171 BrightnessCache[3] = 0;
172
173 for(int i = 0; i < Repeat + 1; i++)
174 {
175 int32 btemp[4];
176
177 if((i * CumulativeTime) >= MaxTime)
178 break;
179
180 btemp[1] = (i * CumulativeTime) + BRTA;
181 if(btemp[1] > MaxTime)
182 btemp[1] = MaxTime;
183 btemp[1] -= (i * CumulativeTime);
184 if(btemp[1] < 0)
185 btemp[1] = 0;
186
187
188 btemp[2] = (i * CumulativeTime) + BRTA + 1 + BRTB;
189 if(btemp[2] > MaxTime)
190 btemp[2] = MaxTime;
191 btemp[2] -= (i * CumulativeTime) + BRTA + 1;
192 if(btemp[2] < 0)
193 btemp[2] = 0;
194
195 //btemp[3] = (i * CumulativeTime) + BRTA + 1 + BRTB + 1 + BRTC;
196 //if(btemp[3] > MaxTime)
197 // btemp[3] = MaxTime;
198 //btemp[3] -= (i * CumulativeTime);
199 //if(btemp[3] < 0)
200 // btemp[3] = 0;
201
202 btemp[3] = (i * CumulativeTime) + BRTA + BRTB + BRTC + 1;
203 if(btemp[3] > MaxTime)
204 btemp[3] = MaxTime;
205 btemp[3] -= (i * CumulativeTime) + 1;
206 if(btemp[3] < 0)
207 btemp[3] = 0;
208
209 BrightnessCache[1] += btemp[1];
210 BrightnessCache[2] += btemp[2];
211 BrightnessCache[3] += btemp[3];
212 }
213
214 //printf("BC: %d %d %d %d\n", BrightnessCache[0], BrightnessCache[1], BrightnessCache[2], BrightnessCache[3]);
215
216 for(int lr = 0; lr < 2; lr++)
217 for(int i = 0; i < 4; i++)
218 {
219 BrightCLUT[lr][i] = ColorLUT[lr][BrightnessCache[i]];
220 //printf("%d %d, %08x\n", lr, i, BrightCLUT[lr][i]);
221 }
222 }
223
Recalc3DModeStuff(bool non_rgb_output=false)224 static void Recalc3DModeStuff(bool non_rgb_output = false)
225 {
226 switch(VB3DMode)
227 {
228 default:
229 if(((Anaglyph_Colors[0] & 0xFF) && (Anaglyph_Colors[1] & 0xFF)) ||
230 ((Anaglyph_Colors[0] & 0xFF00) && (Anaglyph_Colors[1] & 0xFF00)) ||
231 ((Anaglyph_Colors[0] & 0xFF0000) && (Anaglyph_Colors[1] & 0xFF0000)) ||
232 non_rgb_output)
233 {
234 CopyFBColumnToTarget = CopyFBColumnToTarget_AnaglyphSlow;
235 }
236 else
237 CopyFBColumnToTarget = CopyFBColumnToTarget_Anaglyph;
238 break;
239
240 case VB3DMODE_CSCOPE:
241 CopyFBColumnToTarget = CopyFBColumnToTarget_CScope;
242 break;
243
244 case VB3DMODE_SIDEBYSIDE:
245 CopyFBColumnToTarget = CopyFBColumnToTarget_SideBySide;
246 break;
247
248 case VB3DMODE_VLI:
249 CopyFBColumnToTarget = CopyFBColumnToTarget_VLI;
250 break;
251
252 case VB3DMODE_HLI:
253 CopyFBColumnToTarget = CopyFBColumnToTarget_HLI;
254 break;
255 }
256 RecalcBrightnessCache();
257 }
258
VIP_Set3DMode(uint32 mode,bool reverse,uint32 prescale,uint32 sbs_separation)259 void VIP_Set3DMode(uint32 mode, bool reverse, uint32 prescale, uint32 sbs_separation)
260 {
261 VB3DMode = mode;
262 VB3DReverse = reverse ? 1 : 0;
263 VBPrescale = prescale;
264 VBSBS_Separation = sbs_separation;
265
266 VidSettingsDirty = true;
267
268 for(uint32 p = 0; p < 256; p++)
269 {
270 uint32 v;
271 uint8 s[4];
272
273 s[0] = (p >> 0) & 0x3;
274 s[1] = (p >> 2) & 0x3;
275 s[2] = (p >> 4) & 0x3;
276 s[3] = (p >> 6) & 0x3;
277
278 v = 0;
279 for(unsigned int i = 0, shifty = 0; i < 4; i++)
280 {
281 for(unsigned int ps = 0; ps < prescale; ps++)
282 {
283 v |= s[i] << shifty;
284 shifty += 2;
285 }
286 }
287
288 HLILUT[p] = v;
289 }
290 }
291
VIP_SetParallaxDisable(bool disabled)292 void VIP_SetParallaxDisable(bool disabled)
293 {
294 ParallaxDisabled = disabled;
295 }
296
VIP_SetDefaultColor(uint32 default_color)297 void VIP_SetDefaultColor(uint32 default_color)
298 {
299 Default_Color = default_color;
300
301 VidSettingsDirty = true;
302 }
303
VIP_SetLEDOnScale(float coeff)304 void VIP_SetLEDOnScale(float coeff)
305 {
306 VBLEDOnScale = coeff;
307 }
308
VIP_SetAnaglyphColors(uint32 lcolor,uint32 rcolor)309 void VIP_SetAnaglyphColors(uint32 lcolor, uint32 rcolor)
310 {
311 Anaglyph_Colors[0] = lcolor;
312 Anaglyph_Colors[1] = rcolor;
313
314 VidSettingsDirty = true;
315 }
316
VIP_SetInstantDisplayHack(bool val)317 void VIP_SetInstantDisplayHack(bool val)
318 {
319 InstantDisplayHack = val;
320 }
321
VIP_SetAllowDrawSkip(bool val)322 void VIP_SetAllowDrawSkip(bool val)
323 {
324 AllowDrawSkip = val;
325 }
326
327
328 static uint16 FRMCYC;
329
330 static uint16 DPCTRL;
331 static bool DisplayActive;
332
333 #define XPCTRL_XP_RST 0x0001
334 #define XPCTRL_XP_EN 0x0002
335 static uint16 XPCTRL;
336 static uint16 SBCMP; // Derived from XPCTRL
337
338 static uint16 SPT[4]; // SPT0~SPT3, 5f848~5f84e
339 static uint16 GPLT[4];
340 static uint8 GPLT_Cache[4][4];
341
Recalc_GPLT_Cache(int which)342 static INLINE void Recalc_GPLT_Cache(int which)
343 {
344 for(int i = 0; i < 4; i++)
345 GPLT_Cache[which][i] = (GPLT[which] >> (i * 2)) & 3;
346 }
347
348 static uint16 JPLT[4];
349 static uint8 JPLT_Cache[4][4];
350
Recalc_JPLT_Cache(int which)351 static INLINE void Recalc_JPLT_Cache(int which)
352 {
353 for(int i = 0; i < 4; i++)
354 JPLT_Cache[which][i] = (JPLT[which] >> (i * 2)) & 3;
355 }
356
357
358 static uint16 BKCOL;
359
360 //
361 //
362 //
363 static int32 CalcNextEvent(void);
364
365 static int32 last_ts;
366
367 static uint32 Column;
368 static int32 ColumnCounter;
369
370 static int32 DisplayRegion;
371 static bool DisplayFB;
372
373 static int32 GameFrameCounter;
374
375 static int32 DrawingCounter;
376 static bool DrawingActive;
377 static bool DrawingFB;
378 static uint32 DrawingBlock;
379 static int32 SB_Latch;
380 static int32 SBOUT_InactiveTime;
381
382 //static uint8 CTA_L, CTA_R;
383
CheckIRQ(void)384 static void CheckIRQ(void)
385 {
386 VBIRQ_Assert(VBIRQ_SOURCE_VIP, (bool)(InterruptEnable & InterruptPending));
387
388 #if 0
389 printf("%08x\n", InterruptEnable & InterruptPending);
390 if((bool)(InterruptEnable & InterruptPending))
391 puts("IRQ asserted");
392 else
393 puts("IRQ not asserted");
394 #endif
395 }
396
397
VIP_Init(void)398 void VIP_Init(void)
399 {
400 InstantDisplayHack = false;
401 AllowDrawSkip = false;
402 ParallaxDisabled = false;
403 Anaglyph_Colors[0] = 0xFF0000;
404 Anaglyph_Colors[1] = 0x0000FF;
405 VB3DMode = VB3DMODE_ANAGLYPH;
406 Default_Color = 0xFFFFFF;
407 VB3DReverse = 0;
408 VBPrescale = 1;
409 VBSBS_Separation = 0;
410
411 VidSettingsDirty = true;
412 }
413
VIP_Kill(void)414 void VIP_Kill(void)
415 {
416
417
418 }
419
VIP_Power(void)420 void VIP_Power(void)
421 {
422 Repeat = 0;
423 SB_Latch = 0;
424 SBOUT_InactiveTime = -1;
425 last_ts = 0;
426
427 Column = 0;
428 ColumnCounter = 259;
429
430 DisplayRegion = 0;
431 DisplayFB = 0;
432
433 GameFrameCounter = 0;
434
435 DrawingCounter = 0;
436 DrawingActive = false;
437 DrawingFB = 0;
438 DrawingBlock = 0;
439
440 DPCTRL = 2;
441 DisplayActive = false;
442
443
444
445 memset(FB, 0, 0x6000 * 2 * 2);
446 memset(CHR_RAM, 0, 0x8000);
447 memset(DRAM, 0, 0x20000);
448
449 InterruptPending = 0;
450 InterruptEnable = 0;
451
452 BRTA = 0;
453 BRTB = 0;
454 BRTC = 0;
455 REST = 0;
456
457 FRMCYC = 0;
458
459 XPCTRL = 0;
460 SBCMP = 0;
461
462 for(int i = 0; i < 4; i++)
463 {
464 SPT[i] = 0;
465 GPLT[i] = 0;
466 JPLT[i] = 0;
467
468 Recalc_GPLT_Cache(i);
469 Recalc_JPLT_Cache(i);
470 }
471
472 BKCOL = 0;
473 }
474
ReadRegister(int32 & timestamp,uint32 A)475 static INLINE uint16 ReadRegister(int32 ×tamp, uint32 A)
476 {
477 uint16 ret = 0; //0xFFFF;
478
479 if(A & 1)
480 VIP_DBGMSG("Misaligned VIP Read: %08x\n", A);
481
482 switch(A & 0xFE)
483 {
484 default: VIP_DBGMSG("Unknown VIP register read: %08x\n", A);
485 break;
486
487 case 0x00: ret = InterruptPending;
488 break;
489
490 case 0x02: ret = InterruptEnable;
491 break;
492
493 case 0x20: //printf("Read DPSTTS at %d\n", timestamp);
494 ret = DPCTRL & 0x702;
495 if((DisplayRegion & 1) && DisplayActive)
496 {
497 unsigned int DPBSY = 1 << ((DisplayRegion >> 1) & 1);
498
499 if(DisplayFB)
500 DPBSY <<= 2;
501
502 ret |= DPBSY << 2;
503 }
504 //if(!(DisplayRegion & 1)) // FIXME? (Had to do it this way for Galactic Pinball...)
505 ret |= 1 << 6;
506 break;
507
508 // Note: Upper bits of BRTA, BRTB, BRTC, and REST(?) are 0 when read(on real hardware)
509 case 0x24: ret = BRTA;
510 break;
511
512 case 0x26: ret = BRTB;
513 break;
514
515 case 0x28: ret = BRTC;
516 break;
517
518 case 0x2A: ret = REST;
519 break;
520
521 case 0x30: ret = 0xFFFF;
522 break;
523
524 case 0x40: ret = XPCTRL & 0x2;
525 if(DrawingActive)
526 {
527 ret |= (1 + DrawingFB) << 2;
528 }
529 if(timestamp < SBOUT_InactiveTime)
530 {
531 ret |= 0x8000;
532 ret |= /*DrawingBlock*/SB_Latch << 8;
533 }
534 break; // XPSTTS, read-only
535
536 case 0x44: ret = 2; // VIP version. 2 is a known valid version, while the validity of other numbers is unknown, so we'll just go with 2.
537 break;
538
539 case 0x48:
540 case 0x4a:
541 case 0x4c:
542 case 0x4e: ret = SPT[(A >> 1) & 3];
543 break;
544
545 case 0x60:
546 case 0x62:
547 case 0x64:
548 case 0x66: ret = GPLT[(A >> 1) & 3];
549 break;
550
551 case 0x68:
552 case 0x6a:
553 case 0x6c:
554 case 0x6e: ret = JPLT[(A >> 1) & 3];
555 break;
556
557 case 0x70: ret = BKCOL;
558 break;
559 }
560
561 return(ret);
562 }
563
WriteRegister(int32 & timestamp,uint32 A,uint16 V)564 static INLINE void WriteRegister(int32 ×tamp, uint32 A, uint16 V)
565 {
566 if(A & 1)
567 VIP_DBGMSG("Misaligned VIP Write: %08x %04x\n", A, V);
568
569 switch(A & 0xFE)
570 {
571 default: VIP_DBGMSG("Unknown VIP register write: %08x %04x\n", A, V);
572 break;
573
574 case 0x00: break; // Interrupt pending, read-only
575
576 case 0x02: {
577 InterruptEnable = V & 0xE01F;
578
579 VIP_DBGMSG("Interrupt Enable: %04x\n", V);
580
581 if(V & 0x2000)
582 VIP_DBGMSG("Warning: VIP SB Hit Interrupt enable: %04x\n", V);
583 CheckIRQ();
584 }
585 break;
586
587 case 0x04: InterruptPending &= ~V;
588 CheckIRQ();
589 break;
590
591 case 0x20: break; // Display control, read-only.
592
593 case 0x22: DPCTRL = V & (0x703); // Display-control, write-only
594 if(V & 1)
595 {
596 DisplayActive = false;
597 InterruptPending &= ~(INT_TIME_ERR | INT_FRAME_START | INT_GAME_START | INT_RFB_END | INT_LFB_END | INT_SCAN_ERR);
598 CheckIRQ();
599 }
600 break;
601
602 case 0x24: BRTA = V & 0xFF; // BRTA
603 RecalcBrightnessCache();
604 break;
605
606 case 0x26: BRTB = V & 0xFF; // BRTB
607 RecalcBrightnessCache();
608 break;
609
610 case 0x28: BRTC = V & 0xFF; // BRTC
611 RecalcBrightnessCache();
612 break;
613
614 case 0x2A: REST = V & 0xFF; // REST
615 RecalcBrightnessCache();
616 break;
617
618 case 0x2E: FRMCYC = V & 0xF; // FRMCYC, write-only?
619 break;
620
621 case 0x30: break; // CTA, read-only(
622
623 case 0x40: break; // XPSTTS, read-only
624
625 case 0x42: XPCTRL = V & 0x0002; // XPCTRL, write-only
626 SBCMP = (V >> 8) & 0x1F;
627
628 if(V & 1)
629 {
630 VIP_DBGMSG("XPRST\n");
631 DrawingActive = 0;
632 DrawingCounter = 0;
633 InterruptPending &= ~(INT_SB_HIT | INT_XP_END | INT_TIME_ERR);
634 CheckIRQ();
635 }
636 break;
637
638 case 0x44: break; // Version Control, read-only?
639
640 case 0x48:
641 case 0x4a:
642 case 0x4c:
643 case 0x4e: SPT[(A >> 1) & 3] = V & 0x3FF;
644 break;
645
646 case 0x60:
647 case 0x62:
648 case 0x64:
649 case 0x66: GPLT[(A >> 1) & 3] = V & 0xFC;
650 Recalc_GPLT_Cache((A >> 1) & 3);
651 break;
652
653 case 0x68:
654 case 0x6a:
655 case 0x6c:
656 case 0x6e: JPLT[(A >> 1) & 3] = V & 0xFC;
657 Recalc_JPLT_Cache((A >> 1) & 3);
658 break;
659
660 case 0x70: BKCOL = V & 0x3;
661 break;
662
663 }
664 }
665
666 //
667 // Don't update the VIP state on reads/writes, the event system will update it with enough precision as far as VB software cares.
668 //
669
VIP_Read8(int32 & timestamp,uint32 A)670 MDFN_FASTCALL uint8 VIP_Read8(int32 ×tamp, uint32 A)
671 {
672 uint8 ret = 0; //0xFF;
673
674 //VIP_Update(timestamp);
675
676 switch(A >> 16)
677 {
678 case 0x0:
679 case 0x1:
680 if((A & 0x7FFF) >= 0x6000)
681 {
682 ret = ne16_rbo_le<uint8>(CHR_RAM, (A & 0x1FFF) | ((A >> 2) & 0x6000));
683 }
684 else
685 {
686 ret = FB[(A >> 15) & 1][(A >> 16) & 1][A & 0x7FFF];
687 }
688 break;
689
690 case 0x2:
691 case 0x3: ret = ne16_rbo_le<uint8>(DRAM, A & 0x1FFFF);
692 break;
693
694 case 0x4:
695 case 0x5: if(A >= 0x5E000)
696 ret = ReadRegister(timestamp, A);
697 else
698 VIP_DBGMSG("Unknown VIP Read: %08x\n", A);
699 break;
700
701 case 0x6: break;
702
703 case 0x7: if(A >= 0x8000)
704 {
705 ret = ne16_rbo_le<uint8>(CHR_RAM, A & 0x7FFF);
706 }
707 else
708 VIP_DBGMSG("Unknown VIP Read: %08x\n", A);
709 break;
710
711 default: VIP_DBGMSG("Unknown VIP Read: %08x\n", A);
712 break;
713 }
714
715
716 //VB_SetEvent(VB_EVENT_VIP, timestamp + CalcNextEvent());
717
718 return(ret);
719 }
720
VIP_Read16(int32 & timestamp,uint32 A)721 MDFN_FASTCALL uint16 VIP_Read16(int32 ×tamp, uint32 A)
722 {
723 uint16 ret = 0; //0xFFFF;
724
725 //VIP_Update(timestamp);
726
727 switch(A >> 16)
728 {
729 case 0x0:
730 case 0x1:
731 if((A & 0x7FFF) >= 0x6000)
732 {
733 ret = ne16_rbo_le<uint16>(CHR_RAM, (A & 0x1FFF) | ((A >> 2) & 0x6000));
734 }
735 else
736 {
737 ret = MDFN_de16lsb<true>(&FB[(A >> 15) & 1][(A >> 16) & 1][A & 0x7FFF]);
738 }
739 break;
740
741 case 0x2:
742 case 0x3: ret = ne16_rbo_le<uint16>(DRAM, A & 0x1FFFF);
743 break;
744
745 case 0x4:
746 case 0x5:
747 if(A >= 0x5E000)
748 ret = ReadRegister(timestamp, A);
749 else
750 VIP_DBGMSG("Unknown VIP Read: %08x\n", A);
751 break;
752
753 case 0x6: break;
754
755 case 0x7: if(A >= 0x8000)
756 {
757 ret = ne16_rbo_le<uint16>(CHR_RAM, A & 0x7FFF);
758 }
759 else
760 VIP_DBGMSG("Unknown VIP Read: %08x\n", A);
761 break;
762
763 default: VIP_DBGMSG("Unknown VIP Read: %08x\n", A);
764 break;
765 }
766
767
768 //VB_SetEvent(VB_EVENT_VIP, timestamp + CalcNextEvent());
769 return(ret);
770 }
771
VIP_Write8(int32 & timestamp,uint32 A,uint8 V)772 MDFN_FASTCALL void VIP_Write8(int32 ×tamp, uint32 A, uint8 V)
773 {
774 //VIP_Update(timestamp);
775
776 //if(A >= 0x3DC00 && A < 0x3E000)
777 // printf("%08x %02x\n", A, V);
778
779 switch(A >> 16)
780 {
781 case 0x0:
782 case 0x1:
783 if((A & 0x7FFF) >= 0x6000)
784 ne16_wbo_le<uint8>(CHR_RAM, (A & 0x1FFF) | ((A >> 2) & 0x6000), V);
785 else
786 FB[(A >> 15) & 1][(A >> 16) & 1][A & 0x7FFF] = V;
787 break;
788
789 case 0x2:
790 case 0x3: ne16_wbo_le<uint8>(DRAM, A & 0x1FFFF, V);
791 break;
792
793 case 0x4:
794 case 0x5: if(A >= 0x5E000)
795 WriteRegister(timestamp, A, V);
796 else
797 VIP_DBGMSG("Unknown VIP Write: %08x %02x\n", A, V);
798 break;
799
800 case 0x6: VIP_DBGMSG("Unknown VIP Write: %08x %02x\n", A, V);
801 break;
802
803 case 0x7: if(A >= 0x8000)
804 ne16_wbo_le<uint8>(CHR_RAM, A & 0x7FFF, V);
805 else
806 VIP_DBGMSG("Unknown VIP Write: %08x %02x\n", A, V);
807 break;
808
809 default: VIP_DBGMSG("Unknown VIP Write: %08x %02x\n", A, V);
810 break;
811 }
812
813 //VB_SetEvent(VB_EVENT_VIP, timestamp + CalcNextEvent());
814 }
815
VIP_Write16(int32 & timestamp,uint32 A,uint16 V)816 MDFN_FASTCALL void VIP_Write16(int32 ×tamp, uint32 A, uint16 V)
817 {
818 //VIP_Update(timestamp);
819
820 //if(A >= 0x3DC00 && A < 0x3E000)
821 // printf("%08x %04x\n", A, V);
822
823 switch(A >> 16)
824 {
825 case 0x0:
826 case 0x1:
827 if((A & 0x7FFF) >= 0x6000)
828 ne16_wbo_le<uint16>(CHR_RAM, (A & 0x1FFF) | ((A >> 2) & 0x6000), V);
829 else
830 MDFN_en16lsb<true>(&FB[(A >> 15) & 1][(A >> 16) & 1][A & 0x7FFF], V);
831 break;
832
833 case 0x2:
834 case 0x3: ne16_wbo_le<uint16>(DRAM, A & 0x1FFFF, V);
835 break;
836
837 case 0x4:
838 case 0x5: if(A >= 0x5E000)
839 WriteRegister(timestamp, A, V);
840 else
841 VIP_DBGMSG("Unknown VIP Write: %08x %04x\n", A, V);
842 break;
843
844 case 0x6: VIP_DBGMSG("Unknown VIP Write: %08x %04x\n", A, V);
845 break;
846
847 case 0x7: if(A >= 0x8000)
848 ne16_wbo_le<uint16>(CHR_RAM, A & 0x7FFF, V);
849 else
850 VIP_DBGMSG("Unknown VIP Write: %08x %04x\n", A, V);
851 break;
852
853 default: VIP_DBGMSG("Unknown VIP Write: %08x %04x\n", A, V);
854 break;
855 }
856
857
858 //VB_SetEvent(VB_EVENT_VIP, timestamp + CalcNextEvent());
859 }
860
861 static MDFN_Surface *surface;
862 static bool skip;
863
VIP_StartFrame(EmulateSpecStruct * espec)864 void VIP_StartFrame(EmulateSpecStruct *espec)
865 {
866 // puts("Start frame");
867
868 if(espec->VideoFormatChanged || VidSettingsDirty)
869 {
870 MakeColorLUT(espec->surface->format, espec->CustomPalette, espec->CustomPaletteNumEntries);
871 Recalc3DModeStuff(espec->surface->format.colorspace != MDFN_COLORSPACE_RGB);
872
873 VidSettingsDirty = false;
874 }
875
876 espec->DisplayRect.x = 0;
877 espec->DisplayRect.y = 0;
878
879 switch(VB3DMode)
880 {
881 default:
882 espec->DisplayRect.w = 384;
883 espec->DisplayRect.h = 224;
884 break;
885
886 case VB3DMODE_VLI:
887 espec->DisplayRect.w = 768 * VBPrescale;
888 espec->DisplayRect.h = 224;
889 break;
890
891 case VB3DMODE_HLI:
892 espec->DisplayRect.w = 384;
893 espec->DisplayRect.h = 448 * VBPrescale;
894 break;
895
896 case VB3DMODE_CSCOPE:
897 espec->DisplayRect.w = 512;
898 espec->DisplayRect.h = 384;
899 break;
900
901 case VB3DMODE_SIDEBYSIDE:
902 espec->DisplayRect.w = 768 + VBSBS_Separation;
903 espec->DisplayRect.h = 224;
904 break;
905 }
906
907 surface = espec->surface;
908 skip = espec->skip;
909 }
910
VIP_ResetTS(void)911 void VIP_ResetTS(void)
912 {
913 if(SBOUT_InactiveTime >= 0)
914 SBOUT_InactiveTime -= last_ts;
915 last_ts = 0;
916 }
917
CalcNextEvent(void)918 static int32 CalcNextEvent(void)
919 {
920 return(ColumnCounter);
921 }
922
923 #include "vip_draw.inc"
924
CopyFBColumnToTarget_Anaglyph_BASE(const bool DisplayActive_arg,const int lr)925 static INLINE void CopyFBColumnToTarget_Anaglyph_BASE(const bool DisplayActive_arg, const int lr)
926 {
927 const int fb = DisplayFB;
928 uint32 *target = surface->pixels + Column;
929 const int32 pitch32 = surface->pitch32;
930 const uint8 *fb_source = &FB[fb][lr][64 * Column];
931
932 for(int y = 56; y; y--)
933 {
934 uint32 source_bits = *fb_source;
935
936 for(int y_sub = 4; y_sub; y_sub--)
937 {
938 uint32 pixel = BrightCLUT[lr][source_bits & 3];
939
940 if(!DisplayActive_arg)
941 pixel = 0;
942
943 if(lr)
944 *target |= pixel;
945 else
946 *target = pixel;
947
948 source_bits >>= 2;
949 target += pitch32;
950 }
951 fb_source++;
952 }
953 }
954
CopyFBColumnToTarget_Anaglyph(void)955 static void CopyFBColumnToTarget_Anaglyph(void)
956 {
957 const int lr = (DisplayRegion & 2) >> 1;
958
959 if(!DisplayActive)
960 {
961 if(!lr)
962 CopyFBColumnToTarget_Anaglyph_BASE(0, 0);
963 else
964 CopyFBColumnToTarget_Anaglyph_BASE(0, 1);
965 }
966 else
967 {
968 if(!lr)
969 CopyFBColumnToTarget_Anaglyph_BASE(1, 0);
970 else
971 CopyFBColumnToTarget_Anaglyph_BASE(1, 1);
972 }
973 }
974
975 static uint32 AnaSlowBuf[384][224];
976
CopyFBColumnToTarget_AnaglyphSlow_BASE(const bool DisplayActive_arg,const int lr)977 static INLINE void CopyFBColumnToTarget_AnaglyphSlow_BASE(const bool DisplayActive_arg, const int lr)
978 {
979 const int fb = DisplayFB;
980 const uint8 *fb_source = &FB[fb][lr][64 * Column];
981
982 if(!lr)
983 {
984 uint32 *target = AnaSlowBuf[Column];
985
986 for(int y = 56; y; y--)
987 {
988 uint32 source_bits = *fb_source;
989
990 for(int y_sub = 4; y_sub; y_sub--)
991 {
992 uint32 pixel = BrightnessCache[source_bits & 3];
993
994 if(!DisplayActive_arg)
995 pixel = 0;
996
997 *target = pixel;
998 source_bits >>= 2;
999 target++;
1000 }
1001 fb_source++;
1002 }
1003
1004 }
1005 else
1006 {
1007 uint32 *target = surface->pixels + Column;
1008 const uint32 *left_src = AnaSlowBuf[Column];
1009 const int32 pitch32 = surface->pitch32;
1010
1011 for(int y = 56; y; y--)
1012 {
1013 uint32 source_bits = *fb_source;
1014
1015 for(int y_sub = 4; y_sub; y_sub--)
1016 {
1017 uint32 pixel = AnaSlowColorLUT[*left_src][DisplayActive_arg ? BrightnessCache[source_bits & 3] : 0];
1018
1019 *target = pixel;
1020
1021 source_bits >>= 2;
1022 target += pitch32;
1023 left_src++;
1024 }
1025 fb_source++;
1026 }
1027 }
1028 }
1029
CopyFBColumnToTarget_AnaglyphSlow(void)1030 static void CopyFBColumnToTarget_AnaglyphSlow(void)
1031 {
1032 const int lr = (DisplayRegion & 2) >> 1;
1033
1034 if(!DisplayActive)
1035 {
1036 if(!lr)
1037 CopyFBColumnToTarget_AnaglyphSlow_BASE(0, 0);
1038 else
1039 CopyFBColumnToTarget_AnaglyphSlow_BASE(0, 1);
1040 }
1041 else
1042 {
1043 if(!lr)
1044 CopyFBColumnToTarget_AnaglyphSlow_BASE(1, 0);
1045 else
1046 CopyFBColumnToTarget_AnaglyphSlow_BASE(1, 1);
1047 }
1048 }
1049
1050
CopyFBColumnToTarget_CScope_BASE(const bool DisplayActive_arg,const int lr,const int dest_lr)1051 static void CopyFBColumnToTarget_CScope_BASE(const bool DisplayActive_arg, const int lr, const int dest_lr)
1052 {
1053 const int fb = DisplayFB;
1054 uint32 *target = surface->pixels + (dest_lr ? 512 - 16 - 1 : 16) + (dest_lr ? Column : 383 - Column) * surface->pitch32;
1055 const uint8 *fb_source = &FB[fb][lr][64 * Column];
1056
1057 for(int y = 56; y; y--)
1058 {
1059 uint32 source_bits = *fb_source;
1060
1061 for(int y_sub = 4; y_sub; y_sub--)
1062 {
1063 if(DisplayActive_arg)
1064 *target = BrightCLUT[lr][source_bits & 3];
1065 else
1066 *target = 0;
1067
1068 source_bits >>= 2;
1069 if(dest_lr)
1070 target--;
1071 else
1072 target++;
1073 }
1074 fb_source++;
1075 }
1076 }
1077
CopyFBColumnToTarget_CScope(void)1078 static void CopyFBColumnToTarget_CScope(void)
1079 {
1080 const int lr = (DisplayRegion & 2) >> 1;
1081
1082 if(!DisplayActive)
1083 {
1084 if(!lr)
1085 CopyFBColumnToTarget_CScope_BASE(0, 0, 0 ^ VB3DReverse);
1086 else
1087 CopyFBColumnToTarget_CScope_BASE(0, 1, 1 ^ VB3DReverse);
1088 }
1089 else
1090 {
1091 if(!lr)
1092 CopyFBColumnToTarget_CScope_BASE(1, 0, 0 ^ VB3DReverse);
1093 else
1094 CopyFBColumnToTarget_CScope_BASE(1, 1, 1 ^ VB3DReverse);
1095 }
1096 }
1097
CopyFBColumnToTarget_SideBySide_BASE(const bool DisplayActive_arg,const int lr,const int dest_lr)1098 static void CopyFBColumnToTarget_SideBySide_BASE(const bool DisplayActive_arg, const int lr, const int dest_lr)
1099 {
1100 const int fb = DisplayFB;
1101 uint32 *target = surface->pixels + Column + (dest_lr ? (384 + VBSBS_Separation) : 0);
1102 const int32 pitch32 = surface->pitch32;
1103 const uint8 *fb_source = &FB[fb][lr][64 * Column];
1104
1105 for(int y = 56; y; y--)
1106 {
1107 uint32 source_bits = *fb_source;
1108
1109 for(int y_sub = 4; y_sub; y_sub--)
1110 {
1111 if(DisplayActive_arg)
1112 *target = BrightCLUT[lr][source_bits & 3];
1113 else
1114 *target = 0;
1115 source_bits >>= 2;
1116 target += pitch32;
1117 }
1118 fb_source++;
1119 }
1120 }
1121
CopyFBColumnToTarget_SideBySide(void)1122 static void CopyFBColumnToTarget_SideBySide(void)
1123 {
1124 const int lr = (DisplayRegion & 2) >> 1;
1125
1126 if(!DisplayActive)
1127 {
1128 if(!lr)
1129 CopyFBColumnToTarget_SideBySide_BASE(0, 0, 0 ^ VB3DReverse);
1130 else
1131 CopyFBColumnToTarget_SideBySide_BASE(0, 1, 1 ^ VB3DReverse);
1132 }
1133 else
1134 {
1135 if(!lr)
1136 CopyFBColumnToTarget_SideBySide_BASE(1, 0, 0 ^ VB3DReverse);
1137 else
1138 CopyFBColumnToTarget_SideBySide_BASE(1, 1, 1 ^ VB3DReverse);
1139 }
1140 }
1141
CopyFBColumnToTarget_VLI_BASE(const bool DisplayActive_arg,const int lr,const int dest_lr)1142 static INLINE void CopyFBColumnToTarget_VLI_BASE(const bool DisplayActive_arg, const int lr, const int dest_lr)
1143 {
1144 const int fb = DisplayFB;
1145 uint32 *target = surface->pixels + Column * 2 * VBPrescale + dest_lr;
1146 const int32 pitch32 = surface->pitch32;
1147 const uint8 *fb_source = &FB[fb][lr][64 * Column];
1148
1149 for(int y = 56; y; y--)
1150 {
1151 uint32 source_bits = *fb_source;
1152
1153 for(int y_sub = 4; y_sub; y_sub--)
1154 {
1155 uint32 tv;
1156
1157 if(DisplayActive_arg)
1158 tv = BrightCLUT[lr][source_bits & 3];
1159 else
1160 tv = 0;
1161
1162 for(uint32 ps = 0; ps < VBPrescale; ps++)
1163 target[ps * 2] = tv;
1164
1165 source_bits >>= 2;
1166 target += pitch32;
1167 }
1168 fb_source++;
1169 }
1170 }
1171
CopyFBColumnToTarget_VLI(void)1172 static void CopyFBColumnToTarget_VLI(void)
1173 {
1174 const int lr = (DisplayRegion & 2) >> 1;
1175
1176 if(!DisplayActive)
1177 {
1178 if(!lr)
1179 CopyFBColumnToTarget_VLI_BASE(0, 0, 0 ^ VB3DReverse);
1180 else
1181 CopyFBColumnToTarget_VLI_BASE(0, 1, 1 ^ VB3DReverse);
1182 }
1183 else
1184 {
1185 if(!lr)
1186 CopyFBColumnToTarget_VLI_BASE(1, 0, 0 ^ VB3DReverse);
1187 else
1188 CopyFBColumnToTarget_VLI_BASE(1, 1, 1 ^ VB3DReverse);
1189 }
1190 }
1191
CopyFBColumnToTarget_HLI_BASE(const bool DisplayActive_arg,const int lr,const int dest_lr)1192 static INLINE void CopyFBColumnToTarget_HLI_BASE(const bool DisplayActive_arg, const int lr, const int dest_lr)
1193 {
1194 const int fb = DisplayFB;
1195 const int32 pitch32 = surface->pitch32;
1196 uint32 *target = surface->pixels + Column + dest_lr * pitch32;
1197 const uint8 *fb_source = &FB[fb][lr][64 * Column];
1198
1199 if(VBPrescale <= 4)
1200 for(int y = 56; y; y--)
1201 {
1202 uint32 source_bits = HLILUT[*fb_source];
1203
1204 for(int y_sub = 4 * VBPrescale; y_sub; y_sub--)
1205 {
1206 if(DisplayActive_arg)
1207 *target = BrightCLUT[lr][source_bits & 3];
1208 else
1209 *target = 0;
1210
1211 target += pitch32 * 2;
1212 source_bits >>= 2;
1213 }
1214 fb_source++;
1215 }
1216 else
1217 for(int y = 56; y; y--)
1218 {
1219 uint32 source_bits = *fb_source;
1220
1221 for(int y_sub = 4; y_sub; y_sub--)
1222 {
1223 for(uint32 ps = 0; ps < VBPrescale; ps++)
1224 {
1225 if(DisplayActive_arg)
1226 *target = BrightCLUT[lr][source_bits & 3];
1227 else
1228 *target = 0;
1229
1230 target += pitch32 * 2;
1231 }
1232
1233 source_bits >>= 2;
1234 }
1235 fb_source++;
1236 }
1237 }
1238
CopyFBColumnToTarget_HLI(void)1239 static void CopyFBColumnToTarget_HLI(void)
1240 {
1241 const int lr = (DisplayRegion & 2) >> 1;
1242
1243 if(!DisplayActive)
1244 {
1245 if(!lr)
1246 CopyFBColumnToTarget_HLI_BASE(0, 0, 0 ^ VB3DReverse);
1247 else
1248 CopyFBColumnToTarget_HLI_BASE(0, 1, 1 ^ VB3DReverse);
1249 }
1250 else
1251 {
1252 if(!lr)
1253 CopyFBColumnToTarget_HLI_BASE(1, 0, 0 ^ VB3DReverse);
1254 else
1255 CopyFBColumnToTarget_HLI_BASE(1, 1, 1 ^ VB3DReverse);
1256 }
1257 }
1258
1259
VIP_Update(const v810_timestamp_t timestamp)1260 v810_timestamp_t MDFN_FASTCALL VIP_Update(const v810_timestamp_t timestamp)
1261 {
1262 int32 clocks = timestamp - last_ts;
1263 int32 running_timestamp = timestamp;
1264
1265 while(clocks > 0)
1266 {
1267 int32 chunk_clocks = clocks;
1268
1269 if(DrawingCounter > 0 && chunk_clocks > DrawingCounter)
1270 chunk_clocks = DrawingCounter;
1271 if(chunk_clocks > ColumnCounter)
1272 chunk_clocks = ColumnCounter;
1273
1274 running_timestamp += chunk_clocks;
1275
1276 if(DrawingCounter > 0)
1277 {
1278 DrawingCounter -= chunk_clocks;
1279 if(DrawingCounter <= 0)
1280 {
1281 alignas(8) uint8 DrawingBuffers[2][512 * 8]; // Don't decrease this from 512 unless you adjust vip_draw.inc(including areas that draw off-visible >= 384 and >= -7 for speed reasons)
1282
1283 if(skip && InstantDisplayHack && AllowDrawSkip)
1284 {
1285 #if 0
1286 for(int lr = 0; lr < 2; lr++)
1287 {
1288 uint8 *FB_Target = FB[DrawingFB][lr] + DrawingBlock * 2;
1289 for(int x = 0; x < 384; x++)
1290 {
1291 FB_Target[64 * x + 0] = BKCOL;
1292 FB_Target[64 * x + 1] = BKCOL;
1293 }
1294 }
1295 #endif
1296 }
1297 else
1298 {
1299 VIP_DrawBlock(DrawingBlock, DrawingBuffers[0] + 8, DrawingBuffers[1] + 8);
1300
1301 for(int lr = 0; lr < 2; lr++)
1302 {
1303 uint8 *FB_Target = FB[DrawingFB][lr] + DrawingBlock * 2;
1304
1305 for(int x = 0; x < 384; x++)
1306 {
1307 FB_Target[64 * x + 0] = (DrawingBuffers[lr][8 + x + 512 * 0] << 0)
1308 | (DrawingBuffers[lr][8 + x + 512 * 1] << 2)
1309 | (DrawingBuffers[lr][8 + x + 512 * 2] << 4)
1310 | (DrawingBuffers[lr][8 + x + 512 * 3] << 6);
1311
1312 FB_Target[64 * x + 1] = (DrawingBuffers[lr][8 + x + 512 * 4] << 0)
1313 | (DrawingBuffers[lr][8 + x + 512 * 5] << 2)
1314 | (DrawingBuffers[lr][8 + x + 512 * 6] << 4)
1315 | (DrawingBuffers[lr][8 + x + 512 * 7] << 6);
1316
1317 }
1318 }
1319 }
1320
1321 SBOUT_InactiveTime = running_timestamp + 1120;
1322 SB_Latch = DrawingBlock; // Not exactly correct, but probably doesn't matter.
1323
1324 DrawingBlock++;
1325 if(DrawingBlock == 28)
1326 {
1327 DrawingActive = false;
1328
1329 InterruptPending |= INT_XP_END;
1330 CheckIRQ();
1331 }
1332 else
1333 DrawingCounter += 1120 * 4;
1334 }
1335 }
1336
1337 ColumnCounter -= chunk_clocks;
1338 if(ColumnCounter == 0)
1339 {
1340 if(DisplayRegion & 1)
1341 {
1342 if(!(Column & 3))
1343 {
1344 const int lr = (DisplayRegion & 2) >> 1;
1345 uint16 ctdata = ne16_rbo_le<uint16>(DRAM, 0x1DFFE - ((Column >> 2) * 2) - (lr ? 0 : 0x200));
1346
1347 //printf("%02x, repeat: %02x\n", ctdata & 0xFF, ctdata >> 8);
1348
1349 if((ctdata >> 8) != Repeat)
1350 {
1351 Repeat = ctdata >> 8;
1352 RecalcBrightnessCache();
1353 }
1354 }
1355 if(!skip && !InstantDisplayHack)
1356 CopyFBColumnToTarget();
1357 }
1358
1359 ColumnCounter = 259;
1360 Column++;
1361 if(Column == 384)
1362 {
1363 Column = 0;
1364
1365 if(DisplayActive)
1366 {
1367 if(DisplayRegion & 1) // Did we just finish displaying an active region?
1368 {
1369 if(DisplayRegion & 2) // finished displaying right eye
1370 InterruptPending |= INT_RFB_END;
1371 else // Otherwise, left eye
1372 InterruptPending |= INT_LFB_END;
1373
1374 CheckIRQ();
1375 }
1376 }
1377
1378 DisplayRegion = (DisplayRegion + 1) & 3;
1379
1380 if(DisplayRegion == 0) // New frame start
1381 {
1382 DisplayActive = DPCTRL & 0x2;
1383
1384 if(DisplayActive)
1385 {
1386 InterruptPending |= INT_FRAME_START;
1387 CheckIRQ();
1388 }
1389 GameFrameCounter++;
1390 if(GameFrameCounter > FRMCYC) // New game frame start?
1391 {
1392 InterruptPending |= INT_GAME_START;
1393 CheckIRQ();
1394
1395 if(XPCTRL & XPCTRL_XP_EN)
1396 {
1397 DisplayFB ^= 1;
1398
1399 DrawingBlock = 0;
1400 DrawingActive = true;
1401 DrawingCounter = 1120 * 4;
1402 DrawingFB = DisplayFB ^ 1;
1403 }
1404
1405 GameFrameCounter = 0;
1406 }
1407
1408 if(!skip && InstantDisplayHack)
1409 {
1410 // Ugly kludge, fix in the future.
1411 int32 save_DisplayRegion = DisplayRegion;
1412 uint32 save_Column = Column;
1413 uint8 save_Repeat = Repeat;
1414
1415 for(int lr = 0; lr < 2; lr++)
1416 {
1417 DisplayRegion = lr << 1;
1418 for(Column = 0; Column < 384; Column++)
1419 {
1420 if(!(Column & 3))
1421 {
1422 uint16 ctdata = ne16_rbo_le<uint16>(DRAM, 0x1DFFE - ((Column >> 2) * 2) - (lr ? 0 : 0x200));
1423
1424 if((ctdata >> 8) != Repeat)
1425 {
1426 Repeat = ctdata >> 8;
1427 RecalcBrightnessCache();
1428 }
1429 }
1430
1431 CopyFBColumnToTarget();
1432 }
1433 }
1434 DisplayRegion = save_DisplayRegion;
1435 Column = save_Column;
1436 Repeat = save_Repeat;
1437 RecalcBrightnessCache();
1438 }
1439
1440 VB_ExitLoop();
1441 }
1442 }
1443 }
1444
1445 clocks -= chunk_clocks;
1446 }
1447
1448 last_ts = timestamp;
1449
1450 return(timestamp + CalcNextEvent());
1451 }
1452
1453
VIP_StateAction(StateMem * sm,const unsigned load,const bool data_only)1454 void VIP_StateAction(StateMem *sm, const unsigned load, const bool data_only)
1455 {
1456 SFORMAT StateRegs[] =
1457 {
1458 SFVARN(FB, "FB[0][0]"),
1459 SFVAR(CHR_RAM),
1460 SFVAR(DRAM),
1461
1462 SFVAR(InterruptPending),
1463 SFVAR(InterruptEnable),
1464
1465 SFVAR(BRTA),
1466 SFVAR(BRTB),
1467 SFVAR(BRTC),
1468 SFVAR(REST),
1469
1470 SFVAR(FRMCYC),
1471 SFVAR(DPCTRL),
1472
1473 SFVAR(DisplayActive),
1474
1475 SFVAR(XPCTRL),
1476 SFVAR(SBCMP),
1477 SFVAR(SPT),
1478 SFVAR(GPLT), // FIXME
1479 SFVAR(JPLT),
1480
1481 SFVAR(BKCOL),
1482
1483 SFVAR(Column),
1484 SFVAR(ColumnCounter),
1485
1486 SFVAR(DisplayRegion),
1487 SFVAR(DisplayFB),
1488
1489 SFVAR(GameFrameCounter),
1490
1491 SFVAR(DrawingCounter),
1492
1493 SFVAR(DrawingActive),
1494 SFVAR(DrawingFB),
1495 SFVAR(DrawingBlock),
1496
1497 SFVAR(SB_Latch),
1498 SFVAR(SBOUT_InactiveTime),
1499
1500 SFVAR(Repeat),
1501 SFEND
1502 };
1503
1504 MDFNSS_StateAction(sm, load, data_only, StateRegs, "VIP");
1505
1506 if(load)
1507 {
1508 Column %= 384;
1509
1510 if(ColumnCounter < 1)
1511 ColumnCounter = 1;
1512 else if(ColumnCounter > 1000)
1513 ColumnCounter = 1000;
1514
1515 //
1516 //
1517 //
1518 RecalcBrightnessCache();
1519 for(int i = 0; i < 4; i++)
1520 {
1521 Recalc_GPLT_Cache(i);
1522 Recalc_JPLT_Cache(i);
1523 }
1524 }
1525 }
1526
VIP_GetRegister(const unsigned int id,char * special,const uint32 special_len)1527 uint32 VIP_GetRegister(const unsigned int id, char *special, const uint32 special_len)
1528 {
1529 uint32 ret = 0xDEADBEEF;
1530
1531 switch(id)
1532 {
1533 case VIP_GSREG_IPENDING:
1534 ret = InterruptPending;
1535 break;
1536
1537 case VIP_GSREG_IENABLE:
1538 ret = InterruptEnable;
1539 break;
1540
1541 case VIP_GSREG_DPCTRL:
1542 ret = DPCTRL;
1543 break;
1544
1545 case VIP_GSREG_BRTA:
1546 ret = BRTA;
1547 break;
1548
1549 case VIP_GSREG_BRTB:
1550 ret = BRTB;
1551 break;
1552
1553 case VIP_GSREG_BRTC:
1554 ret = BRTC;
1555 break;
1556
1557 case VIP_GSREG_REST:
1558 ret = REST;
1559 break;
1560
1561 case VIP_GSREG_FRMCYC:
1562 ret = FRMCYC;
1563 break;
1564
1565 case VIP_GSREG_XPCTRL:
1566 ret = XPCTRL | (SBCMP << 8);
1567 break;
1568
1569 case VIP_GSREG_SPT0:
1570 case VIP_GSREG_SPT1:
1571 case VIP_GSREG_SPT2:
1572 case VIP_GSREG_SPT3:
1573 ret = SPT[id - VIP_GSREG_SPT0];
1574 break;
1575
1576 case VIP_GSREG_GPLT0:
1577 case VIP_GSREG_GPLT1:
1578 case VIP_GSREG_GPLT2:
1579 case VIP_GSREG_GPLT3:
1580 ret = GPLT[id - VIP_GSREG_GPLT0];
1581 break;
1582
1583 case VIP_GSREG_JPLT0:
1584 case VIP_GSREG_JPLT1:
1585 case VIP_GSREG_JPLT2:
1586 case VIP_GSREG_JPLT3:
1587 ret = JPLT[id - VIP_GSREG_JPLT0];
1588 break;
1589
1590 case VIP_GSREG_BKCOL:
1591 ret = BKCOL;
1592 break;
1593
1594 }
1595
1596 if(id == VIP_GSREG_IPENDING || id == VIP_GSREG_IENABLE)
1597 {
1598 if(special)
1599 trio_snprintf(special, special_len, "%s: %s%s%s%s%s%s%s%s",
1600 (id == VIP_GSREG_IPENDING) ? "Interrupts Pending" : "Interrupts Enabled",
1601 (ret & INT_SCAN_ERR) ? "SCAN_ERR " : "",
1602 (ret & INT_LFB_END) ? "LFB_END " : "",
1603 (ret & INT_RFB_END) ? "RFB_END " : "",
1604 (ret & INT_GAME_START) ? "GAME_START " : "",
1605 (ret & INT_FRAME_START) ? "FRAME_START " : "",
1606 (ret & INT_SB_HIT) ? "SB_HIT " : "",
1607 (ret & INT_XP_END) ? "XP_END " : "",
1608 (ret & INT_TIME_ERR) ? "TIME_ERR " : "");
1609
1610 }
1611
1612 return(ret);
1613 }
1614
VIP_SetRegister(const unsigned int id,const uint32 value)1615 void VIP_SetRegister(const unsigned int id, const uint32 value)
1616 {
1617 switch(id)
1618 {
1619 case VIP_GSREG_IPENDING:
1620 InterruptPending = value & 0xE01F;
1621 CheckIRQ();
1622 break;
1623
1624 case VIP_GSREG_IENABLE:
1625 InterruptEnable = value & 0xE01F;
1626 CheckIRQ();
1627 break;
1628
1629 case VIP_GSREG_DPCTRL:
1630 DPCTRL = value & 0x703; // FIXME(Lower bit?)
1631 break;
1632
1633 case VIP_GSREG_BRTA:
1634 BRTA = value & 0xFF;
1635 RecalcBrightnessCache();
1636 break;
1637
1638 case VIP_GSREG_BRTB:
1639 BRTB = value & 0xFF;
1640 RecalcBrightnessCache();
1641 break;
1642
1643 case VIP_GSREG_BRTC:
1644 BRTC = value & 0xFF;
1645 RecalcBrightnessCache();
1646 break;
1647
1648 case VIP_GSREG_REST:
1649 REST = value & 0xFF;
1650 RecalcBrightnessCache();
1651 break;
1652
1653 case VIP_GSREG_FRMCYC:
1654 FRMCYC = value & 0xF;
1655 break;
1656
1657 case VIP_GSREG_XPCTRL:
1658 XPCTRL = value & 0x2;
1659 SBCMP = (value >> 8) & 0x1f;
1660 break;
1661
1662 case VIP_GSREG_SPT0:
1663 case VIP_GSREG_SPT1:
1664 case VIP_GSREG_SPT2:
1665 case VIP_GSREG_SPT3:
1666 SPT[id - VIP_GSREG_SPT0] = value & 0x3FF;
1667 break;
1668
1669 case VIP_GSREG_GPLT0:
1670 case VIP_GSREG_GPLT1:
1671 case VIP_GSREG_GPLT2:
1672 case VIP_GSREG_GPLT3:
1673 GPLT[id - VIP_GSREG_GPLT0] = value & 0xFC;
1674 Recalc_GPLT_Cache(id - VIP_GSREG_GPLT0);
1675 break;
1676
1677 case VIP_GSREG_JPLT0:
1678 case VIP_GSREG_JPLT1:
1679 case VIP_GSREG_JPLT2:
1680 case VIP_GSREG_JPLT3:
1681 JPLT[id - VIP_GSREG_JPLT0] = value & 0xFC;
1682 Recalc_JPLT_Cache(id - VIP_GSREG_JPLT0);
1683 break;
1684
1685 case VIP_GSREG_BKCOL:
1686 BKCOL = value & 0x03;
1687 break;
1688 }
1689 }
1690
1691
1692 }
1693