OSDN Git Service

[DOC] For release 2017-01-24.
[csp-qt/common_source_project-fm7.git] / source / src / vm / mc6840.cpp
1 /*
2         Skelton for retropc emulator
3
4         Origin : MAME 0.168 Motorola 6840 (PTM)
5         Author : Takeda.Toshiya
6         Date   : 2016.02.24-
7
8         [ MC6840 ]
9 */
10
11 // license:BSD-3-Clause
12 // copyright-holders:James Wallace
13 /***************************************************************************
14
15     Motorola 6840 (PTM)
16
17     Programmable Timer Module
18
19     Written By J.Wallace based on previous work by Aaron Giles,
20    'Re-Animator' and Mathis Rosenhauer.
21
22     Todo:
23          Confirm handling for 'Single Shot' operation.
24          (Datasheet suggests that output starts high, going low
25          on timeout, opposite of continuous case)
26          Establish whether ptm6840_set_c? routines can replace
27          hard coding of external clock frequencies.
28
29
30     Operation:
31     The interface is arranged as follows:
32
33     Internal Clock frequency,
34     Clock 1 frequency, Clock 2 frequency, Clock 3 frequency,
35     Clock 1 output, Clock 2 output, Clock 3 output,
36     IRQ function
37
38     If the external clock frequencies are not fixed, they should be
39     entered as '0', and the ptm6840_set_c?(which, state) functions
40     should be used instead if necessary (This should allow the VBLANK
41     clock on the MCR units to operate).
42
43
44     2009-06 Converted to be a device
45
46 ***************************************************************************/
47
48 #include "mc6840.h"
49
50 #define from_hz(v)      (1000000.0 / (double)(v))
51 typedef double attotime;
52
53 #define m_out0_cb(o,v)  write_signals(&outputs_ch0, (v) ? 0xffffffff : 0)
54 #define m_out1_cb(o,v)  write_signals(&outputs_ch1, (v) ? 0xffffffff : 0)
55 #define m_out2_cb(o,v)  write_signals(&outputs_ch2, (v) ? 0xffffffff : 0)
56 #define m_irq_cb(v)     write_signals(&outputs_irq, (v) ? 0xffffffff : 0)
57
58 //#define PTMVERBOSE 0
59 //#define PLOG(x) do { if (PTMVERBOSE) logerror x; } while (0)
60
61 /***************************************************************************
62     IMPLEMENTATION
63 ***************************************************************************/
64
65 //-------------------------------------------------
66 //  device_start - device-specific startup
67 //-------------------------------------------------
68
69 void MC6840::initialize()
70 {
71         for (int i = 0; i < 3; i++)
72         {
73 //              if ( m_external_clock[i] == 0 )
74 //              {
75 //                      m_external_clock[i] = 1;
76 //              }
77                 m_gate[i] = m_clk[i] = 0;
78         }
79 }
80
81 //-------------------------------------------------
82 //  device_reset - device-specific reset
83 //-------------------------------------------------
84
85 void MC6840::reset()
86 {
87         m_control_reg[2]         = 0;
88         m_control_reg[1]         = 0;
89         m_control_reg[0]         = 1;
90         m_status_reg             = 0;
91         m_t3_divisor             = 1;
92         m_status_read_since_int = 0;
93         m_IRQ                   = 0;
94         m_t3_scaler             = 0;
95         for (int i = 0; i < 3; i++)
96         {
97                 m_counter[i] = 0xffff;
98                 m_latch[i]   = 0xffff;
99                 m_output[i]  = 0;
100                 m_fired[i]   = 0;
101                 m_enabled[i] = 0;
102                 m_mode[i] = 0;
103                 m_timer[i] = -1;
104         }
105 }
106
107 //-------------------------------------------------
108 //  subtract_from_counter - Subtract from Counter
109 //-------------------------------------------------
110
111 void MC6840::subtract_from_counter(int counter, int count)
112 {
113         double clock;
114
115         // Determine the clock frequency for this timer
116         if (m_control_reg[counter] & 0x02)
117         {
118                 clock = m_internal_clock;
119         }
120         else
121         {
122                 clock = m_external_clock[counter];
123         }
124
125         // Dual-byte mode
126         if (m_control_reg[counter] & 0x04)
127         {
128                 int lsb = m_counter[counter] & 0xff;
129                 int msb = m_counter[counter] >> 8;
130
131                 // Count the clocks
132                 lsb -= count;
133
134                 // Loop while we're less than zero
135                 while (lsb < 0)
136                 {
137                         // Borrow from the MSB
138                         lsb += (m_latch[counter] & 0xff) + 1;
139                         msb--;
140
141                         // If MSB goes less than zero, we've expired
142                         if (msb < 0)
143                         {
144                                 timeout(counter);
145                                 msb = (m_latch[counter] >> 8) + 1;
146                         }
147                 }
148
149                 // Store the result
150                 m_counter[counter] = (msb << 8) | lsb;
151         }
152
153         // Word mode
154         else
155         {
156                 int word = m_counter[counter];
157
158                 // Count the clocks
159                 word -= count;
160
161                 // loop while we're less than zero
162                 while (word < 0)
163                 {
164                         // Borrow from the MSB
165                         word += m_latch[counter] + 1;
166
167                         // We've expired
168                         timeout(counter);
169                 }
170
171                 // Store the result
172                 m_counter[counter] = word;
173         }
174
175         if (clock && m_enabled[counter])
176         {
177                 attotime duration = from_hz(clock) * m_counter[counter];
178
179                 if (counter == 2)
180                 {
181                         duration *= m_t3_divisor;
182                 }
183
184                 if (m_timer[counter] != -1)
185                 {
186                         cancel_event(this, m_timer[counter]);
187                 }
188                 register_event(this, counter, duration, false, &m_timer[counter]);
189         }
190 }
191
192 //-------------------------------------------------
193 //  tick
194 //-------------------------------------------------
195
196 void MC6840::tick(int counter, int count)
197 {
198         if (counter == 2)
199         {
200                 m_t3_scaler += count;
201
202                 if ( m_t3_scaler > m_t3_divisor - 1)
203                 {
204                         subtract_from_counter(counter, 1);
205                         m_t3_scaler = 0;
206                 }
207         }
208         else
209         {
210                 subtract_from_counter(counter, count);
211         }
212 }
213
214 //-------------------------------------------------
215 //  update_interrupts - Update Internal Interrupts
216 //-------------------------------------------------
217
218 void MC6840::update_interrupts()
219 {
220         int new_state = ((m_status_reg & 0x01) && (m_control_reg[0] & 0x40)) ||
221                                         ((m_status_reg & 0x02) && (m_control_reg[1] & 0x40)) ||
222                                         ((m_status_reg & 0x04) && (m_control_reg[2] & 0x40));
223
224         if (new_state != m_IRQ)
225         {
226                 m_IRQ = new_state;
227
228                 if (m_IRQ)
229                 {
230                         m_status_reg |= 0x80;
231                 }
232                 else
233                 {
234                         m_status_reg &= ~0x80;
235                 }
236
237                 m_irq_cb(m_IRQ);
238         }
239 }
240
241 //-------------------------------------------------
242 //  compute_counter - Compute Counter
243 //-------------------------------------------------
244
245 UINT16 MC6840::compute_counter( int counter )
246 {
247         double clock;
248
249         // determine the clock frequency for this timer
250         if (m_control_reg[counter] & 0x02)
251         {
252                 clock = m_internal_clock;
253 //              PLOG(("MC6840 #%s: %d internal clock freq %f \n", tag(), counter, clock));
254         }
255         else
256         {
257                 clock = m_external_clock[counter];
258 //              PLOG(("MC6840 #%s: %d external clock freq %f \n", tag(), counter, clock));
259         }
260
261         // If there's no timer, return the count
262         if (!(clock && m_enabled[counter]))
263         {
264 //              PLOG(("MC6840 #%s: read counter(%d): %d\n", tag(), counter, m_counter[counter]));
265                 return m_counter[counter];
266         }
267
268         // See how many are left
269         int remaining = (int)(get_event_remaining_usec(m_timer[counter]) / 1000000.0 * clock);
270
271         // Adjust the count for dual byte mode
272         if (m_control_reg[counter] & 0x04)
273         {
274                 int divisor = (m_counter[counter] & 0xff) + 1;
275                 int msb = remaining / divisor;
276                 int lsb = remaining % divisor;
277                 remaining = (msb << 8) | lsb;
278         }
279 //      PLOG(("MC6840 #%s: read counter(%d): %d\n", tag(), counter, remaining));
280         return remaining;
281 }
282
283 //-------------------------------------------------
284 //  reload_count - Reload Counter
285 //-------------------------------------------------
286
287 void MC6840::reload_count(int idx)
288 {
289         double clock;
290
291         // Copy the latched value in
292         m_counter[idx] = m_latch[idx];
293
294         // Determine the clock frequency for this timer
295         if (m_control_reg[idx] & 0x02)
296         {
297                 clock = m_internal_clock;
298 //              PLOG(("MC6840 #%s: %d internal clock freq %f \n", tag(), idx, clock));
299         }
300         else
301         {
302                 clock = m_external_clock[idx];
303 //              PLOG(("MC6840 #%s: %d external clock freq %f \n", tag(), idx, clock));
304         }
305
306         // Determine the number of clock periods before we expire
307         int count = m_counter[idx];
308         if (m_control_reg[idx] & 0x04)
309         {
310                 count = ((count >> 8) + 1) * ((count & 0xff) + 1);
311         }
312         else
313         {
314                 count = count + 1;
315         }
316
317         m_fired[idx] = 0;
318
319         if ((m_mode[idx] == 4) || (m_mode[idx] == 6))
320         {
321                 m_output[idx] = 1;
322                 switch (idx)
323                 {
324                         case 0:
325                                 m_out0_cb((offs_t)0, m_output[0]);
326                                 break;
327                         case 1:
328                                 m_out1_cb((offs_t)0, m_output[1]);
329                                 break;
330                         case 2:
331                                 m_out2_cb((offs_t)0, m_output[2]);
332                                 break;
333                 }
334         }
335
336         if(clock) {
337                 // Set the timer
338 //              PLOG(("MC6840 #%s: reload_count(%d): clock = %f  count = %d\n", tag(), idx, clock, count));
339
340                 attotime duration = from_hz(clock) * count;
341                 if (idx == 2)
342                 {
343                         duration *= m_t3_divisor;
344                 }
345
346 //              PLOG(("MC6840 #%s: reload_count(%d): output = %f\n", tag(), idx, duration.as_double()));
347
348                 if (m_timer[idx] != -1)
349                 {
350                         cancel_event(this, m_timer[idx]);
351                 }
352                 register_event(this, idx, duration, false, &m_timer[idx]);
353         }
354
355         m_enabled[idx] = 1;
356 }
357
358 //-------------------------------------------------
359 //  read - Read Timer
360 //-------------------------------------------------
361
362 uint32_t MC6840::read_io8(uint32_t offset)
363 {
364         int val;
365
366         switch ( offset & 7 )
367         {
368                 case PTM_6840_CTRL1:
369                 {
370                         val = 0;
371                         break;
372                 }
373
374                 case PTM_6840_STATUS:
375                 {
376 //                      PLOG(("%s: MC6840 #%s: Status read = %04X\n", machine().describe_context(), tag(), m_status_reg));
377                         m_status_read_since_int |= m_status_reg & 0x07;
378                         val = m_status_reg;
379                         break;
380                 }
381
382                 case PTM_6840_MSBBUF1:
383                 case PTM_6840_MSBBUF2:
384                 case PTM_6840_MSBBUF3:
385                 {
386                         int idx = (offset - 2) / 2;
387                         int result = compute_counter(idx);
388
389                         // Clear the interrupt if the status has been read
390                         if (m_status_read_since_int & (1 << idx))
391                         {
392                                 m_status_reg &= ~(1 << idx);
393                                 update_interrupts();
394                         }
395
396                         m_lsb_buffer = result & 0xff;
397
398 //                      PLOG(("%s: MC6840 #%s: Counter %d read = %04X\n", machine().describe_context(), tag(), idx, result >> 8));
399                         val = result >> 8;
400                         break;
401                 }
402
403                 case PTM_6840_LSB1:
404                 case PTM_6840_LSB2:
405                 case PTM_6840_LSB3:
406                 {
407                         val = m_lsb_buffer;
408                         break;
409                 }
410
411                 default:
412                 {
413                         val = 0;
414                         break;
415                 }
416
417         }
418         return val;
419 }
420
421 //-------------------------------------------------
422 //  write - Write Timer
423 //-------------------------------------------------
424
425 void MC6840::write_io8(uint32_t offset, uint32_t data)
426 {
427         switch ( offset & 7 )
428         {
429                 case PTM_6840_CTRL1:
430                 case PTM_6840_CTRL2:
431                 {
432                         int idx = (offset == 1) ? 1 : (m_control_reg[1] & 0x01) ? 0 : 2;
433                         UINT8 diffs = data ^ m_control_reg[idx];
434                         m_t3_divisor = (m_control_reg[2] & 0x01) ? 8 : 1;
435                         m_mode[idx] = (data >> 3) & 0x07;
436                         m_control_reg[idx] = data;
437
438 //                      PLOG(("MC6840 #%s : Control register %d selected\n", tag(), idx));
439 //                      PLOG(("operation mode   = %s\n", opmode[ m_mode[idx] ]));
440 //                      PLOG(("value            = %04X\n", m_control_reg[idx]));
441 //                      PLOG(("t3divisor        = %d\n", m_t3_divisor));
442
443                         if (!(m_control_reg[idx] & 0x80 ))
444                         {
445                                 // Output cleared
446                                 switch (idx)
447                                 {
448                                         case 0:
449                                                 m_out0_cb((offs_t)0, 0);
450                                                 break;
451                                         case 1:
452                                                 m_out1_cb((offs_t)0, 0);
453                                                 break;
454                                         case 2:
455                                                 m_out2_cb((offs_t)0, 0);
456                                                 break;
457                                 }
458                         }
459                         // Reset?
460                         if (idx == 0 && (diffs & 0x01))
461                         {
462                                 // Holding reset down
463                                 if (data & 0x01)
464                                 {
465 //                                      PLOG(("MC6840 #%s : Timer reset\n", tag()));
466                                         for (int i = 0; i < 3; i++)
467                                         {
468                                                 if (m_timer[i] != -1)
469                                                 {
470                                                         cancel_event(this, m_timer[i]);
471                                                         m_timer[i] = -1;
472                                                 }
473                                                 m_enabled[i] = 0;
474                                         }
475                                 }
476                                 // Releasing reset
477                                 else
478                                 {
479                                         for (int i = 0; i < 3; i++)
480                                         {
481                                                 reload_count(i);
482                                         }
483                                 }
484
485                                 m_status_reg = 0;
486                                 update_interrupts();
487
488                                 // Changing the clock source? (e.g. Zwackery)
489                                 if (diffs & 0x02)
490                                 {
491                                         reload_count(idx);
492                                 }
493                         }
494                         break;
495                 }
496
497                 case PTM_6840_MSBBUF1:
498                 case PTM_6840_MSBBUF2:
499                 case PTM_6840_MSBBUF3:
500                 {
501 //                      PLOG(("MC6840 #%s msbbuf%d = %02X\n", tag(), offset / 2, data));
502                         m_msb_buffer = data;
503                         break;
504                 }
505
506                 case PTM_6840_LSB1:
507                 case PTM_6840_LSB2:
508                 case PTM_6840_LSB3:
509                 {
510                         int idx = (offset - 3) / 2;
511                         m_latch[idx] = (m_msb_buffer << 8) | (data & 0xff);
512
513                         // Clear the interrupt
514                         m_status_reg &= ~(1 << idx);
515                         update_interrupts();
516
517                         // Reload the count if in an appropriate mode
518                         if (!(m_control_reg[idx] & 0x10))
519                         {
520                                 reload_count(idx);
521                         }
522
523 //                      PLOG(("%s:MC6840 #%s: Counter %d latch = %04X\n", machine().describe_context(), tag(), idx, m_latch[idx]));
524                         break;
525                 }
526         }
527 }
528
529 //-------------------------------------------------
530 //  timeout - Called if timer is mature
531 //-------------------------------------------------
532
533 void MC6840::timeout(int idx)
534 {
535 //      PLOG(("**ptm6840 %s t%d timeout**\n", tag(), idx));
536
537         // Set the interrupt flag
538         m_status_reg |= (1 << idx);
539         m_status_read_since_int &= ~(1 << idx);
540         update_interrupts();
541
542         if ( m_control_reg[idx] & 0x80 )
543         {
544                 if ((m_mode[idx] == 0)||(m_mode[idx] == 2))
545                 {
546                         m_output[idx] = m_output[idx] ? 0 : 1;
547 //                      PLOG(("**ptm6840 %s t%d output %d **\n", tag(), idx, m_output[idx]));
548
549                         switch (idx)
550                         {
551                                 case 0:
552                                         m_out0_cb((offs_t)0, m_output[0]);
553                                         break;
554                                 case 1:
555                                         m_out1_cb((offs_t)0, m_output[1]);
556                                         break;
557                                 case 2:
558                                         m_out2_cb((offs_t)0, m_output[2]);
559                                         break;
560                         }
561                 }
562                 if ((m_mode[idx] == 4)||(m_mode[idx] == 6))
563                 {
564                         if (!m_fired[idx])
565                         {
566                                 m_output[idx] = 1;
567 //                              PLOG(("**ptm6840 %s t%d output %d **\n", tag(), idx, m_output[idx]));
568
569                                 switch (idx)
570                                 {
571                                         case 0:
572                                                 m_out0_cb((offs_t)0, m_output[0]);
573                                                 break;
574                                         case 1:
575                                                 m_out1_cb((offs_t)0, m_output[1]);
576                                                 break;
577                                         case 2:
578                                                 m_out2_cb((offs_t)0, m_output[2]);
579                                                 break;
580                                 }
581
582                                 // No changes in output until reinit
583                                 m_fired[idx] = 1;
584
585                                 m_status_reg |= (1 << idx);
586                                 m_status_read_since_int &= ~(1 << idx);
587                                 update_interrupts();
588                         }
589                 }
590         }
591         m_enabled[idx]= 0;
592         reload_count(idx);
593 }
594
595 //-------------------------------------------------
596 //  set_gate - set gate status (0 or 1)
597 //-------------------------------------------------
598
599 void MC6840::set_gate(int idx, int state)
600 {
601         if ((m_mode[idx] & 1) == 0)
602         {
603                 if (state == 0 && m_gate[idx])
604                 {
605                         reload_count(idx);
606                 }
607         }
608         m_gate[idx] = state;
609 }
610
611 //-------------------------------------------------
612 //  set_clock - set clock status (0 or 1)
613 //-------------------------------------------------
614
615 void MC6840::set_clock(int idx, int state)
616 {
617
618         if (!(m_control_reg[idx] & 0x02))
619         {
620                 if (state && m_clk[idx] == 0)
621                 {
622                         tick(idx, 1);
623                 }
624         }
625         m_clk[idx] = state;
626 }
627
628 void MC6840::event_callback(int id, int err)
629 {
630         m_timer[id] = -1;
631         timeout(id);
632 }
633
634 void MC6840::write_signal(int id, uint32_t data, uint32_t mask)
635 {
636         switch (id)
637         {
638                 case SIG_MC6840_CLOCK_0:
639                         set_clock(0, (data & mask) ? 1 : 0);
640                         break;
641                 case SIG_MC6840_CLOCK_1:
642                         set_clock(1, (data & mask) ? 1 : 0);
643                         break;
644                 case SIG_MC6840_CLOCK_2:
645                         set_clock(2, (data & mask) ? 1 : 0);
646                         break;
647                 case SIG_MC6840_GATE_0:
648                         set_gate(0, (data & mask) ? 1 : 0);
649                         break;
650                 case SIG_MC6840_GATE_1:
651                         set_gate(1, (data & mask) ? 1 : 0);
652                         break;
653                 case SIG_MC6840_GATE_2:
654                         set_gate(2, (data & mask) ? 1 : 0);
655                         break;
656         }
657 }
658
659 #define STATE_VERSION   1
660
661 void MC6840::save_state(FILEIO* state_fio)
662 {
663         state_fio->FputUint32(STATE_VERSION);
664         state_fio->FputInt32(this_device_id);
665         
666         state_fio->Fwrite(m_control_reg, sizeof(m_control_reg), 1);
667         state_fio->Fwrite(m_output, sizeof(m_output), 1);
668         state_fio->Fwrite(m_gate, sizeof(m_gate), 1);
669         state_fio->Fwrite(m_clk, sizeof(m_clk), 1);
670         state_fio->Fwrite(m_enabled, sizeof(m_enabled), 1);
671         state_fio->Fwrite(m_mode, sizeof(m_mode), 1);
672         state_fio->Fwrite(m_fired, sizeof(m_fired), 1);
673         state_fio->FputUint8(m_t3_divisor);
674         state_fio->FputUint8(m_t3_scaler);
675         state_fio->FputUint8(m_IRQ);
676         state_fio->FputUint8(m_status_reg);
677         state_fio->FputUint8(m_status_read_since_int);
678         state_fio->FputUint8(m_lsb_buffer);
679         state_fio->FputUint8(m_msb_buffer);
680         state_fio->Fwrite(m_timer, sizeof(m_timer), 1);
681         state_fio->Fwrite(m_latch, sizeof(m_latch), 1);
682         state_fio->Fwrite(m_counter, sizeof(m_counter), 1);
683 }
684
685 bool MC6840::load_state(FILEIO* state_fio)
686 {
687         if(state_fio->FgetUint32() != STATE_VERSION) {
688                 return false;
689         }
690         if(state_fio->FgetInt32() != this_device_id) {
691                 return false;
692         }
693         state_fio->Fread(m_control_reg, sizeof(m_control_reg), 1);
694         state_fio->Fread(m_output, sizeof(m_output), 1);
695         state_fio->Fread(m_gate, sizeof(m_gate), 1);
696         state_fio->Fread(m_clk, sizeof(m_clk), 1);
697         state_fio->Fread(m_enabled, sizeof(m_enabled), 1);
698         state_fio->Fread(m_mode, sizeof(m_mode), 1);
699         state_fio->Fread(m_fired, sizeof(m_fired), 1);
700         m_t3_divisor = state_fio->FgetUint8();
701         m_t3_scaler = state_fio->FgetUint8();
702         m_IRQ = state_fio->FgetUint8();
703         m_status_reg = state_fio->FgetUint8();
704         m_status_read_since_int = state_fio->FgetUint8();
705         m_lsb_buffer = state_fio->FgetUint8();
706         m_msb_buffer = state_fio->FgetUint8();
707         state_fio->Fread(m_timer, sizeof(m_timer), 1);
708         state_fio->Fread(m_latch, sizeof(m_latch), 1);
709         state_fio->Fread(m_counter, sizeof(m_counter), 1);
710         return true;
711 }
712