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