ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/CIA.cpp
Revision: 1.4
Committed: 2005-06-27T19:55:48Z (18 years, 9 months ago) by cebix
Branch: MAIN
CVS Tags: VERSION_4_2, HEAD
Changes since 1.3: +1 -1 lines
Log Message:
updated copyright dates

File Contents

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