1 // license:BSD-3-Clause
2 // copyright-holders:Olivier Galibert
3 #include "emu.h"
4 #include "h8_timer8.h"
5 
6 // Verbosity level
7 // 0 = no messages
8 // 1 = timer setup
9 // 2 = everything
10 const int V = 1;
11 
12 DEFINE_DEVICE_TYPE(H8_TIMER8_CHANNEL,  h8_timer8_channel_device,  "h8_timer8_channel",  "H8 8-bit timer channel")
13 DEFINE_DEVICE_TYPE(H8H_TIMER8_CHANNEL, h8h_timer8_channel_device, "h8h_timer8_channel", "H8H 8-bit timer channel")
14 
h8_timer8_channel_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)15 h8_timer8_channel_device::h8_timer8_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
16 	h8_timer8_channel_device(mconfig, H8_TIMER8_CHANNEL, tag, owner, clock)
17 {
18 }
19 
h8_timer8_channel_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)20 h8_timer8_channel_device::h8_timer8_channel_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
21 	device_t(mconfig, type, tag, owner, clock),
22 	cpu(*this, "^"), chained_timer(nullptr), intc(nullptr), chain_tag(nullptr), intc_tag(nullptr), irq_ca(0), irq_cb(0), irq_v(0), chain_type(0), tcr(0), tcsr(0), tcnt(0), extra_clock_bit(false),
23 	has_adte(false), has_ice(false), clock_type(0), clock_divider(0), clear_type(0), counter_cycle(0), last_clock_update(0), event_time(0)
24 {
25 }
26 
set_info(const char * intc,int _irq_ca,int _irq_cb,int _irq_v,int div1,int div2,int div3,int div4,int div5,int div6)27 void h8_timer8_channel_device::set_info(const char *intc, int _irq_ca, int _irq_cb, int _irq_v, int div1, int div2, int div3, int div4, int div5, int div6)
28 {
29 	intc_tag = intc;
30 	irq_ca = _irq_ca;
31 	irq_cb = _irq_cb;
32 	irq_v = _irq_v;
33 	chain_tag = nullptr;
34 	chain_type = STOPPED;
35 	has_adte = false;
36 	has_ice = false;
37 	div_tab[0] = div1;
38 	div_tab[1] = div2;
39 	div_tab[2] = div3;
40 	div_tab[3] = div4;
41 	div_tab[4] = div5;
42 	div_tab[5] = div6;
43 }
44 
tcr_r()45 uint8_t h8_timer8_channel_device::tcr_r()
46 {
47 	return tcr;
48 }
49 
tcr_w(uint8_t data)50 void h8_timer8_channel_device::tcr_w(uint8_t data)
51 {
52 	update_counter();
53 	tcr = data;
54 	update_tcr();
55 	recalc_event();
56 }
57 
set_extra_clock_bit(bool bit)58 void h8_timer8_channel_device::set_extra_clock_bit(bool bit)
59 {
60 	update_counter();
61 	extra_clock_bit = bit;
62 	update_tcr();
63 	recalc_event();
64 }
65 
update_tcr()66 void h8_timer8_channel_device::update_tcr()
67 {
68 	char buf[4096];
69 	char *p = buf;
70 	switch(tcr & TCR_CKS) {
71 	case 0:
72 		clock_type = STOPPED;
73 		clock_divider = 0;
74 		if(V>=1) p += sprintf(p, "clock stopped");
75 		break;
76 
77 	case 1: case 2: case 3:
78 		clock_type = DIV;
79 		clock_divider = div_tab[((tcr & TCR_CKS)-1)*2 + extra_clock_bit];
80 		if(V>=1) p += sprintf(p, "clock %dHz", cpu->clock()/clock_divider);
81 		break;
82 
83 	case 4:
84 		clock_type = chain_type;
85 		clock_divider = 0;
86 		if(V>=1) p += sprintf(p, "clock chained %s", clock_type == CHAIN_A ? "tcora" : "overflow");
87 		break;
88 
89 	case 5:
90 		clock_type = INPUT_UP;
91 		clock_divider = 0;
92 		if(V>=1) p += sprintf(p, "clock external raising edge");
93 		break;
94 
95 	case 6:
96 		clock_type = INPUT_DOWN;
97 		clock_divider = 0;
98 		if(V>=1) p += sprintf(p, "clock external falling edge");
99 		break;
100 
101 	case 7:
102 		clock_type = INPUT_UPDOWN;
103 		clock_divider = 0;
104 		if(V>=1) p += sprintf(p, "clock external both edges");
105 		break;
106 	}
107 
108 	switch(tcr & TCR_CCLR) {
109 	case 0x00:
110 		clear_type = CLEAR_NONE;
111 		if(V>=1) p += sprintf(p, ", no clear");
112 		break;
113 
114 	case 0x08:
115 		clear_type = CLEAR_A;
116 		if(V>=1) p += sprintf(p, ", clear on tcora");
117 		break;
118 
119 	case 0x10:
120 		clear_type = CLEAR_B;
121 		if(V>=1) p += sprintf(p, ", clear on tcorb");
122 		break;
123 
124 	case 0x18:
125 		clear_type = CLEAR_EXTERNAL;
126 		if(V>=1) p += sprintf(p, ", clear on external");
127 		break;
128 	}
129 
130 	if(V>=1) p += sprintf(p, ", irq=%c%c%c\n",
131 						tcr & TCR_CMIEB ? 'b' : '-',
132 						tcr & TCR_CMIEA ? 'a' : '-',
133 						tcr & TCR_OVIE  ? 'o' : '-');
134 	logerror(buf);
135 }
136 
tcsr_r()137 uint8_t h8_timer8_channel_device::tcsr_r()
138 {
139 	return tcsr;
140 }
141 
tcsr_w(uint8_t data)142 void h8_timer8_channel_device::tcsr_w(uint8_t data)
143 {
144 	update_counter();
145 
146 	uint8_t mask = has_adte || has_ice ? 0x1f : 0x0f;
147 	tcsr = (tcsr & ~mask) | (data & mask);
148 	tcsr &= data | 0x1f;
149 
150 	if(V>=2) logerror("tcsr_w %02x\n", tcsr);
151 
152 	recalc_event();
153 }
154 
tcor_r(offs_t offset)155 uint8_t h8_timer8_channel_device::tcor_r(offs_t offset)
156 {
157 	return tcor[offset];
158 }
159 
tcor_w(offs_t offset,uint8_t data)160 void h8_timer8_channel_device::tcor_w(offs_t offset, uint8_t data)
161 {
162 	update_counter();
163 	tcor[offset] = data;
164 	if(V>=2) logerror("tcor%c_w %02x\n", 'a'+offset, data);
165 	recalc_event();
166 }
167 
tcnt_r()168 uint8_t h8_timer8_channel_device::tcnt_r()
169 {
170 	update_counter();
171 	recalc_event();
172 	return tcnt;
173 }
174 
tcnt_w(uint8_t data)175 void h8_timer8_channel_device::tcnt_w(uint8_t data)
176 {
177 	update_counter();
178 	tcnt = data;
179 	if(V>=2) logerror("tcnt_w %02x\n", data);
180 	recalc_event();
181 }
182 
device_start()183 void h8_timer8_channel_device::device_start()
184 {
185 	intc = siblingdevice<h8_intc_device>(intc_tag);
186 	if(chain_tag)
187 		chained_timer = siblingdevice<h8_timer8_channel_device>(chain_tag);
188 	else
189 		chained_timer = nullptr;
190 }
191 
device_reset()192 void h8_timer8_channel_device::device_reset()
193 {
194 	tcr = 0x00;
195 	tcsr = has_adte || has_ice ? 0x00 : 0x10;
196 	tcor[0] = 0xff;
197 	tcor[1] = 0xff;
198 	tcnt = 0x00;
199 	counter_cycle = 0x100;
200 	clock_type = STOPPED;
201 	clock_divider = 0;
202 	clear_type = CLEAR_NONE;
203 	last_clock_update = 0;
204 	event_time = 0;
205 	extra_clock_bit = false;
206 }
207 
internal_update(uint64_t current_time)208 uint64_t h8_timer8_channel_device::internal_update(uint64_t current_time)
209 {
210 	if(event_time && current_time >= event_time) {
211 		update_counter(current_time);
212 		recalc_event(current_time);
213 	}
214 
215 	return event_time;
216 }
217 
update_counter(uint64_t cur_time)218 void h8_timer8_channel_device::update_counter(uint64_t cur_time)
219 {
220 	if(clock_type != DIV)
221 		return;
222 
223 	if(!cur_time)
224 		cur_time = cpu->total_cycles();
225 
226 	uint64_t base_time = (last_clock_update + clock_divider/2) / clock_divider;
227 	uint64_t new_time = (cur_time + clock_divider/2) / clock_divider;
228 
229 	int tt = tcnt + new_time - base_time;
230 	tcnt = tt % counter_cycle;
231 
232 	if(tt == tcor[0] || tcnt == tcor[0]) {
233 		if(chained_timer)
234 			chained_timer->chained_timer_tcora();
235 
236 		if(!(tcsr & TCSR_CMFA)) {
237 			tcsr |= TCSR_CMFA;
238 			if(tcr & TCR_CMIEA)
239 				intc->internal_interrupt(irq_ca);
240 		}
241 	}
242 
243 	if(!(tcsr & TCSR_CMFB) && (tt == tcor[1] || tcnt == tcor[1])) {
244 		tcsr |= TCSR_CMFB;
245 		if(tcr & TCR_CMIEB)
246 			intc->internal_interrupt(irq_cb);
247 	}
248 
249 	if(tt >= 0x100) {
250 		if(chained_timer)
251 			chained_timer->chained_timer_overflow();
252 		if(!(tcsr & TCSR_OVF)) {
253 			tcsr |= TCSR_OVF;
254 			if(tcr & TCR_OVIE)
255 				intc->internal_interrupt(irq_v);
256 		}
257 	}
258 	last_clock_update = cur_time;
259 }
260 
recalc_event(uint64_t cur_time)261 void h8_timer8_channel_device::recalc_event(uint64_t cur_time)
262 {
263 	bool update_cpu = cur_time == 0;
264 	uint64_t old_event_time = event_time;
265 
266 	if(clock_type != DIV) {
267 		event_time = 0;
268 		if(old_event_time && update_cpu)
269 			cpu->internal_update();
270 		return;
271 	}
272 
273 	if(!cur_time)
274 		cur_time = cpu->total_cycles();
275 
276 	uint32_t event_delay = 0xffffffff;
277 	if(clear_type == CLEAR_A || clear_type == CLEAR_B)
278 		counter_cycle = tcor[clear_type - CLEAR_A];
279 	else {
280 		counter_cycle = 0x100;
281 		event_delay = counter_cycle - tcnt;
282 		if(!event_delay)
283 			event_delay = counter_cycle;
284 	}
285 
286 	for(auto & elem : tcor) {
287 		uint32_t new_delay = 0xffffffff;
288 		if(elem > tcnt) {
289 			if(tcnt >= counter_cycle || elem <= counter_cycle)
290 				new_delay = elem - tcnt;
291 		} else if(elem <= counter_cycle) {
292 			if(tcnt < counter_cycle)
293 				new_delay = (counter_cycle - tcnt) + elem;
294 			else
295 				new_delay = (0x100 - tcnt) + elem;
296 		}
297 		if(event_delay > new_delay)
298 			event_delay = new_delay;
299 	}
300 
301 	if(event_delay != 0xffffffff)
302 		event_time = ((((cur_time + clock_divider) / clock_divider) + event_delay - 1) * clock_divider) + clock_divider/2;
303 	else
304 		event_time = 0;
305 
306 	if(old_event_time != event_time && update_cpu)
307 		cpu->internal_update();
308 }
309 
chained_timer_overflow()310 void h8_timer8_channel_device::chained_timer_overflow()
311 {
312 	if(clock_type == CHAIN_OVERFLOW)
313 		timer_tick();
314 }
315 
chained_timer_tcora()316 void h8_timer8_channel_device::chained_timer_tcora()
317 {
318 	if(clock_type == CHAIN_A)
319 		timer_tick();
320 }
321 
timer_tick()322 void h8_timer8_channel_device::timer_tick()
323 {
324 	tcnt++;
325 
326 	if(tcnt == tcor[0]) {
327 		if(chained_timer)
328 			chained_timer->chained_timer_tcora();
329 
330 		if(!(tcsr & TCSR_CMFA)) {
331 			tcsr |= TCSR_CMFA;
332 			if(tcr & TCR_CMIEA)
333 				intc->internal_interrupt(irq_ca);
334 		}
335 	}
336 
337 	if(!(tcsr & TCSR_CMFB) && tcnt == tcor[1]) {
338 		tcsr |= TCSR_CMFB;
339 		if(tcr & TCR_CMIEB)
340 			intc->internal_interrupt(irq_cb);
341 	}
342 
343 	if(tcnt == 0x00) {
344 		if(chained_timer)
345 			chained_timer->chained_timer_overflow();
346 		if(!(tcsr & TCSR_OVF)) {
347 			tcsr |= TCSR_OVF;
348 			if(tcr & TCR_OVIE)
349 				intc->internal_interrupt(irq_v);
350 		}
351 	}
352 }
353 
h8h_timer8_channel_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)354 h8h_timer8_channel_device::h8h_timer8_channel_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
355 	h8_timer8_channel_device(mconfig, H8H_TIMER8_CHANNEL, tag, owner, clock)
356 {
357 }
358 
~h8h_timer8_channel_device()359 h8h_timer8_channel_device::~h8h_timer8_channel_device()
360 {
361 }
362 
set_info(const char * intc,int _irq_ca,int _irq_cb,int _irq_v,const char * _chain_tag,int _chain_type,bool _has_adte,bool _has_ice)363 void h8h_timer8_channel_device::set_info(const char *intc, int _irq_ca, int _irq_cb, int _irq_v, const char *_chain_tag, int _chain_type, bool _has_adte, bool _has_ice)
364 {
365 	intc_tag = intc;
366 	irq_ca = _irq_ca;
367 	irq_cb = _irq_cb;
368 	irq_v = _irq_v;
369 	chain_tag = _chain_tag;
370 	chain_type = _chain_type;
371 	has_adte = _has_adte;
372 	has_ice = _has_ice;
373 	// The extra clock bit is not used for h8h+
374 	div_tab[0] = 8;
375 	div_tab[1] = 8;
376 	div_tab[2] = 64;
377 	div_tab[3] = 64;
378 	div_tab[4] = 8192;
379 	div_tab[5] = 8192;
380 }
381