1 //
2 // Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License version 2 as
6 // published by the Free Software Foundation.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License version 2 for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // version 2 along with this program; if not, write to the
15 // Free Software Foundation, Inc.,
16 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 //
18
19 #include "gambatte-memory.h"
20 #include "inputgetter.h"
21 #include "savestate.h"
22 #include "sound.h"
23 #include "video.h"
24 #include "bootloader.h"
25 #include <cstring>
26
27 namespace gambatte {
28
Memory(Interrupter const & interrupter)29 Memory::Memory(Interrupter const &interrupter) :
30 #ifdef HAVE_NETWORK
31 serialize_value_(0xFF)
32 , serialize_is_fastcgb_(false),
33 #endif
34 getInput_(0)
35 #ifdef HAVE_NETWORK
36 , serial_io_(0)
37 #endif
38 , divLastUpdate_(0)
39 , lastOamDmaUpdate_(disabled_time)
40 , lcd_(ioamhram_, 0, VideoInterruptRequester(intreq_))
41 , interrupter_(interrupter)
42 , dmaSource_(0)
43 , dmaDestination_(0)
44 , oamDmaPos_(0xFE)
45 , serialCnt_(0)
46 , blanklcd_(false)
47 {
48 intreq_.setEventTime<intevent_blit>(144 * 456ul);
49 intreq_.setEventTime<intevent_end>(0);
50 }
51
setStatePtrs(SaveState & state)52 void Memory::setStatePtrs(SaveState &state) {
53 state.mem.ioamhram.set(ioamhram_, sizeof ioamhram_);
54
55 cart_.setStatePtrs(state);
56 lcd_.setStatePtrs(state);
57 psg_.setStatePtrs(state);
58 }
59
saveState(SaveState & state,unsigned long cc)60 unsigned long Memory::saveState(SaveState &state, unsigned long cc) {
61 cc = resetCounters(cc);
62 nontrivial_ff_read(0x05, cc);
63 nontrivial_ff_read(0x0F, cc);
64 nontrivial_ff_read(0x26, cc);
65
66 state.mem.divLastUpdate = divLastUpdate_;
67 state.mem.nextSerialtime = intreq_.eventTime(intevent_serial);
68 state.mem.unhaltTime = intreq_.eventTime(intevent_unhalt);
69 state.mem.lastOamDmaUpdate = lastOamDmaUpdate_;
70 state.mem.dmaSource = dmaSource_;
71 state.mem.dmaDestination = dmaDestination_;
72 state.mem.oamDmaPos = oamDmaPos_;
73 #ifdef HAVE_NETWORK
74 state.mem.serialize_value = serialize_value_;
75 state.mem.serialize_is_fastcgb = serialize_is_fastcgb_;
76 #endif
77
78 intreq_.saveState(state);
79 cart_.saveState(state);
80 tima_.saveState(state);
81 lcd_.saveState(state);
82 psg_.saveState(state);
83
84 return cc;
85 }
86
serialCntFrom(unsigned long cyclesUntilDone,bool cgbFast)87 static int serialCntFrom(unsigned long cyclesUntilDone, bool cgbFast) {
88 return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9;
89 }
90
loadState(SaveState const & state)91 void Memory::loadState(SaveState const &state) {
92 psg_.loadState(state);
93 lcd_.loadState(state, state.mem.oamDmaPos < 0xA0 ? cart_.rdisabledRam() : ioamhram_);
94 tima_.loadState(state, TimaInterruptRequester(intreq_));
95 cart_.loadState(state);
96 intreq_.loadState(state);
97
98 divLastUpdate_ = state.mem.divLastUpdate;
99 intreq_.setEventTime<intevent_serial>(state.mem.nextSerialtime > state.cpu.cycleCounter
100 ? state.mem.nextSerialtime
101 : state.cpu.cycleCounter);
102 intreq_.setEventTime<intevent_unhalt>(state.mem.unhaltTime);
103 lastOamDmaUpdate_ = state.mem.lastOamDmaUpdate;
104 dmaSource_ = state.mem.dmaSource;
105 dmaDestination_ = state.mem.dmaDestination;
106 oamDmaPos_ = state.mem.oamDmaPos;
107 #ifdef HAVE_NETWOWRK
108 serialize_value_ = state.mem.serialize_value;
109 serialize_is_fastcgb_ = state.mem.serialize_is_fastcgb;
110 #endif
111 serialCnt_ = intreq_.eventTime(intevent_serial) != disabled_time
112 ? serialCntFrom(intreq_.eventTime(intevent_serial) - state.cpu.cycleCounter,
113 #ifdef HAVE_NETWORK
114 serialize_is_fastcgb_
115 #else
116 ioamhram_[0x102] & isCgb() * 2
117 #endif
118 )
119 : 8;
120
121 cart_.setVrambank(ioamhram_[0x14F] & isCgb());
122 cart_.setOamDmaSrc(oam_dma_src_off);
123 cart_.setWrambank(isCgb() && (ioamhram_[0x170] & 0x07) ? ioamhram_[0x170] & 0x07 : 1);
124
125 if (lastOamDmaUpdate_ != disabled_time) {
126 oamDmaInitSetup();
127
128 unsigned oamEventPos = oamDmaPos_ < 0xA0 ? 0xA0 : 0x100;
129 intreq_.setEventTime<intevent_oam>(
130 lastOamDmaUpdate_ + (oamEventPos - oamDmaPos_) * 4);
131 }
132
133 intreq_.setEventTime<intevent_blit>((ioamhram_[0x140] & lcdc_en)
134 ? lcd_.nextMode1IrqTime()
135 : state.cpu.cycleCounter);
136 blanklcd_ = false;
137
138 if (!isCgb())
139 std::memset(cart_.vramdata() + 0x2000, 0, 0x2000);
140 }
141
setEndtime(unsigned long cc,unsigned long inc)142 void Memory::setEndtime(unsigned long cc, unsigned long inc) {
143 unsigned is_doublespeed = (unsigned)isDoubleSpeed();
144 if (intreq_.eventTime(intevent_blit) <= cc)
145 intreq_.setEventTime<intevent_blit>(intreq_.eventTime(intevent_blit)
146 + (70224 << is_doublespeed));
147
148 intreq_.setEventTime<intevent_end>(cc + (inc << is_doublespeed));
149 }
150
151 #ifdef HAVE_NETWORK
startSerialTransfer(unsigned long cc,unsigned char data,bool fastCgb)152 void Memory::startSerialTransfer(unsigned long cc, unsigned char data, bool fastCgb)
153 {
154 // If serial interrupt is enabled
155 serialCnt_ = 8;
156
157 serialize_value_ = data;
158 serialize_is_fastcgb_ = fastCgb;
159 intreq_.setEventTime<intevent_serial>(serialize_is_fastcgb_
160 ? (cc & ~0x07ul) + 0x010 * 8
161 : (cc & ~0xFFul) + 0x200 * 8);
162 }
163
checkSerial(unsigned long const cc)164 void Memory::checkSerial(unsigned long const cc) {
165 // Periodically checks if serial data is received
166 if ((serial_io_ != 0) &&
167 ((ioamhram_[0x102] & 0x80) == 0x80) &&
168 (intreq_.eventTime(intevent_serial) == disabled_time)) {
169 unsigned char data;
170 bool fastCgb;
171 if (serial_io_->check(ioamhram_[0x101], data, fastCgb)) {
172 startSerialTransfer(cc, data, fastCgb);
173 }
174 }
175 }
176 #endif
177
updateSerial(unsigned long const cc)178 void Memory::updateSerial(unsigned long const cc) {
179 if (intreq_.eventTime(intevent_serial) != disabled_time) {
180 if (intreq_.eventTime(intevent_serial) <= cc) {
181 #ifdef HAVE_NETWORK
182 bool fire = ((ioamhram_[0x102] & 0x80) == 0x80);
183 ioamhram_[0x101] = ((ioamhram_[0x101] << serialCnt_) |
184 (serialize_value_ >> (8 - serialCnt_))) & 0xFF;
185 #else
186 ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << serialCnt_) - 1) & 0xFF;
187 #endif
188
189 ioamhram_[0x102] &= 0x7F;
190 intreq_.setEventTime<intevent_serial>(disabled_time);
191 #ifdef HAVE_NETWORK
192 if (fire)
193 intreq_.flagIrq(8);
194 #else
195 intreq_.flagIrq(8);
196 #endif
197 } else {
198 int const targetCnt = serialCntFrom(intreq_.eventTime(intevent_serial) - cc,
199 #ifdef HAVE_NETWORK
200 serialize_is_fastcgb_);
201 ioamhram_[0x101] = ((ioamhram_[0x101] << (serialCnt_ - targetCnt)) |
202 (serialize_value_ >> (8 - (serialCnt_ - targetCnt)))) & 0xFF;
203 #else
204 ioamhram_[0x102] & isCgb() * 2);
205 ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << (serialCnt_ - targetCnt)) - 1) & 0xFF;
206 #endif
207 serialCnt_ = targetCnt;
208 }
209 }
210
211 #ifdef HAVE_NETWORK
212 checkSerial(cc);
213 #endif
214 }
215
updateTimaIrq(unsigned long cc)216 void Memory::updateTimaIrq(unsigned long cc) {
217 while (intreq_.eventTime(intevent_tima) <= cc)
218 tima_.doIrqEvent(TimaInterruptRequester(intreq_));
219 }
220
updateIrqs(unsigned long cc)221 void Memory::updateIrqs(unsigned long cc) {
222 updateSerial(cc);
223 updateTimaIrq(cc);
224 lcd_.update(cc);
225 }
226
event(unsigned long cc)227 unsigned long Memory::event(unsigned long cc) {
228 if (lastOamDmaUpdate_ != disabled_time)
229 updateOamDma(cc);
230
231 switch (intreq_.minEventId()) {
232 case intevent_unhalt:
233 intreq_.unhalt();
234 intreq_.setEventTime<intevent_unhalt>(disabled_time);
235 break;
236 case intevent_end:
237 intreq_.setEventTime<intevent_end>(disabled_time - 1);
238
239 while (cc >= intreq_.minEventTime()
240 && intreq_.eventTime(intevent_end) != disabled_time) {
241 cc = event(cc);
242 }
243
244 intreq_.setEventTime<intevent_end>(disabled_time);
245
246 break;
247 case intevent_blit:
248 {
249 bool const lcden = ioamhram_[0x140] & lcdc_en;
250 unsigned long blitTime = intreq_.eventTime(intevent_blit);
251 unsigned is_doublespeed = (unsigned)isDoubleSpeed();
252
253 if (lcden | blanklcd_)
254 {
255 lcd_.updateScreen(blanklcd_, cc);
256 intreq_.setEventTime<intevent_blit>(disabled_time);
257 intreq_.setEventTime<intevent_end>(disabled_time);
258
259 while (cc >= intreq_.minEventTime())
260 cc = event(cc);
261 }
262 else
263 blitTime += 70224 << is_doublespeed;
264
265 blanklcd_ = lcden ^ 1;
266 intreq_.setEventTime<intevent_blit>(blitTime);
267 }
268 break;
269 case intevent_serial:
270 updateSerial(cc);
271 break;
272 case intevent_oam:
273 intreq_.setEventTime<intevent_oam>(lastOamDmaUpdate_ == disabled_time
274 ? static_cast<unsigned long>(disabled_time)
275 : intreq_.eventTime(intevent_oam) + 0xA0 * 4);
276 break;
277 case intevent_dma:
278 {
279 unsigned const doubleSpeed = (unsigned const)isDoubleSpeed();
280 unsigned dmaSrc = dmaSource_;
281 unsigned dmaDest = dmaDestination_;
282 unsigned dmaLength = ((ioamhram_[0x155] & 0x7F) + 0x1) * 0x10;
283 unsigned length = hdmaReqFlagged(intreq_) ? 0x10 : dmaLength;
284
285 ackDmaReq(intreq_);
286
287 if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) {
288 length = 0x10000 - dmaDest;
289 ioamhram_[0x155] |= 0x80;
290 }
291
292 dmaLength -= length;
293
294 if (!(ioamhram_[0x140] & lcdc_en))
295 dmaLength = 0;
296
297 {
298 unsigned long lOamDmaUpdate = lastOamDmaUpdate_;
299 lastOamDmaUpdate_ = disabled_time;
300
301 while (length--) {
302 unsigned const src = dmaSrc++ & 0xFFFF;
303 unsigned const data = (src & 0xE000) == 0x8000 || src > 0xFDFF
304 ? 0xFF
305 : read(src, cc);
306
307 cc += 2 << doubleSpeed;
308
309 if (cc - 3 > lOamDmaUpdate) {
310 oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF;
311 lOamDmaUpdate += 4;
312
313 if (oamDmaPos_ < 0xA0) {
314 if (oamDmaPos_ == 0)
315 startOamDma(lOamDmaUpdate - 1);
316
317 ioamhram_[src & 0xFF] = data;
318 } else if (oamDmaPos_ == 0xA0) {
319 endOamDma(lOamDmaUpdate - 1);
320 lOamDmaUpdate = disabled_time;
321 }
322 }
323
324 nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cc);
325 }
326
327 lastOamDmaUpdate_ = lOamDmaUpdate;
328 }
329
330 cc += 4;
331
332 dmaSource_ = dmaSrc;
333 dmaDestination_ = dmaDest;
334 ioamhram_[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram_[0x155] & 0x80);
335
336 if ((ioamhram_[0x155] & 0x80) && lcd_.hdmaIsEnabled()) {
337 if (lastOamDmaUpdate_ != disabled_time)
338 updateOamDma(cc);
339
340 lcd_.disableHdma(cc);
341 }
342 }
343
344 break;
345 case intevent_tima:
346 tima_.doIrqEvent(TimaInterruptRequester(intreq_));
347 break;
348 case intevent_video:
349 lcd_.update(cc);
350 break;
351 case intevent_interrupts:
352 if (halted()) {
353 if (isCgb())
354 cc += 4;
355
356 intreq_.unhalt();
357 intreq_.setEventTime<intevent_unhalt>(disabled_time);
358 }
359
360 if (ime()) {
361 unsigned const pendingIrqs = intreq_.pendingIrqs();
362 unsigned const n = pendingIrqs & -pendingIrqs;
363 unsigned address;
364 if (n <= 4) {
365 static unsigned char const lut[] = { 0x40, 0x48, 0x48, 0x50 };
366 address = lut[n-1];
367 } else
368 address = 0x50 + n;
369
370 intreq_.ackIrq(n);
371 cc = interrupter_.interrupt(address, cc, *this);
372 }
373
374 break;
375 }
376
377 return cc;
378 }
379
stop(unsigned long cc)380 unsigned long Memory::stop(unsigned long cc) {
381 unsigned is_doublespeed = (unsigned)isDoubleSpeed();
382 cc += 4 + 4 * is_doublespeed;
383
384 if (ioamhram_[0x14D] & isCgb())
385 {
386 psg_.generateSamples(cc, is_doublespeed);
387 lcd_.speedChange(cc);
388 ioamhram_[0x14D] ^= 0x81;
389 intreq_.setEventTime<intevent_blit>((ioamhram_[0x140] & lcdc_en)
390 ? lcd_.nextMode1IrqTime()
391 : cc + (70224 << is_doublespeed));
392
393 if (intreq_.eventTime(intevent_end) > cc)
394 intreq_.setEventTime<intevent_end>(cc
395 + ( (bool)is_doublespeed
396 ? (intreq_.eventTime(intevent_end) - cc) << 1
397 : (intreq_.eventTime(intevent_end) - cc) >> 1));
398 }
399
400 intreq_.halt();
401 intreq_.setEventTime<intevent_unhalt>(cc + 0x20000 + is_doublespeed * 8);
402 return cc;
403 }
404
decCycles(unsigned long & counter,unsigned long dec)405 static void decCycles(unsigned long &counter, unsigned long dec) {
406 if (counter != disabled_time)
407 counter -= dec;
408 }
409
decEventCycles(IntEventId eventId,unsigned long dec)410 void Memory::decEventCycles(IntEventId eventId, unsigned long dec) {
411 if (intreq_.eventTime(eventId) != disabled_time)
412 intreq_.setEventTime(eventId, intreq_.eventTime(eventId) - dec);
413 }
414
resetCounters(unsigned long cc)415 unsigned long Memory::resetCounters(unsigned long cc) {
416 if (lastOamDmaUpdate_ != disabled_time)
417 updateOamDma(cc);
418
419 updateIrqs(cc);
420
421 {
422 unsigned long divinc = (cc - divLastUpdate_) >> 8;
423 ioamhram_[0x104] = (ioamhram_[0x104] + divinc) & 0xFF;
424 divLastUpdate_ += divinc << 8;
425 }
426
427 unsigned long const dec = cc < 0x10000
428 ? 0
429 : (cc & ~0x7FFFul) - 0x8000;
430 decCycles(divLastUpdate_, dec);
431 decCycles(lastOamDmaUpdate_, dec);
432 decEventCycles(intevent_serial, dec);
433 decEventCycles(intevent_oam, dec);
434 decEventCycles(intevent_blit, dec);
435 decEventCycles(intevent_end, dec);
436 decEventCycles(intevent_unhalt, dec);
437
438 unsigned long const oldCC = cc;
439 cc -= dec;
440 intreq_.resetCc(oldCC, cc);
441 tima_.resetCc(oldCC, cc, TimaInterruptRequester(intreq_));
442 lcd_.resetCc(oldCC, cc);
443 psg_.resetCounter(cc, oldCC, isDoubleSpeed());
444 return cc;
445 }
446
updateInput()447 void Memory::updateInput() {
448 unsigned state = 0xF;
449
450 if ((ioamhram_[0x100] & 0x30) != 0x30 && getInput_) {
451 unsigned input = (*getInput_)();
452 unsigned dpad_state = ~input >> 4;
453 unsigned button_state = ~input;
454 if (!(ioamhram_[0x100] & 0x10))
455 state &= dpad_state;
456 if (!(ioamhram_[0x100] & 0x20))
457 state &= button_state;
458 }
459
460 if (state != 0xF && (ioamhram_[0x100] & 0xF) == 0xF)
461 intreq_.flagIrq(0x10);
462
463 ioamhram_[0x100] = (ioamhram_[0x100] & -0x10u) | state;
464 }
465
updateOamDma(unsigned long const cc)466 void Memory::updateOamDma(unsigned long const cc) {
467 unsigned char const *const oamDmaSrc = oamDmaSrcPtr();
468 unsigned cycles = (cc - lastOamDmaUpdate_) >> 2;
469
470 while (cycles--) {
471 oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF;
472 lastOamDmaUpdate_ += 4;
473
474 if (oamDmaPos_ < 0xA0) {
475 if (oamDmaPos_ == 0)
476 startOamDma(lastOamDmaUpdate_ - 1);
477
478 if (oamDmaSrc) ioamhram_[oamDmaPos_] = oamDmaSrc[oamDmaPos_];
479 else if (cart_.isHuC3()) ioamhram_[oamDmaPos_] = cart_.HuC3Read(oamDmaPos_, cc);
480 else ioamhram_[oamDmaPos_] = cart_.rtcRead();
481
482 } else if (oamDmaPos_ == 0xA0) {
483 endOamDma(lastOamDmaUpdate_ - 1);
484 lastOamDmaUpdate_ = disabled_time;
485 break;
486 }
487 }
488 }
489
oamDmaInitSetup()490 void Memory::oamDmaInitSetup() {
491 if (ioamhram_[0x146] < 0xA0) {
492 cart_.setOamDmaSrc(ioamhram_[0x146] < 0x80 ? oam_dma_src_rom : oam_dma_src_vram);
493 } else if (ioamhram_[0x146] < 0xFE - isCgb() * 0x1E) {
494 cart_.setOamDmaSrc(ioamhram_[0x146] < 0xC0 ? oam_dma_src_sram : oam_dma_src_wram);
495 } else
496 cart_.setOamDmaSrc(oam_dma_src_invalid);
497 }
498
oamDmaSrcZero()499 static unsigned char const * oamDmaSrcZero() {
500 static unsigned char zeroMem[0xA0];
501 return zeroMem;
502 }
503
oamDmaSrcPtr() const504 unsigned char const * Memory::oamDmaSrcPtr() const {
505 switch (cart_.oamDmaSrc()) {
506 case oam_dma_src_rom:
507 return cart_.romdata(ioamhram_[0x146] >> 6) + (ioamhram_[0x146] << 8);
508 case oam_dma_src_sram:
509 return cart_.rsrambankptr() ? cart_.rsrambankptr() + (ioamhram_[0x146] << 8) : 0;
510 case oam_dma_src_vram:
511 return cart_.vrambankptr() + (ioamhram_[0x146] << 8);
512 case oam_dma_src_wram:
513 return cart_.wramdata(ioamhram_[0x146] >> 4 & 1) + (ioamhram_[0x146] << 8 & 0xFFF);
514 case oam_dma_src_invalid:
515 case oam_dma_src_off:
516 break;
517 }
518
519 return ioamhram_[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart_.rdisabledRam();
520 }
521
startOamDma(unsigned long cc)522 void Memory::startOamDma(unsigned long cc) {
523 lcd_.oamChange(cart_.rdisabledRam(), cc);
524 }
525
endOamDma(unsigned long cc)526 void Memory::endOamDma(unsigned long cc) {
527 oamDmaPos_ = 0xFE;
528 cart_.setOamDmaSrc(oam_dma_src_off);
529 lcd_.oamChange(ioamhram_, cc);
530 }
531
nontrivial_ff_read(unsigned const p,unsigned long const cc)532 unsigned Memory::nontrivial_ff_read(unsigned const p, unsigned long const cc) {
533 if (lastOamDmaUpdate_ != disabled_time)
534 updateOamDma(cc);
535
536 switch (p) {
537 case 0x00:
538 updateInput();
539 break;
540 case 0x01:
541 case 0x02:
542 updateSerial(cc);
543 break;
544 case 0x04:
545 {
546 unsigned long divcycles = (cc - divLastUpdate_) >> 8;
547 ioamhram_[0x104] = (ioamhram_[0x104] + divcycles) & 0xFF;
548 divLastUpdate_ += divcycles << 8;
549 }
550
551 break;
552 case 0x05:
553 ioamhram_[0x105] = tima_.tima(cc);
554 break;
555 case 0x0F:
556 updateIrqs(cc);
557 ioamhram_[0x10F] = intreq_.ifreg();
558 break;
559 case 0x26:
560 if (ioamhram_[0x126] & 0x80) {
561 psg_.generateSamples(cc, isDoubleSpeed());
562 ioamhram_[0x126] = 0xF0 | psg_.getStatus();
563 } else
564 ioamhram_[0x126] = 0x70;
565
566 break;
567 case 0x30:
568 case 0x31:
569 case 0x32:
570 case 0x33:
571 case 0x34:
572 case 0x35:
573 case 0x36:
574 case 0x37:
575 case 0x38:
576 case 0x39:
577 case 0x3A:
578 case 0x3B:
579 case 0x3C:
580 case 0x3D:
581 case 0x3E:
582 case 0x3F:
583 psg_.generateSamples(cc, isDoubleSpeed());
584 return psg_.waveRamRead(p & 0xF);
585 case 0x41:
586 return ioamhram_[0x141] | lcd_.getStat(ioamhram_[0x145], cc);
587 case 0x44:
588 return lcd_.getLyReg(cc);
589 case 0x69:
590 return lcd_.cgbBgColorRead(ioamhram_[0x168] & 0x3F, cc);
591 case 0x6B:
592 return lcd_.cgbSpColorRead(ioamhram_[0x16A] & 0x3F, cc);
593 default:
594 break;
595 }
596
597 return ioamhram_[p + 0x100];
598 }
599
isInOamDmaConflictArea(OamDmaSrc const oamDmaSrc,unsigned const p,bool const cgb)600 static bool isInOamDmaConflictArea(OamDmaSrc const oamDmaSrc, unsigned const p, bool const cgb) {
601 struct Area { unsigned short areaUpper, exceptAreaLower, exceptAreaWidth, pad; };
602
603 static Area const cgbAreas[] = {
604 { 0xC000, 0x8000, 0x2000, 0 },
605 { 0xC000, 0x8000, 0x2000, 0 },
606 { 0xA000, 0x0000, 0x8000, 0 },
607 { 0xFE00, 0x0000, 0xC000, 0 },
608 { 0xC000, 0x8000, 0x2000, 0 },
609 { 0x0000, 0x0000, 0x0000, 0 }
610 };
611
612 static Area const dmgAreas[] = {
613 { 0xFE00, 0x8000, 0x2000, 0 },
614 { 0xFE00, 0x8000, 0x2000, 0 },
615 { 0xA000, 0x0000, 0x8000, 0 },
616 { 0xFE00, 0x8000, 0x2000, 0 },
617 { 0xFE00, 0x8000, 0x2000, 0 },
618 { 0x0000, 0x0000, 0x0000, 0 }
619 };
620
621 Area const *a = cgb ? cgbAreas : dmgAreas;
622 return p < a[oamDmaSrc].areaUpper
623 && p - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth;
624 }
625
nontrivial_read(unsigned const p,unsigned long const cc)626 unsigned Memory::nontrivial_read(unsigned const p, unsigned long const cc) {
627 if (p < 0xFF80) {
628 if (lastOamDmaUpdate_ != disabled_time) {
629 updateOamDma(cc);
630
631 if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0)
632 return ioamhram_[oamDmaPos_];
633 }
634
635 if (p < 0xC000) {
636 if (p < 0x8000)
637 return cart_.romdata(p >> 14)[p];
638
639 if (p < 0xA000) {
640 if (!lcd_.vramAccessible(cc))
641 return 0xFF;
642
643 return cart_.vrambankptr()[p];
644 }
645
646 if (cart_.rsrambankptr())
647 return cart_.rsrambankptr()[p];
648
649 if (cart_.isHuC3())
650 return cart_.HuC3Read(p, cc);
651
652 return cart_.rtcRead();
653 }
654
655 if (p < 0xFE00)
656 return cart_.wramdata(p >> 12 & 1)[p & 0xFFF];
657
658 long const ffp = long(p) - 0xFF00;
659 if (ffp >= 0)
660 return nontrivial_ff_read(ffp, cc);
661
662 if (!lcd_.oamReadable(cc) || oamDmaPos_ < 0xA0)
663 return 0xFF;
664 }
665
666 return ioamhram_[p - 0xFE00];
667 }
668
nontrivial_ff_write(unsigned const p,unsigned data,unsigned long const cc)669 void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long const cc) {
670 if (lastOamDmaUpdate_ != disabled_time)
671 updateOamDma(cc);
672
673 switch (p & 0xFF) {
674 case 0x00:
675 if ((data ^ ioamhram_[0x100]) & 0x30) {
676 ioamhram_[0x100] = (ioamhram_[0x100] & ~0x30u) | (data & 0x30);
677 updateInput();
678 }
679
680 return;
681 case 0x01:
682 updateSerial(cc);
683 break;
684 case 0x02:
685 updateSerial(cc);
686 serialCnt_ = 8;
687
688 #ifdef HAVE_NETWORK
689 if ((data & 0x81) == 0x81)
690 {
691 unsigned char receivedByte = 0xFF;
692 if (serial_io_ != 0)
693 receivedByte = serial_io_->send(ioamhram_[0x101], (data & isCgb() * 2));
694 startSerialTransfer(cc, receivedByte, (data & isCgb() * 2));
695 }
696 #else
697 if ((data & 0x81) == 0x81)
698 {
699 intreq_.setEventTime<intevent_serial>((data & isCgb() * 2)
700 ? (cc & ~0x07ul) + 0x010 * 8
701 : (cc & ~0xFFul) + 0x200 * 8);
702 }
703 else
704 intreq_.setEventTime<intevent_serial>(disabled_time);
705 #endif
706
707 data |= 0x7E - isCgb() * 2;
708 break;
709 case 0x04:
710 ioamhram_[0x104] = 0;
711 divLastUpdate_ = cc;
712 return;
713 case 0x05:
714 tima_.setTima(data, cc, TimaInterruptRequester(intreq_));
715 break;
716 case 0x06:
717 tima_.setTma(data, cc, TimaInterruptRequester(intreq_));
718 break;
719 case 0x07:
720 data |= 0xF8;
721 tima_.setTac(data, cc, TimaInterruptRequester(intreq_));
722 break;
723 case 0x0F:
724 updateIrqs(cc);
725 intreq_.setIfreg(0xE0 | data);
726 return;
727 case 0x10:
728 if (!psg_.isEnabled())
729 return;
730
731 psg_.generateSamples(cc, isDoubleSpeed());
732 psg_.setNr10(data);
733 data |= 0x80;
734 break;
735 case 0x11:
736 if (!psg_.isEnabled()) {
737 if (isCgb())
738 return;
739
740 data &= 0x3F;
741 }
742
743 psg_.generateSamples(cc, isDoubleSpeed());
744 psg_.setNr11(data);
745 data |= 0x3F;
746 break;
747 case 0x12:
748 if (!psg_.isEnabled())
749 return;
750
751 psg_.generateSamples(cc, isDoubleSpeed());
752 psg_.setNr12(data);
753 break;
754 case 0x13:
755 if (!psg_.isEnabled())
756 return;
757
758 psg_.generateSamples(cc, isDoubleSpeed());
759 psg_.setNr13(data);
760 return;
761 case 0x14:
762 if (!psg_.isEnabled())
763 return;
764
765 psg_.generateSamples(cc, isDoubleSpeed());
766 psg_.setNr14(data);
767 data |= 0xBF;
768 break;
769 case 0x16:
770 if (!psg_.isEnabled()) {
771 if (isCgb())
772 return;
773
774 data &= 0x3F;
775 }
776
777 psg_.generateSamples(cc, isDoubleSpeed());
778 psg_.setNr21(data);
779 data |= 0x3F;
780 break;
781 case 0x17:
782 if (!psg_.isEnabled())
783 return;
784
785 psg_.generateSamples(cc, isDoubleSpeed());
786 psg_.setNr22(data);
787 break;
788 case 0x18:
789 if (!psg_.isEnabled())
790 return;
791
792 psg_.generateSamples(cc, isDoubleSpeed());
793 psg_.setNr23(data);
794 return;
795 case 0x19:
796 if (!psg_.isEnabled())
797 return;
798
799 psg_.generateSamples(cc, isDoubleSpeed());
800 psg_.setNr24(data);
801 data |= 0xBF;
802 break;
803 case 0x1A:
804 if (!psg_.isEnabled())
805 return;
806
807 psg_.generateSamples(cc, isDoubleSpeed());
808 psg_.setNr30(data);
809 data |= 0x7F;
810 break;
811 case 0x1B:
812 if (!psg_.isEnabled() && isCgb())
813 return;
814
815 psg_.generateSamples(cc, isDoubleSpeed());
816 psg_.setNr31(data);
817 return;
818 case 0x1C:
819 if (!psg_.isEnabled())
820 return;
821
822 psg_.generateSamples(cc, isDoubleSpeed());
823 psg_.setNr32(data);
824 data |= 0x9F;
825 break;
826 case 0x1D:
827 if (!psg_.isEnabled())
828 return;
829
830 psg_.generateSamples(cc, isDoubleSpeed());
831 psg_.setNr33(data);
832 return;
833 case 0x1E:
834 if (!psg_.isEnabled())
835 return;
836
837 psg_.generateSamples(cc, isDoubleSpeed());
838 psg_.setNr34(data);
839 data |= 0xBF;
840 break;
841 case 0x20:
842 if (!psg_.isEnabled() && isCgb())
843 return;
844
845 psg_.generateSamples(cc, isDoubleSpeed());
846 psg_.setNr41(data);
847 return;
848 case 0x21:
849 if (!psg_.isEnabled())
850 return;
851
852 psg_.generateSamples(cc, isDoubleSpeed());
853 psg_.setNr42(data);
854 break;
855 case 0x22:
856 if (!psg_.isEnabled())
857 return;
858
859 psg_.generateSamples(cc, isDoubleSpeed());
860 psg_.setNr43(data);
861 break;
862 case 0x23:
863 if (!psg_.isEnabled())
864 return;
865
866 psg_.generateSamples(cc, isDoubleSpeed());
867 psg_.setNr44(data);
868 data |= 0xBF;
869 break;
870 case 0x24:
871 if (!psg_.isEnabled())
872 return;
873
874 psg_.generateSamples(cc, isDoubleSpeed());
875 psg_.setSoVolume(data);
876 break;
877 case 0x25:
878 if (!psg_.isEnabled())
879 return;
880
881 psg_.generateSamples(cc, isDoubleSpeed());
882 psg_.mapSo(data);
883 break;
884 case 0x26:
885 if ((ioamhram_[0x126] ^ data) & 0x80) {
886 psg_.generateSamples(cc, isDoubleSpeed());
887
888 if (!(data & 0x80)) {
889 for (unsigned i = 0x10; i < 0x26; ++i)
890 ff_write(i, 0, cc);
891
892 psg_.setEnabled(false);
893 } else {
894 psg_.reset();
895 psg_.setEnabled(true);
896 }
897 }
898
899 data = (data & 0x80) | (ioamhram_[0x126] & 0x7F);
900 break;
901 case 0x30:
902 case 0x31:
903 case 0x32:
904 case 0x33:
905 case 0x34:
906 case 0x35:
907 case 0x36:
908 case 0x37:
909 case 0x38:
910 case 0x39:
911 case 0x3A:
912 case 0x3B:
913 case 0x3C:
914 case 0x3D:
915 case 0x3E:
916 case 0x3F:
917 psg_.generateSamples(cc, isDoubleSpeed());
918 psg_.waveRamWrite(p & 0xF, data);
919 break;
920 case 0x40:
921 if (ioamhram_[0x140] != data)
922 {
923 unsigned is_doublespeed = (unsigned)isDoubleSpeed();
924 if ((ioamhram_[0x140] ^ data) & lcdc_en)
925 {
926 unsigned const lyc = lcd_.getStat(ioamhram_[0x145], cc)
927 & lcdstat_lycflag;
928 bool const hdmaEnabled = lcd_.hdmaIsEnabled();
929
930 lcd_.lcdcChange(data, cc);
931 ioamhram_[0x144] = 0;
932 ioamhram_[0x141] &= 0xF8;
933
934 if (data & lcdc_en)
935 {
936 intreq_.setEventTime<intevent_blit>(blanklcd_
937 ? lcd_.nextMode1IrqTime()
938 : lcd_.nextMode1IrqTime()
939 + (70224 << is_doublespeed));
940 }
941 else
942 {
943 ioamhram_[0x141] |= lyc;
944 intreq_.setEventTime<intevent_blit>(
945 cc + (456 * 4 << is_doublespeed));
946
947 if (hdmaEnabled)
948 flagHdmaReq(intreq_);
949 }
950 }
951 else
952 lcd_.lcdcChange(data, cc);
953
954 ioamhram_[0x140] = data;
955 }
956 return;
957 case 0x41:
958 lcd_.lcdstatChange(data, cc);
959 data = (ioamhram_[0x141] & 0x87) | (data & 0x78);
960 break;
961 case 0x42:
962 lcd_.scyChange(data, cc);
963 break;
964 case 0x43:
965 lcd_.scxChange(data, cc);
966 break;
967 case 0x45:
968 lcd_.lycRegChange(data, cc);
969 break;
970 case 0x46:
971 if (lastOamDmaUpdate_ != disabled_time)
972 endOamDma(cc);
973
974 lastOamDmaUpdate_ = cc;
975 intreq_.setEventTime<intevent_oam>(cc + 8);
976 ioamhram_[0x146] = data;
977 oamDmaInitSetup();
978 return;
979 case 0x47:
980
981 if (!isCgb() || (ioamhram_[0x14C] == 0x04))//allow in gbc gb mode
982 lcd_.dmgBgPaletteChange(data, cc);
983
984 break;
985 case 0x48:
986
987 if (!isCgb() || (ioamhram_[0x14C] == 0x04))//allow in gbc gb mode
988 lcd_.dmgSpPalette1Change(data, cc);
989
990 break;
991 case 0x49:
992
993 if (!isCgb() || (ioamhram_[0x14C] == 0x04))//allow in gbc gb mode
994 lcd_.dmgSpPalette2Change(data, cc);
995
996 break;
997 case 0x4A:
998 lcd_.wyChange(data, cc);
999 break;
1000 case 0x4B:
1001 lcd_.wxChange(data, cc);
1002 break;
1003 case 0x4C://switch to classic gb mode from gbc mode or lock system to gbc mode
1004 if ((ioamhram_[0x14C] != 0x04)/*gb mode*/ && (ioamhram_[0x14C] != 0x80)/*gbc mode*/) {
1005 //mode has not been set yet, set the mode if data is valid
1006 if (data == 0x04) {
1007 ioamhram_[0x14C] = 0x04;//0x04 is gbc gb mode, lock register and switch mode to gb emulation mode
1008 lcd_.swapToDMG();
1009 }
1010 else if (data == 0x80)
1011 ioamhram_[0x14C] = 0x80;//0x80 is gbc mode, no special operations needed, just lock this register
1012
1013 //any other write to this register is invalid and will just be ignored
1014 }
1015 return;
1016 case 0x4D:
1017 if (isCgb())
1018 ioamhram_[0x14D] = (ioamhram_[0x14D] & ~1u) | (data & 1);
1019
1020 return;
1021 case 0x4F:
1022 if (isCgb()) {
1023 cart_.setVrambank(data & 1);
1024 ioamhram_[0x14F] = 0xFE | data;
1025 }
1026
1027 return;
1028 case 0x50://for bootloader, swap bootloader with rom
1029 bootloader.call_FF50();
1030 ioamhram_[0x150] = 0xFF;
1031 return;
1032 case 0x51:
1033 dmaSource_ = data << 8 | (dmaSource_ & 0xFF);
1034 return;
1035 case 0x52:
1036 dmaSource_ = (dmaSource_ & 0xFF00) | (data & 0xF0);
1037 return;
1038 case 0x53:
1039 dmaDestination_ = data << 8 | (dmaDestination_ & 0xFF);
1040 return;
1041 case 0x54:
1042 dmaDestination_ = (dmaDestination_ & 0xFF00) | (data & 0xF0);
1043 return;
1044 case 0x55:
1045 if (isCgb()) {
1046 ioamhram_[0x155] = data & 0x7F;
1047
1048 if (lcd_.hdmaIsEnabled()) {
1049 if (!(data & 0x80)) {
1050 ioamhram_[0x155] |= 0x80;
1051 lcd_.disableHdma(cc);
1052 }
1053 } else {
1054 if (data & 0x80) {
1055 if (ioamhram_[0x140] & lcdc_en) {
1056 lcd_.enableHdma(cc);
1057 } else
1058 flagHdmaReq(intreq_);
1059 } else
1060 flagGdmaReq(intreq_);
1061 }
1062 }
1063
1064 return;
1065 case 0x56:
1066 if (isCgb())
1067 ioamhram_[0x156] = data | 0x3E;
1068
1069 return;
1070 case 0x68:
1071 if (isCgb())
1072 ioamhram_[0x168] = data | 0x40;
1073
1074 return;
1075 case 0x69:
1076 if (isCgb()) {
1077 unsigned index = ioamhram_[0x168] & 0x3F;
1078 lcd_.cgbBgColorChange(index, data, cc);
1079 ioamhram_[0x168] = (ioamhram_[0x168] & ~0x3F)
1080 | ((index + (ioamhram_[0x168] >> 7)) & 0x3F);
1081 }
1082
1083 return;
1084 case 0x6A:
1085 if (isCgb())
1086 ioamhram_[0x16A] = data | 0x40;
1087
1088 return;
1089 case 0x6B:
1090 if (isCgb()) {
1091 unsigned index = ioamhram_[0x16A] & 0x3F;
1092 lcd_.cgbSpColorChange(index, data, cc);
1093 ioamhram_[0x16A] = (ioamhram_[0x16A] & ~0x3F)
1094 | ((index + (ioamhram_[0x16A] >> 7)) & 0x3F);
1095 }
1096
1097 return;
1098 case 0x6C:
1099 if (isCgb())
1100 ioamhram_[0x16C] = data | 0xFE;
1101
1102 return;
1103 case 0x70:
1104 if (isCgb()) {
1105 cart_.setWrambank((data & 0x07) ? data & 0x07 : 1);
1106 ioamhram_[0x170] = data | 0xF8;
1107 }
1108
1109 return;
1110 case 0x72:
1111 case 0x73:
1112 case 0x74:
1113 if (isCgb())
1114 break;
1115
1116 return;
1117 case 0x75:
1118 if (isCgb())
1119 ioamhram_[0x175] = data | 0x8F;
1120
1121 return;
1122 case 0xFF:
1123 intreq_.setIereg(data);
1124 break;
1125 default:
1126 return;
1127 }
1128
1129 ioamhram_[p + 0x100] = data;
1130 }
1131
nontrivial_write(unsigned const p,unsigned const data,unsigned long const cc)1132 void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned long const cc) {
1133 if (lastOamDmaUpdate_ != disabled_time) {
1134 updateOamDma(cc);
1135
1136 if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0) {
1137 ioamhram_[oamDmaPos_] = data;
1138 return;
1139 }
1140 }
1141
1142 if (p < 0xFE00) {
1143 if (p < 0xA000) {
1144 if (p < 0x8000) {
1145 cart_.mbcWrite(p, data);
1146 } else if (lcd_.vramAccessible(cc)) {
1147 lcd_.vramChange(cc);
1148 cart_.vrambankptr()[p] = data;
1149 }
1150 } else if (p < 0xC000) {
1151 if (cart_.wsrambankptr())
1152 cart_.wsrambankptr()[p] = data;
1153 else if (cart_.isHuC3())
1154 cart_.HuC3Write(p, data);
1155 else
1156 cart_.rtcWrite(data);
1157 } else
1158 cart_.wramdata(p >> 12 & 1)[p & 0xFFF] = data;
1159 } else if (p - 0xFF80u >= 0x7Fu) {
1160 long const ffp = long(p) - 0xFF00;
1161 if (ffp < 0) {
1162 if (lcd_.oamWritable(cc) && oamDmaPos_ >= 0xA0 && (p < 0xFEA0 || isCgb())) {
1163 lcd_.oamChange(cc);
1164 ioamhram_[p - 0xFE00] = data;
1165 }
1166 } else
1167 nontrivial_ff_write(ffp, data, cc);
1168 } else
1169 ioamhram_[p - 0xFE00] = data;
1170 }
1171
fillSoundBuffer(unsigned long cc)1172 std::size_t Memory::fillSoundBuffer(unsigned long cc) {
1173 psg_.generateSamples(cc, isDoubleSpeed());
1174 return psg_.fillBuffer();
1175 }
1176
loadROM(const void * romdata,unsigned int romsize,unsigned int forceModel,const bool multicartCompat)1177 int Memory::loadROM(const void *romdata, unsigned int romsize, unsigned int forceModel, const bool multicartCompat)
1178 {
1179 if (const int fail = cart_.loadROM(romdata, romsize, forceModel, multicartCompat))
1180 return fail;
1181 psg_.init(cart_.isCgb());
1182 lcd_.reset(ioamhram_, cart_.vramdata(), cart_.isCgb());
1183 interrupter_.clearCheats();
1184 return 0;
1185 }
1186
1187 }
1188