Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libsnes/bsnes/snes/chip/spc7110/spc7110.cpp
2 views
1
#include <snes/snes.hpp>
2
3
#define SPC7110_CPP
4
namespace SNES {
5
6
SPC7110 spc7110;
7
8
#include "serialization.cpp"
9
#include "decomp.cpp"
10
11
const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
12
13
void SPC7110::init() {
14
}
15
16
void SPC7110::load() {
17
for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff;
18
if(cartridge.has_spc7110rtc()) cartridge.nvram.append({ "program.rtc", rtc, 20 });
19
}
20
21
void SPC7110::unload() {
22
}
23
24
void SPC7110::power() {
25
}
26
27
void SPC7110::reset() {
28
r4801 = 0x00;
29
r4802 = 0x00;
30
r4803 = 0x00;
31
r4804 = 0x00;
32
r4805 = 0x00;
33
r4806 = 0x00;
34
r4807 = 0x00;
35
r4808 = 0x00;
36
r4809 = 0x00;
37
r480a = 0x00;
38
r480b = 0x00;
39
r480c = 0x00;
40
41
decomp.reset();
42
43
r4811 = 0x00;
44
r4812 = 0x00;
45
r4813 = 0x00;
46
r4814 = 0x00;
47
r4815 = 0x00;
48
r4816 = 0x00;
49
r4817 = 0x00;
50
r4818 = 0x00;
51
52
r481x = 0x00;
53
r4814_latch = false;
54
r4815_latch = false;
55
56
r4820 = 0x00;
57
r4821 = 0x00;
58
r4822 = 0x00;
59
r4823 = 0x00;
60
r4824 = 0x00;
61
r4825 = 0x00;
62
r4826 = 0x00;
63
r4827 = 0x00;
64
r4828 = 0x00;
65
r4829 = 0x00;
66
r482a = 0x00;
67
r482b = 0x00;
68
r482c = 0x00;
69
r482d = 0x00;
70
r482e = 0x00;
71
r482f = 0x00;
72
73
r4830 = 0x00;
74
mmio_write(0x4831, 0);
75
mmio_write(0x4832, 1);
76
mmio_write(0x4833, 2);
77
r4834 = 0x00;
78
79
r4840 = 0x00;
80
r4841 = 0x00;
81
r4842 = 0x00;
82
83
if(cartridge.has_spc7110rtc()) {
84
rtc_state = RTCS_Inactive;
85
rtc_mode = RTCM_Linear;
86
rtc_index = 0;
87
}
88
}
89
90
unsigned SPC7110::datarom_addr(unsigned addr) {
91
unsigned size = cartridge.rom.size() - data_rom_offset;
92
while(addr >= size) addr -= size;
93
return data_rom_offset + addr;
94
}
95
96
unsigned SPC7110::data_pointer() { return r4811 + (r4812 << 8) + (r4813 << 16); }
97
unsigned SPC7110::data_adjust() { return r4814 + (r4815 << 8); }
98
unsigned SPC7110::data_increment() { return r4816 + (r4817 << 8); }
99
void SPC7110::set_data_pointer(unsigned addr) { r4811 = addr; r4812 = addr >> 8; r4813 = addr >> 16; }
100
void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8; }
101
102
void SPC7110::update_time(int offset) {
103
time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24);
104
time_t current_time = SNES::interface()->currentTime() - offset;
105
106
//sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic.
107
//yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by
108
//accounting for overflow at the cost of 1-bit precision (to catch underflow). this will allow
109
//rtc[] timestamp to remain valid for up to ~34 years from the last update, even if
110
//time_t overflows. calculation should be valid regardless of number representation, time_t size,
111
//or whether time_t is signed or unsigned.
112
time_t diff
113
= (current_time >= rtc_time)
114
? (current_time - rtc_time)
115
: (std::numeric_limits<time_t>::max() - rtc_time + current_time + 1); //compensate for overflow
116
if(diff > std::numeric_limits<time_t>::max() / 2) diff = 0; //compensate for underflow
117
118
bool update = true;
119
if(rtc[13] & 1) update = false; //do not update if CR0 timer disable flag is set
120
if(rtc[15] & 3) update = false; //do not update if CR2 timer disable flags are set
121
122
if(diff > 0 && update == true) {
123
unsigned second = rtc[ 0] + rtc[ 1] * 10;
124
unsigned minute = rtc[ 2] + rtc[ 3] * 10;
125
unsigned hour = rtc[ 4] + rtc[ 5] * 10;
126
unsigned day = rtc[ 6] + rtc[ 7] * 10;
127
unsigned month = rtc[ 8] + rtc[ 9] * 10;
128
unsigned year = rtc[10] + rtc[11] * 10;
129
unsigned weekday = rtc[12];
130
131
day--;
132
month--;
133
year += (year >= 90) ? 1900 : 2000; //range = 1990-2089
134
135
second += diff;
136
while(second >= 60) {
137
second -= 60;
138
139
minute++;
140
if(minute < 60) continue;
141
minute = 0;
142
143
hour++;
144
if(hour < 24) continue;
145
hour = 0;
146
147
day++;
148
weekday = (weekday + 1) % 7;
149
unsigned days = months[month % 12];
150
if(days == 28) {
151
bool leapyear = false;
152
if((year % 4) == 0) {
153
leapyear = true;
154
if((year % 100) == 0 && (year % 400) != 0) leapyear = false;
155
}
156
if(leapyear) days++;
157
}
158
if(day < days) continue;
159
day = 0;
160
161
month++;
162
if(month < 12) continue;
163
month = 0;
164
165
year++;
166
}
167
168
day++;
169
month++;
170
year %= 100;
171
172
rtc[ 0] = second % 10;
173
rtc[ 1] = second / 10;
174
rtc[ 2] = minute % 10;
175
rtc[ 3] = minute / 10;
176
rtc[ 4] = hour % 10;
177
rtc[ 5] = hour / 10;
178
rtc[ 6] = day % 10;
179
rtc[ 7] = day / 10;
180
rtc[ 8] = month % 10;
181
rtc[ 9] = month / 10;
182
rtc[10] = year % 10;
183
rtc[11] = (year / 10) % 10;
184
rtc[12] = weekday % 7;
185
}
186
187
rtc[16] = current_time >> 0;
188
rtc[17] = current_time >> 8;
189
rtc[18] = current_time >> 16;
190
rtc[19] = current_time >> 24;
191
}
192
193
uint8 SPC7110::mmio_read(unsigned addr) {
194
addr &= 0xffff;
195
196
switch(addr) {
197
//==================
198
//decompression unit
199
//==================
200
201
case 0x4800: {
202
uint16 counter = (r4809 + (r480a << 8));
203
counter--;
204
r4809 = counter;
205
r480a = counter >> 8;
206
return decomp.read();
207
}
208
case 0x4801: return r4801;
209
case 0x4802: return r4802;
210
case 0x4803: return r4803;
211
case 0x4804: return r4804;
212
case 0x4805: return r4805;
213
case 0x4806: return r4806;
214
case 0x4807: return r4807;
215
case 0x4808: return r4808;
216
case 0x4809: return r4809;
217
case 0x480a: return r480a;
218
case 0x480b: return r480b;
219
case 0x480c: {
220
uint8 status = r480c;
221
r480c &= 0x7f;
222
return status;
223
}
224
225
//==============
226
//data port unit
227
//==============
228
229
case 0x4810: {
230
if(r481x != 0x07) return 0x00;
231
232
unsigned addr = data_pointer();
233
unsigned adjust = data_adjust();
234
if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend
235
236
unsigned adjustaddr = addr;
237
if(r4818 & 2) {
238
adjustaddr += adjust;
239
set_data_adjust(adjust + 1);
240
}
241
242
uint8 data = cartridge.rom.read(datarom_addr(adjustaddr));
243
if(!(r4818 & 2)) {
244
unsigned increment = (r4818 & 1) ? data_increment() : 1;
245
if(r4818 & 4) increment = (int16)increment; //16-bit sign extend
246
247
if((r4818 & 16) == 0) {
248
set_data_pointer(addr + increment);
249
} else {
250
set_data_adjust(adjust + increment);
251
}
252
}
253
254
return data;
255
}
256
case 0x4811: return r4811;
257
case 0x4812: return r4812;
258
case 0x4813: return r4813;
259
case 0x4814: return r4814;
260
case 0x4815: return r4815;
261
case 0x4816: return r4816;
262
case 0x4817: return r4817;
263
case 0x4818: return r4818;
264
case 0x481a: {
265
if(r481x != 0x07) return 0x00;
266
267
unsigned addr = data_pointer();
268
unsigned adjust = data_adjust();
269
if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend
270
271
uint8 data = cartridge.rom.read(datarom_addr(addr + adjust));
272
if((r4818 & 0x60) == 0x60) {
273
if((r4818 & 16) == 0) {
274
set_data_pointer(addr + adjust);
275
} else {
276
set_data_adjust(adjust + adjust);
277
}
278
}
279
280
return data;
281
}
282
283
//=========
284
//math unit
285
//=========
286
287
case 0x4820: return r4820;
288
case 0x4821: return r4821;
289
case 0x4822: return r4822;
290
case 0x4823: return r4823;
291
case 0x4824: return r4824;
292
case 0x4825: return r4825;
293
case 0x4826: return r4826;
294
case 0x4827: return r4827;
295
case 0x4828: return r4828;
296
case 0x4829: return r4829;
297
case 0x482a: return r482a;
298
case 0x482b: return r482b;
299
case 0x482c: return r482c;
300
case 0x482d: return r482d;
301
case 0x482e: return r482e;
302
case 0x482f: {
303
uint8 status = r482f;
304
r482f &= 0x7f;
305
return status;
306
}
307
308
//===================
309
//memory mapping unit
310
//===================
311
312
case 0x4830: return r4830;
313
case 0x4831: return r4831;
314
case 0x4832: return r4832;
315
case 0x4833: return r4833;
316
case 0x4834: return r4834;
317
318
//====================
319
//real-time clock unit
320
//====================
321
322
case 0x4840: return r4840;
323
case 0x4841: {
324
if(rtc_state == RTCS_Inactive || rtc_state == RTCS_ModeSelect) return 0x00;
325
326
r4842 = 0x80;
327
uint8 data = rtc[rtc_index];
328
rtc_index = (rtc_index + 1) & 15;
329
return data;
330
}
331
case 0x4842: {
332
uint8 status = r4842;
333
r4842 &= 0x7f;
334
return status;
335
}
336
}
337
338
return cpu.regs.mdr;
339
}
340
341
void SPC7110::mmio_write(unsigned addr, uint8 data) {
342
addr &= 0xffff;
343
344
switch(addr) {
345
//==================
346
//decompression unit
347
//==================
348
349
case 0x4801: r4801 = data; break;
350
case 0x4802: r4802 = data; break;
351
case 0x4803: r4803 = data; break;
352
case 0x4804: r4804 = data; break;
353
case 0x4805: r4805 = data; break;
354
case 0x4806: {
355
r4806 = data;
356
357
unsigned table = (r4801 + (r4802 << 8) + (r4803 << 16));
358
unsigned index = (r4804 << 2);
359
unsigned length = (r4809 + (r480a << 8));
360
unsigned addr = datarom_addr(table + index);
361
unsigned mode = (cartridge.rom.read(addr + 0));
362
unsigned offset = (cartridge.rom.read(addr + 1) << 16)
363
+ (cartridge.rom.read(addr + 2) << 8)
364
+ (cartridge.rom.read(addr + 3) << 0);
365
366
decomp.init(mode, offset, (r4805 + (r4806 << 8)) << mode);
367
r480c = 0x80;
368
} break;
369
370
case 0x4807: r4807 = data; break;
371
case 0x4808: r4808 = data; break;
372
case 0x4809: r4809 = data; break;
373
case 0x480a: r480a = data; break;
374
case 0x480b: r480b = data; break;
375
376
//==============
377
//data port unit
378
//==============
379
380
case 0x4811: r4811 = data; r481x |= 0x01; break;
381
case 0x4812: r4812 = data; r481x |= 0x02; break;
382
case 0x4813: r4813 = data; r481x |= 0x04; break;
383
case 0x4814: {
384
r4814 = data;
385
r4814_latch = true;
386
if(!r4815_latch) break;
387
if(!(r4818 & 2)) break;
388
if(r4818 & 0x10) break;
389
390
if((r4818 & 0x60) == 0x20) {
391
unsigned increment = data_adjust() & 0xff;
392
if(r4818 & 8) increment = (int8)increment; //8-bit sign extend
393
set_data_pointer(data_pointer() + increment);
394
} else if((r4818 & 0x60) == 0x40) {
395
unsigned increment = data_adjust();
396
if(r4818 & 8) increment = (int16)increment; //16-bit sign extend
397
set_data_pointer(data_pointer() + increment);
398
}
399
} break;
400
case 0x4815: {
401
r4815 = data;
402
r4815_latch = true;
403
if(!r4814_latch) break;
404
if(!(r4818 & 2)) break;
405
if(r4818 & 0x10) break;
406
407
if((r4818 & 0x60) == 0x20) {
408
unsigned increment = data_adjust() & 0xff;
409
if(r4818 & 8) increment = (int8)increment; //8-bit sign extend
410
set_data_pointer(data_pointer() + increment);
411
} else if((r4818 & 0x60) == 0x40) {
412
unsigned increment = data_adjust();
413
if(r4818 & 8) increment = (int16)increment; //16-bit sign extend
414
set_data_pointer(data_pointer() + increment);
415
}
416
} break;
417
case 0x4816: r4816 = data; break;
418
case 0x4817: r4817 = data; break;
419
case 0x4818: {
420
if(r481x != 0x07) break;
421
422
r4818 = data;
423
r4814_latch = r4815_latch = false;
424
} break;
425
426
//=========
427
//math unit
428
//=========
429
430
case 0x4820: r4820 = data; break;
431
case 0x4821: r4821 = data; break;
432
case 0x4822: r4822 = data; break;
433
case 0x4823: r4823 = data; break;
434
case 0x4824: r4824 = data; break;
435
case 0x4825: {
436
r4825 = data;
437
438
if(r482e & 1) {
439
//signed 16-bit x 16-bit multiplication
440
int16 r0 = (int16)(r4824 + (r4825 << 8));
441
int16 r1 = (int16)(r4820 + (r4821 << 8));
442
443
signed result = r0 * r1;
444
r4828 = result;
445
r4829 = result >> 8;
446
r482a = result >> 16;
447
r482b = result >> 24;
448
} else {
449
//unsigned 16-bit x 16-bit multiplication
450
uint16 r0 = (uint16)(r4824 + (r4825 << 8));
451
uint16 r1 = (uint16)(r4820 + (r4821 << 8));
452
453
unsigned result = r0 * r1;
454
r4828 = result;
455
r4829 = result >> 8;
456
r482a = result >> 16;
457
r482b = result >> 24;
458
}
459
460
r482f = 0x80;
461
} break;
462
case 0x4826: r4826 = data; break;
463
case 0x4827: {
464
r4827 = data;
465
466
if(r482e & 1) {
467
//signed 32-bit x 16-bit division
468
int32 dividend = (int32)(r4820 + (r4821 << 8) + (r4822 << 16) + (r4823 << 24));
469
int16 divisor = (int16)(r4826 + (r4827 << 8));
470
471
int32 quotient;
472
int16 remainder;
473
474
if(divisor) {
475
quotient = (int32)(dividend / divisor);
476
remainder = (int32)(dividend % divisor);
477
} else {
478
//illegal division by zero
479
quotient = 0;
480
remainder = dividend & 0xffff;
481
}
482
483
r4828 = quotient;
484
r4829 = quotient >> 8;
485
r482a = quotient >> 16;
486
r482b = quotient >> 24;
487
488
r482c = remainder;
489
r482d = remainder >> 8;
490
} else {
491
//unsigned 32-bit x 16-bit division
492
uint32 dividend = (uint32)(r4820 + (r4821 << 8) + (r4822 << 16) + (r4823 << 24));
493
uint16 divisor = (uint16)(r4826 + (r4827 << 8));
494
495
uint32 quotient;
496
uint16 remainder;
497
498
if(divisor) {
499
quotient = (uint32)(dividend / divisor);
500
remainder = (uint16)(dividend % divisor);
501
} else {
502
//illegal division by zero
503
quotient = 0;
504
remainder = dividend & 0xffff;
505
}
506
507
r4828 = quotient;
508
r4829 = quotient >> 8;
509
r482a = quotient >> 16;
510
r482b = quotient >> 24;
511
512
r482c = remainder;
513
r482d = remainder >> 8;
514
}
515
516
r482f = 0x80;
517
} break;
518
519
case 0x482e: {
520
//reset math unit
521
r4820 = r4821 = r4822 = r4823 = 0;
522
r4824 = r4825 = r4826 = r4827 = 0;
523
r4828 = r4829 = r482a = r482b = 0;
524
r482c = r482d = 0;
525
526
r482e = data;
527
} break;
528
529
//===================
530
//memory mapping unit
531
//===================
532
533
case 0x4830: r4830 = data; break;
534
535
case 0x4831: {
536
r4831 = data;
537
dx_offset = datarom_addr(data * 0x100000);
538
} break;
539
540
case 0x4832: {
541
r4832 = data;
542
ex_offset = datarom_addr(data * 0x100000);
543
} break;
544
545
case 0x4833: {
546
r4833 = data;
547
fx_offset = datarom_addr(data * 0x100000);
548
} break;
549
550
case 0x4834: r4834 = data; break;
551
552
//====================
553
//real-time clock unit
554
//====================
555
556
case 0x4840: {
557
r4840 = data;
558
if(!(r4840 & 1)) {
559
//disable RTC
560
rtc_state = RTCS_Inactive;
561
update_time();
562
} else {
563
//enable RTC
564
r4842 = 0x80;
565
rtc_state = RTCS_ModeSelect;
566
}
567
} break;
568
569
case 0x4841: {
570
r4841 = data;
571
572
switch(rtc_state) {
573
case RTCS_ModeSelect: {
574
if(data == RTCM_Linear || data == RTCM_Indexed) {
575
r4842 = 0x80;
576
rtc_state = RTCS_IndexSelect;
577
rtc_mode = (RTC_Mode)data;
578
rtc_index = 0;
579
}
580
} break;
581
582
case RTCS_IndexSelect: {
583
r4842 = 0x80;
584
rtc_index = data & 15;
585
if(rtc_mode == RTCM_Linear) rtc_state = RTCS_Write;
586
} break;
587
588
case RTCS_Write: {
589
r4842 = 0x80;
590
591
//control register 0
592
if(rtc_index == 13) {
593
//increment second counter
594
if(data & 2) update_time(+1);
595
596
//round minute counter
597
if(data & 8) {
598
update_time();
599
600
unsigned second = rtc[ 0] + rtc[ 1] * 10;
601
//clear seconds
602
rtc[0] = 0;
603
rtc[1] = 0;
604
605
if(second >= 30) update_time(+60);
606
}
607
}
608
609
//control register 2
610
if(rtc_index == 15) {
611
//disable timer and clear second counter
612
if((data & 1) && !(rtc[15] & 1)) {
613
update_time();
614
615
//clear seconds
616
rtc[0] = 0;
617
rtc[1] = 0;
618
}
619
620
//disable timer
621
if((data & 2) && !(rtc[15] & 2)) {
622
update_time();
623
}
624
}
625
626
rtc[rtc_index] = data & 15;
627
rtc_index = (rtc_index + 1) & 15;
628
} break;
629
} //switch(rtc_state)
630
} break;
631
}
632
}
633
634
SPC7110::SPC7110() :
635
rtc(nullptr)
636
{
637
638
}
639
640
SPC7110::~SPC7110()
641
{
642
interface()->freeSharedMemory(rtc);
643
}
644
645
void SPC7110::initialize()
646
{
647
rtc = (uint8*)interface()->allocSharedMemory("SPC7110_RTC", 20);
648
}
649
//============
650
//SPC7110::MCU
651
//============
652
653
uint8 SPC7110::mcu_read(unsigned addr) {
654
if(addr <= 0xdfffff) return cartridge.rom.read(dx_offset + (addr & 0x0fffff));
655
if(addr <= 0xefffff) return cartridge.rom.read(ex_offset + (addr & 0x0fffff));
656
if(addr <= 0xffffff) return cartridge.rom.read(fx_offset + (addr & 0x0fffff));
657
return cpu.regs.mdr;
658
}
659
660
void SPC7110::mcu_write(unsigned addr, uint8 data) {
661
}
662
663
//============
664
//SPC7110::DCU
665
//============
666
667
uint8 SPC7110::dcu_read(unsigned) {
668
return mmio_read(0x4800);
669
}
670
671
void SPC7110::dcu_write(unsigned, uint8) {
672
}
673
674
//============
675
//SPC7110::RAM
676
//============
677
678
uint8 SPC7110::ram_read(unsigned addr) {
679
return cartridge.ram.read(addr & 0x1fff);
680
}
681
682
void SPC7110::ram_write(unsigned addr, uint8 data) {
683
if(r4830 & 0x80) cartridge.ram.write(addr & 0x1fff, data);
684
}
685
686
}
687
688