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