1 /*
2 * KCemu -- The emulator for the KC85 homecomputer series and much more.
3 * Copyright (C) 1997-2010 Torsten Paul
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <iostream>
21 #include <iomanip>
22
23 #include "kc/system.h"
24
25 #include "kc/kc.h"
26 #include "ui/ui.h"
27 #include "kc/ctc.h"
28 #include "kc/tape.h"
29
30 #include "libdbg/dbg.h"
31
32 using namespace std;
33
CTC(const char * name)34 CTC::CTC(const char *name) : InterfaceCircuit(name), Callback(name)
35 {
36 _irq_valid[0] = 0;
37 _irq_valid[1] = 1;
38 _irq_valid[2] = 2;
39 _irq_valid[3] = 3;
40 _cb_list[0] = NULL;
41 _cb_list[1] = NULL;
42 _cb_list[2] = NULL;
43 _cb_list[3] = NULL;
44
45 reset(true);
46 }
47
~CTC(void)48 CTC::~CTC(void)
49 {
50 }
51
52 void
reti(void)53 CTC::reti(void)
54 {
55 int a, b;
56
57 b = 0;
58 for (a = 0;a < 4;a++)
59 {
60 if (_irq_active[a])
61 {
62 b++;
63 _irq_active[a] = 0;
64 }
65 }
66
67 if (b > 1)
68 DBG(2, form("KCemu/CTC/reti",
69 "CTC::reti(): more than one irq active!!!"));
70
71 if (b == 0)
72 return;
73
74 DBG(2, form("KCemu/CTC/reti",
75 "CTC::reti(): active: %d %d %d %d - pending: %d %d %d %d\n",
76 _irq_active[0],
77 _irq_active[1],
78 _irq_active[2],
79 _irq_active[3],
80 _irq_pending[0],
81 _irq_pending[1],
82 _irq_pending[2],
83 _irq_pending[3]));
84
85 for (a = 0;a < 4;a++)
86 {
87 if (_irq_pending[a])
88 {
89 DBG(2, form("KCemu/CTC/reti",
90 "CTC::reti(): trigger_irq(): channel = %d\n",
91 a));
92 try_trigger_irq(a);
93 break;
94 }
95 }
96 }
97
98 void
irqreq(void)99 CTC::irqreq(void)
100 {
101 }
102
103 word_t
irqack(void)104 CTC::irqack(void)
105 {
106 return IRQ_NOT_ACK;
107 }
108
109 void
reset(bool power_on)110 CTC::reset(bool power_on)
111 {
112 int a;
113
114 _irq_vector = 0;
115 for (a = 0;a < 4;a++)
116 {
117 _control[a] = 0x23;
118 _value[a] = 0;
119 _restart[a] = 0;
120 _timer_value[a] = 0;
121 _irq_active[a] = 0;
122 _irq_pending[a] = 0;
123 _irq_valid[a] += 4;
124 }
125 }
126
127 void
trigger(byte_t channel)128 CTC::trigger(byte_t channel)
129 {
130 byte_t c = channel & 3;
131
132 if ((_control[c] & MODE) != MODE_COUNTER)
133 return;
134
135 _value[c]--;
136 if (_value[c] > 0)
137 return;
138
139 _value[c] = _timer_value[c];
140 try_trigger_irq(c);
141 }
142
143 void
handle_counter_mode(int channel)144 CTC::handle_counter_mode(int channel)
145 {
146 long cv;
147
148 switch (channel)
149 {
150 case 0: cv = counter_value_0(); break;
151 case 1: cv = counter_value_1(); break;
152 case 2: cv = counter_value_2(); break;
153 case 3: cv = counter_value_3(); break;
154 }
155
156 if (cv == 0)
157 return;
158
159 cv *= _timer_value[channel]; // mapping from 0 to 256 is done in c_out()
160
161 run_cb_tc(channel, _timer_value[channel]);
162
163 _irq_valid[channel] += 4;
164 add_callback(cv, this, (void *)((long)_irq_valid[channel]));
165 }
166
167 void
try_trigger_irq(int channel)168 CTC::try_trigger_irq(int channel)
169 {
170 if ((_control[channel] & IRQ) == IRQ_DISABLED)
171 {
172 _irq_pending[channel] = 0;
173 return;
174 }
175
176 _irq_pending[channel] = 1;
177 trigger_irq(channel);
178 }
179
180 void
callback(void * data)181 CTC::callback(void *data)
182 {
183 bool cont;
184 long val = (long)data;
185 byte_t c = val & 3;
186
187 if (_irq_valid[c] != val)
188 return;
189
190 DBG(2, form("KCemu/CTC/reti",
191 "CTC::callback(): active: %d %d %d %d - pending: %d %d %d %d\n",
192 _irq_active[0],
193 _irq_active[1],
194 _irq_active[2],
195 _irq_active[3],
196 _irq_pending[0],
197 _irq_pending[1],
198 _irq_pending[2],
199 _irq_pending[3]));
200
201 switch (c)
202 {
203 case 0:
204 DBG(2, form("KCemu/CTC/irq/0",
205 "CTC::callback() : irq channel 0\n"));
206 cont = irq_0();
207 break;
208 case 1:
209 DBG(2, form("KCemu/CTC/irq/1",
210 "CTC::callback() : irq channel 1\n"));
211 cont = irq_1();
212 break;
213 case 2:
214 DBG(2, form("KCemu/CTC/irq/2",
215 "CTC::callback() : irq channel 2\n"));
216 cont = irq_2();
217 break;
218 case 3:
219 DBG(2, form("KCemu/CTC/irq/3",
220 "CTC::callback() : irq channel 3\n"));
221 cont = irq_3();
222 break;
223 }
224
225 /*
226 * COUNTER mode (clock source comes from the CLK pin)
227 */
228 if ((_control[c] & MODE) == MODE_COUNTER)
229 {
230 handle_counter_mode(c);
231 return;
232 }
233
234 if ((_control[c] & IRQ) == IRQ_ENABLED)
235 {
236 DBG(2, form("KCemu/CTC/callback",
237 "CTC::callback(): trigger_irq(): _irq_pending = %d\n",
238 _irq_pending[c]));
239 try_trigger_irq(c);
240 }
241
242 add_callback(_timer_value[c], this, (void *)((long)val));
243 }
244
245 byte_t
c_in(byte_t c)246 CTC::c_in(byte_t c)
247 {
248 word_t val;
249 unsigned long long diff = 0;
250
251 if (_timer_value[c] == 0)
252 {
253 return 0;
254 }
255
256 if ((_control[c] & MODE) == MODE_COUNTER)
257 {
258 diff = 0;
259 }
260 else
261 {
262 if ((_control[c] & RESET) == RESET_ACTIVE)
263 {
264 diff = 0;
265 }
266 else
267 {
268 diff = get_counter() - _counter[c];
269 }
270 }
271
272 if ((_control[c] & PRESCALER) == PRESCALER_16)
273 {
274 diff /= 16;
275 val = ((_value[c] / 16) - diff) & 0xff;
276 }
277 else
278 {
279 diff /= 256;
280 val = ((_value[c] / 256) - diff) & 0xff;
281 }
282
283 _counter[c] = get_counter();
284
285 return val;
286 }
287
288 void
c_out(byte_t channel,byte_t val)289 CTC::c_out(byte_t channel, byte_t val)
290 {
291 if ((_control[channel] & CONSTANT) == CONSTANT_LOAD)
292 {
293 _control[channel] &= ~(CONSTANT | RESET);
294 run_cb_start(channel);
295
296 if ((_control[channel] & MODE) == MODE_TIMER)
297 {
298 if ((_control[channel] & PRESCALER) == PRESCALER_16)
299 {
300 _timer_value[channel] = val << 4; /* div 16 */
301 if (_timer_value[channel] == 0)
302 _timer_value[channel] = 4096;
303 }
304 else
305 {
306 _timer_value[channel] = val << 8; /* div 256 */
307 /*
308 * well, this should be 65536 but this would need a dword
309 * for _timer_value
310 */
311 if (_timer_value[channel] == 0)
312 _timer_value[channel] = 65535;
313 }
314 }
315 else
316 {
317 /*
318 * COUNTER MODE has no prescaler
319 */
320 _timer_value[channel] = val;
321 }
322
323 _counter[channel] = get_counter();
324 _value[channel] = _timer_value[channel];
325 run_cb_tc(channel, _timer_value[channel]);
326
327 if ((_control[channel] & MODE) == MODE_COUNTER)
328 {
329 handle_counter_mode(channel);
330 return;
331 }
332
333 _irq_valid[channel] += 4;
334 /*
335 * Added a fixed offset for callback timing added for the poly880
336 * emulation. Without offset the CTC caused NMI is triggered
337 * one opcode too early.
338 *
339 * FIXME: check timing
340 */
341 add_callback(_timer_value[channel] + 4, this, (void *)((long)_irq_valid[channel]));
342 return;
343 }
344
345 if ((val & CONTROL) == CONTROL_VECTOR)
346 {
347 if (channel != 0) return;
348 _irq_vector = val & ~ 7;
349 return;
350 }
351
352 if ((val & RESET) == RESET_ACTIVE)
353 {
354 _value[channel] = _timer_value[channel];
355 _irq_valid[channel] += 4;
356 if ((_control[channel] & RESET) != RESET_ACTIVE)
357 run_cb_stop(channel);
358 }
359
360 _control[channel] = val;
361 }
362
363 void
register_callback(int channel,CTCCallbackInterface * cbi)364 CTC::register_callback(int channel, CTCCallbackInterface *cbi)
365 {
366 int c = channel & 3;
367
368 if (_cb_list[c] == NULL)
369 {
370 _cb_list[c] = new cb_list_t();
371 }
372
373 _cb_list[c]->push_back(cbi);
374 }
375
run_cb_start(int channel)376 void CTC::run_cb_start(int channel)
377 {
378 if (_cb_list[channel] == NULL)
379 return;
380
381 for (iterator it = _cb_list[channel]->begin();it != _cb_list[channel]->end();it++)
382 (*it)->ctc_callback_start(channel);
383 }
384
run_cb_stop(int channel)385 void CTC::run_cb_stop(int channel)
386 {
387 if (_cb_list[channel] == NULL)
388 return;
389
390 for (iterator it = _cb_list[channel]->begin();it != _cb_list[channel]->end();it++)
391 (*it)->ctc_callback_stop(channel);
392 }
393
run_cb_tc(int channel,long tc)394 void CTC::run_cb_tc(int channel, long tc)
395 {
396 if (_cb_list[channel] == NULL)
397 return;
398
399 for (iterator it = _cb_list[channel]->begin();it != _cb_list[channel]->end();it++)
400 (*it)->ctc_callback_TC(channel, tc);
401 }
402
403 void
info(void)404 CTC::info(void)
405 {
406 cout << " CTC:" << endl;
407 cout << " ---- channel 0 channel 1 "
408 << "channel 2 channel 3" << endl;
409 cout << "\tirq vector: "
410 << hex << setfill('0') << setw(2) << (int)(_irq_vector)
411 << "h "
412 << hex << setfill('0') << setw(2) << (int)(_irq_vector | 0x02)
413 << "h "
414 << hex << setfill('0') << setw(2) << (int)(_irq_vector | 0x04)
415 << "h "
416 << hex << setfill('0') << setw(2) << (int)(_irq_vector | 0x06)
417 << "h" << endl;
418 cout << "\tirq enabled: "
419 << ((_control[0] & IRQ)?"yes":"no ")
420 << " "
421 << ((_control[1] & IRQ)?"yes":"no ")
422 << " "
423 << ((_control[2] & IRQ)?"yes":"no ")
424 << " "
425 << ((_control[3] & IRQ)?"yes":"no ") << endl;
426 cout << "\tcounter mode: "
427 << ((_control[0] & MODE)?"counter":"timer ")
428 << " "
429 << ((_control[1] & MODE)?"counter":"timer ")
430 << " "
431 << ((_control[2] & MODE)?"counter":"timer ")
432 << " "
433 << ((_control[3] & MODE)?"counter":"timer") << endl;
434 cout << "\tclock divider: "
435 << ((_control[0] & PRESCALER)?"256":"16 ")
436 << " "
437 << ((_control[1] & PRESCALER)?"256":"16 ")
438 << " "
439 << ((_control[2] & PRESCALER)?"256":"16 ")
440 << " "
441 << ((_control[3] & PRESCALER)?"256":"16 ") << endl;
442 cout << "\twait for tv: "
443 << ((_control[0] & CONSTANT)?"yes":"no ")
444 << " "
445 << ((_control[1] & CONSTANT)?"yes":"no ")
446 << " "
447 << ((_control[2] & CONSTANT)?"yes":"no ")
448 << " "
449 << ((_control[3] & CONSTANT)?"yes":"no ") << endl;
450 cout << "\treset: "
451 << ((_control[0] & RESET)?"yes":"no ")
452 << " "
453 << ((_control[1] & RESET)?"yes":"no ")
454 << " "
455 << ((_control[2] & RESET)?"yes":"no ")
456 << " "
457 << ((_control[3] & RESET)?"yes":"no ") << endl;
458 cout << "\ttimer value: "
459 << hex << setfill('0') << setw(2) << (int)_timer_value[0]
460 << "h "
461 << hex << setfill('0') << setw(2) << (int)_timer_value[1]
462 << "h "
463 << hex << setfill('0') << setw(2) << (int)_timer_value[2]
464 << "h "
465 << hex << setfill('0') << setw(2) << (int)_timer_value[3]
466 << "h" << endl;
467 cout << "\tvalue: "
468 << hex << setfill('0') << setw(2) << (int)_value[0]
469 << "h "
470 << hex << setfill('0') << setw(2) << (int)_value[1]
471 << "h "
472 << hex << setfill('0') << setw(2) << (int)_value[2]
473 << "h "
474 << hex << setfill('0') << setw(2) << (int)_value[3]
475 << "h" << endl << endl;
476 }
477