Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libgambatte/src/video.cpp
2 views
1
/***************************************************************************
2
* Copyright (C) 2007 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 "video.h"
20
#include "savestate.h"
21
#include <cstring>
22
#include <algorithm>
23
24
namespace gambatte {
25
26
void LCD::setDmgPalette(unsigned long *const palette, const unsigned long *const dmgColors, const unsigned data) {
27
palette[0] = dmgColors[data & 3];
28
palette[1] = dmgColors[data >> 2 & 3];
29
palette[2] = dmgColors[data >> 4 & 3];
30
palette[3] = dmgColors[data >> 6 & 3];
31
}
32
33
void LCD::setCgbPalette(unsigned *lut) {
34
for (int i = 0; i < 32768; i++)
35
cgbColorsRgb32[i] = lut[i];
36
refreshPalettes();
37
}
38
39
unsigned long LCD::gbcToRgb32(const unsigned bgr15) {
40
return cgbColorsRgb32[bgr15 & 0x7FFF];
41
}
42
43
44
LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram, const VideoInterruptRequester memEventRequester) :
45
ppu(nextM0Time_, oamram, vram),
46
eventTimes_(memEventRequester),
47
statReg(0),
48
m2IrqStatReg_(0),
49
m1IrqStatReg_(0),
50
scanlinecallback(0),
51
scanlinecallbacksl(0)
52
{
53
std::memset( bgpData, 0, sizeof bgpData);
54
std::memset(objpData, 0, sizeof objpData);
55
56
for (std::size_t i = 0; i < sizeof(dmgColorsRgb32) / sizeof(dmgColorsRgb32[0]); ++i)
57
setDmgPaletteColor(i, (3 - (i & 3)) * 85 * 0x010101);
58
59
reset(oamram, vram, false);
60
setVideoBuffer(0, 160);
61
}
62
63
void LCD::reset(const unsigned char *const oamram, const unsigned char *vram, const bool cgb) {
64
ppu.reset(oamram, vram, cgb);
65
lycIrq.setCgb(cgb);
66
refreshPalettes();
67
}
68
69
static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned long cycleCounter) {
70
if (!(statReg & 0x20))
71
return DISABLED_TIME;
72
73
unsigned next = lyCounter.time() - cycleCounter;
74
75
if (lyCounter.ly() >= 143 || (lyCounter.ly() == 142 && next <= 4) || (statReg & 0x08)) {
76
next += (153u - lyCounter.ly()) * lyCounter.lineTime();
77
} else {
78
if (next <= 4)
79
next += lyCounter.lineTime();
80
81
next -= 4;
82
}
83
84
return cycleCounter + next;
85
}
86
87
static inline unsigned long m0IrqTimeFromXpos166Time(const unsigned long xpos166Time, const bool cgb, const bool ds) {
88
return xpos166Time + cgb - ds;
89
}
90
91
static inline unsigned long hdmaTimeFromM0Time(const unsigned long m0Time, const bool ds) {
92
return m0Time + 1 - ds;
93
}
94
95
static unsigned long nextHdmaTime(const unsigned long lastM0Time,
96
const unsigned long nextM0Time, const unsigned long cycleCounter, const bool ds) {
97
return cycleCounter < hdmaTimeFromM0Time(lastM0Time, ds)
98
? hdmaTimeFromM0Time(lastM0Time, ds)
99
: hdmaTimeFromM0Time(nextM0Time, ds);
100
}
101
102
void LCD::setStatePtrs(SaveState &state) {
103
state.ppu.bgpData.set( bgpData, sizeof bgpData);
104
state.ppu.objpData.set(objpData, sizeof objpData);
105
ppu.setStatePtrs(state);
106
}
107
108
void LCD::loadState(const SaveState &state, const unsigned char *const oamram) {
109
statReg = state.mem.ioamhram.get()[0x141];
110
m2IrqStatReg_ = statReg;
111
m1IrqStatReg_ = statReg;
112
113
ppu.loadState(state, oamram);
114
lycIrq.loadState(state);
115
m0Irq_.loadState(state);
116
117
if (ppu.lcdc() & 0x80) {
118
nextM0Time_.predictNextM0Time(ppu);
119
lycIrq.reschedule(ppu.lyCounter(), ppu.now());
120
121
eventTimes_.setm<ONESHOT_LCDSTATIRQ>(state.ppu.pendingLcdstatIrq
122
? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
123
eventTimes_.setm<ONESHOT_UPDATEWY2>(state.ppu.oldWy != state.mem.ioamhram.get()[0x14A]
124
? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
125
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
126
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), ppu.now()));
127
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
128
eventTimes_.setm<MODE1_IRQ>(ppu.lyCounter().nextFrameCycle(144 * 456, ppu.now()));
129
eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(statReg, ppu.lyCounter(), ppu.now()));
130
eventTimes_.setm<MODE0_IRQ>((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast<unsigned long>(DISABLED_TIME));
131
eventTimes_.setm<HDMA_REQ>(state.mem.hdmaTransfer
132
? nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), ppu.now(), isDoubleSpeed())
133
: static_cast<unsigned long>(DISABLED_TIME));
134
} else for (int i = 0; i < NUM_MEM_EVENTS; ++i)
135
eventTimes_.set(static_cast<MemEvent>(i), DISABLED_TIME);
136
137
refreshPalettes();
138
}
139
140
void LCD::refreshPalettes() {
141
if (ppu.cgb()) {
142
for (unsigned i = 0; i < 8 * 8; i += 2) {
143
ppu.bgPalette()[i >> 1] = gbcToRgb32( bgpData[i] | bgpData[i + 1] << 8);
144
ppu.spPalette()[i >> 1] = gbcToRgb32(objpData[i] | objpData[i + 1] << 8);
145
}
146
} else {
147
setDmgPalette(ppu.bgPalette() , dmgColorsRgb32 , bgpData[0]);
148
setDmgPalette(ppu.spPalette() , dmgColorsRgb32 + 4, objpData[0]);
149
setDmgPalette(ppu.spPalette() + 4, dmgColorsRgb32 + 8, objpData[1]);
150
}
151
}
152
153
namespace {
154
155
template<typename T>
156
static void clear(T *buf, const unsigned long color, const int dpitch) {
157
unsigned lines = 144;
158
159
while (lines--) {
160
std::fill_n(buf, 160, color);
161
buf += dpitch;
162
}
163
}
164
165
}
166
167
void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) {
168
update(cycleCounter);
169
170
if (blanklcd && ppu.frameBuf().fb()) {
171
const unsigned long color = ppu.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32[0];
172
clear(ppu.frameBuf().fb(), color, ppu.frameBuf().pitch());
173
}
174
}
175
176
void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) {
177
update(oldCc);
178
ppu.resetCc(oldCc, newCc);
179
180
if (ppu.lcdc() & 0x80) {
181
const unsigned long dec = oldCc - newCc;
182
183
nextM0Time_.invalidatePredictedNextM0Time();
184
lycIrq.reschedule(ppu.lyCounter(), newCc);
185
186
for (int i = 0; i < NUM_MEM_EVENTS; ++i) {
187
if (eventTimes_(static_cast<MemEvent>(i)) != DISABLED_TIME)
188
eventTimes_.set(static_cast<MemEvent>(i), eventTimes_(static_cast<MemEvent>(i)) - dec);
189
}
190
191
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
192
}
193
}
194
195
void LCD::speedChange(const unsigned long cycleCounter) {
196
update(cycleCounter);
197
ppu.speedChange(cycleCounter);
198
199
if (ppu.lcdc() & 0x80) {
200
nextM0Time_.predictNextM0Time(ppu);
201
lycIrq.reschedule(ppu.lyCounter(), cycleCounter);
202
203
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
204
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
205
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
206
eventTimes_.setm<MODE1_IRQ>(ppu.lyCounter().nextFrameCycle(144 * 456, cycleCounter));
207
eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(statReg, ppu.lyCounter(), cycleCounter));
208
209
if (eventTimes_(MODE0_IRQ) != DISABLED_TIME && eventTimes_(MODE0_IRQ) - cycleCounter > 1)
210
eventTimes_.setm<MODE0_IRQ>(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()));
211
212
if (hdmaIsEnabled() && eventTimes_(HDMA_REQ) - cycleCounter > 1) {
213
eventTimes_.setm<HDMA_REQ>(nextHdmaTime(ppu.lastM0Time(),
214
nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed()));
215
}
216
}
217
}
218
219
static inline unsigned long m0TimeOfCurrentLine(const unsigned long nextLyTime,
220
const unsigned long lastM0Time, const unsigned long nextM0Time)
221
{
222
return nextM0Time < nextLyTime ? nextM0Time : lastM0Time;
223
}
224
225
unsigned long LCD::m0TimeOfCurrentLine(const unsigned long cc) {
226
if (cc >= nextM0Time_.predictedNextM0Time()) {
227
update(cc);
228
nextM0Time_.predictNextM0Time(ppu);
229
}
230
231
return gambatte::m0TimeOfCurrentLine(ppu.lyCounter().time(), ppu.lastM0Time(), nextM0Time_.predictedNextM0Time());
232
}
233
234
static bool isHdmaPeriod(const LyCounter &lyCounter,
235
const unsigned long m0TimeOfCurrentLy, const unsigned long cycleCounter)
236
{
237
const unsigned timeToNextLy = lyCounter.time() - cycleCounter;
238
239
return /*(ppu.lcdc & 0x80) && */lyCounter.ly() < 144 && timeToNextLy > 4
240
&& cycleCounter >= hdmaTimeFromM0Time(m0TimeOfCurrentLy, lyCounter.isDoubleSpeed());
241
}
242
243
void LCD::enableHdma(const unsigned long cycleCounter) {
244
if (cycleCounter >= nextM0Time_.predictedNextM0Time()) {
245
update(cycleCounter);
246
nextM0Time_.predictNextM0Time(ppu);
247
} else if (cycleCounter >= eventTimes_.nextEventTime())
248
update(cycleCounter);
249
250
if (isHdmaPeriod(ppu.lyCounter(),
251
gambatte::m0TimeOfCurrentLine(ppu.lyCounter().time(),
252
ppu.lastM0Time(), nextM0Time_.predictedNextM0Time()), cycleCounter)) {
253
eventTimes_.flagHdmaReq();
254
}
255
256
eventTimes_.setm<HDMA_REQ>(nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed()));
257
}
258
259
void LCD::disableHdma(const unsigned long cycleCounter) {
260
if (cycleCounter >= eventTimes_.nextEventTime())
261
update(cycleCounter);
262
263
eventTimes_.setm<HDMA_REQ>(DISABLED_TIME);
264
}
265
266
bool LCD::vramAccessible(const unsigned long cycleCounter) {
267
if (cycleCounter >= eventTimes_.nextEventTime())
268
update(cycleCounter);
269
270
return !(ppu.lcdc() & 0x80) || ppu.lyCounter().ly() >= 144
271
|| ppu.lyCounter().lineCycles(cycleCounter) < 80U
272
|| cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
273
}
274
275
bool LCD::cgbpAccessible(const unsigned long cycleCounter) {
276
if (cycleCounter >= eventTimes_.nextEventTime())
277
update(cycleCounter);
278
279
return !(ppu.lcdc() & 0x80) || ppu.lyCounter().ly() >= 144
280
|| ppu.lyCounter().lineCycles(cycleCounter) < 80U + isDoubleSpeed()
281
|| cycleCounter >= m0TimeOfCurrentLine(cycleCounter) + 3 - isDoubleSpeed();
282
}
283
284
void LCD::doCgbColorChange(unsigned char *const pdata,
285
unsigned long *const palette, unsigned index, const unsigned data) {
286
pdata[index] = data;
287
index >>= 1;
288
palette[index] = gbcToRgb32(pdata[index << 1] | pdata[(index << 1) + 1] << 8);
289
}
290
291
void LCD::doCgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
292
if (cgbpAccessible(cycleCounter)) {
293
update(cycleCounter);
294
doCgbColorChange(bgpData, ppu.bgPalette(), index, data);
295
}
296
}
297
298
void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
299
if (cgbpAccessible(cycleCounter)) {
300
update(cycleCounter);
301
doCgbColorChange(objpData, ppu.spPalette(), index, data);
302
}
303
}
304
305
bool LCD::oamReadable(const unsigned long cycleCounter) {
306
if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
307
return true;
308
309
if (cycleCounter >= eventTimes_.nextEventTime())
310
update(cycleCounter);
311
312
if (ppu.lyCounter().lineCycles(cycleCounter) + 4 - ppu.lyCounter().isDoubleSpeed() * 3u >= 456)
313
return ppu.lyCounter().ly() >= 144-1 && ppu.lyCounter().ly() != 153;
314
315
return ppu.lyCounter().ly() >= 144 || cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
316
}
317
318
bool LCD::oamWritable(const unsigned long cycleCounter) {
319
if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
320
return true;
321
322
if (cycleCounter >= eventTimes_.nextEventTime())
323
update(cycleCounter);
324
325
if (ppu.lyCounter().lineCycles(cycleCounter) + 3 + ppu.cgb() - ppu.lyCounter().isDoubleSpeed() * 2u >= 456)
326
return ppu.lyCounter().ly() >= 144-1 && ppu.lyCounter().ly() != 153;
327
328
return ppu.lyCounter().ly() >= 144 || cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
329
}
330
331
void LCD::mode3CyclesChange() {
332
nextM0Time_.invalidatePredictedNextM0Time();
333
334
if (eventTimes_(MODE0_IRQ) != DISABLED_TIME
335
&& eventTimes_(MODE0_IRQ) > m0IrqTimeFromXpos166Time(ppu.now(), ppu.cgb(), isDoubleSpeed())) {
336
eventTimes_.setm<MODE0_IRQ>(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()));
337
}
338
339
if (eventTimes_(HDMA_REQ) != DISABLED_TIME
340
&& eventTimes_(HDMA_REQ) > hdmaTimeFromM0Time(ppu.lastM0Time(), isDoubleSpeed())) {
341
nextM0Time_.predictNextM0Time(ppu);
342
eventTimes_.setm<HDMA_REQ>(hdmaTimeFromM0Time(nextM0Time_.predictedNextM0Time(), isDoubleSpeed()));
343
}
344
}
345
346
void LCD::wxChange(const unsigned newValue, const unsigned long cycleCounter) {
347
update(cycleCounter + isDoubleSpeed() + 1);
348
ppu.setWx(newValue);
349
mode3CyclesChange();
350
}
351
352
void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) {
353
update(cycleCounter + 1);
354
ppu.setWy(newValue);
355
// mode3CyclesChange(); // should be safe to wait until after wy2 delay, because no mode3 events are close to when wy1 is read.
356
357
// wy2 is a delayed version of wy. really just slowness of ly == wy comparison.
358
if (ppu.cgb() && (ppu.lcdc() & 0x80)) {
359
eventTimes_.setm<ONESHOT_UPDATEWY2>(cycleCounter + 5);
360
} else {
361
update(cycleCounter + 2);
362
ppu.updateWy2();
363
mode3CyclesChange();
364
}
365
}
366
367
void LCD::scxChange(const unsigned newScx, const unsigned long cycleCounter) {
368
update(cycleCounter + ppu.cgb() + isDoubleSpeed());
369
ppu.setScx(newScx);
370
mode3CyclesChange();
371
}
372
373
void LCD::scyChange(const unsigned newValue, const unsigned long cycleCounter) {
374
update(cycleCounter + ppu.cgb() + isDoubleSpeed());
375
ppu.setScy(newValue);
376
}
377
378
void LCD::oamChange(const unsigned long cycleCounter) {
379
if (ppu.lcdc() & 0x80) {
380
update(cycleCounter);
381
ppu.oamChange(cycleCounter);
382
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
383
}
384
}
385
386
void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycleCounter) {
387
update(cycleCounter);
388
ppu.oamChange(oamram, cycleCounter);
389
390
if (ppu.lcdc() & 0x80)
391
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
392
}
393
394
void LCD::lcdcChange(const unsigned data, const unsigned long cycleCounter) {
395
const unsigned oldLcdc = ppu.lcdc();
396
update(cycleCounter);
397
398
if ((oldLcdc ^ data) & 0x80) {
399
ppu.setLcdc(data, cycleCounter);
400
401
if (data & 0x80) {
402
lycIrq.lcdReset();
403
m0Irq_.lcdReset(statReg, lycIrq.lycReg());
404
405
if (lycIrq.lycReg() == 0 && (statReg & 0x40))
406
eventTimes_.flagIrq(2);
407
408
nextM0Time_.predictNextM0Time(ppu);
409
lycIrq.reschedule(ppu.lyCounter(), cycleCounter);
410
411
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
412
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
413
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
414
eventTimes_.setm<MODE1_IRQ>(ppu.lyCounter().nextFrameCycle(144 * 456, cycleCounter));
415
eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(statReg, ppu.lyCounter(), cycleCounter));
416
417
if (statReg & 0x08)
418
eventTimes_.setm<MODE0_IRQ>(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()));
419
420
if (hdmaIsEnabled()) {
421
eventTimes_.setm<HDMA_REQ>(nextHdmaTime(ppu.lastM0Time(),
422
nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed()));
423
}
424
} else for (int i = 0; i < NUM_MEM_EVENTS; ++i)
425
eventTimes_.set(static_cast<MemEvent>(i), DISABLED_TIME);
426
} else if (data & 0x80) {
427
if (ppu.cgb()) {
428
ppu.setLcdc((oldLcdc & ~0x14) | (data & 0x14), cycleCounter);
429
430
if ((oldLcdc ^ data) & 0x04)
431
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
432
433
update(cycleCounter + isDoubleSpeed() + 1);
434
ppu.setLcdc(data, cycleCounter + isDoubleSpeed() + 1);
435
436
if ((oldLcdc ^ data) & 0x20)
437
mode3CyclesChange();
438
} else {
439
ppu.setLcdc(data, cycleCounter);
440
441
if ((oldLcdc ^ data) & 0x04)
442
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
443
444
if ((oldLcdc ^ data) & 0x22)
445
mode3CyclesChange();
446
}
447
} else
448
ppu.setLcdc(data, cycleCounter);
449
}
450
451
namespace {
452
struct LyCnt {
453
unsigned ly; int timeToNextLy;
454
LyCnt(unsigned ly, int timeToNextLy) : ly(ly), timeToNextLy(timeToNextLy) {}
455
};
456
457
static LyCnt const getLycCmpLy(LyCounter const &lyCounter, unsigned long cc) {
458
unsigned ly = lyCounter.ly();
459
int timeToNextLy = lyCounter.time() - cc;
460
461
if (ly == 153) {
462
if (timeToNextLy - (448 << lyCounter.isDoubleSpeed()) > 0) {
463
timeToNextLy -= (448 << lyCounter.isDoubleSpeed());
464
} else {
465
ly = 0;
466
timeToNextLy += lyCounter.lineTime();
467
}
468
}
469
470
return LyCnt(ly, timeToNextLy);
471
}
472
}
473
474
void LCD::lcdstatChange(unsigned const data, unsigned long const cycleCounter) {
475
if (cycleCounter >= eventTimes_.nextEventTime())
476
update(cycleCounter);
477
478
unsigned const old = statReg;
479
statReg = data;
480
lycIrq.statRegChange(data, ppu.lyCounter(), cycleCounter);
481
482
if (ppu.lcdc() & 0x80) {
483
int const timeToNextLy = ppu.lyCounter().time() - cycleCounter;
484
LyCnt const lycCmp = getLycCmpLy(ppu.lyCounter(), cycleCounter);
485
486
if (!ppu.cgb()) {
487
if (ppu.lyCounter().ly() < 144) {
488
if (cycleCounter + 1 < m0TimeOfCurrentLine(cycleCounter)) {
489
if (lycCmp.ly == lycIrq.lycReg() && !(old & 0x40))
490
eventTimes_.flagIrq(2);
491
} else {
492
if (!(old & 0x08) && !(lycCmp.ly == lycIrq.lycReg() && (old & 0x40)))
493
eventTimes_.flagIrq(2);
494
}
495
} else {
496
if (!(old & 0x10) && !(lycCmp.ly == lycIrq.lycReg() && (old & 0x40)))
497
eventTimes_.flagIrq(2);
498
}
499
} else if (data & ~old & 0x78) {
500
bool const lycperiod = lycCmp.ly == lycIrq.lycReg() && lycCmp.timeToNextLy > 4 - isDoubleSpeed() * 4;
501
502
if (!(lycperiod && (old & 0x40))) {
503
if (ppu.lyCounter().ly() < 144) {
504
if (cycleCounter + isDoubleSpeed() * 2 < m0TimeOfCurrentLine(cycleCounter) || timeToNextLy <= 4) {
505
if (lycperiod && (data & 0x40))
506
eventTimes_.flagIrq(2);
507
} else if (!(old & 0x08)) {
508
if ((data & 0x08) || (lycperiod && (data & 0x40)))
509
eventTimes_.flagIrq(2);
510
}
511
} else if (!(old & 0x10)) {
512
if ((data & 0x10) && (ppu.lyCounter().ly() < 153 || timeToNextLy > 4 - isDoubleSpeed() * 4)) {
513
eventTimes_.flagIrq(2);
514
} else if (lycperiod && (data & 0x40))
515
eventTimes_.flagIrq(2);
516
}
517
}
518
519
if ((data & 0x28) == 0x20 && !(old & 0x20)
520
&& ((timeToNextLy <= 4 && ppu.lyCounter().ly() < 143)
521
|| (timeToNextLy == 456*2 && ppu.lyCounter().ly() < 144))) {
522
eventTimes_.flagIrq(2);
523
}
524
}
525
526
if ((data & 0x08) && eventTimes_(MODE0_IRQ) == DISABLED_TIME) {
527
update(cycleCounter);
528
eventTimes_.setm<MODE0_IRQ>(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()));
529
}
530
531
eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(data, ppu.lyCounter(), cycleCounter));
532
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
533
}
534
535
m2IrqStatReg_ = eventTimes_(MODE2_IRQ) - cycleCounter > (ppu.cgb() - isDoubleSpeed()) * 4U
536
? data : (m2IrqStatReg_ & 0x10) | (statReg & ~0x10);
537
m1IrqStatReg_ = eventTimes_(MODE1_IRQ) - cycleCounter > (ppu.cgb() - isDoubleSpeed()) * 4U
538
? data : (m1IrqStatReg_ & 0x08) | (statReg & ~0x08);
539
540
m0Irq_.statRegChange(data, eventTimes_(MODE0_IRQ), cycleCounter, ppu.cgb());
541
}
542
543
void LCD::lycRegChange(unsigned const data, unsigned long const cycleCounter) {
544
unsigned const old = lycIrq.lycReg();
545
546
if (data == old)
547
return;
548
549
if (cycleCounter >= eventTimes_.nextEventTime())
550
update(cycleCounter);
551
552
m0Irq_.lycRegChange(data, eventTimes_(MODE0_IRQ), cycleCounter, isDoubleSpeed(), ppu.cgb());
553
lycIrq.lycRegChange(data, ppu.lyCounter(), cycleCounter);
554
555
if (!(ppu.lcdc() & 0x80))
556
return;
557
558
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
559
560
int const timeToNextLy = ppu.lyCounter().time() - cycleCounter;
561
562
if ((statReg & 0x40) && data < 154
563
&& (ppu.lyCounter().ly() < 144
564
? !(statReg & 0x08) || cycleCounter < m0TimeOfCurrentLine(cycleCounter) || timeToNextLy <= 4 << ppu.cgb()
565
: !(statReg & 0x10) || (ppu.lyCounter().ly() == 153 && timeToNextLy <= 4 && ppu.cgb() && !isDoubleSpeed()))) {
566
LyCnt lycCmp = getLycCmpLy(ppu.lyCounter(), cycleCounter);
567
568
if (lycCmp.timeToNextLy <= 4 << ppu.cgb()) {
569
lycCmp.ly = old != lycCmp.ly || (lycCmp.timeToNextLy <= 4 && ppu.cgb() && !isDoubleSpeed())
570
? (lycCmp.ly == 153 ? 0 : lycCmp.ly + 1)
571
: 0xFF; // simultaneous ly/lyc inc. lyc flag never goes low -> no trigger.
572
}
573
574
if (data == lycCmp.ly) {
575
if (ppu.cgb() && !isDoubleSpeed()) {
576
eventTimes_.setm<ONESHOT_LCDSTATIRQ>(cycleCounter + 5);
577
} else
578
eventTimes_.flagIrq(2);
579
}
580
}
581
}
582
583
unsigned LCD::getStat(unsigned const lycReg, unsigned long const cycleCounter) {
584
unsigned stat = 0;
585
586
if (ppu.lcdc() & 0x80) {
587
if (cycleCounter >= eventTimes_.nextEventTime())
588
update(cycleCounter);
589
590
int const timeToNextLy = ppu.lyCounter().time() - cycleCounter;
591
592
if (ppu.lyCounter().ly() > 143) {
593
if (ppu.lyCounter().ly() < 153 || timeToNextLy > 4 - isDoubleSpeed() * 4)
594
stat = 1;
595
} else {
596
unsigned const lineCycles = 456 - (timeToNextLy >> isDoubleSpeed());
597
598
if (lineCycles < 80) {
599
if (!ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
600
stat = 2;
601
} else if (cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 < m0TimeOfCurrentLine(cycleCounter))
602
stat = 3;
603
}
604
605
LyCnt const lycCmp = getLycCmpLy(ppu.lyCounter(), cycleCounter);
606
607
if (lycReg == lycCmp.ly && lycCmp.timeToNextLy > 4 - isDoubleSpeed() * 4)
608
stat |= 4;
609
}
610
611
return stat;
612
}
613
614
inline void LCD::doMode2IrqEvent() {
615
const unsigned ly = eventTimes_(LY_COUNT) - eventTimes_(MODE2_IRQ) < 8
616
? (ppu.lyCounter().ly() == 153 ? 0 : ppu.lyCounter().ly() + 1)
617
: ppu.lyCounter().ly();
618
619
if ((ly != 0 || !(m2IrqStatReg_ & 0x10)) &&
620
(!(m2IrqStatReg_ & 0x40) || (lycIrq.lycReg() != 0 ? ly != (lycIrq.lycReg() + 1U) : ly > 1))) {
621
eventTimes_.flagIrq(2);
622
}
623
624
m2IrqStatReg_ = statReg;
625
626
if (!(statReg & 0x08)) {
627
unsigned long nextTime = eventTimes_(MODE2_IRQ) + ppu.lyCounter().lineTime();
628
629
if (ly == 0) {
630
nextTime -= 4;
631
} else if (ly == 143)
632
nextTime += ppu.lyCounter().lineTime() * 10 + 4;
633
634
eventTimes_.setm<MODE2_IRQ>(nextTime);
635
} else
636
eventTimes_.setm<MODE2_IRQ>(eventTimes_(MODE2_IRQ) + (70224 << isDoubleSpeed()));
637
}
638
639
inline void LCD::event() {
640
switch (eventTimes_.nextEvent()) {
641
case MEM_EVENT:
642
switch (eventTimes_.nextMemEvent()) {
643
case MODE1_IRQ:
644
eventTimes_.flagIrq((m1IrqStatReg_ & 0x18) == 0x10 ? 3 : 1);
645
m1IrqStatReg_ = statReg;
646
eventTimes_.setm<MODE1_IRQ>(eventTimes_(MODE1_IRQ) + (70224 << isDoubleSpeed()));
647
break;
648
649
case LYC_IRQ: {
650
unsigned char ifreg = 0;
651
lycIrq.doEvent(&ifreg, ppu.lyCounter());
652
eventTimes_.flagIrq(ifreg);
653
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
654
break;
655
}
656
657
case SPRITE_MAP:
658
eventTimes_.setm<SPRITE_MAP>(ppu.doSpriteMapEvent(eventTimes_(SPRITE_MAP)));
659
mode3CyclesChange();
660
break;
661
662
case HDMA_REQ:
663
eventTimes_.flagHdmaReq();
664
nextM0Time_.predictNextM0Time(ppu);
665
eventTimes_.setm<HDMA_REQ>(hdmaTimeFromM0Time(nextM0Time_.predictedNextM0Time(), isDoubleSpeed()));
666
break;
667
668
case MODE2_IRQ:
669
doMode2IrqEvent();
670
break;
671
672
case MODE0_IRQ:
673
{
674
unsigned char ifreg = 0;
675
m0Irq_.doEvent(&ifreg, ppu.lyCounter().ly(), statReg, lycIrq.lycReg());
676
eventTimes_.flagIrq(ifreg);
677
}
678
679
eventTimes_.setm<MODE0_IRQ>((statReg & 0x08)
680
? m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed())
681
: static_cast<unsigned long>(DISABLED_TIME));
682
break;
683
684
case ONESHOT_LCDSTATIRQ:
685
eventTimes_.flagIrq(2);
686
eventTimes_.setm<ONESHOT_LCDSTATIRQ>(DISABLED_TIME);
687
break;
688
689
case ONESHOT_UPDATEWY2:
690
ppu.updateWy2();
691
mode3CyclesChange();
692
eventTimes_.setm<ONESHOT_UPDATEWY2>(DISABLED_TIME);
693
break;
694
}
695
696
break;
697
698
case LY_COUNT:
699
ppu.doLyCountEvent();
700
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
701
if (scanlinecallback && ppu.lyCounter().ly() == (unsigned)scanlinecallbacksl)
702
scanlinecallback();
703
break;
704
}
705
}
706
707
void LCD::update(const unsigned long cycleCounter) {
708
if (!(ppu.lcdc() & 0x80))
709
return;
710
711
while (cycleCounter >= eventTimes_.nextEventTime()) {
712
ppu.update(eventTimes_.nextEventTime());
713
event();
714
}
715
716
ppu.update(cycleCounter);
717
}
718
719
void LCD::setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
720
ppu.setFrameBuf(videoBuf, pitch);
721
}
722
723
void LCD::setDmgPaletteColor(const unsigned index, const unsigned long rgb32) {
724
dmgColorsRgb32[index] = rgb32;
725
}
726
727
void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, const unsigned long rgb32) {
728
if (palNum > 2 || colorNum > 3)
729
return;
730
731
setDmgPaletteColor(palNum * 4 | colorNum, rgb32);
732
refreshPalettes();
733
}
734
735
// don't need to save or load rgb32 color data
736
737
SYNCFUNC(LCD)
738
{
739
SSS(ppu);
740
NSS(bgpData);
741
NSS(objpData);
742
SSS(eventTimes_);
743
SSS(m0Irq_);
744
SSS(lycIrq);
745
SSS(nextM0Time_);
746
747
NSS(statReg);
748
NSS(m2IrqStatReg_);
749
NSS(m1IrqStatReg_);
750
}
751
752
}
753
754