1 /******************************************************************************/
2 /* Mednafen Sega Saturn Emulation Module */
3 /******************************************************************************/
4 /* vdp2.cpp - VDP2 Emulation
5 ** Copyright (C) 2015-2019 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 // TODO: Emulate brokenness(missing vblank) that occurs when switching from 240-height mode to 224-height mode around lines 224-239.
23
24 // TODO: Update output signals on Reset()? Might have to call VDP2::Reset() before other _Reset() then...and take special care in the
25 // SMPC clock change code.
26
27 #include "ss.h"
28 #include <mednafen/mednafen.h>
29 #include <mednafen/general.h>
30 #include <mednafen/FileStream.h>
31 #include "vdp1.h"
32 #include "vdp2.h"
33 #include "scu.h"
34 #include "smpc.h"
35
36 #include "vdp2_common.h"
37 #include "vdp2_render.h"
38
39 namespace MDFN_IEN_SS
40 {
41 #include "sh7095.h"
42
43 namespace VDP2
44 {
45
46 static bool PAL;
47 static sscpu_timestamp_t lastts;
48 //
49 //
50 //
51 static uint16 RawRegs[0x100]; // For debugging
52
53 static bool DisplayOn;
54 static bool BorderMode;
55 bool ExLatchEnable;
56 static bool ExSyncEnable;
57 static bool ExBGEnable;
58 static bool DispAreaSelect;
59
60 static bool VRAMSize;
61
62 static uint8 HRes, VRes;
63 static uint8 InterlaceMode;
64 enum { IM_NONE, IM_ILLEGAL, IM_SINGLE, IM_DOUBLE };
65
66 static uint16 RAMCTL_Raw;
67 static uint8 CRAM_Mode;
68 enum
69 {
70 CRAM_MODE_RGB555_1024 = 0,
71 CRAM_MODE_RGB555_2048 = 1,
72 CRAM_MODE_RGB888_1024 = 2,
73 CRAM_MODE_ILLEGAL = 3
74 };
75
76 static uint16 BGON;
77 static uint8 VCPRegs[4][8];
78 static uint32 VRAMPenalty[4];
79
80 static uint32 RPTA;
81 static uint8 RPRCTL[2];
82 static uint8 KTAOF[2];
83
84 static struct
85 {
86 uint16 YStart, YEnd;
87 bool YEndMet;
88 bool YIn;
89 } Window[2];
90
91 static uint16 VRAM[262144];
92
93 static uint16 CRAM[2048];
94
95 static struct
96 {
97 // Signed values are stored sign-extended to the full 32 bits.
98 int32 Xst, Yst, Zst; // 1.12.10
99 int32 DXst, DYst; // 1. 2.10
100 int32 DX, DY; // 1. 2.10
101 int32 RotMatrix[6]; // 1. 3.10
102 int32 Px, Py, Pz; // 1.13. 0
103 int32 Cx, Cy, Cz; // 1.13. 0
104 int32 Mx, My; // 1.13.10
105 int32 kx, ky; // 1. 7.16
106
107 uint32 KAst; // 0.16.10
108 uint32 DKAst; // 1. 9.10
109 uint32 DKAx; // 1. 9.10
110 //
111 //
112 //
113 uint32 XstAccum, YstAccum; // 1.12.10 (sorta)
114 uint32 KAstAccum; // .10
115 } RotParams[2];
116
FetchRotParams(const bool field)117 static void FetchRotParams(const bool field)
118 {
119 uint32 a = RPTA & 0x7FFBE;
120
121 for(unsigned i = 0; i < 2; i++)
122 {
123 auto& rp = RotParams[i];
124
125 rp.Xst = sign_x_to_s32(23, ne16_rbo_be<uint32>(VRAM, ((a + 0x00) & 0x3FFFF) << 1) >> 6);
126 rp.Yst = sign_x_to_s32(23, ne16_rbo_be<uint32>(VRAM, ((a + 0x02) & 0x3FFFF) << 1) >> 6);
127 rp.Zst = sign_x_to_s32(23, ne16_rbo_be<uint32>(VRAM, ((a + 0x04) & 0x3FFFF) << 1) >> 6);
128
129 rp.DXst = sign_x_to_s32(13, ne16_rbo_be<uint32>(VRAM, ((a + 0x06) & 0x3FFFF) << 1) >> 6);
130 rp.DYst = sign_x_to_s32(13, ne16_rbo_be<uint32>(VRAM, ((a + 0x08) & 0x3FFFF) << 1) >> 6);
131
132 rp.DX = sign_x_to_s32(13, ne16_rbo_be<uint32>(VRAM, ((a + 0x0A) & 0x3FFFF) << 1) >> 6);
133 rp.DY = sign_x_to_s32(13, ne16_rbo_be<uint32>(VRAM, ((a + 0x0C) & 0x3FFFF) << 1) >> 6);
134
135 for(unsigned m = 0; m < 6; m++)
136 {
137 rp.RotMatrix[m] = sign_x_to_s32(14, ne16_rbo_be<uint32>(VRAM, ((a + 0x0E + (m << 1)) & 0x3FFFF) << 1) >> 6);
138 }
139
140 rp.Px = sign_x_to_s32(14, VRAM[(a + 0x1A) & 0x3FFFF]);
141 rp.Py = sign_x_to_s32(14, VRAM[(a + 0x1B) & 0x3FFFF]);
142 rp.Pz = sign_x_to_s32(14, VRAM[(a + 0x1C) & 0x3FFFF]);
143
144 rp.Cx = sign_x_to_s32(14, VRAM[(a + 0x1E) & 0x3FFFF]);
145 rp.Cy = sign_x_to_s32(14, VRAM[(a + 0x1F) & 0x3FFFF]);
146 rp.Cz = sign_x_to_s32(14, VRAM[(a + 0x20) & 0x3FFFF]);
147
148 rp.Mx = sign_x_to_s32(24, ne16_rbo_be<uint32>(VRAM, ((a + 0x22) & 0x3FFFF) << 1) >> 6);
149 rp.My = sign_x_to_s32(24, ne16_rbo_be<uint32>(VRAM, ((a + 0x24) & 0x3FFFF) << 1) >> 6);
150
151 rp.kx = sign_x_to_s32(24, ne16_rbo_be<uint32>(VRAM, ((a + 0x26) & 0x3FFFF) << 1));
152 rp.ky = sign_x_to_s32(24, ne16_rbo_be<uint32>(VRAM, ((a + 0x28) & 0x3FFFF) << 1));
153
154 rp.KAst = ne16_rbo_be<uint32>(VRAM, ((a + 0x2A) & 0x3FFFF) << 1) >> 6;
155 rp.DKAst = sign_x_to_s32(20, ne16_rbo_be<uint32>(VRAM, ((a + 0x2C) & 0x3FFFF) << 1) >> 6);
156 rp.DKAx = sign_x_to_s32(20, ne16_rbo_be<uint32>(VRAM, ((a + 0x2E) & 0x3FFFF) << 1) >> 6);
157
158 a += 0x40;
159 //
160 // Interlace mode doesn't seem to affect operation?
161 //
162 // const bool imft = (InterlaceMode == IM_DOUBLE && field);
163
164 if(RPRCTL[i] & 0x01)
165 rp.XstAccum = rp.Xst; // + rp.DXst * imft;
166 else
167 rp.XstAccum += rp.DXst; // << (InterlaceMode == IM_DOUBLE);
168
169 if(RPRCTL[i] & 0x02)
170 rp.YstAccum = rp.Yst; // + rp.DYst * imft;
171 else
172 rp.YstAccum += rp.DYst; // << (InterlaceMode == IM_DOUBLE);
173
174 if(RPRCTL[i] & 0x04)
175 rp.KAstAccum = (KTAOF[i] << 26) + rp.KAst; // + rp.DKAst * imft;
176 else
177 rp.KAstAccum += rp.DKAst; // << (InterlaceMode == IM_DOUBLE);
178 }
179 }
180
181 enum
182 {
183 VPHASE_ACTIVE = 0,
184
185 VPHASE_BOTTOM_BORDER,
186 VPHASE_BOTTOM_BLANKING,
187
188 VPHASE_VSYNC,
189
190 VPHASE_TOP_BLANKING,
191 VPHASE_TOP_BORDER,
192
193 VPHASE__COUNT
194 };
195
196 static const int32 VTimings[2][4][VPHASE__COUNT] = // End lines
197 {
198 { // NTSC:
199 { 0x0E0, 0xE8, 0xED, 0xF0, 0x0FF, 0x107 },
200 { 0x0F0, 0xF0, 0xF5, 0xF8, 0x107, 0x107 },
201 { 0x0E0, 0xE8, 0xED, 0xF0, 0x0FF, 0x107 },
202 { 0x0F0, 0xF0, 0xF5, 0xF8, 0x107, 0x107 },
203 },
204 { // PAL:
205 // btm brdr begin, btm blnk begin, vsync begin, /***/ top blnk begin, top brdr begin, total
206 { 0x0E0, 0x100, 0x103, /***/ 0x103 + 3/*?*/, 0x119, 0x139 },
207 { 0x0F0, 0x108, 0x10B, /***/ 0x10B + 3/*?*/, 0x121, 0x139 },
208 { 0x100, 0x110, 0x113, /***/ 0x113 + 3/*?*/, 0x129, 0x139 },
209 { 0x100, 0x110, 0x113, /***/ 0x113 + 3/*?*/, 0x129, 0x139 },
210 },
211 };
212
213 static bool Out_VB; // VB output signal
214
215 static uint32 VPhase;
216 /*static*/ int32 VCounter;
217 static bool InternalVB;
218 static bool Odd;
219
220 static uint32 CRTLineCounter;
221 static bool Clock28M;
222 //
223 static int SurfInterlaceField;
224 //
225 //
226 //
227
228
229 // (No 0) 8 accesses, No split: 0 added cycles
230 // (No 4) 4 accesses, No split: 1 added cycles
231 // (No 6) 2 accesses, No split: 2 added cycles
232 // (No 7) 1 accesses, No split: 3, Split: 3.51? added cycles
233 // (No 8) 0 accesses, No split: 4, Split: 5.34? added cycles
RecalcVRAMPenalty(void)234 static INLINE void RecalcVRAMPenalty(void)
235 {
236 if(InternalVB)
237 VRAMPenalty[0] = VRAMPenalty[1] = VRAMPenalty[2] = VRAMPenalty[3] = 0;
238 else
239 {
240 const unsigned VRAM_Mode = (RAMCTL_Raw >> 8) & 0x3;
241 const unsigned RDBS_Mode = (RAMCTL_Raw & 0xFF);
242 const size_t sh = ((HRes & 0x6) ? 0 : 4);
243 uint8 vcp_type_penalty[0x10];
244
245 for(unsigned vcp_type = 0; vcp_type < 0x10; vcp_type++)
246 {
247 bool penalty;
248
249 if((vcp_type < 0x8) || vcp_type == 0xC || vcp_type == 0xD)
250 penalty = (bool)(BGON & (1U << (vcp_type & 0x3)));
251 else
252 penalty = false;
253
254 vcp_type_penalty[vcp_type] = penalty;
255 }
256
257 for(unsigned bank = 0; bank < 4; bank++)
258 {
259 const unsigned esb = bank & (2 | ((VRAM_Mode >> (bank >> 1)) & 1));
260 const uint8 rdbs = (RDBS_Mode >> (esb << 1)) & 0x3;
261 uint32 tmp = 0;
262
263 if(BGON & 0x20)
264 {
265 if(bank >= 2 || ((BGON & 0x10) && rdbs != RDBS_UNUSED))
266 tmp = 8;
267 }
268 else
269 {
270 if((BGON & 0x10) && rdbs != RDBS_UNUSED)
271 tmp = 8;
272 else if(BGON & 0x0F)
273 {
274 tmp += vcp_type_penalty[VCPRegs[esb][0]];
275 tmp += vcp_type_penalty[VCPRegs[esb][1]];
276 tmp += vcp_type_penalty[VCPRegs[esb][2]];
277 tmp += vcp_type_penalty[VCPRegs[esb][3]];
278
279 tmp += vcp_type_penalty[VCPRegs[esb][sh + 0]];
280 tmp += vcp_type_penalty[VCPRegs[esb][sh + 1]];
281 tmp += vcp_type_penalty[VCPRegs[esb][sh + 2]];
282 tmp += vcp_type_penalty[VCPRegs[esb][sh + 3]];
283 }
284 }
285
286 static const uint8 tab[9] = { 0, 0, 0, 0, 1, 1, 2, 3, 4 };
287 VRAMPenalty[bank] = tab[tmp];
288 //printf("%d, %d\n", bank, tmp);
289 }
290 }
291 }
292
293 enum
294 {
295 HPHASE_ACTIVE = 0,
296
297 HPHASE_RIGHT_BORDER,
298 HPHASE_HSYNC,
299
300 // ... ? ? ?
301
302 HPHASE__COUNT
303 };
304
305 static const int32 HTimings[2][HPHASE__COUNT] =
306 {
307 { 0x140, 0x15B, 0x1AB },
308 { 0x160, 0x177, 0x1C7 },
309 };
310
311 static uint32 HPhase;
312 /*static*/ int32 HCounter;
313
314 static uint16 Latched_VCNT, Latched_HCNT;
315 static bool HVIsExLatched;
316 bool ExLatchIn;
317 bool ExLatchPending;
318
319
GetNLVCounter(void)320 static INLINE unsigned GetNLVCounter(void)
321 {
322 unsigned ret;
323
324 if(VPhase >= VPHASE_VSYNC)
325 ret = VCounter + (0x200 - VTimings[PAL][VRes][VPHASE__COUNT - 1]);
326 else
327 ret = VCounter;
328
329 if(InterlaceMode == IM_DOUBLE)
330 ret = (ret << 1) | !Odd;
331
332 return ret;
333 }
334
LatchHV(void)335 static void LatchHV(void)
336 {
337 Latched_VCNT = GetNLVCounter();
338
339 if(HPhase >= HPHASE_HSYNC)
340 Latched_HCNT = (HCounter + (0x200 - HTimings[HRes & 1][HPHASE__COUNT - 1])) << 1;
341 else
342 Latched_HCNT = HCounter << 1;
343 }
344
345 //
346 //
GetGunXTranslation(const bool clock28m,float * scale,float * offs)347 void GetGunXTranslation(const bool clock28m, float* scale, float* offs)
348 {
349 VDP2REND_GetGunXTranslation(clock28m, scale, offs);
350 }
351
StartFrame(EmulateSpecStruct * espec,const bool clock28m)352 void StartFrame(EmulateSpecStruct* espec, const bool clock28m)
353 {
354 Clock28M = clock28m;
355 //printf("StartFrame: %d\n", SurfInterlaceField);
356 VDP2REND_StartFrame(espec, clock28m, SurfInterlaceField);
357 CRTLineCounter = 0;
358 }
359
360 //
361 //
IncVCounter(const sscpu_timestamp_t event_timestamp)362 static INLINE void IncVCounter(const sscpu_timestamp_t event_timestamp)
363 {
364 const unsigned prev_nlvc = GetNLVCounter();
365 //
366 VCounter = (VCounter + 1) & 0x1FF;
367
368 if(VCounter == (VTimings[PAL][VRes][VPHASE__COUNT - 1] - 1))
369 {
370 Out_VB = false;
371 Window[0].YEndMet = Window[1].YEndMet = false;
372 }
373
374 #if 1
375 if(MDFN_UNLIKELY(ss_horrible_hacks & HORRIBLEHACK_NOSH2DMALINE106))
376 {
377 const bool s = (VCounter == (VTimings[PAL][VRes][VPHASE__COUNT - 1] - 1));
378
379 for(size_t i = 0; i < 2; i++)
380 CPU[i].SetExtHaltDMAKludgeFromVDP2(s);
381 }
382 #endif
383
384 // - 1, so the CPU loop will have plenty of time to exit before we reach non-hblank top border area
385 // (exit granularity could be large if program is executing from SCSP RAM space, for example).
386 if(VCounter == (VTimings[PAL][VRes][VPHASE_TOP_BLANKING] - 1))
387 {
388 #if 0
389 for(unsigned bank = 0; bank < 4; bank++)
390 {
391 printf("Bank %d: ", bank);
392 for(unsigned vc = 0; vc < 8; vc++)
393 printf("%01x ", VCPRegs[bank][vc]);
394 printf("\n");
395 }
396 #endif
397
398 SS_RequestMLExit();
399 VDP2REND_EndFrame();
400 //printf("Meow: %d\n", VCounter);
401 }
402
403 while(VCounter >= VTimings[PAL][VRes][VPhase] - ((VPhase == VPHASE_VSYNC - 1) && InterlaceMode))
404 {
405 VPhase++;
406
407 if(VPhase == VPHASE__COUNT)
408 {
409 VPhase = 0;
410 VCounter -= VTimings[PAL][VRes][VPHASE__COUNT - 1];
411 }
412
413 if(VPhase == VPHASE_ACTIVE)
414 InternalVB = !DisplayOn;
415 else if(VPhase == VPHASE_BOTTOM_BORDER)
416 {
417 SS_SetEventNT(&events[SS_EVENT_MIDSYNC], event_timestamp + 1);
418 InternalVB = true;
419 Out_VB = true;
420 }
421 else if(VPhase == VPHASE_BOTTOM_BLANKING)
422 {
423 CRTLineCounter = 0x80000000U;
424 }
425 else if(VPhase == VPHASE_VSYNC)
426 {
427 if(InterlaceMode)
428 {
429 Odd = !Odd;
430 VCounter += Odd;
431 SurfInterlaceField = !Odd;
432 }
433 else
434 {
435 SurfInterlaceField = -1;
436 Odd = true;
437 }
438 }
439 }
440
441 //
442 //
443 {
444 const unsigned nlvc = GetNLVCounter();
445 const unsigned mask = (InterlaceMode == IM_DOUBLE) ? 0x1FE : 0x1FF;
446
447 for(unsigned d = 0; d < 2; d++)
448 {
449 if((nlvc & mask) == (Window[d].YStart & mask))
450 {
451 //printf("Window%d YStartMet at VC=0x%03x ---- %03x %03x\n", d, nlvc, Window[d].YStart, Window[d].YEnd);
452 Window[d].YIn = true;
453 }
454
455 if((prev_nlvc & mask) == (Window[d].YEnd & mask))
456 {
457 //printf("Window%d YEndMet at VC=0x%03x ---- %03x %03x\n", d, nlvc, Window[d].YStart, Window[d].YEnd);
458 Window[d].YEndMet = true;
459 }
460
461 Window[d].YIn &= !Window[d].YEndMet;
462 }
463 }
464 //
465 //
466
467 RecalcVRAMPenalty();
468
469 SMPC_SetVBVS(event_timestamp, Out_VB, VPhase == VPHASE_VSYNC);
470 }
471
AddHCounter(const sscpu_timestamp_t event_timestamp,int32 count)472 static INLINE int32 AddHCounter(const sscpu_timestamp_t event_timestamp, int32 count)
473 {
474 HCounter += count;
475
476 //if(HCounter > HTimings[HRes & 1][HPhase])
477 // printf("VDP2 oops: %d %d\n", HCounter, HTimings[HRes & 1][HPhase]);
478
479 while(HCounter >= HTimings[HRes & 1][HPhase])
480 {
481 HPhase++;
482
483 if(HPhase == HPHASE__COUNT)
484 {
485 HPhase = 0;
486 HCounter -= HTimings[HRes & 1][HPHASE__COUNT - 1];
487 }
488 //
489 //
490 //
491 if(HPhase == HPHASE_ACTIVE)
492 {
493 {
494 const int32 div = Clock28M ? 61 : 65;
495 const int32 coord_adj = 6832 - 80 * div;
496
497 SMPC_LineHook(event_timestamp, CRTLineCounter, div, coord_adj);
498 }
499
500 if(VPhase == VPHASE_ACTIVE)
501 {
502 VDP2Rend_LIB* lib = VDP2REND_GetLIB(VCounter);
503
504 lib->win_ymet[0] = Window[0].YIn;
505 lib->win_ymet[1] = Window[1].YIn;
506
507 if(!InternalVB)
508 {
509 if(BGON & 0x30)
510 {
511 if(VCounter == 0)
512 RPRCTL[0] = RPRCTL[1] = 0x07;
513 FetchRotParams(false/*field*/);
514 RPRCTL[0] = RPRCTL[1] = 0;
515 }
516
517 for(unsigned i = 0; i < 2; i++)
518 {
519 auto const& rp = RotParams[i];
520 auto& r = lib->rv[i];
521
522 r.Xsp = ((int64)rp.RotMatrix[0] * ((int32)rp.XstAccum - (rp.Px * 1024)) +
523 (int64)rp.RotMatrix[1] * ((int32)rp.YstAccum - (rp.Py * 1024)) +
524 (int64)rp.RotMatrix[2] * (rp.Zst - (rp.Pz * 1024))) >> 10;
525 r.Ysp = ((int64)rp.RotMatrix[3] * ((int32)rp.XstAccum - (rp.Px * 1024)) +
526 (int64)rp.RotMatrix[4] * ((int32)rp.YstAccum - (rp.Py * 1024)) +
527 (int64)rp.RotMatrix[5] * (rp.Zst - (rp.Pz * 1024))) >> 10;
528
529 r.Xp = rp.RotMatrix[0] * (rp.Px - rp.Cx) +
530 rp.RotMatrix[1] * (rp.Py - rp.Cy) +
531 rp.RotMatrix[2] * (rp.Pz - rp.Cz) +
532 (rp.Cx * 1024) + rp.Mx;
533
534 r.Yp = rp.RotMatrix[3] * (rp.Px - rp.Cx) +
535 rp.RotMatrix[4] * (rp.Py - rp.Cy) +
536 rp.RotMatrix[5] * (rp.Pz - rp.Cz) +
537 (rp.Cy * 1024) + rp.My;
538
539 r.dX = (rp.RotMatrix[0] * rp.DX + rp.RotMatrix[1] * rp.DY) >> 10;
540 r.dY = (rp.RotMatrix[3] * rp.DX + rp.RotMatrix[4] * rp.DY) >> 10;
541
542 r.kx = rp.kx;
543 r.ky = rp.ky;
544
545 r.KAstAccum = rp.KAstAccum;
546 r.DKAx = rp.DKAx;
547 }
548 }
549 //printf("%d, 0x%08x(%f) 0x%08x(%f)\n", VCounter, RotParams[0].KAstAccum >> 10, (int32)RotParams[0].DKAst / 1024.0, RotParams[1].KAstAccum >> 10, (int32)RotParams[1].DKAst / 1024.0);
550 //printf("DL: %d\n", VCounter);
551 lib->vdp1_hires8 = VDP1::GetLine(VCounter, lib->vdp1_line, (HRes & 1) ? 352 : 320, (int32)RotParams[0].XstAccum >> 1, (int32)RotParams[0].YstAccum >> 1, (int32)RotParams[0].DX >> 1, (int32)RotParams[0].DY >> 1); // Always call, has side effects.
552 VDP2REND_DrawLine(InternalVB ? -1 : VCounter, CRTLineCounter, !Odd);
553 CRTLineCounter++;
554 }
555 else if(VPhase == VPHASE_TOP_BORDER || VPhase == VPHASE_BOTTOM_BORDER)
556 {
557 VDP2REND_DrawLine(-1, CRTLineCounter, !Odd);
558 CRTLineCounter++;
559 }
560 }
561 else if(HPhase == HPHASE_HSYNC)
562 {
563 IncVCounter(event_timestamp);
564 }
565 }
566
567 return (HTimings[HRes & 1][HPhase] - HCounter);
568 }
569
Update(sscpu_timestamp_t timestamp)570 sscpu_timestamp_t Update(sscpu_timestamp_t timestamp)
571 {
572 int32 clocks = (timestamp - lastts) >> 2;
573
574 if(MDFN_UNLIKELY(timestamp < lastts))
575 {
576 SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] [BUG] timestamp(%d) < lastts(%d)", timestamp, lastts);
577 clocks = 0;
578 }
579
580 lastts += clocks << 2;
581 //
582 //
583 int32 ne;
584 int32 tmp;
585
586 ne = AddHCounter(timestamp, clocks);
587 VDP1::SetHBVB(timestamp, HPhase > HPHASE_ACTIVE, Out_VB);
588 tmp = SCU_SetHBVB(clocks, HPhase > HPHASE_ACTIVE, Out_VB);
589 if(tmp < ne)
590 ne = tmp;
591
592 //
593 //
594 //
595 if(MDFN_UNLIKELY(ExLatchPending))
596 {
597 LatchHV();
598 HVIsExLatched = true;
599 ExLatchPending = false;
600 //printf("ExLatch: %04x %04x\n", Latched_VCNT, Latched_HCNT);
601 }
602
603 return lastts + (ne << 2);
604 }
605
606 //
607 // Register writes seem to always be 16-bit
608 //
RegsWrite(uint32 A,uint16 V)609 static INLINE void RegsWrite(uint32 A, uint16 V)
610 {
611 A &= 0x1FE;
612
613 RawRegs[A >> 1] = V;
614
615 SS_DBGTI(SS_DBG_VDP2_REGW, "[VDP2] Register write 0x%03x: 0x%04x", A, V);
616
617 switch(A)
618 {
619 default:
620 // SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] Unknown write to register at 0x%08x of value 0x%04x", A, V);
621 break;
622
623 case 0x00:
624 Update(SH7095_mem_timestamp);
625 //
626 DisplayOn = (V >> 15) & 0x1;
627 BorderMode = (V >> 8) & 0x1;
628 InterlaceMode = (V >> 6) & 0x3;
629 VRes = (V >> 4) & 0x3;
630 HRes = (V >> 0) & 0x7;
631 //
632 InternalVB |= !DisplayOn;
633 //
634 SS_SetEventNT(&events[SS_EVENT_VDP2], Update(SH7095_mem_timestamp));
635 break;
636
637 case 0x02:
638 ExLatchEnable = (V >> 9) & 0x1;
639 ExSyncEnable = (V >> 8) & 0x1;
640
641 DispAreaSelect = (V >> 1) & 0x1;
642 ExBGEnable = (V >> 0) & 0x1;
643 break;
644
645 case 0x06:
646 VRAMSize = (V >> 15) & 0x1;
647
648 if(VRAMSize)
649 SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] VRAMSize=%d (unemulated)", VRAMSize);
650 break;
651
652 case 0x0E:
653 RAMCTL_Raw = V & 0xB3FF;
654 CRAM_Mode = (V >> 12) & 0x3;
655 break;
656
657 case 0x10:
658 case 0x12:
659 case 0x14:
660 case 0x16:
661 case 0x18:
662 case 0x1A:
663 case 0x1C:
664 case 0x1E:
665 {
666 uint8* const b = &VCPRegs[(A >> 2) & 3][(A & 0x2) << 1];
667 b[0] = (V >> 12) & 0xF;
668 b[1] = (V >> 8) & 0xF;
669 b[2] = (V >> 4) & 0xF;
670 b[3] = (V >> 0) & 0xF;
671 }
672 break;
673
674 case 0x20:
675 BGON = V & 0x1F3F;
676 break;
677
678 case 0xB2:
679 RPRCTL[0] = (V >> 0) & 0x7;
680 RPRCTL[1] = (V >> 8) & 0x7;
681 break;
682
683 case 0xB6:
684 KTAOF[0] = (V >> 0) & 0x7;
685 KTAOF[1] = (V >> 8) & 0x7;
686 break;
687
688 case 0xBC:
689 RPTA = (RPTA & 0xFFFF) | ((V & 0x7) << 16);
690 break;
691
692 case 0xBE:
693 RPTA = (RPTA & ~0xFFFF) | (V & 0xFFFE);
694 break;
695
696 case 0xC2: Window[0].YStart = V & 0x1FF; break;
697 case 0xC6: Window[0].YEnd = V & 0x1FF; break;
698
699 case 0xCA: Window[1].YStart = V & 0x1FF; break;
700 case 0xCE: Window[1].YEnd = V & 0x1FF; break;
701 }
702 }
703
RegsRead(uint32 A)704 static INLINE uint16 RegsRead(uint32 A)
705 {
706 switch(A & 0x1FE)
707 {
708 default:
709 SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] Unknown read from 0x%08x", A);
710 return 0;
711
712 case 0x00:
713 return (DisplayOn << 15) | (BorderMode << 8) | (InterlaceMode << 6) | (VRes << 4) | (HRes << 0);
714
715 case 0x02:
716 if(!ExLatchEnable)
717 {
718 SS_SetEventNT(&events[SS_EVENT_VDP2], Update(SH7095_mem_timestamp));
719 LatchHV();
720 }
721 return (ExLatchEnable << 9) | (ExSyncEnable << 8) | (DispAreaSelect << 1) | (ExBGEnable << 0);
722
723 case 0x04:
724 SS_SetEventNT(&events[SS_EVENT_VDP2], Update(SH7095_mem_timestamp));
725 {
726 // TODO: EXSYFG
727 uint16 ret = (HVIsExLatched << 9) | (InternalVB << 3) | ((HPhase > HPHASE_ACTIVE) << 2) | (Odd << 1) | (PAL << 0);
728
729 HVIsExLatched = false;
730
731 return ret;
732 }
733
734 case 0x06:
735 return VRAMSize << 15;
736
737 case 0x08:
738 return Latched_HCNT;
739
740 case 0x0A:
741 return Latched_VCNT;
742
743 case 0x0E:
744 return RAMCTL_Raw;
745 }
746 }
747
748 template<typename T, bool IsWrite>
RW(uint32 A,uint16 * DB)749 static INLINE uint32 RW(uint32 A, uint16* DB)
750 {
751 static_assert(IsWrite || sizeof(T) == 2, "Wrong type for read.");
752
753 A &= 0x1FFFFF;
754
755 //
756 // VRAM
757 //
758 if(A < 0x100000)
759 {
760 const size_t vri = (A & 0x7FFFF) >> 1;
761
762 if(IsWrite)
763 {
764 const unsigned mask = (sizeof(T) == 2) ? 0xFFFF : (0xFF00 >> ((A & 1) << 3));
765
766 VRAM[vri] = (VRAM[vri] &~ mask) | (*DB & mask);
767 }
768 else
769 *DB = VRAM[vri];
770
771 return VRAMPenalty[vri >> 16];
772 }
773
774 //
775 // CRAM
776 //
777 if(A < 0x180000)
778 {
779 const unsigned cri = (A & 0xFFF) >> 1;
780
781 if(IsWrite)
782 {
783 switch(CRAM_Mode)
784 {
785 case CRAM_MODE_RGB555_1024:
786 (CRAM + 0x000)[cri & 0x3FF] = *DB;
787 (CRAM + 0x400)[cri & 0x3FF] = *DB;
788 break;
789
790 case CRAM_MODE_RGB555_2048:
791 CRAM[cri] = *DB;
792 break;
793
794 case CRAM_MODE_RGB888_1024:
795 case CRAM_MODE_ILLEGAL:
796 default:
797 CRAM[((cri >> 1) & 0x3FF) | ((cri & 1) << 10)] = *DB;
798 break;
799 }
800 }
801 else
802 {
803 switch(CRAM_Mode)
804 {
805 case CRAM_MODE_RGB555_1024:
806 case CRAM_MODE_RGB555_2048:
807 *DB = CRAM[cri];
808 break;
809
810 case CRAM_MODE_RGB888_1024:
811 case CRAM_MODE_ILLEGAL:
812 default:
813 *DB = CRAM[((cri >> 1) & 0x3FF) | ((cri & 1) << 10)];
814 break;
815 }
816 }
817
818 return 0;
819 }
820
821 //
822 // Registers
823 //
824 if(A < 0x1C0000)
825 {
826 if(IsWrite)
827 {
828 if(sizeof(T) == 1)
829 SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] Byte-write to register at 0x%08x(DB=0x%04x)", A, *DB);
830
831 RegsWrite(A, *DB);
832 }
833 else
834 *DB = RegsRead(A);
835
836 return 0;
837 }
838
839 if(IsWrite)
840 {
841 //SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] Unknown %zu-byte write to 0x%08x(DB=0x%04x)", sizeof(T), A, *DB);
842 }
843 else
844 {
845 //SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] Unknown %zu-byte read from 0x%08x", sizeof(T), A);
846 *DB = 0;
847 }
848
849 return 0;
850 }
851
Read16_DB(uint32 A)852 uint16 Read16_DB(uint32 A)
853 {
854 uint16 DB;
855
856 RW<uint16, false>(A, &DB);
857
858 return DB;
859 }
860
861
Write8_DB(uint32 A,uint16 DB)862 uint32 Write8_DB(uint32 A, uint16 DB)
863 {
864 VDP2REND_Write8_DB(A, DB);
865
866 return RW<uint8, true>(A, &DB);
867 }
868
Write16_DB(uint32 A,uint16 DB)869 uint32 Write16_DB(uint32 A, uint16 DB)
870 {
871 VDP2REND_Write16_DB(A, DB);
872
873 return RW<uint16, true>(A, &DB);
874 }
875
876
877 //
878 //
879 //
880
AdjustTS(const int32 delta)881 void AdjustTS(const int32 delta)
882 {
883 lastts += delta;
884 }
885
886
Init(const bool IsPAL,const uint64 affinity)887 void Init(const bool IsPAL, const uint64 affinity)
888 {
889 SurfInterlaceField = -1;
890 PAL = IsPAL;
891 lastts = 0;
892 CRTLineCounter = 0x80000000U;
893 Clock28M = false;
894
895 SS_SetPhysMemMap(0x05E00000, 0x05EFFFFF, VRAM, 0x80000, true);
896
897 ExLatchIn = false;
898
899 VDP2REND_Init(IsPAL, affinity);
900 }
901
SetGetVideoParams(MDFNGI * gi,const bool caspect,const int sls,const int sle,const bool show_h_overscan,const bool dohblend)902 void SetGetVideoParams(MDFNGI* gi, const bool caspect, const int sls, const int sle, const bool show_h_overscan, const bool dohblend)
903 {
904 if(PAL)
905 gi->fps = 65536 * 256 * (1734687500.0 / 61 / 4 / 455 / ((313 + 312.5) / 2.0));
906 else
907 gi->fps = 65536 * 256 * (1746818181.8 / 61 / 4 / 455 / ((263 + 262.5) / 2.0));
908
909 VDP2REND_SetGetVideoParams(gi, caspect, sls, sle, show_h_overscan, dohblend);
910 }
911
Kill(void)912 void Kill(void)
913 {
914 VDP2REND_Kill();
915 }
916
917 //
918 // TODO: Check reset versus power on values.
919 //
Reset(bool powering_up)920 void Reset(bool powering_up)
921 {
922 memset(RawRegs, 0, sizeof(RawRegs));
923
924 DisplayOn = false;
925 BorderMode = false;
926 ExLatchEnable = false;
927 ExSyncEnable = false;
928 ExBGEnable = false;
929 DispAreaSelect = false;
930 HRes = 0;
931 VRes = 0;
932 InterlaceMode = 0;
933
934 VRAMSize = 0;
935
936 InternalVB = true;
937 Out_VB = false;
938 VPhase = VPHASE_ACTIVE;
939 VCounter = 0;
940 Odd = true;
941
942 for(size_t i = 0; i < 2; i++)
943 CPU[i].SetExtHaltDMAKludgeFromVDP2(false);
944
945 RAMCTL_Raw = 0;
946 CRAM_Mode = 0;
947
948 BGON = 0;
949
950 memset(VCPRegs, 0, sizeof(VCPRegs));
951
952 for(unsigned i = 0; i < 2; i++)
953 {
954 KTAOF[i] = 0;
955 RPRCTL[i] = 0;
956 }
957 RPTA = 0;
958 memset(RotParams, 0, sizeof(RotParams));
959
960 for(unsigned w = 0; w < 2; w++)
961 {
962 Window[w].YStart = 0;
963 Window[w].YEnd = 0;
964 Window[w].YEndMet = false;
965 Window[w].YIn = false;
966 }
967 //
968 //
969 //
970 if(powering_up)
971 {
972 HPhase = 0;
973 HCounter = 0;
974
975 Latched_VCNT = 0;
976 Latched_HCNT = 0;
977 HVIsExLatched = false;
978 ExLatchPending = false;
979 }
980 //
981 // FIXME(init values), also in VDP2REND.
982 if(powering_up)
983 {
984 memset(VRAM, 0, sizeof(VRAM));
985 memset(CRAM, 0, sizeof(CRAM));
986 }
987 //
988 RecalcVRAMPenalty();
989 //
990 //
991 VDP2REND_Reset(powering_up);
992 }
993
994 //
995 //
996 //
997 //
GetRegister(const unsigned id,char * const special,const uint32 special_len)998 uint32 GetRegister(const unsigned id, char* const special, const uint32 special_len)
999 {
1000 uint32 ret = 0xDEADBEEF;
1001
1002 switch(id)
1003 {
1004 case GSREG_LINE:
1005 ret = VCounter;
1006 break;
1007
1008 case GSREG_DON:
1009 ret = DisplayOn;
1010 break;
1011
1012 case GSREG_BM:
1013 ret = BorderMode;
1014 break;
1015
1016 case GSREG_IM:
1017 ret = InterlaceMode;
1018 break;
1019
1020 case GSREG_VRES:
1021 ret = VRes;
1022 break;
1023
1024 case GSREG_HRES:
1025 ret = HRes;
1026 break;
1027
1028 case GSREG_RAMCTL:
1029 ret = RAMCTL_Raw;
1030 break;
1031
1032 case GSREG_CYCA0:
1033 case GSREG_CYCA1:
1034 case GSREG_CYCB0:
1035 case GSREG_CYCB1:
1036 {
1037 static const char* tab[0x10] =
1038 {
1039 "NBG0 PN", "NBG1 PN", "NBG2 PN", "NBG3 PN",
1040 "NBG0 CG", "NBG1 CG", "NBG2 CG", "NBG3 CG",
1041 "ILLEGAL", "ILLEGAL", "ILLEGAL", "ILLEGAL",
1042 "NBG0 VCS", "NBG1 VCS", "CPU", "NOP"
1043 };
1044 const size_t idx = (id - GSREG_CYCA0);
1045 ret = (RawRegs[(0x10 >> 1) + (idx << 1)] << 16) | RawRegs[(0x12 >> 1) + (idx << 1)];
1046
1047 if(special)
1048 trio_snprintf(special, special_len, "0: %s, 1: %s, 2: %s, 3: %s, 4: %s, 5: %s, 6: %s, 7: %s",
1049 tab[(ret >> 28) & 0xF], tab[(ret >> 24) & 0xF], tab[(ret >> 20) & 0xF], tab[(ret >> 16) & 0xF],
1050 tab[(ret >> 12) & 0xF], tab[(ret >> 8) & 0xF], tab[(ret >> 4) & 0xF], tab[(ret >> 0) & 0xF]);
1051 }
1052 break;
1053
1054 case GSREG_BGON:
1055 ret = BGON;
1056 break;
1057
1058 case GSREG_MZCTL:
1059 ret = RawRegs[0x22 >> 1] & 0xFF1F;
1060 break;
1061
1062 case GSREG_SFSEL:
1063 ret = RawRegs[0x24 >> 1] & 0x001F;
1064 break;
1065
1066 case GSREG_SFCODE:
1067 ret = RawRegs[0x26 >> 1];
1068 break;
1069
1070 case GSREG_CHCTLA:
1071 ret = RawRegs[0x28 >> 1] & 0x3F7F;
1072 break;
1073
1074 case GSREG_CHCTLB:
1075 ret = RawRegs[0x2A >> 1] & 0x7733;
1076 break;
1077 //
1078 //
1079 case GSREG_SCXIN0:
1080 ret = RawRegs[0x70 >> 1] & 0x07FF;
1081 break;
1082
1083 case GSREG_SCXDN0:
1084 ret = RawRegs[0x72 >> 1] & 0xFF00;
1085 break;
1086
1087 case GSREG_SCYIN0:
1088 ret = RawRegs[0x74 >> 1] & 0x07FF;
1089 break;
1090
1091 case GSREG_SCYDN0:
1092 ret = RawRegs[0x76 >> 1] & 0xFF00;
1093 break;
1094
1095 case GSREG_ZMXIN0:
1096 ret = RawRegs[0x78 >> 1] & 0x0007;
1097 break;
1098
1099 case GSREG_ZMXDN0:
1100 ret = RawRegs[0x7A >> 1] & 0xFF00;
1101 break;
1102
1103 case GSREG_ZMYIN0:
1104 ret = RawRegs[0x7C >> 1] & 0x0007;
1105 break;
1106
1107 case GSREG_ZMYDN0:
1108 ret = RawRegs[0x7E >> 1] & 0xFF00;
1109 break;
1110
1111 case GSREG_SCXIN1:
1112 ret = RawRegs[0x80 >> 1] & 0x07FF;
1113 break;
1114
1115 case GSREG_SCXDN1:
1116 ret = RawRegs[0x82 >> 1] & 0xFF00;
1117 break;
1118
1119 case GSREG_SCYIN1:
1120 ret = RawRegs[0x84 >> 1] & 0x07FF;
1121 break;
1122
1123 case GSREG_SCYDN1:
1124 ret = RawRegs[0x86 >> 1] & 0xFF00;
1125 break;
1126
1127 case GSREG_ZMXIN1:
1128 ret = RawRegs[0x88 >> 1] & 0x0007;
1129 break;
1130
1131 case GSREG_ZMXDN1:
1132 ret = RawRegs[0x8A >> 1] & 0xFF00;
1133 break;
1134
1135 case GSREG_ZMYIN1:
1136 ret = RawRegs[0x8C >> 1] & 0x0007;
1137 break;
1138
1139 case GSREG_ZMYDN1:
1140 ret = RawRegs[0x8E >> 1] & 0xFF00;
1141 break;
1142
1143 case GSREG_SCXN2:
1144 ret = RawRegs[0x90 >> 1] & 0x07FF;
1145 break;
1146
1147 case GSREG_SCYN2:
1148 ret = RawRegs[0x92 >> 1] & 0x07FF;
1149 break;
1150
1151 case GSREG_SCXN3:
1152 ret = RawRegs[0x94 >> 1] & 0x07FF;
1153 break;
1154
1155 case GSREG_SCYN3:
1156 ret = RawRegs[0x96 >> 1] & 0x07FF;
1157 break;
1158
1159 case GSREG_ZMCTL:
1160 ret = RawRegs[0x98 >> 1] & 0x0303;
1161 break;
1162
1163 case GSREG_SCRCTL:
1164 ret = RawRegs[0x9A >> 1] & 0x3F3F;
1165 break;
1166 }
1167
1168 return ret;
1169 }
1170
SetRegister(const unsigned id,const uint32 value)1171 void SetRegister(const unsigned id, const uint32 value)
1172 {
1173 int rr = -1;
1174
1175 switch(id)
1176 {
1177 case GSREG_BGON:
1178 BGON = value & 0x1F3F;
1179 rr = 0x20 >> 1;
1180 break;
1181
1182 case GSREG_MZCTL:
1183 rr = 0x22 >> 1;
1184 break;
1185
1186 case GSREG_SFSEL:
1187 rr = 0x24 >> 1;
1188 break;
1189
1190 case GSREG_SFCODE:
1191 rr = 0x26 >> 1;
1192 break;
1193
1194 case GSREG_CHCTLA:
1195 rr = 0x28 >> 1;
1196 break;
1197
1198 case GSREG_CHCTLB:
1199 rr = 0x2A >> 1;
1200 break;
1201 //
1202 //
1203 case GSREG_SCXIN0:
1204 rr = 0x70 >> 1;
1205 break;
1206
1207 case GSREG_SCXDN0:
1208 rr = 0x72 >> 1;
1209 break;
1210
1211 case GSREG_SCYIN0:
1212 rr = 0x74 >> 1;
1213 break;
1214
1215 case GSREG_SCYDN0:
1216 rr = 0x76 >> 1;
1217 break;
1218
1219 case GSREG_ZMXIN0:
1220 rr = 0x78 >> 1;
1221 break;
1222
1223 case GSREG_ZMXDN0:
1224 rr = 0x7A >> 1;
1225 break;
1226
1227 case GSREG_ZMYIN0:
1228 rr = 0x7C >> 1;
1229 break;
1230
1231 case GSREG_ZMYDN0:
1232 rr = 0x7E >> 1;
1233 break;
1234
1235 case GSREG_SCXIN1:
1236 rr = 0x80 >> 1;
1237 break;
1238
1239 case GSREG_SCXDN1:
1240 rr = 0x82 >> 1;
1241 break;
1242
1243 case GSREG_SCYIN1:
1244 rr = 0x84 >> 1;
1245 break;
1246
1247 case GSREG_SCYDN1:
1248 rr = 0x86 >> 1;
1249 break;
1250
1251 case GSREG_ZMXIN1:
1252 rr = 0x88 >> 1;
1253 break;
1254
1255 case GSREG_ZMXDN1:
1256 rr = 0x8A >> 1;
1257 break;
1258
1259 case GSREG_ZMYIN1:
1260 rr = 0x8C >> 1;
1261 break;
1262
1263 case GSREG_ZMYDN1:
1264 rr = 0x8E >> 1;
1265 break;
1266
1267 case GSREG_SCXN2:
1268 rr = 0x90 >> 1;
1269 break;
1270
1271 case GSREG_SCYN2:
1272 rr = 0x92 >> 1;
1273 break;
1274
1275 case GSREG_SCXN3:
1276 rr = 0x94 >> 1;
1277 break;
1278
1279 case GSREG_SCYN3:
1280 rr = 0x96 >> 1;
1281 break;
1282
1283 case GSREG_ZMCTL:
1284 rr = 0x98 >> 1;
1285 break;
1286
1287 case GSREG_SCRCTL:
1288 rr = 0x9A >> 1;
1289 break;
1290 }
1291
1292 if(rr >= 0)
1293 {
1294 RawRegs[rr] = (uint16)value;
1295 VDP2REND_Write16_DB(0x180000 + (rr << 1), RawRegs[rr]);
1296 }
1297 }
1298
PeekVRAM(uint32 addr)1299 uint8 PeekVRAM(uint32 addr)
1300 {
1301 return ne16_rbo_be<uint8>(VRAM, addr & 0x7FFFF);
1302 }
1303
PokeVRAM(uint32 addr,const uint8 val)1304 void PokeVRAM(uint32 addr, const uint8 val)
1305 {
1306 addr &= 0x7FFFF;
1307
1308 ne16_wbo_be<uint8>(VRAM, addr, val);
1309 VDP2REND_Write16_DB(addr & ~1, ne16_rbo_be<uint16>(VRAM, addr & ~1));
1310 }
1311
SetLayerEnableMask(uint64 mask)1312 void SetLayerEnableMask(uint64 mask)
1313 {
1314 VDP2REND_SetLayerEnableMask(mask);
1315 }
1316
StateAction(StateMem * sm,const unsigned load,const bool data_only)1317 void StateAction(StateMem* sm, const unsigned load, const bool data_only)
1318 {
1319 SFORMAT StateRegs[] =
1320 {
1321 SFVAR(lastts),
1322
1323 SFVAR(RawRegs),
1324 SFVAR(DisplayOn),
1325 SFVAR(BorderMode),
1326 SFVAR(ExLatchEnable),
1327 SFVAR(ExSyncEnable),
1328 SFVAR(ExBGEnable),
1329 SFVAR(DispAreaSelect),
1330
1331 SFVAR(VRAMSize),
1332
1333 SFVAR(HRes),
1334 SFVAR(VRes),
1335 SFVAR(InterlaceMode),
1336
1337 SFVAR(RAMCTL_Raw),
1338 SFVAR(CRAM_Mode),
1339
1340 SFVAR(BGON),
1341 SFVARN(VCPRegs, "&VCPRegs[0][0]"),
1342 SFVAR(VRAMPenalty),
1343
1344 SFVAR(RPTA),
1345 SFVAR(RPRCTL),
1346 SFVAR(KTAOF),
1347
1348 SFVAR(VRAM),
1349 SFVAR(CRAM),
1350
1351 SFVAR(RotParams->Xst, 2, sizeof(*RotParams), RotParams),
1352 SFVAR(RotParams->Yst, 2, sizeof(*RotParams), RotParams),
1353 SFVAR(RotParams->Zst, 2, sizeof(*RotParams), RotParams),
1354 SFVAR(RotParams->DXst, 2, sizeof(*RotParams), RotParams),
1355 SFVAR(RotParams->DYst, 2, sizeof(*RotParams), RotParams),
1356 SFVAR(RotParams->DX, 2, sizeof(*RotParams), RotParams),
1357 SFVAR(RotParams->DY, 2, sizeof(*RotParams), RotParams),
1358 SFVAR(RotParams->RotMatrix, 2, sizeof(*RotParams), RotParams),
1359 SFVAR(RotParams->Px, 2, sizeof(*RotParams), RotParams),
1360 SFVAR(RotParams->Py, 2, sizeof(*RotParams), RotParams),
1361 SFVAR(RotParams->Pz, 2, sizeof(*RotParams), RotParams),
1362 SFVAR(RotParams->Cx, 2, sizeof(*RotParams), RotParams),
1363 SFVAR(RotParams->Cy, 2, sizeof(*RotParams), RotParams),
1364 SFVAR(RotParams->Cz, 2, sizeof(*RotParams), RotParams),
1365 SFVAR(RotParams->Mx, 2, sizeof(*RotParams), RotParams),
1366 SFVAR(RotParams->My, 2, sizeof(*RotParams), RotParams),
1367 SFVAR(RotParams->kx, 2, sizeof(*RotParams), RotParams),
1368 SFVAR(RotParams->ky, 2, sizeof(*RotParams), RotParams),
1369 SFVAR(RotParams->KAst, 2, sizeof(*RotParams), RotParams),
1370 SFVAR(RotParams->DKAst, 2, sizeof(*RotParams), RotParams),
1371 SFVAR(RotParams->DKAx, 2, sizeof(*RotParams), RotParams),
1372 SFVAR(RotParams->XstAccum, 2, sizeof(*RotParams), RotParams),
1373 SFVAR(RotParams->YstAccum, 2, sizeof(*RotParams), RotParams),
1374 SFVAR(RotParams->KAstAccum, 2, sizeof(*RotParams), RotParams),
1375
1376 SFVAR(Out_VB),
1377
1378 SFVAR(VPhase),
1379 SFVAR(VCounter),
1380 SFVAR(InternalVB),
1381 SFVAR(Odd),
1382
1383 SFVAR(CRTLineCounter),
1384 SFVAR(Clock28M),
1385 //
1386 SFVAR(SurfInterlaceField),
1387
1388 SFVAR(HPhase),
1389 SFVAR(HCounter),
1390
1391 SFVAR(Latched_VCNT),
1392 SFVAR(Latched_HCNT),
1393 SFVAR(HVIsExLatched),
1394 SFVAR(ExLatchIn),
1395 SFVAR(ExLatchPending),
1396
1397 SFVAR(Window->YStart, 2, sizeof(*Window), Window),
1398 SFVAR(Window->YEnd, 2, sizeof(*Window), Window),
1399 SFVAR(Window->YEndMet, 2, sizeof(*Window), Window),
1400 SFVAR(Window->YIn, 2, sizeof(*Window), Window),
1401
1402 SFEND
1403 };
1404
1405 MDFNSS_StateAction(sm, load, data_only, StateRegs, "VDP2");
1406
1407 if(load)
1408 {
1409 if(load < 0x00102100)
1410 {
1411 Window[0].YStart = RawRegs[0xC2 >> 1] & 0x1FF;
1412 Window[0].YEnd = RawRegs[0xC6 >> 1] & 0x1FF;
1413
1414 Window[1].YStart = RawRegs[0xCA >> 1] & 0x1FF;
1415 Window[1].YEnd = RawRegs[0xCE >> 1] & 0x1FF;
1416
1417 //printf("%08x %03x:%03x, %03x:%03x\n", load, Window[0].YStart, Window[0].YEnd, Window[1].YStart, Window[1].YEnd);
1418
1419 for(unsigned d = 0; d < 2; d++)
1420 {
1421 Window[d].YEndMet = false;
1422 Window[d].YIn = false;
1423 }
1424 }
1425 }
1426
1427 VDP2REND_StateAction(sm, load, data_only, RawRegs, CRAM, VRAM);
1428 }
1429
MakeDump(const std::string & path)1430 void MakeDump(const std::string& path)
1431 {
1432 FileStream fp(path, FileStream::MODE_WRITE);
1433
1434 fp.print_format(" { ");
1435 for(unsigned i = 0; i < 0x100; i++)
1436 fp.print_format("0x%04x, ", RawRegs[i]);
1437 fp.print_format(" }, \n");
1438
1439 fp.print_format(" { ");
1440 for(unsigned i = 0; i < 2048; i++)
1441 fp.print_format("0x%04x, ", CRAM[i]);
1442 fp.print_format(" }, \n");
1443
1444 fp.print_format(" { ");
1445 for(unsigned i = 0; i < 0x40000; i++)
1446 fp.print_format("0x%04x, ", VRAM[i]);
1447 fp.print_format(" }, \n");
1448
1449 fp.close();
1450 }
1451
1452 }
1453
1454 }
1455