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