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