Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libgambatte/src/video/ppu.cpp
2 views
1
/***************************************************************************
2
* Copyright (C) 2010 by Sindre Aamås *
3
* [email protected] *
4
* *
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU General Public License version 2 as *
7
* published by the Free Software Foundation. *
8
* *
9
* This program is distributed in the hope that it will be useful, *
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12
* GNU General Public License version 2 for more details. *
13
* *
14
* You should have received a copy of the GNU General Public License *
15
* version 2 along with this program; if not, write to the *
16
* Free Software Foundation, Inc., *
17
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18
***************************************************************************/
19
#include "ppu.h"
20
#include "savestate.h"
21
#include <algorithm>
22
#include <cstring>
23
#include <cstddef>
24
25
namespace {
26
27
using namespace gambatte;
28
29
#define PREP(u8) (((u8) << 7 & 0x80) | ((u8) << 5 & 0x40) | ((u8) << 3 & 0x20) | ((u8) << 1 & 0x10) | \
30
((u8) >> 1 & 0x08) | ((u8) >> 3 & 0x04) | ((u8) >> 5 & 0x02) | ((u8) >> 7 & 0x01))
31
32
#define EXPAND(u8) ((PREP(u8) << 7 & 0x4000) | (PREP(u8) << 6 & 0x1000) | (PREP(u8) << 5 & 0x0400) | (PREP(u8) << 4 & 0x0100) | \
33
(PREP(u8) << 3 & 0x0040) | (PREP(u8) << 2 & 0x0010) | (PREP(u8) << 1 & 0x0004) | (PREP(u8) & 0x0001))
34
35
#define EXPAND_ROW(n) EXPAND((n)|0x0), EXPAND((n)|0x1), EXPAND((n)|0x2), EXPAND((n)|0x3), \
36
EXPAND((n)|0x4), EXPAND((n)|0x5), EXPAND((n)|0x6), EXPAND((n)|0x7), \
37
EXPAND((n)|0x8), EXPAND((n)|0x9), EXPAND((n)|0xA), EXPAND((n)|0xB), \
38
EXPAND((n)|0xC), EXPAND((n)|0xD), EXPAND((n)|0xE), EXPAND((n)|0xF)
39
40
#define EXPAND_TABLE EXPAND_ROW(0x00), EXPAND_ROW(0x10), EXPAND_ROW(0x20), EXPAND_ROW(0x30), \
41
EXPAND_ROW(0x40), EXPAND_ROW(0x50), EXPAND_ROW(0x60), EXPAND_ROW(0x70), \
42
EXPAND_ROW(0x80), EXPAND_ROW(0x90), EXPAND_ROW(0xA0), EXPAND_ROW(0xB0), \
43
EXPAND_ROW(0xC0), EXPAND_ROW(0xD0), EXPAND_ROW(0xE0), EXPAND_ROW(0xF0)
44
45
static const unsigned short expand_lut[0x200] = {
46
EXPAND_TABLE,
47
48
#undef PREP
49
#define PREP(u8) (u8)
50
51
EXPAND_TABLE
52
};
53
54
#undef EXPAND_TABLE
55
#undef EXPAND_ROW
56
#undef EXPAND
57
#undef PREP
58
59
#define DECLARE_FUNC(n, id) \
60
enum { ID##n = id }; \
61
static void f##n (PPUPriv &); \
62
static unsigned predictCyclesUntilXpos_f##n (const PPUPriv &, int targetxpos, unsigned cycles); \
63
static const PPUState f##n##_ = { f##n, predictCyclesUntilXpos_f##n, ID##n }
64
65
namespace M2 {
66
namespace Ly0 { DECLARE_FUNC(0, 0); }
67
namespace LyNon0 { DECLARE_FUNC(0, 0); DECLARE_FUNC(1, 0); }
68
}
69
70
namespace M3Start { DECLARE_FUNC(0, 0); DECLARE_FUNC(1, 0); }
71
72
namespace M3Loop {
73
namespace Tile {
74
DECLARE_FUNC(0, 0x80);
75
DECLARE_FUNC(1, 0x81);
76
DECLARE_FUNC(2, 0x82);
77
DECLARE_FUNC(3, 0x83);
78
DECLARE_FUNC(4, 0x84);
79
DECLARE_FUNC(5, 0x85);
80
}
81
82
namespace LoadSprites {
83
DECLARE_FUNC(0, 0x88);
84
DECLARE_FUNC(1, 0x89);
85
DECLARE_FUNC(2, 0x8A);
86
DECLARE_FUNC(3, 0x8B);
87
DECLARE_FUNC(4, 0x8C);
88
DECLARE_FUNC(5, 0x8D);
89
}
90
91
namespace StartWindowDraw {
92
DECLARE_FUNC(0, 0x90);
93
DECLARE_FUNC(1, 0x91);
94
DECLARE_FUNC(2, 0x92);
95
DECLARE_FUNC(3, 0x93);
96
DECLARE_FUNC(4, 0x94);
97
DECLARE_FUNC(5, 0x95);
98
}
99
}
100
101
#undef DECLARE_FUNC
102
103
enum { WIN_DRAW_START = 1, WIN_DRAW_STARTED = 2 };
104
105
enum { M2_DS_OFFSET = 3 };
106
enum { MAX_M3START_CYCLES = 80 };
107
108
static inline unsigned weMasterCheckPriorToLyIncLineCycle(const bool cgb) { return 450 - cgb; }
109
static inline unsigned weMasterCheckAfterLyIncLineCycle(const bool cgb) { return 454 - cgb; }
110
static inline unsigned m3StartLineCycle(const bool /*cgb*/) { return 83; }
111
112
static inline void nextCall(const int cycles, const PPUState &state, PPUPriv &p) {
113
const int c = p.cycles - cycles;
114
115
if (c >= 0) {
116
p.cycles = c;
117
return state.f(p);
118
}
119
120
p.cycles = c;
121
p.nextCallPtr = &state;
122
}
123
124
namespace M2 {
125
namespace Ly0 {
126
static void f0(PPUPriv &p) {
127
p.weMaster = (p.lcdc & 0x20) && 0 == p.wy;
128
p.winYPos = 0xFF;
129
nextCall(m3StartLineCycle(p.cgb), M3Start::f0_, p);
130
}
131
}
132
133
namespace LyNon0 {
134
static void f0(PPUPriv &p) {
135
p.weMaster |= (p.lcdc & 0x20) && p.lyCounter.ly() == p.wy;
136
nextCall(weMasterCheckAfterLyIncLineCycle(p.cgb) - weMasterCheckPriorToLyIncLineCycle(p.cgb), f1_, p);
137
}
138
139
static void f1(PPUPriv &p) {
140
p.weMaster |= (p.lcdc & 0x20) && p.lyCounter.ly() + 1 == p.wy;
141
nextCall(456 - weMasterCheckAfterLyIncLineCycle(p.cgb) + m3StartLineCycle(p.cgb), M3Start::f0_, p);
142
}
143
}
144
145
/*struct SpriteLess {
146
bool operator()(const Sprite lhs, const Sprite rhs) const {
147
return lhs.spx < rhs.spx;
148
}
149
};
150
151
static void f0(PPUPriv &p) {
152
std::memset(&p.spLut, 0, sizeof(p.spLut));
153
p.reg0 = 0;
154
p.nextSprite = 0;
155
p.nextCallPtr = &f1_;
156
f1(p);
157
}
158
159
static void f1(PPUPriv &p) {
160
int cycles = p.cycles;
161
unsigned oampos = p.reg0;
162
unsigned nextSprite = p.nextSprite;
163
const unsigned nly = (p.lyCounter.ly() + 1 == 154 ? 0 : p.lyCounter.ly() + 1) + ((p.lyCounter.time()-(p.now-p.cycles)) <= 4);
164
const bool ls = p.spriteMapper.largeSpritesSource();
165
166
do {
167
const unsigned spy = p.spriteMapper.oamram()[oampos ];
168
const unsigned spx = p.spriteMapper.oamram()[oampos+1];
169
const unsigned ydiff = spy - nly;
170
171
if (ls ? ydiff < 16u : ydiff - 8u < 8u) {
172
p.spriteList[nextSprite].spx = spx;
173
p.spriteList[nextSprite].line = 15u - ydiff;
174
p.spriteList[nextSprite].oampos = oampos;
175
176
if (++nextSprite == 10) {
177
cycles -= (0xA0 - 4 - oampos) >> 1;
178
oampos = 0xA0 - 4;
179
}
180
}
181
182
oampos += 4;
183
} while ((cycles-=2) >= 0 && oampos != 0xA0);
184
185
p.reg0 = oampos;
186
p.nextSprite = nextSprite;
187
p.cycles = cycles;
188
189
if (oampos == 0xA0) {
190
insertionSort(p.spriteList, p.spriteList + nextSprite, SpriteLess());
191
p.spriteList[nextSprite].spx = 0xFF;
192
p.nextSprite = 0;
193
nextCall(0, M3Start::f0_, p);
194
}
195
}*/
196
}
197
198
namespace M3Start {
199
static void f0(PPUPriv &p) {
200
p.xpos = 0;
201
202
if (p.winDrawState & p.lcdc >> 5 & WIN_DRAW_START) {
203
p.winDrawState = WIN_DRAW_STARTED;
204
p.wscx = 8 + (p.scx & 7);
205
++p.winYPos;
206
} else
207
p.winDrawState = 0;
208
209
p.nextCallPtr = &f1_;
210
f1(p);
211
}
212
213
static void f1(PPUPriv &p) {
214
while (p.xpos < MAX_M3START_CYCLES) {
215
if ((p.xpos & 7) == (p.scx & 7))
216
break;
217
218
switch (p.xpos & 7) {
219
case 0:
220
if ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) {
221
p.reg1 = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + (p.wscx >> 3 & 0x1F) + 0x1800];
222
p.nattrib = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + (p.wscx >> 3 & 0x1F) + 0x3800];
223
} else {
224
p.reg1 = p.vram[((p.lcdc << 7 | p.scx >> 3) & 0x41F) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800];
225
p.nattrib = p.vram[((p.lcdc << 7 | p.scx >> 3) & 0x41F) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x3800];
226
}
227
228
break;
229
case 2:
230
{
231
const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly();
232
233
p.reg0 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000)
234
+ p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2];
235
}
236
237
break;
238
case 4:
239
{
240
const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly();
241
const unsigned r1 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000)
242
+ p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2 + 1];
243
244
p.ntileword = (expand_lut + (p.nattrib << 3 & 0x100))[p.reg0] +
245
(expand_lut + (p.nattrib << 3 & 0x100))[r1 ] * 2;
246
}
247
248
break;
249
}
250
251
++p.xpos;
252
253
if (--p.cycles < 0)
254
return;
255
}
256
257
{
258
const unsigned ly = p.lyCounter.ly();
259
const unsigned numSprites = p.spriteMapper.numSprites(ly);
260
const unsigned char *const sprites = p.spriteMapper.sprites(ly);
261
262
for (unsigned i = 0; i < numSprites; ++i) {
263
const unsigned pos = sprites[i];
264
const unsigned spy = p.spriteMapper.posbuf()[pos ];
265
const unsigned spx = p.spriteMapper.posbuf()[pos+1];
266
267
p.spriteList[i].spx = spx;
268
p.spriteList[i].line = ly + 16u - spy;
269
p.spriteList[i].oampos = pos * 2;
270
p.spwordList[i] = 0;
271
}
272
273
p.spriteList[numSprites].spx = 0xFF;
274
p.nextSprite = 0;
275
}
276
277
p.xpos = 0;
278
p.endx = 8 - (p.scx & 7);
279
280
static const PPUState *const flut[8] = {
281
&M3Loop::Tile::f0_,
282
&M3Loop::Tile::f1_,
283
&M3Loop::Tile::f2_,
284
&M3Loop::Tile::f3_,
285
&M3Loop::Tile::f4_,
286
&M3Loop::Tile::f5_,
287
&M3Loop::Tile::f5_,
288
&M3Loop::Tile::f5_
289
};
290
291
nextCall(1-p.cgb, *flut[p.scx & 7], p);
292
}
293
}
294
295
namespace M3Loop {
296
static void doFullTilesUnrolledDmg(PPUPriv &p, const int xend, uint_least32_t *const dbufline,
297
const unsigned char *const tileMapLine, const unsigned tileline, unsigned tileMapXpos) {
298
const unsigned tileIndexSign = ~p.lcdc << 3 & 0x80;
299
const unsigned char *const tileDataLine = p.vram + tileIndexSign * 32 + tileline * 2;
300
int xpos = p.xpos;
301
302
do {
303
int nextSprite = p.nextSprite;
304
305
if (static_cast<int>(p.spriteList[nextSprite].spx) < xpos + 8) {
306
int cycles = p.cycles - 8;
307
308
if (p.lcdc & 2) {
309
cycles -= std::max(11 - (static_cast<int>(p.spriteList[nextSprite].spx) - xpos), 6);
310
311
for (unsigned i = nextSprite + 1; static_cast<int>(p.spriteList[i].spx) < xpos + 8; ++i)
312
cycles -= 6;
313
314
if (cycles < 0)
315
break;
316
317
p.cycles = cycles;
318
319
do {
320
unsigned reg0, reg1 = p.spriteMapper.oamram()[p.spriteList[nextSprite].oampos + 2] * 16;
321
const unsigned attrib = p.spriteMapper.oamram()[p.spriteList[nextSprite].oampos + 3];
322
323
{
324
const unsigned spline = (attrib & 0x40 ?
325
p.spriteList[nextSprite].line ^ 15 : p.spriteList[nextSprite].line) * 2;
326
reg0 = p.vram[(p.lcdc & 4 ? (reg1 & ~16) | spline : reg1 | (spline & ~16)) ];
327
reg1 = p.vram[(p.lcdc & 4 ? (reg1 & ~16) | spline : reg1 | (spline & ~16)) + 1];
328
}
329
330
p.spwordList[nextSprite] = expand_lut[reg0 + (attrib << 3 & 0x100)]
331
+ expand_lut[reg1 + (attrib << 3 & 0x100)] * 2;
332
p.spriteList[nextSprite].attrib = attrib;
333
++nextSprite;
334
} while (static_cast<int>(p.spriteList[nextSprite].spx) < xpos + 8);
335
} else {
336
if (cycles < 0)
337
break;
338
339
p.cycles = cycles;
340
341
do {
342
++nextSprite;
343
} while (static_cast<int>(p.spriteList[nextSprite].spx) < xpos + 8);
344
}
345
346
p.nextSprite = nextSprite;
347
} else if (nextSprite-1 < 0 || static_cast<int>(p.spriteList[nextSprite-1].spx) <= xpos - 8) {
348
if (!(p.cycles & ~7))
349
break;
350
351
int n = (( xend + 7 < static_cast<int>(p.spriteList[nextSprite].spx)
352
? xend + 7 : static_cast<int>(p.spriteList[nextSprite].spx)) - xpos) & ~7;
353
n = (p.cycles & ~7) < n ? p.cycles & ~7 : n;
354
p.cycles -= n;
355
356
unsigned ntileword = p.ntileword;
357
uint_least32_t * dst = dbufline + xpos - 8;
358
uint_least32_t *const dstend = dst + n;
359
xpos += n;
360
361
if (!(p.lcdc & 1)) {
362
do { *dst++ = p.bgPalette[0]; } while (dst != dstend);
363
tileMapXpos += n >> 3;
364
365
unsigned const tno = tileMapLine[(tileMapXpos - 1) & 0x1F];
366
ntileword = expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[0]]
367
+ expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[1]] * 2;
368
} else do {
369
dst[0] = p.bgPalette[ ntileword & 0x0003 ];
370
dst[1] = p.bgPalette[(ntileword & 0x000C) >> 2];
371
dst[2] = p.bgPalette[(ntileword & 0x0030) >> 4];
372
dst[3] = p.bgPalette[(ntileword & 0x00C0) >> 6];
373
dst[4] = p.bgPalette[(ntileword & 0x0300) >> 8];
374
dst[5] = p.bgPalette[(ntileword & 0x0C00) >> 10];
375
dst[6] = p.bgPalette[(ntileword & 0x3000) >> 12];
376
dst[7] = p.bgPalette[ ntileword >> 14];
377
dst += 8;
378
379
unsigned const tno = tileMapLine[tileMapXpos & 0x1F];
380
tileMapXpos = (tileMapXpos & 0x1F) + 1;
381
ntileword = expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[0]]
382
+ expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[1]] * 2;
383
} while (dst != dstend);
384
385
p.ntileword = ntileword;
386
continue;
387
} else {
388
int cycles = p.cycles - 8;
389
390
if (cycles < 0)
391
break;
392
393
p.cycles = cycles;
394
}
395
396
{
397
uint_least32_t *const dst = dbufline + (xpos - 8);
398
const unsigned tileword = -(p.lcdc & 1U) & p.ntileword;
399
400
dst[0] = p.bgPalette[ tileword & 0x0003 ];
401
dst[1] = p.bgPalette[(tileword & 0x000C) >> 2];
402
dst[2] = p.bgPalette[(tileword & 0x0030) >> 4];
403
dst[3] = p.bgPalette[(tileword & 0x00C0) >> 6];
404
dst[4] = p.bgPalette[(tileword & 0x0300) >> 8];
405
dst[5] = p.bgPalette[(tileword & 0x0C00) >> 10];
406
dst[6] = p.bgPalette[(tileword & 0x3000) >> 12];
407
dst[7] = p.bgPalette[ tileword >> 14];
408
409
int i = nextSprite - 1;
410
411
if(p.layersMask & LAYER_MASK_OBJ)
412
{
413
if (!(p.lcdc & 2)) {
414
do {
415
const int pos = static_cast<int>(p.spriteList[i].spx) - xpos;
416
p.spwordList[i] >>= pos * 2 >= 0 ? 16 - pos * 2 : 16 + pos * 2;
417
--i;
418
} while (i >= 0 && static_cast<int>(p.spriteList[i].spx) > xpos - 8);
419
} else {
420
do {
421
int n;
422
int pos = static_cast<int>(p.spriteList[i].spx) - xpos;
423
424
if (pos < 0) {
425
n = pos + 8;
426
pos = 0;
427
} else
428
n = 8 - pos;
429
430
const unsigned attrib = p.spriteList[i].attrib;
431
unsigned spword = p.spwordList[i];
432
const unsigned long *const spPalette = p.spPalette + (attrib >> 2 & 4);
433
uint_least32_t *d = dst + pos;
434
435
if (!(attrib & 0x80)) {
436
switch (n) {
437
case 8: if (spword >> 14 ) { d[7] = spPalette[spword >> 14 ]; }
438
case 7: if (spword >> 12 & 3) { d[6] = spPalette[spword >> 12 & 3]; }
439
case 6: if (spword >> 10 & 3) { d[5] = spPalette[spword >> 10 & 3]; }
440
case 5: if (spword >> 8 & 3) { d[4] = spPalette[spword >> 8 & 3]; }
441
case 4: if (spword >> 6 & 3) { d[3] = spPalette[spword >> 6 & 3]; }
442
case 3: if (spword >> 4 & 3) { d[2] = spPalette[spword >> 4 & 3]; }
443
case 2: if (spword >> 2 & 3) { d[1] = spPalette[spword >> 2 & 3]; }
444
case 1: if (spword & 3) { d[0] = spPalette[spword & 3]; }
445
}
446
447
spword >>= n * 2;
448
449
/*do {
450
if (spword & 3)
451
dst[pos] = spPalette[spword & 3];
452
453
spword >>= 2;
454
++pos;
455
} while (--n);*/
456
} else {
457
unsigned tw = tileword >> pos * 2;
458
d += n;
459
n = -n;
460
461
do {
462
if (spword & 3) {
463
d[n] = tw & 3 ? p.bgPalette[tw & 3]
464
: spPalette[spword & 3];
465
}
466
467
spword >>= 2;
468
tw >>= 2;
469
} while (++n);
470
}
471
472
p.spwordList[i] = spword;
473
--i;
474
} while (i >= 0 && static_cast<int>(p.spriteList[i].spx) > xpos - 8);
475
}
476
}
477
}
478
479
{
480
unsigned const tno = tileMapLine[tileMapXpos & 0x1F];
481
tileMapXpos = (tileMapXpos & 0x1F) + 1;
482
p.ntileword = expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[0]]
483
+ expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[1]] * 2;
484
}
485
486
xpos = xpos + 8;
487
} while (xpos < xend);
488
489
p.xpos = xpos;
490
}
491
492
static void doFullTilesUnrolledCgb(PPUPriv &p, const int xend, uint_least32_t *const dbufline,
493
const unsigned char *const tileMapLine, const unsigned tileline, unsigned tileMapXpos) {
494
int xpos = p.xpos;
495
const unsigned char *const vram = p.vram;
496
const unsigned tdoffset = tileline * 2 + (~p.lcdc & 0x10) * 0x100;
497
498
if(!(p.layersMask & LAYER_MASK_BG))
499
{
500
for(int x=xpos,i=0;x<xend;x++,i++)
501
dbufline[i] = p.bgPalette[0]; //guessing?
502
return;
503
}
504
505
do {
506
int nextSprite = p.nextSprite;
507
508
if (static_cast<int>(p.spriteList[nextSprite].spx) < xpos + 8) {
509
int cycles = p.cycles - 8;
510
cycles -= std::max(11 - (static_cast<int>(p.spriteList[nextSprite].spx) - xpos), 6);
511
512
for (unsigned i = nextSprite + 1; static_cast<int>(p.spriteList[i].spx) < xpos + 8; ++i)
513
cycles -= 6;
514
515
if (cycles < 0)
516
break;
517
518
p.cycles = cycles;
519
520
do {
521
unsigned reg0, reg1 = p.spriteMapper.oamram()[p.spriteList[nextSprite].oampos + 2] * 16;
522
const unsigned attrib = p.spriteMapper.oamram()[p.spriteList[nextSprite].oampos + 3];
523
524
{
525
const unsigned spline = (attrib & 0x40 ?
526
p.spriteList[nextSprite].line ^ 15 : p.spriteList[nextSprite].line) * 2;
527
reg0 = vram[(attrib << 10 & 0x2000) +
528
(p.lcdc & 4 ? (reg1 & ~16) | spline : reg1 | (spline & ~16)) ];
529
reg1 = vram[(attrib << 10 & 0x2000) +
530
(p.lcdc & 4 ? (reg1 & ~16) | spline : reg1 | (spline & ~16)) + 1];
531
}
532
533
p.spwordList[nextSprite] = expand_lut[reg0 + (attrib << 3 & 0x100)]
534
+ expand_lut[reg1 + (attrib << 3 & 0x100)] * 2;
535
p.spriteList[nextSprite].attrib = attrib;
536
++nextSprite;
537
} while (static_cast<int>(p.spriteList[nextSprite].spx) < xpos + 8);
538
539
p.nextSprite = nextSprite;
540
} else if (nextSprite-1 < 0 || static_cast<int>(p.spriteList[nextSprite-1].spx) <= xpos - 8) {
541
if (!(p.cycles & ~7))
542
break;
543
544
int n = (( xend + 7 < static_cast<int>(p.spriteList[nextSprite].spx)
545
? xend + 7 : static_cast<int>(p.spriteList[nextSprite].spx)) - xpos) & ~7;
546
n = (p.cycles & ~7) < n ? p.cycles & ~7 : n;
547
p.cycles -= n;
548
549
unsigned ntileword = p.ntileword;
550
unsigned nattrib = p.nattrib;
551
uint_least32_t * dst = dbufline + xpos - 8;
552
uint_least32_t *const dstend = dst + n;
553
xpos += n;
554
555
do {
556
const unsigned long *const bgPalette = p.bgPalette + (nattrib & 7) * 4;
557
dst[0] = bgPalette[ ntileword & 0x0003 ];
558
dst[1] = bgPalette[(ntileword & 0x000C) >> 2];
559
dst[2] = bgPalette[(ntileword & 0x0030) >> 4];
560
dst[3] = bgPalette[(ntileword & 0x00C0) >> 6];
561
dst[4] = bgPalette[(ntileword & 0x0300) >> 8];
562
dst[5] = bgPalette[(ntileword & 0x0C00) >> 10];
563
dst[6] = bgPalette[(ntileword & 0x3000) >> 12];
564
dst[7] = bgPalette[ ntileword >> 14];
565
dst += 8;
566
567
unsigned const tno = tileMapLine[ tileMapXpos & 0x1F ];
568
nattrib = tileMapLine[(tileMapXpos & 0x1F) + 0x2000];
569
tileMapXpos = (tileMapXpos & 0x1F) + 1;
570
571
unsigned const tdo = tdoffset & ~(tno << 5);
572
unsigned char const *const td = vram + tno * 16
573
+ (nattrib & 0x40 ? tdo ^ 14 : tdo) + (nattrib << 10 & 0x2000);
574
unsigned short const *const explut = expand_lut + (nattrib << 3 & 0x100);
575
ntileword = explut[td[0]] + explut[td[1]] * 2;
576
} while (dst != dstend);
577
578
p.ntileword = ntileword;
579
p.nattrib = nattrib;
580
continue;
581
} else {
582
int cycles = p.cycles - 8;
583
584
if (cycles < 0)
585
break;
586
587
p.cycles = cycles;
588
}
589
590
{
591
uint_least32_t *const dst = dbufline + (xpos - 8);
592
const unsigned tileword = p.ntileword;
593
const unsigned attrib = p.nattrib;
594
const unsigned long *const bgPalette = p.bgPalette + (attrib & 7) * 4;
595
596
dst[0] = bgPalette[ tileword & 0x0003 ];
597
dst[1] = bgPalette[(tileword & 0x000C) >> 2];
598
dst[2] = bgPalette[(tileword & 0x0030) >> 4];
599
dst[3] = bgPalette[(tileword & 0x00C0) >> 6];
600
dst[4] = bgPalette[(tileword & 0x0300) >> 8];
601
dst[5] = bgPalette[(tileword & 0x0C00) >> 10];
602
dst[6] = bgPalette[(tileword & 0x3000) >> 12];
603
dst[7] = bgPalette[ tileword >> 14];
604
605
int i = nextSprite - 1;
606
607
if(p.layersMask & LAYER_MASK_OBJ)
608
{
609
if (!(p.lcdc & 2)) {
610
do {
611
const int pos = static_cast<int>(p.spriteList[i].spx) - xpos;
612
p.spwordList[i] >>= pos * 2 >= 0 ? 16 - pos * 2 : 16 + pos * 2;
613
--i;
614
} while (i >= 0 && static_cast<int>(p.spriteList[i].spx) > xpos - 8);
615
} else {
616
unsigned char idtab[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
617
const unsigned bgenmask = p.lcdc << 7 & 0x80;
618
619
do {
620
int n;
621
int pos = static_cast<int>(p.spriteList[i].spx) - xpos;
622
623
if (pos < 0) {
624
n = pos + 8;
625
pos = 0;
626
} else
627
n = 8 - pos;
628
629
const unsigned char id = p.spriteList[i].oampos;
630
const unsigned sattrib = p.spriteList[i].attrib;
631
unsigned spword = p.spwordList[i];
632
const unsigned long *const spPalette = p.spPalette + (sattrib & 7) * 4;
633
634
if (!((attrib | sattrib) & bgenmask)) {
635
unsigned char *const idt = idtab + pos;
636
uint_least32_t *const d = dst + pos;
637
638
switch (n) {
639
case 8: if ((spword >> 14 ) && id < idt[7]) {
640
idt[7] = id;
641
d[7] = spPalette[spword >> 14 ];
642
}
643
case 7: if ((spword >> 12 & 3) && id < idt[6]) {
644
idt[6] = id;
645
d[6] = spPalette[spword >> 12 & 3];
646
}
647
case 6: if ((spword >> 10 & 3) && id < idt[5]) {
648
idt[5] = id;
649
d[5] = spPalette[spword >> 10 & 3];
650
}
651
case 5: if ((spword >> 8 & 3) && id < idt[4]) {
652
idt[4] = id;
653
d[4] = spPalette[spword >> 8 & 3];
654
}
655
case 4: if ((spword >> 6 & 3) && id < idt[3]) {
656
idt[3] = id;
657
d[3] = spPalette[spword >> 6 & 3];
658
}
659
case 3: if ((spword >> 4 & 3) && id < idt[2]) {
660
idt[2] = id;
661
d[2] = spPalette[spword >> 4 & 3];
662
}
663
case 2: if ((spword >> 2 & 3) && id < idt[1]) {
664
idt[1] = id;
665
d[1] = spPalette[spword >> 2 & 3];
666
}
667
case 1: if ((spword & 3) && id < idt[0]) {
668
idt[0] = id;
669
d[0] = spPalette[spword & 3];
670
}
671
}
672
673
spword >>= n * 2;
674
675
/*do {
676
if ((spword & 3) && id < idtab[pos]) {
677
idtab[pos] = id;
678
dst[pos] = spPalette[spword & 3];
679
}
680
681
spword >>= 2;
682
++pos;
683
} while (--n);*/
684
} else {
685
unsigned tw = tileword >> pos * 2;
686
687
do {
688
if ((spword & 3) && id < idtab[pos]) {
689
idtab[pos] = id;
690
dst[pos] = tw & 3 ? bgPalette[tw & 3] : spPalette[spword & 3];
691
}
692
693
spword >>= 2;
694
tw >>= 2;
695
++pos;
696
} while (--n);
697
}
698
699
p.spwordList[i] = spword;
700
--i;
701
} while (i >= 0 && static_cast<int>(p.spriteList[i].spx) > xpos - 8);
702
}
703
}
704
}
705
706
{
707
unsigned const tno = tileMapLine[ tileMapXpos & 0x1F ];
708
unsigned const nattrib = tileMapLine[(tileMapXpos & 0x1F) + 0x2000];
709
tileMapXpos = (tileMapXpos & 0x1F) + 1;
710
711
unsigned const tdo = tdoffset & ~(tno << 5);
712
unsigned char const *const td = vram + tno * 16
713
+ (nattrib & 0x40 ? tdo ^ 14 : tdo) + (nattrib << 10 & 0x2000);
714
unsigned short const *const explut = expand_lut + (nattrib << 3 & 0x100);
715
p.ntileword = explut[td[0]] + explut[td[1]] * 2;
716
p.nattrib = nattrib;
717
}
718
719
xpos = xpos + 8;
720
} while (xpos < xend);
721
722
p.xpos = xpos;
723
}
724
725
static void doFullTilesUnrolled(PPUPriv &p) {
726
int xpos = p.xpos;
727
const int xend = static_cast<int>(p.wx) < xpos || p.wx >= 168 ? 161 : static_cast<int>(p.wx) - 7;
728
729
if (xpos >= xend)
730
return;
731
732
uint_least32_t *const dbufline = p.framebuf.fbline();
733
const unsigned char *tileMapLine;
734
unsigned tileline;
735
unsigned tileMapXpos;
736
737
if ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) {
738
tileMapLine = p.vram + (p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + 0x1800;
739
tileMapXpos = (xpos + p.wscx) >> 3;
740
tileline = p.winYPos & 7;
741
} else {
742
tileMapLine = p.vram + (p.lcdc << 7 & 0x400) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800;
743
tileMapXpos = (p.scx + xpos + 1 - p.cgb) >> 3;
744
tileline = (p.scy + p.lyCounter.ly()) & 7;
745
}
746
747
if (xpos < 8) {
748
uint_least32_t prebuf[16];
749
750
if (p.cgb) {
751
doFullTilesUnrolledCgb(p, xend < 8 ? xend : 8, prebuf + (8 - xpos), tileMapLine, tileline, tileMapXpos);
752
} else
753
doFullTilesUnrolledDmg(p, xend < 8 ? xend : 8, prebuf + (8 - xpos), tileMapLine, tileline, tileMapXpos);
754
755
const int newxpos = p.xpos;
756
757
if (newxpos > 8) {
758
std::memcpy(dbufline, prebuf + (8 - xpos), (newxpos - 8) * sizeof *dbufline);
759
} else if (newxpos < 8)
760
return;
761
762
if (newxpos >= xend)
763
return;
764
765
tileMapXpos += (newxpos - xpos) >> 3;
766
}
767
768
if (p.cgb) {
769
doFullTilesUnrolledCgb(p, xend, dbufline, tileMapLine, tileline, tileMapXpos);
770
} else
771
doFullTilesUnrolledDmg(p, xend, dbufline, tileMapLine, tileline, tileMapXpos);
772
}
773
774
static void plotPixel(PPUPriv &p) {
775
//ZING!
776
const int xpos = p.xpos;
777
const unsigned tileword = p.tileword;
778
uint_least32_t *const fbline = p.framebuf.fbline();
779
780
if (static_cast<int>(p.wx) == xpos && (p.weMaster || (p.wy2 == p.lyCounter.ly() && (p.lcdc & 0x20))) && xpos < 167) {
781
if (p.winDrawState == 0 && (p.lcdc & 0x20)) {
782
p.winDrawState = WIN_DRAW_START | WIN_DRAW_STARTED;
783
++p.winYPos;
784
} else if (!p.cgb && (p.winDrawState == 0 || xpos == 166))
785
p.winDrawState |= WIN_DRAW_START;
786
}
787
788
const unsigned twdata = tileword & ((p.lcdc & 1) | p.cgb) * 3;
789
unsigned long pixel = p.bgPalette[twdata + (p.attrib & 7) * 4];
790
int i = static_cast<int>(p.nextSprite) - 1;
791
792
if(!(p.layersMask & LAYER_MASK_BG))
793
{
794
pixel = p.bgPalette[0]; //guessing? clobber the tile that was read
795
}
796
797
if (i >= 0 && static_cast<int>(p.spriteList[i].spx) > xpos - 8) {
798
unsigned spdata = 0;
799
unsigned attrib = 0;
800
801
if (p.cgb) {
802
unsigned minId = 0xFF;
803
804
do {
805
if ((p.spwordList[i] & 3) && p.spriteList[i].oampos < minId) {
806
spdata = p.spwordList[i] & 3;
807
attrib = p.spriteList[i].attrib;
808
minId = p.spriteList[i].oampos;
809
}
810
811
p.spwordList[i] >>= 2;
812
--i;
813
} while (i >= 0 && static_cast<int>(p.spriteList[i].spx) > xpos - 8);
814
815
if(p.layersMask & LAYER_MASK_OBJ)
816
{
817
if (spdata && (p.lcdc & 2) && (!((attrib | p.attrib) & 0x80) || !twdata || !(p.lcdc & 1)))
818
pixel = p.spPalette[(attrib & 7) * 4 + spdata];
819
}
820
} else {
821
do {
822
if (p.spwordList[i] & 3) {
823
spdata = p.spwordList[i] & 3;
824
attrib = p.spriteList[i].attrib;
825
}
826
827
p.spwordList[i] >>= 2;
828
--i;
829
} while (i >= 0 && static_cast<int>(p.spriteList[i].spx) > xpos - 8);
830
831
if(p.layersMask & LAYER_MASK_OBJ)
832
{
833
if (spdata && (p.lcdc & 2) && (!(attrib & 0x80) || !twdata))
834
pixel = p.spPalette[(attrib >> 2 & 4) + spdata];
835
}
836
}
837
}
838
839
if (xpos - 8 >= 0)
840
fbline[xpos - 8] = pixel;
841
842
p.xpos = xpos + 1;
843
p.tileword = tileword >> 2;
844
}
845
846
static void plotPixelIfNoSprite(PPUPriv &p) {
847
if (p.spriteList[p.nextSprite].spx == p.xpos) {
848
if (!((p.lcdc & 2) | p.cgb)) {
849
do {
850
++p.nextSprite;
851
} while (p.spriteList[p.nextSprite].spx == p.xpos);
852
853
plotPixel(p);
854
}
855
} else
856
plotPixel(p);
857
}
858
859
static unsigned long nextM2Time(const PPUPriv &p) {
860
unsigned long nextm2 = p.lyCounter.isDoubleSpeed()
861
? p.lyCounter.time() + (weMasterCheckPriorToLyIncLineCycle(true ) + M2_DS_OFFSET) * 2 - 456 * 2
862
: p.lyCounter.time() + weMasterCheckPriorToLyIncLineCycle(p.cgb) - 456 ;
863
864
if (p.lyCounter.ly() == 143)
865
nextm2 += (456 * 10 + 456 - weMasterCheckPriorToLyIncLineCycle(p.cgb)) << p.lyCounter.isDoubleSpeed();
866
867
return nextm2;
868
}
869
870
static void xpos168(PPUPriv &p) {
871
p.lastM0Time = p.now - (p.cycles << p.lyCounter.isDoubleSpeed());
872
873
const unsigned long nextm2 = nextM2Time(p);
874
875
p.cycles = p.now >= nextm2
876
? (static_cast<long>(p.now - nextm2) >> p.lyCounter.isDoubleSpeed())
877
: -(static_cast<long>(nextm2 - p.now) >> p.lyCounter.isDoubleSpeed());
878
879
nextCall(0, p.lyCounter.ly() == 143 ? M2::Ly0::f0_ : M2::LyNon0::f0_, p);
880
}
881
882
static bool handleWinDrawStartReq(const PPUPriv &p, const int xpos, unsigned char &winDrawState) {
883
const bool startWinDraw = (xpos < 167 || p.cgb) && (winDrawState &= WIN_DRAW_STARTED);
884
885
if (!(p.lcdc & 0x20))
886
winDrawState &= ~WIN_DRAW_STARTED;
887
888
return startWinDraw;
889
}
890
891
static bool handleWinDrawStartReq(PPUPriv &p) {
892
return handleWinDrawStartReq(p, p.xpos, p.winDrawState);
893
}
894
895
namespace StartWindowDraw {
896
static void inc(const PPUState &nextf, PPUPriv &p) {
897
if (!(p.lcdc & 0x20) && p.cgb) {
898
plotPixelIfNoSprite(p);
899
900
if (p.xpos == p.endx) {
901
if (p.xpos < 168) {
902
nextCall(1,Tile::f0_,p);
903
} else
904
xpos168(p);
905
906
return;
907
}
908
}
909
910
nextCall(1,nextf,p);
911
}
912
913
static void f0(PPUPriv &p) {
914
if (p.xpos == p.endx) {
915
p.tileword = p.ntileword;
916
p.attrib = p.nattrib;
917
p.endx = p.xpos < 160 ? p.xpos + 8 : 168;
918
}
919
920
p.wscx = 8 - p.xpos;
921
922
if ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) {
923
p.reg1 = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + 0x1800];
924
p.nattrib = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + 0x3800];
925
} else {
926
p.reg1 = p.vram[(p.lcdc << 7 & 0x400) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800];
927
p.nattrib = p.vram[(p.lcdc << 7 & 0x400) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x3800];
928
}
929
930
inc(f1_,p);
931
}
932
933
static void f1(PPUPriv &p) {
934
inc(f2_,p);
935
}
936
937
static void f2(PPUPriv &p) {
938
const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly();
939
940
p.reg0 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000)
941
- ((p.reg1 * 32 | p.lcdc << 8) & 0x1000)
942
+ p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2];
943
944
inc(f3_,p);
945
}
946
947
static void f3(PPUPriv &p) {
948
inc(f4_,p);
949
}
950
951
static void f4(PPUPriv &p) {
952
const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly();
953
const unsigned r1 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000)
954
- ((p.reg1 * 32 | p.lcdc << 8) & 0x1000)
955
+ p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2 + 1];
956
957
p.ntileword = (expand_lut + (p.nattrib << 3 & 0x100))[p.reg0] +
958
(expand_lut + (p.nattrib << 3 & 0x100))[r1 ] * 2;
959
960
inc(f5_,p);
961
}
962
963
static void f5(PPUPriv &p) {
964
inc(Tile::f0_,p);
965
}
966
};
967
968
namespace LoadSprites {
969
static void inc(const PPUState &nextf, PPUPriv &p) {
970
plotPixelIfNoSprite(p);
971
972
if (p.xpos == p.endx) {
973
if (p.xpos < 168) {
974
nextCall(1,Tile::f0_,p);
975
} else
976
xpos168(p);
977
} else
978
nextCall(1,nextf,p);
979
}
980
981
static void f0(PPUPriv &p) {
982
p.reg1 = p.spriteMapper.oamram()[p.spriteList[p.currentSprite].oampos + 2];
983
nextCall(1,f1_,p);
984
}
985
986
static void f1(PPUPriv &p) {
987
if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p))
988
return StartWindowDraw::f0(p);
989
990
p.spriteList[p.currentSprite].attrib = p.spriteMapper.oamram()[p.spriteList[p.currentSprite].oampos + 3];
991
inc(f2_,p);
992
}
993
994
static void f2(PPUPriv &p) {
995
if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p))
996
return StartWindowDraw::f0(p);
997
998
const unsigned spline = ((p.spriteList[p.currentSprite].attrib & 0x40) ?
999
p.spriteList[p.currentSprite].line ^ 15 : p.spriteList[p.currentSprite].line) * 2;
1000
p.reg0 = p.vram[(p.spriteList[p.currentSprite].attrib << 10 & p.cgb * 0x2000) +
1001
((p.lcdc & 4) ? (p.reg1 * 16 & ~16) | spline : p.reg1 * 16 | (spline & ~16))];
1002
inc(f3_,p);
1003
}
1004
1005
static void f3(PPUPriv &p) {
1006
if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p))
1007
return StartWindowDraw::f0(p);
1008
1009
inc(f4_,p);
1010
}
1011
1012
static void f4(PPUPriv &p) {
1013
if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p))
1014
return StartWindowDraw::f0(p);
1015
1016
const unsigned spline = ((p.spriteList[p.currentSprite].attrib & 0x40) ?
1017
p.spriteList[p.currentSprite].line ^ 15 : p.spriteList[p.currentSprite].line) * 2;
1018
p.reg1 = p.vram[(p.spriteList[p.currentSprite].attrib << 10 & p.cgb * 0x2000) +
1019
((p.lcdc & 4) ? (p.reg1 * 16 & ~16) | spline : p.reg1 * 16 | (spline & ~16)) + 1];
1020
inc(f5_,p);
1021
}
1022
1023
static void f5(PPUPriv &p) {
1024
if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p))
1025
return StartWindowDraw::f0(p);
1026
1027
plotPixelIfNoSprite(p);
1028
1029
unsigned entry = p.currentSprite;
1030
1031
if (entry == p.nextSprite) {
1032
++p.nextSprite;
1033
} else {
1034
entry = p.nextSprite - 1;
1035
p.spriteList[entry] = p.spriteList[p.currentSprite];
1036
}
1037
1038
p.spwordList[entry] = expand_lut[p.reg0 + (p.spriteList[entry].attrib << 3 & 0x100)] +
1039
expand_lut[p.reg1 + (p.spriteList[entry].attrib << 3 & 0x100)] * 2;
1040
p.spriteList[entry].spx = p.xpos;
1041
1042
if (p.xpos == p.endx) {
1043
if (p.xpos < 168) {
1044
nextCall(1,Tile::f0_,p);
1045
} else
1046
xpos168(p);
1047
} else {
1048
p.nextCallPtr = &Tile::f5_;
1049
nextCall(1,Tile::f5_,p);
1050
}
1051
}
1052
};
1053
1054
namespace Tile {
1055
static void inc(const PPUState &nextf, PPUPriv &p) {
1056
plotPixelIfNoSprite(p);
1057
1058
if (p.xpos == 168) {
1059
xpos168(p);
1060
} else
1061
nextCall(1,nextf,p);
1062
}
1063
1064
static void f0(PPUPriv &p) {
1065
if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p))
1066
return StartWindowDraw::f0(p);
1067
1068
doFullTilesUnrolled(p);
1069
1070
if (p.xpos == 168) {
1071
++p.cycles;
1072
return xpos168(p);
1073
}
1074
1075
p.tileword = p.ntileword;
1076
p.attrib = p.nattrib;
1077
p.endx = p.xpos < 160 ? p.xpos + 8 : 168;
1078
1079
if ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW) ) {
1080
p.reg1 = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4
1081
+ ((p.xpos + p.wscx) >> 3 & 0x1F) + 0x1800];
1082
p.nattrib = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4
1083
+ ((p.xpos + p.wscx) >> 3 & 0x1F) + 0x3800];
1084
} else {
1085
p.reg1 = p.vram[((p.lcdc << 7 | (p.scx + p.xpos + 1 - p.cgb) >> 3) & 0x41F)
1086
+ ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800];
1087
p.nattrib = p.vram[((p.lcdc << 7 | (p.scx + p.xpos + 1 - p.cgb) >> 3) & 0x41F)
1088
+ ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x3800];
1089
}
1090
1091
inc(f1_,p);
1092
}
1093
1094
static void f1(PPUPriv &p) {
1095
if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p))
1096
return StartWindowDraw::f0(p);
1097
1098
inc(f2_,p);
1099
}
1100
1101
static void f2(PPUPriv &p) {
1102
if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p))
1103
return StartWindowDraw::f0(p);
1104
1105
const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly();
1106
1107
p.reg0 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000)
1108
- ((p.reg1 * 32 | p.lcdc << 8) & 0x1000)
1109
+ p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2];
1110
1111
inc(f3_,p);
1112
}
1113
1114
static void f3(PPUPriv &p) {
1115
if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p))
1116
return StartWindowDraw::f0(p);
1117
1118
inc(f4_,p);
1119
}
1120
1121
static void f4(PPUPriv &p) {
1122
if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p))
1123
return StartWindowDraw::f0(p);
1124
1125
const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly();
1126
const unsigned r1 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000)
1127
- ((p.reg1 * 32 | p.lcdc << 8) & 0x1000)
1128
+ p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2 + 1];
1129
1130
p.ntileword = (expand_lut + (p.nattrib << 3 & 0x100))[p.reg0] +
1131
(expand_lut + (p.nattrib << 3 & 0x100))[r1 ] * 2;
1132
1133
plotPixelIfNoSprite(p);
1134
1135
if (p.xpos == 168) {
1136
xpos168(p);
1137
} else
1138
nextCall(1,f5_,p);
1139
}
1140
1141
static void f5(PPUPriv &p) {
1142
int endx = p.endx;
1143
p.nextCallPtr = &f5_;
1144
1145
do {
1146
if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p))
1147
return StartWindowDraw::f0(p);
1148
1149
if (p.spriteList[p.nextSprite].spx == p.xpos) {
1150
if ((p.lcdc & 2) | p.cgb) {
1151
p.currentSprite = p.nextSprite;
1152
return LoadSprites::f0(p);
1153
}
1154
1155
do {
1156
++p.nextSprite;
1157
} while (p.spriteList[p.nextSprite].spx == p.xpos);
1158
}
1159
1160
plotPixel(p);
1161
1162
if (p.xpos == endx) {
1163
if (endx < 168) {
1164
nextCall(1,f0_,p);
1165
} else
1166
xpos168(p);
1167
1168
return;
1169
}
1170
} while (--p.cycles >= 0);
1171
}
1172
};
1173
};
1174
1175
1176
namespace M2 {
1177
namespace Ly0 {
1178
static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, unsigned winDrawState, int targetxpos, unsigned cycles);
1179
}
1180
1181
namespace LyNon0 {
1182
static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, unsigned winDrawState, int targetxpos, unsigned cycles);
1183
}
1184
}
1185
1186
namespace M3Loop {
1187
static unsigned predictCyclesUntilXposNextLine(const PPUPriv &p, unsigned winDrawState, const int targetx) {
1188
if (p.wx == 166 && !p.cgb && p.xpos < 167 && (p.weMaster || (p.wy2 == p.lyCounter.ly() && (p.lcdc & 0x20))))
1189
winDrawState = WIN_DRAW_START | (WIN_DRAW_STARTED & p.lcdc >> 4);
1190
1191
const unsigned cycles = (nextM2Time(p) - p.now) >> p.lyCounter.isDoubleSpeed();
1192
1193
return p.lyCounter.ly() == 143
1194
? M2::Ly0::predictCyclesUntilXpos_f0(p, winDrawState, targetx, cycles)
1195
: M2::LyNon0::predictCyclesUntilXpos_f0(p, winDrawState, targetx, cycles);
1196
}
1197
1198
namespace StartWindowDraw {
1199
static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, int xpos, int endx, unsigned ly,
1200
unsigned nextSprite, bool weMaster, unsigned winDrawState, int fno, int targetx, unsigned cycles);
1201
}
1202
1203
namespace Tile {
1204
static const unsigned char* addSpriteCycles(const unsigned char *nextSprite,
1205
const unsigned char *spriteEnd, const unsigned char *const spxOf, const unsigned maxSpx,
1206
const unsigned firstTileXpos, unsigned prevSpriteTileNo, unsigned *const cyclesAccumulator) {
1207
unsigned sum = 0;
1208
1209
while (nextSprite < spriteEnd && spxOf[*nextSprite] <= maxSpx) {
1210
unsigned cycles = 6;
1211
const unsigned distanceFromTileStart = (spxOf[*nextSprite] - firstTileXpos) & 7;
1212
const unsigned tileNo = (spxOf[*nextSprite] - firstTileXpos) & ~7;
1213
1214
if (distanceFromTileStart < 5 && tileNo != prevSpriteTileNo)
1215
cycles = 11 - distanceFromTileStart;
1216
1217
prevSpriteTileNo = tileNo;
1218
sum += cycles;
1219
++nextSprite;
1220
}
1221
1222
*cyclesAccumulator += sum;
1223
1224
return nextSprite;
1225
}
1226
1227
static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, const int xpos,
1228
const int endx, const unsigned ly, const unsigned nextSprite,
1229
const bool weMaster, unsigned char winDrawState, const int fno, const int targetx, unsigned cycles) {
1230
if ((winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p, xpos, winDrawState)) {
1231
return StartWindowDraw::predictCyclesUntilXpos_fn(p, xpos, endx,
1232
ly, nextSprite, weMaster, WIN_DRAW_STARTED & p.lcdc >> 4, 0, targetx, cycles);
1233
}
1234
1235
if (xpos > targetx)
1236
return predictCyclesUntilXposNextLine(p, winDrawState, targetx);
1237
1238
enum { NO_TILE_NUMBER = 1 }; // low bit set, so it will never be equal to an actual tile number.
1239
1240
int nwx = 0xFF;
1241
cycles += targetx - xpos;
1242
1243
if (p.wx - static_cast<unsigned>(xpos) < targetx - static_cast<unsigned>(xpos) && (p.lcdc & 0x20)
1244
&& (weMaster || p.wy2 == ly) && !(winDrawState & WIN_DRAW_STARTED) && (p.cgb || p.wx != 166)) {
1245
nwx = p.wx;
1246
cycles += 6;
1247
}
1248
1249
if ((p.lcdc & 2) | p.cgb) {
1250
const unsigned char *sprite = p.spriteMapper.sprites(ly);
1251
const unsigned char *const spriteEnd = sprite + p.spriteMapper.numSprites(ly);
1252
sprite += nextSprite;
1253
1254
if (sprite < spriteEnd) {
1255
const int spx = p.spriteMapper.posbuf()[*sprite + 1];
1256
unsigned firstTileXpos = static_cast<unsigned>(endx) & 7; // ok even if endx is capped at 168, because fno will be used.
1257
unsigned prevSpriteTileNo = (xpos - firstTileXpos) & ~7; // this tile. all sprites on this tile will now add 6 cycles.
1258
1259
// except this one
1260
if (fno + spx - xpos < 5 && spx <= nwx) {
1261
cycles += 11 - (fno + spx - xpos);
1262
sprite += 1;
1263
}
1264
1265
if (nwx < targetx) {
1266
sprite = addSpriteCycles(sprite, spriteEnd, p.spriteMapper.posbuf() + 1,
1267
nwx, firstTileXpos, prevSpriteTileNo, &cycles);
1268
firstTileXpos = nwx + 1;
1269
prevSpriteTileNo = NO_TILE_NUMBER;
1270
}
1271
1272
addSpriteCycles(sprite, spriteEnd, p.spriteMapper.posbuf() + 1,
1273
targetx, firstTileXpos, prevSpriteTileNo, &cycles);
1274
}
1275
}
1276
1277
return cycles;
1278
}
1279
1280
static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p,
1281
const int endx, const int fno, const int targetx, const unsigned cycles) {
1282
return predictCyclesUntilXpos_fn(p, p.xpos, endx, p.lyCounter.ly(),
1283
p.nextSprite, p.weMaster, p.winDrawState, fno, targetx, cycles);
1284
}
1285
1286
static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, int targetx, unsigned cycles) {
1287
return predictCyclesUntilXpos_fn(p, p.xpos < 160 ? p.xpos + 8 : 168, 0, targetx, cycles);
1288
}
1289
static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, int targetx, unsigned cycles) {
1290
return predictCyclesUntilXpos_fn(p, p.endx, 1, targetx, cycles);
1291
}
1292
static unsigned predictCyclesUntilXpos_f2(const PPUPriv &p, int targetx, unsigned cycles) {
1293
return predictCyclesUntilXpos_fn(p, p.endx, 2, targetx, cycles);
1294
}
1295
static unsigned predictCyclesUntilXpos_f3(const PPUPriv &p, int targetx, unsigned cycles) {
1296
return predictCyclesUntilXpos_fn(p, p.endx, 3, targetx, cycles);
1297
}
1298
static unsigned predictCyclesUntilXpos_f4(const PPUPriv &p, int targetx, unsigned cycles) {
1299
return predictCyclesUntilXpos_fn(p, p.endx, 4, targetx, cycles);
1300
}
1301
static unsigned predictCyclesUntilXpos_f5(const PPUPriv &p, int targetx, unsigned cycles) {
1302
return predictCyclesUntilXpos_fn(p, p.endx, 5, targetx, cycles);
1303
}
1304
}
1305
1306
namespace StartWindowDraw {
1307
static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, int xpos,
1308
const int endx, const unsigned ly, const unsigned nextSprite, const bool weMaster,
1309
const unsigned winDrawState, const int fno, const int targetx, unsigned cycles) {
1310
if (xpos > targetx)
1311
return predictCyclesUntilXposNextLine(p, winDrawState, targetx);
1312
1313
unsigned cinc = 6 - fno;
1314
1315
if (!(p.lcdc & 0x20) && p.cgb) {
1316
unsigned xinc = std::min<int>(cinc, std::min(endx, targetx + 1) - xpos);
1317
1318
if (((p.lcdc & 2) | p.cgb) && p.spriteList[nextSprite].spx < xpos + xinc) {
1319
xpos = p.spriteList[nextSprite].spx;
1320
} else {
1321
cinc = xinc;
1322
xpos += xinc;
1323
}
1324
}
1325
1326
cycles += cinc;
1327
1328
if (xpos <= targetx) {
1329
return Tile::predictCyclesUntilXpos_fn(p, xpos, xpos < 160 ? xpos + 8 : 168,
1330
ly, nextSprite, weMaster, winDrawState, 0, targetx, cycles);
1331
}
1332
1333
return cycles - 1;
1334
}
1335
1336
static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, const int endx,
1337
const int fno, const int targetx, const unsigned cycles) {
1338
return predictCyclesUntilXpos_fn(p, p.xpos, endx,
1339
p.lyCounter.ly(), p.nextSprite, p.weMaster, p.winDrawState, fno, targetx, cycles);
1340
}
1341
1342
static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, const int targetx, const unsigned cycles) {
1343
return predictCyclesUntilXpos_fn(p, p.xpos == p.endx ? (p.xpos < 160 ? p.xpos + 8 : 168) : p.endx, 0, targetx, cycles);
1344
}
1345
static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, int targetx, unsigned cycles) {
1346
return predictCyclesUntilXpos_fn(p, p.endx, 1, targetx, cycles);
1347
}
1348
static unsigned predictCyclesUntilXpos_f2(const PPUPriv &p, int targetx, unsigned cycles) {
1349
return predictCyclesUntilXpos_fn(p, p.endx, 2, targetx, cycles);
1350
}
1351
static unsigned predictCyclesUntilXpos_f3(const PPUPriv &p, int targetx, unsigned cycles) {
1352
return predictCyclesUntilXpos_fn(p, p.endx, 3, targetx, cycles);
1353
}
1354
static unsigned predictCyclesUntilXpos_f4(const PPUPriv &p, int targetx, unsigned cycles) {
1355
return predictCyclesUntilXpos_fn(p, p.endx, 4, targetx, cycles);
1356
}
1357
static unsigned predictCyclesUntilXpos_f5(const PPUPriv &p, int targetx, unsigned cycles) {
1358
return predictCyclesUntilXpos_fn(p, p.endx, 5, targetx, cycles);
1359
}
1360
}
1361
1362
namespace LoadSprites {
1363
static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, const int fno, const int targetx, unsigned cycles) {
1364
unsigned nextSprite = p.nextSprite;
1365
1366
if ((p.lcdc & 2) | p.cgb) {
1367
cycles += 6 - fno;
1368
nextSprite += 1;
1369
}
1370
1371
return Tile::predictCyclesUntilXpos_fn(p, p.xpos, p.endx, p.lyCounter.ly(),
1372
nextSprite, p.weMaster, p.winDrawState, 5, targetx, cycles);
1373
}
1374
1375
static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, int targetx, unsigned cycles) {
1376
return predictCyclesUntilXpos_fn(p, 0, targetx, cycles);
1377
}
1378
static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, int targetx, unsigned cycles) {
1379
return predictCyclesUntilXpos_fn(p, 1, targetx, cycles);
1380
}
1381
static unsigned predictCyclesUntilXpos_f2(const PPUPriv &p, int targetx, unsigned cycles) {
1382
return predictCyclesUntilXpos_fn(p, 2, targetx, cycles);
1383
}
1384
static unsigned predictCyclesUntilXpos_f3(const PPUPriv &p, int targetx, unsigned cycles) {
1385
return predictCyclesUntilXpos_fn(p, 3, targetx, cycles);
1386
}
1387
static unsigned predictCyclesUntilXpos_f4(const PPUPriv &p, int targetx, unsigned cycles) {
1388
return predictCyclesUntilXpos_fn(p, 4, targetx, cycles);
1389
}
1390
static unsigned predictCyclesUntilXpos_f5(const PPUPriv &p, int targetx, unsigned cycles) {
1391
return predictCyclesUntilXpos_fn(p, 5, targetx, cycles);
1392
}
1393
}
1394
}
1395
1396
namespace M3Start {
1397
static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, const unsigned xpos, const unsigned ly,
1398
const bool weMaster, const unsigned winDrawState, const int targetx, unsigned cycles) {
1399
cycles += std::min((static_cast<unsigned>(p.scx) - static_cast<unsigned>(xpos)) & 7, MAX_M3START_CYCLES - xpos) + 1 - p.cgb;
1400
return M3Loop::Tile::predictCyclesUntilXpos_fn(p, 0, 8 - (p.scx & 7), ly, 0,
1401
weMaster, winDrawState, std::min(p.scx & 7, 5), targetx, cycles);
1402
}
1403
1404
static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, const unsigned ly,
1405
const bool weMaster, unsigned winDrawState, const int targetx, const unsigned cycles) {
1406
winDrawState = (winDrawState & p.lcdc >> 5 & WIN_DRAW_START) ? WIN_DRAW_STARTED : 0;
1407
return predictCyclesUntilXpos_f1(p, 0, ly, weMaster, winDrawState, targetx, cycles);
1408
}
1409
1410
static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, const int targetx, const unsigned cycles) {
1411
const unsigned ly = p.lyCounter.ly() + (p.lyCounter.time() - p.now < 16);
1412
return predictCyclesUntilXpos_f0(p, ly, p.weMaster, p.winDrawState, targetx, cycles);
1413
}
1414
1415
static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, const int targetx, const unsigned cycles) {
1416
return predictCyclesUntilXpos_f1(p, p.xpos, p.lyCounter.ly(), p.weMaster, p.winDrawState, targetx, cycles);
1417
}
1418
}
1419
1420
namespace M2 {
1421
namespace Ly0 {
1422
static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p,
1423
const unsigned winDrawState, const int targetx, const unsigned cycles) {
1424
const bool weMaster = (p.lcdc & 0x20) && 0 == p.wy;
1425
const unsigned ly = 0;
1426
1427
return M3Start::predictCyclesUntilXpos_f0(p, ly, weMaster,
1428
winDrawState, targetx, cycles + m3StartLineCycle(p.cgb));
1429
1430
}
1431
1432
static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, const int targetx, const unsigned cycles) {
1433
return predictCyclesUntilXpos_f0(p, p.winDrawState, targetx, cycles);
1434
}
1435
}
1436
1437
namespace LyNon0 {
1438
static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, bool weMaster,
1439
const unsigned winDrawState, const int targetx, const unsigned cycles) {
1440
const unsigned ly = p.lyCounter.ly() + 1;
1441
1442
weMaster |= (p.lcdc & 0x20) && ly == p.wy;
1443
1444
return M3Start::predictCyclesUntilXpos_f0(p, ly, weMaster, winDrawState, targetx,
1445
cycles + 456 - weMasterCheckAfterLyIncLineCycle(p.cgb) + m3StartLineCycle(p.cgb));
1446
}
1447
1448
static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, const int targetx, const unsigned cycles) {
1449
return predictCyclesUntilXpos_f1(p, p.weMaster, p.winDrawState, targetx, cycles);
1450
}
1451
1452
static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p,
1453
const unsigned winDrawState, const int targetx, const unsigned cycles) {
1454
const bool weMaster = p.weMaster || ((p.lcdc & 0x20) && p.lyCounter.ly() == p.wy);
1455
1456
return predictCyclesUntilXpos_f1(p, weMaster, winDrawState, targetx,
1457
cycles + weMasterCheckAfterLyIncLineCycle(p.cgb) - weMasterCheckPriorToLyIncLineCycle(p.cgb));
1458
}
1459
1460
static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, const int targetx, const unsigned cycles) {
1461
return predictCyclesUntilXpos_f0(p, p.winDrawState, targetx, cycles);
1462
}
1463
}
1464
}
1465
1466
} // anon namespace
1467
1468
namespace gambatte {
1469
1470
PPUPriv::PPUPriv(NextM0Time &nextM0Time, const unsigned char *const oamram, const unsigned char *const vram) :
1471
nextSprite(0),
1472
currentSprite(0xFF),
1473
layersMask(LAYER_MASK_BG|LAYER_MASK_OBJ),
1474
vram(vram),
1475
nextCallPtr(&M2::Ly0::f0_),
1476
now(0),
1477
lastM0Time(0),
1478
cycles(-4396),
1479
tileword(0),
1480
ntileword(0),
1481
spriteMapper(nextM0Time, lyCounter, oamram),
1482
lcdc(0),
1483
scy(0),
1484
scx(0),
1485
wy(0),
1486
wy2(0),
1487
wx(0),
1488
winDrawState(0),
1489
wscx(0),
1490
winYPos(0),
1491
reg0(0),
1492
reg1(0),
1493
attrib(0),
1494
nattrib(0),
1495
xpos(0),
1496
endx(0),
1497
cgb(false),
1498
weMaster(false)
1499
{
1500
std::memset(spriteList, 0, sizeof spriteList);
1501
std::memset(spwordList, 0, sizeof spwordList);
1502
}
1503
1504
namespace {
1505
1506
template<class T, class K, std::size_t start, std::size_t len>
1507
struct BSearch {
1508
static std::size_t upperBound(const T a[], const K e) {
1509
if (e < a[start + len / 2])
1510
return BSearch<T, K, start, len / 2>::upperBound(a, e);
1511
1512
return BSearch<T, K, start + len / 2 + 1, len - (len / 2 + 1)>::upperBound(a, e);
1513
}
1514
};
1515
1516
template<class T, class K, std::size_t start>
1517
struct BSearch<T, K, start, 0> {
1518
static std::size_t upperBound(const T[], const K) {
1519
return start;
1520
}
1521
};
1522
1523
template<std::size_t len, class T, class K>
1524
std::size_t upperBound(const T a[], const K e) {
1525
return BSearch<T, K, 0, len>::upperBound(a, e);
1526
}
1527
1528
struct CycleState {
1529
const PPUState *state;
1530
long cycle;
1531
operator long() const { return cycle; }
1532
};
1533
1534
static const PPUState * decodeM3LoopState(const unsigned state) {
1535
switch (state) {
1536
case M3Loop::Tile::ID0: return &M3Loop::Tile::f0_;
1537
case M3Loop::Tile::ID1: return &M3Loop::Tile::f1_;
1538
case M3Loop::Tile::ID2: return &M3Loop::Tile::f2_;
1539
case M3Loop::Tile::ID3: return &M3Loop::Tile::f3_;
1540
case M3Loop::Tile::ID4: return &M3Loop::Tile::f4_;
1541
case M3Loop::Tile::ID5: return &M3Loop::Tile::f5_;
1542
1543
case M3Loop::LoadSprites::ID0: return &M3Loop::LoadSprites::f0_;
1544
case M3Loop::LoadSprites::ID1: return &M3Loop::LoadSprites::f1_;
1545
case M3Loop::LoadSprites::ID2: return &M3Loop::LoadSprites::f2_;
1546
case M3Loop::LoadSprites::ID3: return &M3Loop::LoadSprites::f3_;
1547
case M3Loop::LoadSprites::ID4: return &M3Loop::LoadSprites::f4_;
1548
case M3Loop::LoadSprites::ID5: return &M3Loop::LoadSprites::f5_;
1549
1550
case M3Loop::StartWindowDraw::ID0: return &M3Loop::StartWindowDraw::f0_;
1551
case M3Loop::StartWindowDraw::ID1: return &M3Loop::StartWindowDraw::f1_;
1552
case M3Loop::StartWindowDraw::ID2: return &M3Loop::StartWindowDraw::f2_;
1553
case M3Loop::StartWindowDraw::ID3: return &M3Loop::StartWindowDraw::f3_;
1554
case M3Loop::StartWindowDraw::ID4: return &M3Loop::StartWindowDraw::f4_;
1555
case M3Loop::StartWindowDraw::ID5: return &M3Loop::StartWindowDraw::f5_;
1556
}
1557
1558
return 0;
1559
}
1560
1561
static long cyclesUntilM0Upperbound(const PPUPriv &p) {
1562
long cycles = 168 - p.xpos + 6;
1563
1564
for (unsigned i = p.nextSprite; i < 10 && p.spriteList[i].spx < 168; ++i)
1565
cycles += 11;
1566
1567
return cycles;
1568
}
1569
1570
static void loadSpriteList(PPUPriv &p, const SaveState &ss) {
1571
if (ss.ppu.videoCycles < 144 * 456UL && ss.ppu.xpos < 168) {
1572
const unsigned ly = ss.ppu.videoCycles / 456;
1573
const unsigned numSprites = p.spriteMapper.numSprites(ly);
1574
const unsigned char *const sprites = p.spriteMapper.sprites(ly);
1575
1576
for (unsigned i = 0; i < numSprites; ++i) {
1577
const unsigned pos = sprites[i];
1578
const unsigned spy = p.spriteMapper.posbuf()[pos ];
1579
const unsigned spx = p.spriteMapper.posbuf()[pos+1];
1580
1581
p.spriteList[i].spx = spx;
1582
p.spriteList[i].line = ly + 16u - spy;
1583
p.spriteList[i].oampos = pos * 2;
1584
p.spriteList[i].attrib = ss.ppu.spAttribList[i] & 0xFF;
1585
p.spwordList[i] = (ss.ppu.spByte1List[i] * 0x100 + ss.ppu.spByte0List[i]) & 0xFFFF;
1586
}
1587
1588
p.spriteList[numSprites].spx = 0xFF;
1589
p.nextSprite = std::min<unsigned>(ss.ppu.nextSprite, numSprites);
1590
1591
while (p.spriteList[p.nextSprite].spx < ss.ppu.xpos)
1592
++p.nextSprite;
1593
1594
p.currentSprite = std::min<unsigned>(p.nextSprite, ss.ppu.currentSprite);
1595
}
1596
}
1597
1598
}
1599
1600
void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) {
1601
const PPUState *const m3loopState = decodeM3LoopState(ss.ppu.state);
1602
const long videoCycles = std::min(ss.ppu.videoCycles, 70223UL);
1603
const bool ds = p_.cgb & ss.mem.ioamhram.get()[0x14D] >> 7;
1604
const long vcycs = videoCycles - ds * M2_DS_OFFSET < 0
1605
? videoCycles - ds * M2_DS_OFFSET + 70224
1606
: videoCycles - ds * M2_DS_OFFSET;
1607
const long lineCycles = static_cast<unsigned long>(vcycs) % 456;
1608
1609
p_.now = ss.cpu.cycleCounter;
1610
p_.lcdc = ss.mem.ioamhram.get()[0x140];
1611
p_.lyCounter.setDoubleSpeed(ds);
1612
p_.lyCounter.reset(std::min(ss.ppu.videoCycles, 70223ul), ss.cpu.cycleCounter);
1613
p_.spriteMapper.loadState(ss, oamram);
1614
p_.winYPos = ss.ppu.winYPos;
1615
p_.scy = ss.mem.ioamhram.get()[0x142];
1616
p_.scx = ss.mem.ioamhram.get()[0x143];
1617
p_.wy = ss.mem.ioamhram.get()[0x14A];
1618
p_.wy2 = ss.ppu.oldWy;
1619
p_.wx = ss.mem.ioamhram.get()[0x14B];
1620
p_.xpos = std::min<int>(ss.ppu.xpos, 168);
1621
p_.endx = (p_.xpos & ~7) + (ss.ppu.endx & 7);
1622
p_.endx = std::min(p_.endx <= p_.xpos ? p_.endx + 8 : p_.endx, 168);
1623
p_.reg0 = ss.ppu.reg0 & 0xFF;
1624
p_.reg1 = ss.ppu.reg1 & 0xFF;
1625
p_.tileword = ss.ppu.tileword & 0xFFFF;
1626
p_.ntileword = ss.ppu.ntileword & 0xFFFF;
1627
p_.attrib = ss.ppu.attrib & 0xFF;
1628
p_.nattrib = ss.ppu.nattrib & 0xFF;
1629
p_.wscx = ss.ppu.wscx;
1630
p_.weMaster = ss.ppu.weMaster;
1631
p_.winDrawState = ss.ppu.winDrawState & (WIN_DRAW_START | WIN_DRAW_STARTED);
1632
p_.lastM0Time = p_.now - ss.ppu.lastM0Time;
1633
loadSpriteList(p_, ss);
1634
1635
if (m3loopState && videoCycles < 144 * 456L && p_.xpos < 168
1636
&& lineCycles + cyclesUntilM0Upperbound(p_) < static_cast<long>(weMasterCheckPriorToLyIncLineCycle(p_.cgb))) {
1637
p_.nextCallPtr = m3loopState;
1638
p_.cycles = -1;
1639
} else if (vcycs < 143 * 456L + static_cast<long>(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) {
1640
const struct CycleState lineCycleStates[] = {
1641
{ &M3Start::f0_, m3StartLineCycle(p_.cgb) },
1642
{ &M3Start::f1_, m3StartLineCycle(p_.cgb) + MAX_M3START_CYCLES },
1643
{ &M2::LyNon0::f0_, weMasterCheckPriorToLyIncLineCycle(p_.cgb) },
1644
{ &M2::LyNon0::f1_, weMasterCheckAfterLyIncLineCycle(p_.cgb) },
1645
{ &M3Start::f0_, m3StartLineCycle(p_.cgb) + 456 }
1646
};
1647
1648
const std::size_t pos =
1649
upperBound<sizeof(lineCycleStates) / sizeof(lineCycleStates[0]) - 1>(lineCycleStates, lineCycles);
1650
1651
p_.cycles = lineCycles - lineCycleStates[pos].cycle;
1652
p_.nextCallPtr = lineCycleStates[pos].state;
1653
1654
if (&M3Start::f1_ == lineCycleStates[pos].state) {
1655
p_.xpos = lineCycles - m3StartLineCycle(p_.cgb) + 1;
1656
p_.cycles = -1;
1657
}
1658
} else {
1659
p_.cycles = vcycs - 70224;
1660
p_.nextCallPtr = &M2::Ly0::f0_;
1661
}
1662
}
1663
1664
void PPU::reset(const unsigned char *const oamram, const unsigned char *const vram, const bool cgb) {
1665
p_.vram = vram;
1666
p_.cgb = cgb;
1667
p_.spriteMapper.reset(oamram, cgb);
1668
}
1669
1670
void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) {
1671
const unsigned long dec = oldCc - newCc;
1672
const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
1673
1674
p_.now -= dec;
1675
p_.lastM0Time = p_.lastM0Time ? p_.lastM0Time - dec : p_.lastM0Time;
1676
p_.lyCounter.reset(videoCycles, p_.now);
1677
p_.spriteMapper.resetCycleCounter(oldCc, newCc);
1678
}
1679
1680
void PPU::speedChange(const unsigned long cycleCounter) {
1681
const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
1682
1683
p_.spriteMapper.preSpeedChange(cycleCounter);
1684
p_.lyCounter.setDoubleSpeed(!p_.lyCounter.isDoubleSpeed());
1685
p_.lyCounter.reset(videoCycles, p_.now);
1686
p_.spriteMapper.postSpeedChange(cycleCounter);
1687
1688
if (&M2::Ly0::f0_ == p_.nextCallPtr || &M2::LyNon0::f0_ == p_.nextCallPtr) {
1689
if (p_.lyCounter.isDoubleSpeed()) {
1690
p_.cycles -= M2_DS_OFFSET;
1691
} else
1692
p_.cycles += M2_DS_OFFSET;
1693
}
1694
}
1695
1696
unsigned long PPU::predictedNextXposTime(const unsigned xpos) const {
1697
return p_.now + (p_.nextCallPtr->predictCyclesUntilXpos_f(p_, xpos, -p_.cycles) << p_.lyCounter.isDoubleSpeed());
1698
}
1699
1700
void PPU::setLcdc(const unsigned lcdc, const unsigned long cc) {
1701
if ((p_.lcdc ^ lcdc) & lcdc & 0x80) {
1702
p_.now = cc;
1703
p_.lastM0Time = 0;
1704
p_.lyCounter.reset(0, p_.now);
1705
p_.spriteMapper.enableDisplay(cc);
1706
p_.weMaster = (lcdc & 0x20) && 0 == p_.wy;
1707
p_.winDrawState = 0;
1708
p_.nextCallPtr = &M3Start::f0_;
1709
p_.cycles = -static_cast<int>(m3StartLineCycle(p_.cgb) + M2_DS_OFFSET * p_.lyCounter.isDoubleSpeed());
1710
} else if ((p_.lcdc ^ lcdc) & 0x20) {
1711
if (!(lcdc & 0x20)) {
1712
if (p_.winDrawState == WIN_DRAW_STARTED || p_.xpos == 168)
1713
p_.winDrawState &= ~WIN_DRAW_STARTED;
1714
} else if (p_.winDrawState == WIN_DRAW_START) {
1715
p_.winDrawState |= WIN_DRAW_STARTED;
1716
++p_.winYPos;
1717
}
1718
}
1719
1720
if ((p_.lcdc ^ lcdc) & 0x04) {
1721
if (p_.lcdc & lcdc & 0x80)
1722
p_.spriteMapper.oamChange(cc);
1723
1724
p_.spriteMapper.setLargeSpritesSource(lcdc & 0x04);
1725
}
1726
1727
p_.lcdc = lcdc;
1728
}
1729
1730
void PPU::update(const unsigned long cc) {
1731
const int cycles = (cc - p_.now) >> p_.lyCounter.isDoubleSpeed();
1732
1733
p_.now += cycles << p_.lyCounter.isDoubleSpeed();
1734
p_.cycles += cycles;
1735
1736
if (p_.cycles >= 0) {
1737
p_.framebuf.setFbline(p_.lyCounter.ly());
1738
p_.nextCallPtr->f(p_);
1739
}
1740
}
1741
1742
SYNCFUNC(PPU)
1743
{
1744
NSS(p_.bgPalette);
1745
NSS(p_.spPalette);
1746
NSS(p_.spriteList);
1747
NSS(p_.spwordList);
1748
NSS(p_.nextSprite);
1749
NSS(p_.currentSprite);
1750
1751
EBS(p_.nextCallPtr, 0);
1752
EVS(p_.nextCallPtr, &M2::Ly0::f0_, 1);
1753
EVS(p_.nextCallPtr, &M2::LyNon0::f0_, 2);
1754
EVS(p_.nextCallPtr, &M2::LyNon0::f1_, 3);
1755
EVS(p_.nextCallPtr, &M3Start::f0_, 4);
1756
EVS(p_.nextCallPtr, &M3Start::f1_, 5);
1757
EVS(p_.nextCallPtr, &M3Loop::Tile::f0_, 6);
1758
EVS(p_.nextCallPtr, &M3Loop::Tile::f1_, 7);
1759
EVS(p_.nextCallPtr, &M3Loop::Tile::f2_, 8);
1760
EVS(p_.nextCallPtr, &M3Loop::Tile::f3_, 9);
1761
EVS(p_.nextCallPtr, &M3Loop::Tile::f4_, 10);
1762
EVS(p_.nextCallPtr, &M3Loop::Tile::f5_, 11);
1763
EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f0_, 12);
1764
EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f1_, 13);
1765
EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f2_, 14);
1766
EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f3_, 15);
1767
EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f4_, 16);
1768
EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f5_, 17);
1769
EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f0_, 18);
1770
EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f1_, 19);
1771
EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f2_, 20);
1772
EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f3_, 21);
1773
EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f4_, 22);
1774
EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f5_, 23);
1775
EES(p_.nextCallPtr, NULL);
1776
1777
NSS(p_.now);
1778
NSS(p_.lastM0Time);
1779
NSS(p_.cycles);
1780
1781
NSS(p_.tileword);
1782
NSS(p_.ntileword);
1783
1784
SSS(p_.spriteMapper);
1785
SSS(p_.lyCounter);
1786
//SSS(p_.framebuf); // no state
1787
1788
NSS(p_.lcdc);
1789
NSS(p_.scy);
1790
NSS(p_.scx);
1791
NSS(p_.wy);
1792
NSS(p_.wy2);
1793
NSS(p_.wx);
1794
NSS(p_.winDrawState);
1795
NSS(p_.wscx);
1796
NSS(p_.winYPos);
1797
NSS(p_.reg0);
1798
NSS(p_.reg1);
1799
NSS(p_.attrib);
1800
NSS(p_.nattrib);
1801
NSS(p_.xpos);
1802
NSS(p_.endx);
1803
1804
NSS(p_.cgb);
1805
NSS(p_.weMaster);
1806
}
1807
1808
}
1809
1810