1 /***************************************************************************
2
3 Z80 CTC (Z8430) implementation
4
5 based on original version (c) 1997, Tatsuyuki Satoh
6
7 Copyright Nicola Salmoria and the MAME Team.
8 Visit http://mamedev.org for licensing and usage restrictions.
9
10 ***************************************************************************/
11
12 #include "burnint.h"
13 #include "z80ctc.h"
14 #include "z80.h"
15 #include "z80daisy.h"
16
17 /***************************************************************************
18 DEBUGGING
19 ***************************************************************************/
20
21 #define VERBOSE 0
22
23 #define VPRINTF(x)
24
25 /***************************************************************************
26 CONSTANTS
27 ***************************************************************************/
28
29 #define CLEAR_LINE 0
30 #define ASSERT_LINE 1
31
32 /* these are the bits of the incoming commands to the CTC */
33 #define INTERRUPT 0x80
34 #define INTERRUPT_ON 0x80
35 #define INTERRUPT_OFF 0x00
36
37 #define MODE 0x40
38 #define MODE_TIMER 0x00
39 #define MODE_COUNTER 0x40
40
41 #define PRESCALER 0x20
42 #define PRESCALER_256 0x20
43 #define PRESCALER_16 0x00
44
45 #define EDGE 0x10
46 #define EDGE_FALLING 0x00
47 #define EDGE_RISING 0x10
48
49 #define TRIGGER 0x08
50 #define TRIGGER_AUTO 0x00
51 #define TRIGGER_CLOCK 0x08
52
53 #define CONSTANT 0x04
54 #define CONSTANT_LOAD 0x04
55 #define CONSTANT_NONE 0x00
56
57 #define RESET 0x02
58 #define RESET_CONTINUE 0x00
59 #define RESET_ACTIVE 0x02
60
61 #define CONTROL 0x01
62 #define CONTROL_VECTOR 0x00
63 #define CONTROL_WORD 0x01
64
65 /* these extra bits help us keep things accurate */
66 #define WAITING_FOR_TRIG 0x100
67
68
69 /***************************************************************************
70 TYPE DEFINITIONS
71 ***************************************************************************/
72
73 struct ctc_channel
74 {
75 UINT8 notimer; /* no timer masks */
76 UINT16 mode; /* current mode */
77 UINT16 tconst; /* time constant */
78 UINT16 down; /* down counter (clock mode only) */
79 UINT8 extclk; /* current signal from the external clock */
80 UINT8 int_state; /* interrupt status (for daisy chain) */
81 };
82
83 struct z80ctc
84 {
85 // config stuff
86 UINT32 clock; /* system clock */
87 INT32 period16; /* 16/system clock */
88 INT32 period256; /* 256/system clock */
89
90 // stuff to save
91 UINT8 vector; /* interrupt vector */
92 ctc_channel channel[4]; /* data for each channel */
93
94 void (*intr)(int which); /* interrupt callback */
95 void (*zc[4])(int, UINT8); /* zero crossing callbacks */
96 };
97
98 static z80ctc *ctc = NULL;
99
100 static void timercallback(int param); // forward
101
102 // simple timer system -dink 2019
103 // note: all time is in z80-cycles relative to the cpu using this ctc.
104 #define TIMERS_MAX 4
105 struct timer_type
106 {
107 INT32 running;
108 INT32 time_trig;
109 INT32 time_current;
110 INT32 timer_param;
111 };
112
113 static void (*timer_exec[4])(int);
114
115 static timer_type timers[TIMERS_MAX];
116
timer_reset()117 void timer_reset()
118 {
119 for (INT32 i = 0; i < TIMERS_MAX; i++)
120 {
121 timers[i].running = 0;
122 timers[i].time_trig = 0;
123 timers[i].time_current = 0;
124 timer_exec[i] = NULL;
125 timers[i].timer_param = 0;
126 }
127 }
128
timer_start(INT32 timernum,INT32 time,void (* callback)(INT32),INT32 tparam,INT32 running)129 void timer_start(INT32 timernum, INT32 time, void (*callback)(INT32), INT32 tparam, INT32 running)
130 {
131 if (timernum >= TIMERS_MAX) return;
132 timers[timernum].running = running;
133 timers[timernum].time_trig = time; // cycle to execute @
134 timers[timernum].time_current = 0;
135 timer_exec[timernum] = callback;
136 timers[timernum].timer_param = tparam;
137 }
138
timer_stop(INT32 timernum)139 void timer_stop(INT32 timernum)
140 {
141 if (timernum >= TIMERS_MAX) return;
142 timers[timernum].running = 0;
143 timers[timernum].time_current = 0;
144 }
145
timer_isrunning(INT32 timernum)146 INT32 timer_isrunning(INT32 timernum)
147 {
148 if (timernum >= TIMERS_MAX) return 0;
149 return timers[timernum].running;
150 }
151
timer_timeleft(INT32 timernum)152 INT32 timer_timeleft(INT32 timernum)
153 {
154 if (timernum >= TIMERS_MAX) return 0;
155 return timers[timernum].time_trig - timers[timernum].time_current; // maybe?
156 }
157
z80ctc_timer_update(INT32 cycles)158 void z80ctc_timer_update(INT32 cycles)
159 {
160 for (INT32 i = 0; i < TIMERS_MAX; i++)
161 {
162 if (timers[i].running) {
163 timers[i].time_current += cycles;
164 while (timers[i].time_current >= timers[i].time_trig) {
165 timer_exec[i](timers[i].timer_param);
166 timers[i].time_current -= timers[i].time_trig;
167 }
168 }
169 }
170 }
171
z80ctc_timer_scan(INT32 nAction)172 static void z80ctc_timer_scan(INT32 nAction) // called from z80ctc_scan()! (below)
173 {
174 SCAN_VAR(timers);
175
176 if (nAction & ACB_WRITE) {
177 // state load: re-set the timer callback pointer for running timers.
178 for (INT32 i = 0; i < TIMERS_MAX; i++)
179 {
180 if (timers[i].running) {
181 timer_exec[i] = timercallback;
182 }
183 }
184 }
185 }
186
187
188 /***************************************************************************
189 INTERNAL STATE MANAGEMENT
190 ***************************************************************************/
191
interrupt_check()192 static void interrupt_check()
193 {
194 /* if we have a callback, update it with the current state */
195 if (ctc->intr != NULL)
196 (*ctc->intr)((z80ctc_irq_state() & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE);
197 }
198
199
timercallback(int param)200 static void timercallback(int param)
201 {
202 ctc_channel *channel = &ctc->channel[param];
203
204 /* down counter has reached zero - see if we should interrupt */
205 if ((channel->mode & INTERRUPT) == INTERRUPT_ON)
206 {
207 channel->int_state |= Z80_DAISY_INT;
208 VPRINTF(("CTC timer ch%d\n", param));
209 interrupt_check();
210 }
211
212 /* generate the clock pulse */
213 if (ctc->zc[param] != NULL)
214 {
215 ctc->zc[param](0, 1);
216 ctc->zc[param](0, 0);
217 }
218
219 /* reset the down counter */
220 channel->down = channel->tconst;
221 }
222
223
224
225 /***************************************************************************
226 INITIALIZATION/CONFIGURATION
227 ***************************************************************************/
228
z80ctc_getperiod(int ch)229 INT32 z80ctc_getperiod(int ch)
230 {
231
232 ctc_channel *channel = &ctc->channel[ch];
233
234 /* if reset active, no period */
235 if ((channel->mode & RESET) == RESET_ACTIVE)
236 return 0; //attotime_zero;
237
238 /* if counter mode, no real period */
239 if ((channel->mode & MODE) == MODE_COUNTER)
240 {
241 //logerror("CTC %d is CounterMode : Can't calculate period\n", ch );
242 return 0; //attotime_zero;
243 }
244
245 /* compute the period */
246 INT32 period = ((channel->mode & PRESCALER) == PRESCALER_16) ? ctc->period16 : ctc->period256;
247 //return attotime_mul(period, channel->tconst);
248 return period * channel->tconst; // tih?-timmy? (dink)
249 }
250
251
252
253 /***************************************************************************
254 WRITE HANDLERS
255 ***************************************************************************/
256
z80ctc_write(int offset,UINT8 data)257 void z80ctc_write(int offset, UINT8 data)
258 {
259 int ch = offset & 3;
260 ctc_channel *channel = &ctc->channel[ch];
261 int mode;
262
263 /* get the current mode */
264 mode = channel->mode;
265
266 /* if we're waiting for a time constant, this is it */
267 if ((mode & CONSTANT) == CONSTANT_LOAD)
268 {
269 VPRINTF(("CTC ch.%d constant = %02x\n", ch, data));
270
271 /* set the time constant (0 -> 0x100) */
272 channel->tconst = data ? data : 0x100;
273
274 /* clear the internal mode -- we're no longer waiting */
275 channel->mode &= ~CONSTANT;
276
277 /* also clear the reset, since the constant gets it going again */
278 channel->mode &= ~RESET;
279
280 /* if we're in timer mode.... */
281 if ((mode & MODE) == MODE_TIMER)
282 {
283 /* if we're triggering on the time constant, reset the down counter now */
284 if ((mode & TRIGGER) == TRIGGER_AUTO)
285 {
286 if (!channel->notimer)
287 {
288 INT32 period = ((mode & PRESCALER) == PRESCALER_16) ? ctc->period16 : ctc->period256;
289 period *= channel->tconst;
290 //period = attotime_mul(period, channel->tconst);
291 timer_start(ch, period, timercallback, ch, 1);
292 //timer_adjust_periodic(channel->timer, period, ch, period);
293 }
294 else
295 {
296 timer_stop(ch); // off
297 //timer_adjust_oneshot(channel->timer, attotime_never, 0);
298 }
299 }
300
301 /* else set the bit indicating that we're waiting for the appropriate trigger */
302 else
303 channel->mode |= WAITING_FOR_TRIG;
304 }
305
306 /* also set the down counter in case we're clocking externally */
307 channel->down = channel->tconst;
308
309 /* all done here */
310 return;
311 }
312
313 /* if we're writing the interrupt vector, handle it specially */
314 #if 0 /* Tatsuyuki Satoh changes */
315 /* The 'Z80family handbook' wrote, */
316 /* interrupt vector is able to set for even channel (0 or 2) */
317 if ((data & CONTROL) == CONTROL_VECTOR && (ch&1) == 0)
318 #else
319 if ((data & CONTROL) == CONTROL_VECTOR && ch == 0)
320 #endif
321 {
322 ctc->vector = data & 0xf8;
323 //logerror("CTC Vector = %02x\n", ctc->vector);
324 return;
325 }
326
327 /* this must be a control word */
328 if ((data & CONTROL) == CONTROL_WORD)
329 {
330 /* set the new mode */
331 channel->mode = data;
332 VPRINTF(("CTC ch.%d mode = %02x\n", ch, data));
333
334 /* if we're being reset, clear out any pending timers for this channel */
335 if ((data & RESET) == RESET_ACTIVE)
336 {
337 timer_stop(ch);
338 //timer_adjust_oneshot(channel->timer, attotime_never, 0);
339 /* note that we don't clear the interrupt state here! */
340 }
341
342 /* all done here */
343 return;
344 }
345 }
346
347
348
349 /***************************************************************************
350 READ HANDLERS
351 ***************************************************************************/
352
z80ctc_read(int offset)353 UINT8 z80ctc_read(int offset)
354 {
355 int ch = offset & 3;
356 ctc_channel *channel = &ctc->channel[ch];
357
358 /* if we're in counter mode, just return the count */
359 if ((channel->mode & MODE) == MODE_COUNTER || (channel->mode & WAITING_FOR_TRIG))
360 return channel->down;
361
362 /* else compute the down counter value */
363 else
364 {
365 INT32 period = ((channel->mode & PRESCALER) == PRESCALER_16) ? ctc->period16 : ctc->period256;
366
367 //VPRINTF(("CTC clock %f\n",ATTOSECONDS_TO_HZ(period.attoseconds)));
368
369 if (timer_isrunning(ch))
370 return ((int)timer_timeleft(ch) / period + 1) & 0xff;
371 else
372 return 0;
373 #if 0
374 if (channel->timer != NULL)
375 return ((int)(attotime_to_double(timer_timeleft(channel->timer)) * attotime_to_double(period)) + 1) & 0xff;
376 else
377 return 0;
378 #endif
379 }
380 }
381
382
383
384 /***************************************************************************
385 EXTERNAL TRIGGERS
386 ***************************************************************************/
387
z80ctc_trg_write(int ch,UINT8 data)388 void z80ctc_trg_write(int ch, UINT8 data)
389 {
390 ctc_channel *channel = &ctc->channel[ch];
391
392 /* normalize data */
393 data = data ? 1 : 0;
394
395 /* see if the trigger value has changed */
396 if (data != channel->extclk)
397 {
398 channel->extclk = data;
399
400 /* see if this is the active edge of the trigger */
401 if (((channel->mode & EDGE) == EDGE_RISING && data) || ((channel->mode & EDGE) == EDGE_FALLING && !data))
402 {
403 /* if we're waiting for a trigger, start the timer */
404 if ((channel->mode & WAITING_FOR_TRIG) && (channel->mode & MODE) == MODE_TIMER)
405 {
406 if (!channel->notimer)
407 {
408 INT32 period = ((channel->mode & PRESCALER) == PRESCALER_16) ? ctc->period16 : ctc->period256;
409 //period = attotime_mul(period, channel->tconst);
410 period = period * channel->tconst;
411
412 //VPRINTF(("CTC period %s\n", attotime_string(period, 9)));
413 timer_start(ch, period, timercallback, ch, 1);
414 //timer_adjust_periodic(channel->timer, period, ch, period);
415 }
416 else
417 {
418 VPRINTF(("CTC disabled\n"));
419 timer_stop(ch);
420 //timer_adjust_oneshot(channel->timer, attotime_never, 0);
421 }
422 }
423
424 /* we're no longer waiting */
425 channel->mode &= ~WAITING_FOR_TRIG;
426
427 /* if we're clocking externally, decrement the count */
428 if ((channel->mode & MODE) == MODE_COUNTER)
429 {
430 channel->down--;
431
432 /* if we hit zero, do the same thing as for a timer interrupt */
433 if (!channel->down)
434 {
435 timercallback(ch);
436 }
437 }
438 }
439 }
440 }
441
442
443 /***************************************************************************
444 DAISY CHAIN INTERFACE
445 ***************************************************************************/
446
z80ctc_irq_state()447 int z80ctc_irq_state()
448 {
449 int state = 0;
450 int ch;
451
452 VPRINTF(("CTC IRQ state = %d%d%d%d\n", ctc->channel[0].int_state, ctc->channel[1].int_state, ctc->channel[2].int_state, ctc->channel[3].int_state));
453
454 /* loop over all channels */
455 for (ch = 0; ch < 4; ch++)
456 {
457 ctc_channel *channel = &ctc->channel[ch];
458
459 /* if we're servicing a request, don't indicate more interrupts */
460 if (channel->int_state & Z80_DAISY_IEO)
461 {
462 state |= Z80_DAISY_IEO;
463 break;
464 }
465 state |= channel->int_state;
466 }
467
468 return state;
469 }
470
471
z80ctc_irq_ack()472 int z80ctc_irq_ack()
473 {
474 int ch;
475
476 /* loop over all channels */
477 for (ch = 0; ch < 4; ch++)
478 {
479 ctc_channel *channel = &ctc->channel[ch];
480
481 /* find the first channel with an interrupt requested */
482 if (channel->int_state & Z80_DAISY_INT)
483 {
484 VPRINTF(("CTC IRQAck ch%d\n", ch));
485
486 /* clear interrupt, switch to the IEO state, and update the IRQs */
487 channel->int_state = Z80_DAISY_IEO;
488 interrupt_check();
489 return ctc->vector + ch * 2;
490 }
491 }
492
493 //logerror("z80ctc_irq_ack: failed to find an interrupt to ack!\n");
494 return ctc->vector;
495 }
496
497
z80ctc_irq_reti()498 void z80ctc_irq_reti()
499 {
500 int ch;
501
502 /* loop over all channels */
503 for (ch = 0; ch < 4; ch++)
504 {
505 ctc_channel *channel = &ctc->channel[ch];
506
507 /* find the first channel with an IEO pending */
508 if (channel->int_state & Z80_DAISY_IEO)
509 {
510 VPRINTF(("CTC IRQReti ch%d\n", ch));
511
512 /* clear the IEO state and update the IRQs */
513 channel->int_state &= ~Z80_DAISY_IEO;
514 interrupt_check();
515 return;
516 }
517 }
518
519 //logerror("z80ctc_irq_reti: failed to find an interrupt to clear IEO on!\n");
520 }
521
z80ctc_init(INT32 clock,INT32 notimer,void (* intr)(int),void (* zc0)(int,UINT8),void (* zc1)(int,UINT8),void (* zc2)(int,UINT8))522 void z80ctc_init(INT32 clock, INT32 notimer, void (*intr)(int), void (*zc0)(int, UINT8), void (*zc1)(int, UINT8), void (*zc2)(int, UINT8))
523 {
524 ctc = (z80ctc *)BurnMalloc(sizeof(z80ctc));
525 ctc->clock = clock;
526 ctc->period16 = 16;
527 ctc->period256 = 256;
528 //ctc->period16 = attotime_mul(ATTOTIME_IN_HZ(ctc->clock), 16);
529 //ctc->period256 = attotime_mul(ATTOTIME_IN_HZ(ctc->clock), 256);
530 for (int ch = 0; ch < 4; ch++)
531 {
532 ctc_channel *channel = &ctc->channel[ch];
533 channel->notimer = (notimer >> ch) & 1;
534 //channel->timer = timer_alloc(timercallback, ptr);
535 }
536 ctc->intr = intr;
537 ctc->zc[0] = zc0;
538 ctc->zc[1] = zc1;
539 ctc->zc[2] = zc2;
540 ctc->zc[3] = NULL;
541 }
542
z80ctc_exit()543 void z80ctc_exit()
544 {
545 BurnFree(ctc);
546 }
547
z80ctc_reset()548 void z80ctc_reset()
549 {
550 int ch;
551
552 /* set up defaults */
553 for (ch = 0; ch < 4; ch++)
554 {
555 ctc_channel *channel = &ctc->channel[ch];
556 channel->mode = RESET_ACTIVE;
557 channel->tconst = 0x100;
558 //timer_adjust_oneshot(channel->timer, attotime_never, 0);
559 channel->int_state = 0;
560 }
561 interrupt_check();
562
563 timer_reset();
564
565 VPRINTF(("CTC Reset\n"));
566 }
567
z80ctc_scan(INT32 nAction)568 void z80ctc_scan(INT32 nAction)
569 {
570 SCAN_VAR(ctc->vector);
571 SCAN_VAR(ctc->channel);
572
573 z80ctc_timer_scan(nAction);
574 }
575
576