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