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