ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/CIA.cpp
Revision: 1.2
Committed: 2003-07-01T17:51:17Z (20 years, 9 months ago) by cebix
Branch: MAIN
Changes since 1.1: +1 -1 lines
Log Message:
updated copyright date

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * CIA.cpp - 6526 emulation
3     *
4 cebix 1.2 * Frodo (C) 1994-1997,2002-2003 Christian Bauer
5 cebix 1.1 *
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     }