1 // license:LGPL-2.1+
2 // copyright-holders:Angelo Salese, R. Belmont
3 /************************************************************************************
4
5 Sega Saturn SMPC - System Manager and Peripheral Control MCU simulation
6
7 The SMPC is actually a 4-bit Hitachi HD404920FS MCU, labeled with a Sega custom
8 315-5744 (that needs decapping)
9
10 MCU simulation by Angelo Salese & R. Belmont
11
12 TODO:
13 - timings;
14 - fix intback issue with inputs (according to the docs, it should fall in between
15 VBLANK-IN and OUT, for obvious reasons);
16 - clean-ups;
17 - RTC subdevice (unknown type, handled here for convenience);
18 - Does ST-V even has a battery backed NVRAM?
19
20 Notes:
21 SMPC NVRAM contents:
22 [0] unknown (always 0)
23 [1] unknown (always 0)
24 [2] ---- -x-- Button Labels (0=enable)
25 ---- --x- Audio Out (1=Mono 0=Stereo)
26 ---- ---x BIOS audio SFXs enable (0=enable)
27 [3] language select (0=English, 5=Japanese)
28
29 *************************************************************************************/
30 /* SMPC Addresses
31
32 00
33 01 -w Input Register 0 (IREG)
34 02
35 03 -w Input Register 1
36 04
37 05 -w Input Register 2
38 06
39 07 -w Input Register 3
40 08
41 09 -w Input Register 4
42 0a
43 0b -w Input Register 5
44 0c
45 0d -w Input Register 6
46 0e
47 0f
48 10
49 11
50 12
51 13
52 14
53 15
54 16
55 17
56 18
57 19
58 1a
59 1b
60 1c
61 1d
62 1e
63 1f -w Command Register (COMREG)
64 20
65 21 r- Output Register 0 (OREG)
66 22
67 23 r- Output Register 1
68 24
69 25 r- Output Register 2
70 26
71 27 r- Output Register 3
72 28
73 29 r- Output Register 4
74 2a
75 2b r- Output Register 5
76 2c
77 2d r- Output Register 6
78 2e
79 2f r- Output Register 7
80 30
81 31 r- Output Register 8
82 32
83 33 r- Output Register 9
84 34
85 35 r- Output Register 10
86 36
87 37 r- Output Register 11
88 38
89 39 r- Output Register 12
90 3a
91 3b r- Output Register 13
92 3c
93 3d r- Output Register 14
94 3e
95 3f r- Output Register 15
96 40
97 41 r- Output Register 16
98 42
99 43 r- Output Register 17
100 44
101 45 r- Output Register 18
102 46
103 47 r- Output Register 19
104 48
105 49 r- Output Register 20
106 4a
107 4b r- Output Register 21
108 4c
109 4d r- Output Register 22
110 4e
111 4f r- Output Register 23
112 50
113 51 r- Output Register 24
114 52
115 53 r- Output Register 25
116 54
117 55 r- Output Register 26
118 56
119 57 r- Output Register 27
120 58
121 59 r- Output Register 28
122 5a
123 5b r- Output Register 29
124 5c
125 5d r- Output Register 30
126 5e
127 5f r- Output Register 31
128 60
129 61 r- SR
130 62
131 63 rw SF
132 64
133 65
134 66
135 67
136 68
137 69
138 6a
139 6b
140 6c
141 6d
142 6e
143 6f
144 70
145 71
146 72
147 73
148 74
149 75 rw PDR1
150 76
151 77 rw PDR2
152 78
153 79 -w DDR1
154 7a
155 7b -w DDR2
156 7c
157 7d -w IOSEL2/1
158 7e
159 7f -w EXLE2/1
160 */
161
162 #include "emu.h"
163 #include "machine/smpc.h"
164
165 #include "screen.h"
166 #include "coreutil.h"
167
168
169 #define LOG_SMPC 0
170 #define LOG_PAD_CMD 0
171
172 //**************************************************************************
173 // GLOBAL VARIABLES
174 //**************************************************************************
175
176 // device type definition
177 DEFINE_DEVICE_TYPE(SMPC_HLE, smpc_hle_device, "smpc_hle", "Sega Saturn SMPC HLE (HD404920FS)")
178
179 // TODO: use DEVICE_ADDRESS_MAP once this fatalerror is fixed:
180 // "uplift_submaps unhandled case: range straddling slots."
smpc_regs(address_map & map)181 void smpc_hle_device::smpc_regs(address_map &map)
182 {
183 // map.unmap_value_high();
184 map(0x00, 0x0d).w(FUNC(smpc_hle_device::ireg_w));
185 map(0x1f, 0x1f).w(FUNC(smpc_hle_device::command_register_w));
186 map(0x20, 0x5f).r(FUNC(smpc_hle_device::oreg_r));
187 map(0x61, 0x61).r(FUNC(smpc_hle_device::status_register_r));
188 map(0x63, 0x63).rw(FUNC(smpc_hle_device::status_flag_r), FUNC(smpc_hle_device::status_flag_w));
189 map(0x75, 0x75).rw(FUNC(smpc_hle_device::pdr1_r), FUNC(smpc_hle_device::pdr1_w));
190 map(0x77, 0x77).rw(FUNC(smpc_hle_device::pdr2_r), FUNC(smpc_hle_device::pdr2_w));
191 map(0x79, 0x79).w(FUNC(smpc_hle_device::ddr1_w));
192 map(0x7b, 0x7b).w(FUNC(smpc_hle_device::ddr2_w));
193 map(0x7d, 0x7d).w(FUNC(smpc_hle_device::iosel_w));
194 map(0x7f, 0x7f).w(FUNC(smpc_hle_device::exle_w));
195 }
196
197 //**************************************************************************
198 // LIVE DEVICE
199 //**************************************************************************
200
201 //-------------------------------------------------
202 // smpc_hle_device - constructor
203 //-------------------------------------------------
204
smpc_hle_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)205 smpc_hle_device::smpc_hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
206 : device_t(mconfig, SMPC_HLE, tag, owner, clock)
207 , device_memory_interface(mconfig, *this)
208 , m_space_config("regs", ENDIANNESS_LITTLE, 8, 7, 0, address_map_constructor(FUNC(smpc_hle_device::smpc_regs), this))
209 , m_mini_nvram(*this, "smem")
210 , m_mshres(*this)
211 , m_mshnmi(*this)
212 , m_sshres(*this)
213 , m_sndres(*this)
214 , m_sysres(*this)
215 , m_syshalt(*this)
216 , m_dotsel(*this)
217 , m_pdr1_read(*this)
218 , m_pdr2_read(*this)
219 , m_pdr1_write(*this)
220 , m_pdr2_write(*this)
221 , m_irq_line(*this)
222 , m_ctrl1(*this, finder_base::DUMMY_TAG)
223 , m_ctrl2(*this, finder_base::DUMMY_TAG)
224 , m_screen(*this, finder_base::DUMMY_TAG)
225 {
226 m_has_ctrl_ports = false;
227 }
228
229 //-------------------------------------------------
230 // device_add_mconfig - device-specific machine
231 // configuration addiitons
232 //-------------------------------------------------
233
device_add_mconfig(machine_config & config)234 void smpc_hle_device::device_add_mconfig(machine_config &config)
235 {
236 NVRAM(config, "smem", nvram_device::DEFAULT_ALL_0);
237
238 // TODO: custom RTC subdevice
239 }
240
241 //-------------------------------------------------
242 // device_start - device-specific startup
243 //-------------------------------------------------
244
device_start()245 void smpc_hle_device::device_start()
246 {
247 system_time systime;
248 machine().base_datetime(systime);
249
250 // check if SMEM has valid data via byte 4 in the array, if not then simulate a battery backup fail
251 // (-> call the RTC / Language select menu for Saturn)
252 m_mini_nvram->set_base(&m_smem, 5);
253
254 m_mshres.resolve_safe();
255 m_mshnmi.resolve_safe();
256 m_sshres.resolve_safe();
257 m_sndres.resolve_safe();
258 m_sysres.resolve_safe();
259 m_syshalt.resolve_safe();
260 m_dotsel.resolve_safe();
261 m_irq_line.resolve_safe();
262
263 m_pdr1_read.resolve_safe(0xff);
264 m_pdr2_read.resolve_safe(0xff);
265 m_pdr1_write.resolve_safe();
266 m_pdr2_write.resolve_safe();
267
268 save_item(NAME(m_sf));
269 save_item(NAME(m_sr));
270 save_item(NAME(m_ddr1));
271 save_item(NAME(m_ddr2));
272 save_item(NAME(m_pdr1_readback));
273 save_item(NAME(m_pdr2_readback));
274 save_item(NAME(m_iosel1));
275 save_item(NAME(m_iosel2));
276 save_item(NAME(m_exle1));
277 save_item(NAME(m_exle2));
278 save_item(NAME(m_ireg));
279 save_item(NAME(m_oreg));
280 save_item(NAME(m_comreg));
281 save_item(NAME(m_command_in_progress));
282 save_item(NAME(m_intback_buf));
283 save_item(NAME(m_intback_stage));
284 save_item(NAME(m_pmode));
285 save_item(NAME(m_rtc_data));
286 save_item(NAME(m_smem));
287
288 m_cmd_timer = timer_alloc(COMMAND_ID);
289 m_rtc_timer = timer_alloc(RTC_ID);
290 m_intback_timer = timer_alloc(INTBACK_ID);
291 m_sndres_timer = timer_alloc(SNDRES_ID);
292
293 m_rtc_data[0] = DectoBCD(systime.local_time.year / 100);
294 m_rtc_data[1] = DectoBCD(systime.local_time.year % 100);
295 m_rtc_data[2] = (systime.local_time.weekday << 4) | (systime.local_time.month+1);
296 m_rtc_data[3] = DectoBCD(systime.local_time.mday);
297 m_rtc_data[4] = DectoBCD(systime.local_time.hour);
298 m_rtc_data[5] = DectoBCD(systime.local_time.minute);
299 m_rtc_data[6] = DectoBCD(systime.local_time.second);
300 }
301
302
303 //-------------------------------------------------
304 // device_reset - device-specific reset
305 //-------------------------------------------------
306
device_reset()307 void smpc_hle_device::device_reset()
308 {
309 m_sr = 0x40; // this bit is always on according to docs (?)
310 m_sf = false;
311 m_cd_sf = false;
312 m_ddr1 = 0;
313 m_ddr2 = 0;
314 m_pdr1_readback = 0;
315 m_pdr2_readback = 0;
316
317 memset(m_ireg,0,7);
318 memset(m_oreg,0,32);
319
320 m_cmd_timer->reset();
321 m_intback_timer->reset();
322 m_sndres_timer->reset();
323 m_comreg = 0xff;
324 m_command_in_progress = false;
325 m_NMI_reset = false;
326 m_cur_dotsel = false;
327
328 m_rtc_timer->adjust(attotime::zero, 0, attotime::from_seconds(1));
329 }
330
memory_space_config() const331 device_memory_interface::space_config_vector smpc_hle_device::memory_space_config() const
332 {
333 return space_config_vector {
334 std::make_pair(0, &m_space_config)
335 };
336 }
337
338
339 //**************************************************************************
340 // READ/WRITE HANDLERS
341 //**************************************************************************
342
ireg_w(offs_t offset,uint8_t data)343 void smpc_hle_device::ireg_w(offs_t offset, uint8_t data)
344 {
345 if (!(offset & 1)) // avoid writing to even bytes
346 return;
347
348 m_ireg[offset >> 1] = data;
349
350 if(offset == 1) // check if we are under intback
351 {
352 if(m_intback_stage)
353 {
354 if(data & 0x40)
355 {
356 if(LOG_PAD_CMD) printf("SMPC: BREAK request\n");
357 sr_ack();
358 m_intback_stage = 0;
359 }
360 else if(data & 0x80)
361 {
362 if(LOG_PAD_CMD) printf("SMPC: CONTINUE request\n");
363
364 m_intback_timer->adjust(attotime::from_usec(700)); // TODO: is timing correct?
365
366 // TODO: following looks wrong here
367 m_oreg[31] = 0x10;
368 sf_set();
369 }
370 }
371 }
372 }
373
oreg_r(offs_t offset)374 uint8_t smpc_hle_device::oreg_r(offs_t offset)
375 {
376 if (!(offset & 1)) // avoid reading to even bytes (TODO: is it 0s or 1s?)
377 return 0x00;
378
379 return m_oreg[offset >> 1];
380 }
381
status_register_r()382 uint8_t smpc_hle_device::status_register_r()
383 {
384 return m_sr;
385 }
386
status_flag_r()387 uint8_t smpc_hle_device::status_flag_r()
388 {
389 // bit 3: CD enable related?
390 return (m_sf<<0) | (m_cd_sf<<3);
391 }
392
status_flag_w(uint8_t data)393 void smpc_hle_device::status_flag_w(uint8_t data)
394 {
395 m_sf = BIT(data,0);
396 m_cd_sf = false;
397 }
398
pdr1_r()399 uint8_t smpc_hle_device::pdr1_r()
400 {
401 uint8_t res = (m_pdr1_read() & ~m_ddr1) | m_pdr1_readback;
402
403 return res;
404 }
405
pdr2_r()406 uint8_t smpc_hle_device::pdr2_r()
407 {
408 uint8_t res = (m_pdr2_read() & ~m_ddr2) | m_pdr2_readback;
409
410 return res;
411 }
412
pdr1_w(uint8_t data)413 void smpc_hle_device::pdr1_w(uint8_t data)
414 {
415 // pins defined as output returns in input
416 m_pdr1_readback = (data & m_ddr1);
417 m_pdr1_readback &= 0x7f;
418 m_pdr1_write(m_pdr1_readback);
419 // bit 7 can be read back apparently
420 m_pdr1_readback |= data & 0x80;
421 }
422
pdr2_w(uint8_t data)423 void smpc_hle_device::pdr2_w(uint8_t data)
424 {
425 // pins defined as output returns in input
426 m_pdr2_readback = (data & m_ddr2);
427 m_pdr2_readback &= 0x7f;
428 m_pdr2_write(m_pdr2_readback);
429 // bit 7 can be read back apparently
430 m_pdr2_readback |= data & 0x80;
431 }
432
ddr1_w(uint8_t data)433 void smpc_hle_device::ddr1_w(uint8_t data)
434 {
435 m_ddr1 = data & 0x7f;
436 }
437
ddr2_w(uint8_t data)438 void smpc_hle_device::ddr2_w(uint8_t data)
439 {
440 m_ddr2 = data & 0x7f;
441 }
442
iosel_w(uint8_t data)443 void smpc_hle_device::iosel_w(uint8_t data)
444 {
445 m_iosel1 = BIT(data,0);
446 m_iosel2 = BIT(data,1);
447 }
448
exle_w(uint8_t data)449 void smpc_hle_device::exle_w(uint8_t data)
450 {
451 m_exle1 = BIT(data,0);
452 m_exle2 = BIT(data,1);
453 }
454
sr_ack()455 inline void smpc_hle_device::sr_ack()
456 {
457 m_sr &= 0x0f;
458 }
459
sr_set(uint8_t data)460 inline void smpc_hle_device::sr_set(uint8_t data)
461 {
462 m_sr = data;
463 }
464
sf_ack(bool cd_enable)465 inline void smpc_hle_device::sf_ack(bool cd_enable)
466 {
467 m_sf = false;
468 m_cd_sf = cd_enable;
469 }
470
sf_set()471 inline void smpc_hle_device::sf_set()
472 {
473 m_sf = true;
474 }
475
476 // Saturn Direct Mode polling check for delegate
get_iosel(bool which)477 bool smpc_hle_device::get_iosel(bool which)
478 {
479 return which == true ? m_iosel2 : m_iosel1;
480 }
481
get_ddr(bool which)482 uint8_t smpc_hle_device::get_ddr(bool which)
483 {
484 return which == true ? m_ddr2 : m_ddr1;
485 }
486
487
master_sh2_nmi()488 inline void smpc_hle_device::master_sh2_nmi()
489 {
490 m_mshnmi(1);
491 m_mshnmi(0);
492 }
493
irq_request()494 inline void smpc_hle_device::irq_request()
495 {
496 m_irq_line(1);
497 m_irq_line(0);
498 }
499
500 // TODO: trampolines that needs to go away
501
read(offs_t offset)502 uint8_t smpc_hle_device::read(offs_t offset)
503 {
504 return this->space().read_byte(offset);
505 }
506
write(offs_t offset,uint8_t data)507 void smpc_hle_device::write(offs_t offset, uint8_t data)
508 {
509 this->space().write_byte(offset,data);
510 }
511
512 //**************************************************************************
513 // Command simulation
514 //**************************************************************************
515
command_register_w(uint8_t data)516 void smpc_hle_device::command_register_w(uint8_t data)
517 {
518 // don't send a command if previous one is still in progress
519 // ST-V tries to send a sysres command if OREG31 doesn't return the ack command
520 if(m_command_in_progress == true)
521 return;
522
523 m_comreg = data & 0x1f;
524
525 if(data & 0xe0)
526 logerror("%s COMREG = %02x!?\n",this->tag(),data);
527
528 m_command_in_progress = true;
529 if(m_comreg == 0x0e || m_comreg == 0x0f)
530 {
531 /* on ST-V timing of this is pretty fussy, you get 2 credits at start-up otherwise
532 * My current theory is that the PLL device can halt the whole system until the frequency change occurs.
533 * (cfr. diagram on page 3 of SMPC manual)
534 * I really don't think that the system can do an usable mid-frame clock switching anyway.
535 */
536 m_syshalt(1);
537
538 m_cmd_timer->adjust(m_screen->time_until_pos(m_screen->visible_area().max_y,0));
539 }
540 else if(m_comreg == 0x10)
541 {
542 // copy ireg to our intback buffer
543 for(int i=0;i<3;i++)
544 m_intback_buf[i] = m_ireg[i];
545
546 // calculate the timing for intback command
547 int timing;
548
549 timing = 8;
550
551 if( m_ireg[0] != 0) // non-peripheral data
552 timing += 8;
553
554 // TODO: At vblank-out actually ...
555 if( m_ireg[1] & 8) // peripheral data
556 timing += 700;
557
558 // TODO: check against ireg2, must be 0xf0
559
560 m_cmd_timer->adjust(attotime::from_usec(timing));
561 }
562 else
563 m_cmd_timer->adjust(attotime::from_usec(m_cmd_table_timing[m_comreg]));
564 }
565
566
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)567 void smpc_hle_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
568 {
569 switch(id)
570 {
571 case COMMAND_ID:
572 {
573 switch(m_comreg)
574 {
575 case 0x00: // MSHON
576 // enable Master SH2
577 m_mshres(m_comreg & 1);
578 break;
579
580 case 0x02: // SSHON
581 case 0x03: // SSHOFF
582 // enable or disable Slave SH2
583 m_sshres(m_comreg & 1);
584 break;
585
586 case 0x06: // SNDON
587 case 0x07: // SNDOFF
588 // enable or disable 68k
589 m_sndres(m_comreg & 1);
590 break;
591
592 case 0x08: // CDON
593 case 0x09: // CDOFF
594 // ...
595 m_command_in_progress = false;
596 m_oreg[31] = m_comreg;
597 sf_ack(true); //clear hand-shake flag (TODO: diagnostic wants this to have bit 3 high)
598 return;
599
600 // case 0x0a: // NETLINKON
601 // case 0x0b: // NETLINKOFF
602
603 case 0x0d: // SYSRES
604 // send a 1 -> 0 to device reset lines
605 m_sysres(1);
606 m_sysres(0);
607
608 // send a 1 -> 0 transition to reset line (was PULSE_LINE)
609 m_mshres(1);
610 m_mshres(0);
611 break;
612
613 case 0x0e: // CKCHG352
614 case 0x0f: // CKCHG320
615 m_dotsel(m_comreg & 1);
616
617 // send a NMI to Master SH2 if enabled
618 if(m_NMI_reset == false)
619 master_sh2_nmi();
620
621 // assert Slave SH2 line
622 m_sshres(1);
623 // clear PLL system halt
624 m_syshalt(0);
625
626 // setup the new dot select
627 m_cur_dotsel = (m_comreg & 1) ^ 1;
628 break;
629
630 case 0x10: // INTBACK
631 resolve_intback();
632 return;
633
634 case 0x16: // SETTIME
635 {
636 for(int i=0;i<7;i++)
637 m_rtc_data[i] = m_ireg[i];
638 break;
639 }
640
641 case 0x17: // SETSMEM
642 {
643 for(int i=0;i<4;i++)
644 m_smem[i] = m_ireg[i];
645
646 // clear the SETIME variable, simulate a cr2032 battery alive in the system
647 m_smem[4] = 0xff;
648 break;
649 }
650
651 case 0x18: // NMIREQ
652 // NMI is unconditionally requested
653 master_sh2_nmi();
654 break;
655
656 case 0x19: // RESENAB
657 case 0x1a: // RESDISA
658 m_NMI_reset = m_comreg & 1;
659 break;
660
661 default:
662 logerror("%s unemulated %02x command\n",this->tag(),m_comreg);
663 return;
664 }
665
666 m_command_in_progress = false;
667 m_oreg[31] = m_comreg;
668 sf_ack(false);
669 break;
670 }
671
672 case INTBACK_ID: intback_continue_request(); break;
673 case RTC_ID: handle_rtc_increment(); break;
674
675 // from m68k reset opcode trigger
676 case SNDRES_ID:
677 m_sndres(1);
678 m_sndres(0);
679 break;
680
681 default:
682 printf("%d\n",id);
683 break;
684 }
685 }
686
resolve_intback()687 void smpc_hle_device::resolve_intback()
688 {
689 int i;
690
691 m_command_in_progress = false;
692
693 if(m_intback_buf[0] != 0)
694 {
695 m_oreg[0] = ((m_smem[4] & 0x80) | ((m_NMI_reset & 1) << 6));
696
697 for(i=0;i<7;i++)
698 m_oreg[1+i] = m_rtc_data[i];
699
700 m_oreg[8] = 0; // CTG0 / CTG1?
701
702 m_oreg[9] = m_region_code; // TODO: system region on Saturn
703
704 /*
705 0-11 -1-- unknown
706 -x-- ---- VDP2 dot select
707 ---- x--- MSHNMI
708 ---- --x- SYSRES
709 ---- ---x SOUNDRES
710 */
711 m_oreg[10] = 0 << 7 |
712 m_cur_dotsel << 6 |
713 1 << 5 |
714 1 << 4 |
715 0 << 3 |
716 1 << 2 |
717 0 << 1 |
718 0 << 0;
719
720 m_oreg[11] = 0 << 6; // CDRES
721
722 for(i=0;i<4;i++)
723 m_oreg[12+i] = m_smem[i];
724
725 for(i=0;i<15;i++)
726 m_oreg[16+i] = 0xff; // undefined
727
728 m_intback_stage = (m_intback_buf[1] & 8) >> 3; // first peripheral
729 sr_set(0x40 | (m_intback_stage << 5));
730 m_pmode = m_intback_buf[0]>>4;
731
732 irq_request();
733
734 // put issued command in OREG31
735 m_oreg[31] = 0x10; // TODO: doc says 0?
736 /* clear hand-shake flag */
737 sf_ack(false);
738 }
739 else if(m_intback_buf[1] & 8)
740 {
741 m_intback_stage = (m_intback_buf[1] & 8) >> 3; // first peripheral
742 sr_set(0x40);
743 m_oreg[31] = 0x10;
744 intback_continue_request();
745 }
746 else
747 {
748 /* Shienryu calls this, it would be plainly illegal on Saturn, I'll just return the command and clear the hs flag for now. */
749 m_oreg[31] = 0x10;
750 sf_ack(false);
751 }
752 }
753
intback_continue_request()754 void smpc_hle_device::intback_continue_request()
755 {
756 if( m_has_ctrl_ports == true )
757 read_saturn_ports();
758
759 if (m_intback_stage == 2)
760 {
761 sr_set(0x80 | m_pmode); // pad 2, no more data, echo back pad mode set by intback
762 m_intback_stage = 0;
763 }
764 else
765 {
766 sr_set(0xc0 | m_pmode); // pad 1, more data, echo back pad mode set by intback
767 m_intback_stage ++;
768 }
769 irq_request();
770
771 m_oreg[31] = 0x10; // callback for last command issued
772 sf_ack(false);
773 }
774
DectoBCD(int num)775 int smpc_hle_device::DectoBCD(int num)
776 {
777 int i, cnt = 0, tmp, res = 0;
778
779 while (num > 0) {
780 tmp = num;
781 while (tmp >= 10) tmp %= 10;
782 for (i=0; i<cnt; i++)
783 tmp *= 16;
784 res += tmp;
785 cnt++;
786 num /= 10;
787 }
788
789 return res;
790 }
791
792 //**************************************************************************
793 // RTC handling
794 //**************************************************************************
795
handle_rtc_increment()796 void smpc_hle_device::handle_rtc_increment()
797 {
798 const uint8_t dpm[12] = { 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31 };
799 int year_num, year_count;
800
801 /*
802 m_smpc.rtc_data[0] = DectoBCD(systime.local_time.year /100);
803 m_smpc.rtc_data[1] = DectoBCD(systime.local_time.year %100);
804 m_smpc.rtc_data[2] = (systime.local_time.weekday << 4) | (systime.local_time.month+1);
805 m_smpc.rtc_data[3] = DectoBCD(systime.local_time.mday);
806 m_smpc.rtc_data[4] = DectoBCD(systime.local_time.hour);
807 m_smpc.rtc_data[5] = DectoBCD(systime.local_time.minute);
808 m_smpc.rtc_data[6] = DectoBCD(systime.local_time.second);
809 */
810
811 m_rtc_data[6]++;
812
813 /* seconds from 9 -> 10*/
814 if((m_rtc_data[6] & 0x0f) >= 0x0a) { m_rtc_data[6]+=0x10; m_rtc_data[6]&=0xf0; }
815 /* seconds from 59 -> 0 */
816 if((m_rtc_data[6] & 0xf0) >= 0x60) { m_rtc_data[5]++; m_rtc_data[6] = 0; }
817 /* minutes from 9 -> 10 */
818 if((m_rtc_data[5] & 0x0f) >= 0x0a) { m_rtc_data[5]+=0x10; m_rtc_data[5]&=0xf0; }
819 /* minutes from 59 -> 0 */
820 if((m_rtc_data[5] & 0xf0) >= 0x60) { m_rtc_data[4]++; m_rtc_data[5] = 0; }
821 /* hours from 9 -> 10 */
822 if((m_rtc_data[4] & 0x0f) >= 0x0a) { m_rtc_data[4]+=0x10; m_rtc_data[4]&=0xf0; }
823 /* hours from 23 -> 0 */
824 if((m_rtc_data[4] & 0xff) >= 0x24) { m_rtc_data[3]++; m_rtc_data[2]+=0x10; m_rtc_data[4] = 0; }
825 /* week day name sunday -> monday */
826 if((m_rtc_data[2] & 0xf0) >= 0x70) { m_rtc_data[2]&=0x0f; }
827 /* day number 9 -> 10 */
828 if((m_rtc_data[3] & 0x0f) >= 0x0a) { m_rtc_data[3]+=0x10; m_rtc_data[3]&=0xf0; }
829
830 // year BCD to dec conversion (for the leap year stuff)
831 {
832 year_num = (m_rtc_data[1] & 0xf);
833
834 for(year_count = 0; year_count < (m_rtc_data[1] & 0xf0); year_count += 0x10)
835 year_num += 0xa;
836
837 year_num += (m_rtc_data[0] & 0xf)*0x64;
838
839 for(year_count = 0; year_count < (m_rtc_data[0] & 0xf0); year_count += 0x10)
840 year_num += 0x3e8;
841 }
842
843 /* month +1 check */
844 /* the RTC have a range of 1980 - 2100, so we don't actually need to support the leap year special conditions */
845 if(((year_num % 4) == 0) && (m_rtc_data[2] & 0xf) == 2)
846 {
847 if((m_rtc_data[3] & 0xff) >= dpm[(m_rtc_data[2] & 0xf)-1]+1+1)
848 { m_rtc_data[2]++; m_rtc_data[3] = 0x01; }
849 }
850 else if((m_rtc_data[3] & 0xff) >= dpm[(m_rtc_data[2] & 0xf)-1]+1){ m_rtc_data[2]++; m_rtc_data[3] = 0x01; }
851 /* year +1 check */
852 if((m_rtc_data[2] & 0x0f) > 12) { m_rtc_data[1]++; m_rtc_data[2] = (m_rtc_data[2] & 0xf0) | 0x01; }
853 /* year from 9 -> 10 */
854 if((m_rtc_data[1] & 0x0f) >= 0x0a) { m_rtc_data[1]+=0x10; m_rtc_data[1]&=0xf0; }
855 /* year from 99 -> 100 */
856 if((m_rtc_data[1] & 0xf0) >= 0xa0) { m_rtc_data[0]++; m_rtc_data[1] = 0; }
857
858 // probably not SO precise, here just for reference ...
859 /* year from 999 -> 1000 */
860 //if((m_rtc_data[0] & 0x0f) >= 0x0a) { m_rtc_data[0]+=0x10; m_rtc_data[0]&=0xf0; }
861 /* year from 9999 -> 0 */
862 //if((m_rtc_data[0] & 0xf0) >= 0xa0) { m_rtc_data[0] = 0; } //roll over
863 }
864
865
866
867
868 /********************************************
869 *
870 * Saturn handlers
871 *
872 *******************************************/
873
874 /*
875 [0] port status:
876 0x04 Sega-tap
877 0x16 Multi-tap
878 0x2x clock serial peripheral
879 0xf0 peripheral isn't connected
880 0xf1 peripheral is connected
881 [1] Peripheral ID (note: lowest four bits determines the size of the input packet)
882 0x02 digital pad
883 0x25 (tested by Game Basic?)
884 0x34 keyboard
885
886 Lower 4 bits of the port status tell the number of controllers to check for the port
887 Lower 4 bits of the peripheral ID tell the number of registers used by each controller
888 For multitap / segatap, we have implemented the following logic:
889 SMPC reads in sequence
890 - status for port 1
891 - ID first controller, followed by the number of reads needed by the plugged controller
892 - ID second controller, followed by the number of reads needed by the plugged controller
893 - and so on... until the 4th (for SegaTap) or 6th (for Multitap) controller is read
894 TODO: how does the multitap check if a controller is connected? does it ask for the
895 controller status of each subport? how does this work exactly?
896 currently, there is a small problem in some specific controller config which seems to
897 lose track of one controller. E.g. if I put multitap in port2 with inserted joy1, joy2 and joy4
898 it does not see joy4 controller, but if I put joy1, joy2, joy4 and joy5 it sees
899 all four of them. The same happens if I skip controllers with id = 0xff...
900 how did a real unit behave in this case?
901 */
902
read_saturn_ports()903 void smpc_hle_device::read_saturn_ports()
904 {
905 uint8_t status1 = m_ctrl1 ? m_ctrl1->read_status() : 0xf0;
906 uint8_t status2 = m_ctrl2 ? m_ctrl2->read_status() : 0xf0;
907
908 uint8_t reg_offset = 0;
909 uint8_t ctrl1_offset = 0; // this is used when there is segatap or multitap connected
910 uint8_t ctrl2_offset = 0; // this is used when there is segatap or multitap connected
911
912 m_oreg[reg_offset++] = status1;
913
914 // read ctrl1
915 for (int i = 0; i < (status1 & 0xf); i++)
916 {
917 uint8_t id = m_ctrl1->read_id(i);
918
919 m_oreg[reg_offset++] = id;
920 for (int j = 0; j < (id & 0xf); j++)
921 m_oreg[reg_offset++] = m_ctrl1->read_ctrl(j + ctrl1_offset);
922
923 ctrl1_offset += (id & 0xf);
924 }
925
926 m_oreg[reg_offset++] = status2;
927
928 // read ctrl2
929 for (int i = 0; i < (status2 & 0xf); i++)
930 {
931 uint8_t id = m_ctrl2->read_id(i);
932
933 m_oreg[reg_offset++] = id;
934
935 for (int j = 0; j < (id & 0xf); j++)
936 m_oreg[reg_offset++] = m_ctrl2->read_ctrl(j + ctrl2_offset);
937
938 ctrl2_offset += (id & 0xf);
939 }
940 }
941
INPUT_CHANGED_MEMBER(smpc_hle_device::trigger_nmi_r)942 INPUT_CHANGED_MEMBER(smpc_hle_device::trigger_nmi_r )
943 {
944 // punt if NMI trigger is disabled
945 if(!m_NMI_reset)
946 return;
947
948 // TODO: generated during the 3VINT period according to manual
949 if(newval)
950 master_sh2_nmi();
951 }
952
953 /* Official documentation says that the "RESET/TAS opcodes aren't supported", but Out Run definitely contradicts with it.
954 Since that m68k can't reset itself via the RESET opcode I suppose that the SMPC actually do it by reading an i/o
955 connected to this opcode. */
m68k_reset_trigger()956 void smpc_hle_device::m68k_reset_trigger()
957 {
958 m_sndres_timer->adjust(attotime::from_usec(100));
959 }
960