1 /*
2 * CIA.cpp - 6526 emulation
3 *
4 * Frodo (C) 1994-1997,2002 Christian Bauer
5 *
6
7 *
8 * Notes:
9 * ------
10 *
11 * - The EmulateLine() function is called for every emulated raster
12 * line. It counts down the timers and triggers interrupts if
13 * necessary.
14 * - The TOD clocks are counted by CountTOD() during the VBlank, so
15 * the input frequency is 50Hz
16 * - The fields KeyMatrix and RevMatrix contain one bit for each
17 * key on the C64 keyboard (0: key pressed, 1: key released).
18 * KeyMatrix is used for normal keyboard polling (PRA->PRB),
19 * RevMatrix for reversed polling (PRB->PRA).
20 *
21 * Incompatibilities:
22 * ------------------
23 *
24 * - The TOD clock should not be stopped on a read access, but
25 * latched
26 * - The SDR interrupt is faked
27 */
28
29 #include "sysdeps.h"
30
31 #include "CIA.h"
32 #include "CPUC64.h"
33 #include "CPU1541.h"
34 #include "VIC.h"
35 #include "Prefs.h"
36
37
38 /*
39 * Constructors
40 */
41
MOS6526(MOS6510 * CPU)42 MOS6526::MOS6526(MOS6510 *CPU) : the_cpu(CPU) {}
MOS6526_1(MOS6510 * CPU,MOS6569 * VIC)43 MOS6526_1::MOS6526_1(MOS6510 *CPU, MOS6569 *VIC) : MOS6526(CPU), the_vic(VIC) {}
MOS6526_2(MOS6510 * CPU,MOS6569 * VIC,MOS6502_1541 * CPU1541)44 MOS6526_2::MOS6526_2(MOS6510 *CPU, MOS6569 *VIC, MOS6502_1541 *CPU1541) : MOS6526(CPU), the_vic(VIC), the_cpu_1541(CPU1541) {}
45
46
47 /*
48 * Reset the CIA
49 */
50
Reset(void)51 void MOS6526::Reset(void)
52 {
53 pra = prb = ddra = ddrb = 0;
54
55 ta = tb = 0xffff;
56 latcha = latchb = 1;
57
58 tod_10ths = tod_sec = tod_min = tod_hr = 0;
59 alm_10ths = alm_sec = alm_min = alm_hr = 0;
60
61 sdr = icr = cra = crb = int_mask = 0;
62
63 tod_halt = ta_cnt_phi2 = tb_cnt_phi2 = tb_cnt_ta = false;
64 tod_divider = 0;
65 }
66
Reset(void)67 void MOS6526_1::Reset(void)
68 {
69 MOS6526::Reset();
70
71 // Clear keyboard matrix and joystick states
72 for (int i=0; i<8; i++)
73 KeyMatrix[i] = RevMatrix[i] = 0xff;
74
75 Joystick1 = Joystick2 = 0xff;
76 prev_lp = 0x10;
77 }
78
Reset(void)79 void MOS6526_2::Reset(void)
80 {
81 MOS6526::Reset();
82
83 // VA14/15 = 0
84 the_vic->ChangedVA(0);
85
86 // IEC
87 IECLines = 0xd0;
88 }
89
90
91 /*
92 * Get CIA state
93 */
94
GetState(MOS6526State * cs)95 void MOS6526::GetState(MOS6526State *cs)
96 {
97 cs->pra = pra;
98 cs->prb = prb;
99 cs->ddra = ddra;
100 cs->ddrb = ddrb;
101
102 cs->ta_lo = ta & 0xff;
103 cs->ta_hi = ta >> 8;
104 cs->tb_lo = tb & 0xff;
105 cs->tb_hi = tb >> 8;
106 cs->latcha = latcha;
107 cs->latchb = latchb;
108 cs->cra = cra;
109 cs->crb = crb;
110
111 cs->tod_10ths = tod_10ths;
112 cs->tod_sec = tod_sec;
113 cs->tod_min = tod_min;
114 cs->tod_hr = tod_hr;
115 cs->alm_10ths = alm_10ths;
116 cs->alm_sec = alm_sec;
117 cs->alm_min = alm_min;
118 cs->alm_hr = alm_hr;
119
120 cs->sdr = sdr;
121
122 cs->int_data = icr;
123 cs->int_mask = int_mask;
124 }
125
126
127 /*
128 * Restore CIA state
129 */
130
SetState(MOS6526State * cs)131 void MOS6526::SetState(MOS6526State *cs)
132 {
133 pra = cs->pra;
134 prb = cs->prb;
135 ddra = cs->ddra;
136 ddrb = cs->ddrb;
137
138 ta = (cs->ta_hi << 8) | cs->ta_lo;
139 tb = (cs->tb_hi << 8) | cs->tb_lo;
140 latcha = cs->latcha;
141 latchb = cs->latchb;
142 cra = cs->cra;
143 crb = cs->crb;
144
145 tod_10ths = cs->tod_10ths;
146 tod_sec = cs->tod_sec;
147 tod_min = cs->tod_min;
148 tod_hr = cs->tod_hr;
149 alm_10ths = cs->alm_10ths;
150 alm_sec = cs->alm_sec;
151 alm_min = cs->alm_min;
152 alm_hr = cs->alm_hr;
153
154 sdr = cs->sdr;
155
156 icr = cs->int_data;
157 int_mask = cs->int_mask;
158
159 tod_halt = false;
160 ta_cnt_phi2 = ((cra & 0x21) == 0x01);
161 tb_cnt_phi2 = ((crb & 0x61) == 0x01);
162 tb_cnt_ta = ((crb & 0x61) == 0x41);
163 }
164
165
166 /*
167 * Read from register (CIA 1)
168 */
169
ReadRegister(uint16 adr)170 uint8 MOS6526_1::ReadRegister(uint16 adr)
171 {
172 switch (adr) {
173 case 0x00: {
174 uint8 ret = pra | ~ddra, tst = (prb | ~ddrb) & Joystick1;
175 if (!(tst & 0x01)) ret &= RevMatrix[0]; // AND all active columns
176 if (!(tst & 0x02)) ret &= RevMatrix[1];
177 if (!(tst & 0x04)) ret &= RevMatrix[2];
178 if (!(tst & 0x08)) ret &= RevMatrix[3];
179 if (!(tst & 0x10)) ret &= RevMatrix[4];
180 if (!(tst & 0x20)) ret &= RevMatrix[5];
181 if (!(tst & 0x40)) ret &= RevMatrix[6];
182 if (!(tst & 0x80)) ret &= RevMatrix[7];
183 return ret & Joystick2;
184 }
185 case 0x01: {
186 uint8 ret = ~ddrb, tst = (pra | ~ddra) & Joystick2;
187 if (!(tst & 0x01)) ret &= KeyMatrix[0]; // AND all active rows
188 if (!(tst & 0x02)) ret &= KeyMatrix[1];
189 if (!(tst & 0x04)) ret &= KeyMatrix[2];
190 if (!(tst & 0x08)) ret &= KeyMatrix[3];
191 if (!(tst & 0x10)) ret &= KeyMatrix[4];
192 if (!(tst & 0x20)) ret &= KeyMatrix[5];
193 if (!(tst & 0x40)) ret &= KeyMatrix[6];
194 if (!(tst & 0x80)) ret &= KeyMatrix[7];
195 return (ret | (prb & ddrb)) & Joystick1;
196 }
197 case 0x02: return ddra;
198 case 0x03: return ddrb;
199 case 0x04: return ta;
200 case 0x05: return ta >> 8;
201 case 0x06: return tb;
202 case 0x07: return tb >> 8;
203 case 0x08: tod_halt = false; return tod_10ths;
204 case 0x09: return tod_sec;
205 case 0x0a: return tod_min;
206 case 0x0b: tod_halt = true; return tod_hr;
207 case 0x0c: return sdr;
208 case 0x0d: {
209 uint8 ret = icr; // Read and clear ICR
210 icr = 0;
211 the_cpu->ClearCIAIRQ(); // Clear IRQ
212 return ret;
213 }
214 case 0x0e: return cra;
215 case 0x0f: return crb;
216 }
217 return 0; // Can't happen
218 }
219
220
221 /*
222 * Read from register (CIA 2)
223 */
224
ReadRegister(uint16 adr)225 uint8 MOS6526_2::ReadRegister(uint16 adr)
226 {
227 switch (adr) {
228 case 0x00:
229 return (pra | ~ddra) & 0x3f
230 | IECLines & the_cpu_1541->IECLines;
231 case 0x01: return prb | ~ddrb;
232 case 0x02: return ddra;
233 case 0x03: return ddrb;
234 case 0x04: return ta;
235 case 0x05: return ta >> 8;
236 case 0x06: return tb;
237 case 0x07: return tb >> 8;
238 case 0x08: tod_halt = false; return tod_10ths;
239 case 0x09: return tod_sec;
240 case 0x0a: return tod_min;
241 case 0x0b: tod_halt = true; return tod_hr;
242 case 0x0c: return sdr;
243 case 0x0d: {
244 uint8 ret = icr; // Read and clear ICR
245 icr = 0;
246 the_cpu->ClearNMI(); // Clear NMI
247 return ret;
248 }
249 case 0x0e: return cra;
250 case 0x0f: return crb;
251 }
252 return 0; // Can't happen
253 }
254
255
256 /*
257 * Write to register (CIA 1)
258 */
259
260 // Write to port B, check for lightpen interrupt
check_lp(void)261 inline void MOS6526_1::check_lp(void)
262 {
263 if ((prb | ~ddrb) & 0x10 != prev_lp)
264 the_vic->TriggerLightpen();
265 prev_lp = (prb | ~ddrb) & 0x10;
266 }
267
WriteRegister(uint16 adr,uint8 byte)268 void MOS6526_1::WriteRegister(uint16 adr, uint8 byte)
269 {
270 switch (adr) {
271 case 0x0: pra = byte; break;
272 case 0x1:
273 prb = byte;
274 check_lp();
275 break;
276 case 0x2: ddra = byte; break;
277 case 0x3:
278 ddrb = byte;
279 check_lp();
280 break;
281
282 case 0x4: latcha = (latcha & 0xff00) | byte; break;
283 case 0x5:
284 latcha = (latcha & 0xff) | (byte << 8);
285 if (!(cra & 1)) // Reload timer if stopped
286 ta = latcha;
287 break;
288
289 case 0x6: latchb = (latchb & 0xff00) | byte; break;
290 case 0x7:
291 latchb = (latchb & 0xff) | (byte << 8);
292 if (!(crb & 1)) // Reload timer if stopped
293 tb = latchb;
294 break;
295
296 case 0x8:
297 if (crb & 0x80)
298 alm_10ths = byte & 0x0f;
299 else
300 tod_10ths = byte & 0x0f;
301 break;
302 case 0x9:
303 if (crb & 0x80)
304 alm_sec = byte & 0x7f;
305 else
306 tod_sec = byte & 0x7f;
307 break;
308 case 0xa:
309 if (crb & 0x80)
310 alm_min = byte & 0x7f;
311 else
312 tod_min = byte & 0x7f;
313 break;
314 case 0xb:
315 if (crb & 0x80)
316 alm_hr = byte & 0x9f;
317 else
318 tod_hr = byte & 0x9f;
319 break;
320
321 case 0xc:
322 sdr = byte;
323 TriggerInterrupt(8); // Fake SDR interrupt for programs that need it
324 break;
325
326 case 0xd:
327 if (ThePrefs.CIAIRQHack) // Hack for addressing modes that read from the address
328 icr = 0;
329 if (byte & 0x80) {
330 int_mask |= byte & 0x7f;
331 if (icr & int_mask & 0x1f) { // Trigger IRQ if pending
332 icr |= 0x80;
333 the_cpu->TriggerCIAIRQ();
334 }
335 } else
336 int_mask &= ~byte;
337 break;
338
339 case 0xe:
340 cra = byte & 0xef;
341 if (byte & 0x10) // Force load
342 ta = latcha;
343 ta_cnt_phi2 = ((byte & 0x21) == 0x01);
344 break;
345
346 case 0xf:
347 crb = byte & 0xef;
348 if (byte & 0x10) // Force load
349 tb = latchb;
350 tb_cnt_phi2 = ((byte & 0x61) == 0x01);
351 tb_cnt_ta = ((byte & 0x61) == 0x41);
352 break;
353 }
354 }
355
356
357 /*
358 * Write to register (CIA 2)
359 */
360
WriteRegister(uint16 adr,uint8 byte)361 void MOS6526_2::WriteRegister(uint16 adr, uint8 byte)
362 {
363 switch (adr) {
364 case 0x0:{
365 pra = byte;
366 byte = ~pra & ddra;
367 the_vic->ChangedVA(byte & 3);
368 uint8 old_lines = IECLines;
369 IECLines = (byte << 2) & 0x80 // DATA
370 | (byte << 2) & 0x40 // CLK
371 | (byte << 1) & 0x10; // ATN
372 if ((IECLines ^ old_lines) & 0x10) { // ATN changed
373 the_cpu_1541->NewATNState();
374 if (old_lines & 0x10) // ATN 1->0
375 the_cpu_1541->IECInterrupt();
376 }
377 break;
378 }
379 case 0x1: prb = byte; break;
380
381 case 0x2:
382 ddra = byte;
383 the_vic->ChangedVA(~(pra | ~ddra) & 3);
384 break;
385 case 0x3: ddrb = byte; break;
386
387 case 0x4: latcha = (latcha & 0xff00) | byte; break;
388 case 0x5:
389 latcha = (latcha & 0xff) | (byte << 8);
390 if (!(cra & 1)) // Reload timer if stopped
391 ta = latcha;
392 break;
393
394 case 0x6: latchb = (latchb & 0xff00) | byte; break;
395 case 0x7:
396 latchb = (latchb & 0xff) | (byte << 8);
397 if (!(crb & 1)) // Reload timer if stopped
398 tb = latchb;
399 break;
400
401 case 0x8:
402 if (crb & 0x80)
403 alm_10ths = byte & 0x0f;
404 else
405 tod_10ths = byte & 0x0f;
406 break;
407 case 0x9:
408 if (crb & 0x80)
409 alm_sec = byte & 0x7f;
410 else
411 tod_sec = byte & 0x7f;
412 break;
413 case 0xa:
414 if (crb & 0x80)
415 alm_min = byte & 0x7f;
416 else
417 tod_min = byte & 0x7f;
418 break;
419 case 0xb:
420 if (crb & 0x80)
421 alm_hr = byte & 0x9f;
422 else
423 tod_hr = byte & 0x9f;
424 break;
425
426 case 0xc:
427 sdr = byte;
428 TriggerInterrupt(8); // Fake SDR interrupt for programs that need it
429 break;
430
431 case 0xd:
432 if (ThePrefs.CIAIRQHack)
433 icr = 0;
434 if (byte & 0x80) {
435 int_mask |= byte & 0x7f;
436 if (icr & int_mask & 0x1f) { // Trigger NMI if pending
437 icr |= 0x80;
438 the_cpu->TriggerNMI();
439 }
440 } else
441 int_mask &= ~byte;
442 break;
443
444 case 0xe:
445 cra = byte & 0xef;
446 if (byte & 0x10) // Force load
447 ta = latcha;
448 ta_cnt_phi2 = ((byte & 0x21) == 0x01);
449 break;
450
451 case 0xf:
452 crb = byte & 0xef;
453 if (byte & 0x10) // Force load
454 tb = latchb;
455 tb_cnt_phi2 = ((byte & 0x61) == 0x01);
456 tb_cnt_ta = ((byte & 0x61) == 0x41);
457 break;
458 }
459 }
460
461
462 /*
463 * Count CIA TOD clock (called during VBlank)
464 */
465
CountTOD(void)466 void MOS6526::CountTOD(void)
467 {
468 uint8 lo, hi;
469
470 // Decrement frequency divider
471 if (tod_divider)
472 tod_divider--;
473 else {
474
475 // Reload divider according to 50/60 Hz flag
476 if (cra & 0x80)
477 tod_divider = 4;
478 else
479 tod_divider = 5;
480
481 // 1/10 seconds
482 tod_10ths++;
483 if (tod_10ths > 9) {
484 tod_10ths = 0;
485
486 // Seconds
487 lo = (tod_sec & 0x0f) + 1;
488 hi = tod_sec >> 4;
489 if (lo > 9) {
490 lo = 0;
491 hi++;
492 }
493 if (hi > 5) {
494 tod_sec = 0;
495
496 // Minutes
497 lo = (tod_min & 0x0f) + 1;
498 hi = tod_min >> 4;
499 if (lo > 9) {
500 lo = 0;
501 hi++;
502 }
503 if (hi > 5) {
504 tod_min = 0;
505
506 // Hours
507 lo = (tod_hr & 0x0f) + 1;
508 hi = (tod_hr >> 4) & 1;
509 tod_hr &= 0x80; // Keep AM/PM flag
510 if (lo > 9) {
511 lo = 0;
512 hi++;
513 }
514 tod_hr |= (hi << 4) | lo;
515 if ((tod_hr & 0x1f) > 0x11)
516 tod_hr = tod_hr & 0x80 ^ 0x80;
517 } else
518 tod_min = (hi << 4) | lo;
519 } else
520 tod_sec = (hi << 4) | lo;
521 }
522
523 // Alarm time reached? Trigger interrupt if enabled
524 if (tod_10ths == alm_10ths && tod_sec == alm_sec &&
525 tod_min == alm_min && tod_hr == alm_hr)
526 TriggerInterrupt(4);
527 }
528 }
529
530
531 /*
532 * Trigger IRQ (CIA 1)
533 */
534
TriggerInterrupt(int bit)535 void MOS6526_1::TriggerInterrupt(int bit)
536 {
537 icr |= bit;
538 if (int_mask & bit) {
539 icr |= 0x80;
540 the_cpu->TriggerCIAIRQ();
541 }
542 }
543
544
545 /*
546 * Trigger NMI (CIA 2)
547 */
548
TriggerInterrupt(int bit)549 void MOS6526_2::TriggerInterrupt(int bit)
550 {
551 icr |= bit;
552 if (int_mask & bit) {
553 icr |= 0x80;
554 the_cpu->TriggerNMI();
555 }
556 }
557