Main Page | Class Hierarchy | Compound List | File List | Compound Members | File Members | Related Pages

CIA.cpp

Go to the documentation of this file.
00001 /*
00002  *  CIA.cpp - 6526 emulation
00003  *
00004  *  Frodo (C) 1994-1997 Christian Bauer
00005  *
00006 
00007  *
00008  * Notes:
00009  * ------
00010  *
00011  *  - The EmulateLine() function is called for every emulated raster
00012  *    line. It counts down the timers and triggers interrupts if
00013  *    necessary.
00014  *  - The TOD clocks are counted by CountTOD() during the VBlank, so
00015  *    the input frequency is 50Hz
00016  *  - The fields KeyMatrix and RevMatrix contain one bit for each
00017  *    key on the C64 keyboard (0: key pressed, 1: key released).
00018  *    KeyMatrix is used for normal keyboard polling (PRA->PRB),
00019  *    RevMatrix for reversed polling (PRB->PRA).
00020  *
00021  * Incompatibilities:
00022  * ------------------
00023  *
00024  *  - The TOD clock should not be stopped on a read access, but
00025  *    latched
00026  *  - The SDR interrupt is faked
00027  */
00028 
00029 #include "sysdeps.h"
00030 
00031 #include "CIA.h"
00032 #include "CPUC64.h"
00033 #include "CPU1541.h"
00034 #include "VIC.h"
00035 #include "Prefs.h"
00036 
00037 
00038 /*
00039  *  Constructors
00040  */
00041 
00042 MOS6526::MOS6526(MOS6510 *CPU) : the_cpu(CPU) {}
00043 MOS6526_1::MOS6526_1(MOS6510 *CPU, MOS6569 *VIC) : MOS6526(CPU), the_vic(VIC)
00044 {
00045         __CHECK_NULL(CPU);
00046         __CHECK_NULL(VIC);
00047 
00048 //      ThePrefs = VIC->ThePrefs;       //AEH 991202
00049         CTOR(MOS6526_1);
00050 }
00051 MOS6526_2::MOS6526_2(MOS6510 *CPU, MOS6569 *VIC, MOS6502_1541 *CPU1541) : MOS6526(CPU), the_vic(VIC), the_cpu_1541(CPU1541)
00052 {
00053         __CHECK_NULL(CPU);
00054         __CHECK_NULL(VIC);
00055         __CHECK_NULL(CPU1541);
00056 
00057 //      ThePrefs = VIC->ThePrefs;       //AEH 991202
00058         CTOR(MOS6526_2);
00059 }
00060 
00061 /*
00062  *      Destructors
00063  */
00064 
00065 MOS6526_1::~MOS6526_1()
00066         {
00067         DTOR(MOS6526_1);
00068         }
00069 
00070 MOS6526_2::~MOS6526_2()
00071         {
00072         DTOR(MOS6526_2);
00073         }
00074 
00075 
00076 /*
00077  *  Reset the CIA
00078  */
00079 
00080 void MOS6526::Reset(void)
00081 {
00082         pra = prb = ddra = ddrb = 0;
00083 
00084         ta = tb = 0xffff;
00085         latcha = latchb = 1;
00086 
00087         tod_10ths = tod_sec = tod_min = tod_hr = 0;
00088         alm_10ths = alm_sec = alm_min = alm_hr = 0;
00089 
00090         sdr = icr = cra = crb = int_mask = 0;
00091 
00092         tod_halt = ta_cnt_phi2 = tb_cnt_phi2 = tb_cnt_ta = false;
00093         tod_divider = 0;
00094 }
00095 
00096 void MOS6526_1::Reset(void)
00097 {
00098         MOS6526::Reset();
00099 
00100         // Clear keyboard matrix and joystick states
00101         for (int i=0; i<8; i++)
00102                 KeyMatrix[i] = RevMatrix[i] = 0xff;
00103 
00104         Joystick1 = Joystick2 = 0xff;
00105         prev_lp = 0x10;
00106 
00107         ELOG1(_L8("CIA1 reset\n"));
00108 }
00109 
00110 void MOS6526_2::Reset(void)
00111 {
00112         MOS6526::Reset();
00113 
00114         // VA14/15 = 0
00115         the_vic->ChangedVA(0);
00116 
00117         // IEC
00118         IECLines = 0xd0;
00119 
00120         ELOG1(_L8("CIA2 reset\n"));
00121 }
00122 
00123 
00124 /*
00125  *  Get CIA state
00126  */
00127 
00128 void MOS6526::GetState(MOS6526State *cs)
00129 {
00130         cs->pra = pra;
00131         cs->prb = prb;
00132         cs->ddra = ddra;
00133         cs->ddrb = ddrb;
00134 
00135         cs->ta_lo = ta & 0xff;
00136         cs->ta_hi = ta >> 8;
00137         cs->tb_lo = tb & 0xff;
00138         cs->tb_hi = tb >> 8;
00139         cs->latcha = latcha;
00140         cs->latchb = latchb;
00141         cs->cra = cra;
00142         cs->crb = crb;
00143 
00144         cs->tod_10ths = tod_10ths;
00145         cs->tod_sec = tod_sec;
00146         cs->tod_min = tod_min;
00147         cs->tod_hr = tod_hr;
00148         cs->alm_10ths = alm_10ths;
00149         cs->alm_sec = alm_sec;
00150         cs->alm_min = alm_min;
00151         cs->alm_hr = alm_hr;
00152 
00153         cs->sdr = sdr;
00154 
00155         cs->int_data = icr;
00156         cs->int_mask = int_mask;
00157 }
00158 
00159 
00160 /*
00161  *  Restore CIA state
00162  */
00163 
00164 void MOS6526::SetState(MOS6526State *cs)
00165 {
00166         pra = cs->pra;
00167         prb = cs->prb;
00168         ddra = cs->ddra;
00169         ddrb = cs->ddrb;
00170 
00171         ta = (cs->ta_hi << 8) | cs->ta_lo;
00172         tb = (cs->tb_hi << 8) | cs->tb_lo;
00173         latcha = cs->latcha;
00174         latchb = cs->latchb;
00175         cra = cs->cra;
00176         crb = cs->crb;
00177 
00178         tod_10ths = cs->tod_10ths;
00179         tod_sec = cs->tod_sec;
00180         tod_min = cs->tod_min;
00181         tod_hr = cs->tod_hr;
00182         alm_10ths = cs->alm_10ths;
00183         alm_sec = cs->alm_sec;
00184         alm_min = cs->alm_min;
00185         alm_hr = cs->alm_hr;
00186 
00187         sdr = cs->sdr;
00188 
00189         icr = cs->int_data;
00190         int_mask = cs->int_mask;
00191 
00192         tod_halt = false;
00193         ta_cnt_phi2 = ((cra & 0x21) == 0x01);
00194         tb_cnt_phi2 = ((crb & 0x61) == 0x01);
00195         tb_cnt_ta = ((crb & 0x61) == 0x41);
00196 }
00197 
00198 
00199 /*
00200  *  Read from register (CIA 1)
00201  */
00202 
00203 uint8 MOS6526_1::ReadRegister(uint16 adr)
00204 {
00205         switch (adr) {
00206                 case 0x00: {
00207                         uint8 ret = pra | ~ddra, tst = (prb | ~ddrb) & Joystick1;
00208                         if (!(tst & 0x01)) ret &= RevMatrix[0]; // AND all active columns
00209                         if (!(tst & 0x02)) ret &= RevMatrix[1];
00210                         if (!(tst & 0x04)) ret &= RevMatrix[2];
00211                         if (!(tst & 0x08)) ret &= RevMatrix[3];
00212                         if (!(tst & 0x10)) ret &= RevMatrix[4];
00213                         if (!(tst & 0x20)) ret &= RevMatrix[5];
00214                         if (!(tst & 0x40)) ret &= RevMatrix[6];
00215                         if (!(tst & 0x80)) ret &= RevMatrix[7];
00216                         return ret & Joystick2;
00217                 }
00218                 case 0x01: {
00219                         uint8 ret = ~ddrb, tst = (pra | ~ddra) & Joystick2;
00220                         if (!(tst & 0x01)) ret &= KeyMatrix[0]; // AND all active rows
00221                         if (!(tst & 0x02)) ret &= KeyMatrix[1];
00222                         if (!(tst & 0x04)) ret &= KeyMatrix[2];
00223                         if (!(tst & 0x08)) ret &= KeyMatrix[3];
00224                         if (!(tst & 0x10)) ret &= KeyMatrix[4];
00225                         if (!(tst & 0x20)) ret &= KeyMatrix[5];
00226                         if (!(tst & 0x40)) ret &= KeyMatrix[6];
00227                         if (!(tst & 0x80)) ret &= KeyMatrix[7];
00228                         return (ret | (prb & ddrb)) & Joystick1;
00229                 }
00230                 case 0x02: return ddra;
00231                 case 0x03: return ddrb;
00232                 case 0x04: return ta;
00233                 case 0x05: return ta >> 8;
00234                 case 0x06: return tb;
00235                 case 0x07: return tb >> 8;
00236                 case 0x08: tod_halt = false; return tod_10ths;
00237                 case 0x09: return tod_sec;
00238                 case 0x0a: return tod_min;
00239                 case 0x0b: tod_halt = true; return tod_hr;
00240                 case 0x0c: return sdr;
00241                 case 0x0d: {
00242                         uint8 ret = icr;                // Read and clear ICR
00243                         icr = 0;
00244                         the_cpu->ClearCIAIRQ(); // Clear IRQ
00245                         return ret;
00246                 }
00247                 case 0x0e: return cra;
00248                 case 0x0f: return crb;
00249         }
00250         return 0;       // Can't happen
00251 }
00252 
00253 
00254 /*
00255  *  Read from register (CIA 2)
00256  */
00257 
00258 uint8 MOS6526_2::ReadRegister(uint16 adr)
00259 {
00260         switch (adr) {
00261                 case 0x00:
00262                         return (pra | ~ddra) & 0x3f
00263                                 | IECLines & the_cpu_1541->IECLines;
00264                 case 0x01: return prb | ~ddrb;
00265                 case 0x02: return ddra;
00266                 case 0x03: return ddrb;
00267                 case 0x04: return ta;
00268                 case 0x05: return ta >> 8;
00269                 case 0x06: return tb;
00270                 case 0x07: return tb >> 8;
00271                 case 0x08: tod_halt = false; return tod_10ths;
00272                 case 0x09: return tod_sec;
00273                 case 0x0a: return tod_min;
00274                 case 0x0b: tod_halt = true; return tod_hr;
00275                 case 0x0c: return sdr;
00276                 case 0x0d: {
00277                         uint8 ret = icr;                // Read and clear ICR
00278                         icr = 0;
00279                         the_cpu->ClearNMI();    // Clear NMI
00280                         return ret;
00281                 }
00282                 case 0x0e: return cra;
00283                 case 0x0f: return crb;
00284         }
00285         return 0;       // Can't happen
00286 }
00287 
00288 
00289 /*
00290  *  Write to register (CIA 1)
00291  */
00292 
00293 // Write to port B, check for lightpen interrupt
00294 inline void MOS6526_1::check_lp(void)
00295 {
00296         if ( ( (prb | ~ddrb) & 0x10 ) != prev_lp)
00297                 the_vic->TriggerLightpen();
00298         prev_lp = (prb | ~ddrb) & 0x10;
00299 }
00300 
00301 void MOS6526_1::WriteRegister(uint16 adr, uint8 byte)
00302 {
00303         switch (adr) {
00304                 case 0x0: pra = byte; break;
00305                 case 0x1:
00306                         prb = byte;
00307                         check_lp();
00308                         break;
00309                 case 0x2: ddra = byte; break;
00310                 case 0x3:
00311                         ddrb = byte;
00312                         check_lp();
00313                         break;
00314 
00315                 case 0x4: latcha = (latcha & 0xff00) | byte; break;
00316                 case 0x5:
00317                         latcha = (latcha & 0xff) | (byte << 8);
00318                         if (!(cra & 1)) // Reload timer if stopped
00319                                 ta = latcha;
00320                         break;
00321 
00322                 case 0x6: latchb = (latchb & 0xff00) | byte; break;
00323                 case 0x7:
00324                         latchb = (latchb & 0xff) | (byte << 8);
00325                         if (!(crb & 1)) // Reload timer if stopped
00326                                 tb = latchb;
00327                         break;
00328 
00329                 case 0x8:
00330                         if (crb & 0x80)
00331                                 alm_10ths = byte & 0x0f;
00332                         else
00333                                 tod_10ths = byte & 0x0f;
00334                         break;
00335                 case 0x9:
00336                         if (crb & 0x80)
00337                                 alm_sec = byte & 0x7f;
00338                         else
00339                                 tod_sec = byte & 0x7f;
00340                         break;
00341                 case 0xa:
00342                         if (crb & 0x80)
00343                                 alm_min = byte & 0x7f;
00344                         else
00345                                 tod_min = byte & 0x7f;
00346                         break;
00347                 case 0xb:
00348                         if (crb & 0x80)
00349                                 alm_hr = byte & 0x9f;
00350                         else
00351                                 tod_hr = byte & 0x9f;
00352                         break;
00353 
00354                 case 0xc:
00355                         sdr = byte;
00356                         TriggerInterrupt(8);    // Fake SDR interrupt for programs that need it
00357                         break;
00358 
00359                 case 0xd:
00360 //                      if (ThePrefs->CIAIRQHack)       // Hack for addressing modes that read from the address
00361                                 icr = 0;
00362                         if (byte & 0x80) {
00363                                 int_mask |= byte & 0x7f;
00364                                 if (icr & int_mask & 0x1f) { // Trigger IRQ if pending
00365                                         icr |= 0x80;
00366                                         the_cpu->TriggerCIAIRQ();
00367                                 }
00368                         } else
00369                                 int_mask &= ~byte;
00370                         break;
00371 
00372                 case 0xe:
00373                         cra = byte & 0xef;
00374                         if (byte & 0x10) // Force load
00375                                 ta = latcha;
00376                         ta_cnt_phi2 = ((byte & 0x21) == 0x01);
00377                         break;
00378 
00379                 case 0xf:
00380                         crb = byte & 0xef;
00381                         if (byte & 0x10) // Force load
00382                                 tb = latchb;
00383                         tb_cnt_phi2 = ((byte & 0x61) == 0x01);
00384                         tb_cnt_ta = ((byte & 0x61) == 0x41);
00385                         break;
00386         }
00387 }
00388 
00389 
00390 /*
00391  *  Write to register (CIA 2)
00392  */
00393 
00394 void MOS6526_2::WriteRegister(uint16 adr, uint8 byte)
00395 {
00396         switch (adr) {
00397                 case 0x0:{
00398                         pra = byte;
00399                         byte = ~pra & ddra;
00400                         the_vic->ChangedVA(byte & 3);
00401                         uint8 old_lines = IECLines;
00402                         IECLines = (byte << 2) & 0x80   // DATA
00403                                 | (byte << 2) & 0x40            // CLK
00404                                 | (byte << 1) & 0x10;           // ATN
00405                         if ((IECLines ^ old_lines) & 0x10) {    // ATN changed
00406                                 the_cpu_1541->NewATNState();
00407                                 if (old_lines & 0x10)                           // ATN 1->0
00408                                         the_cpu_1541->IECInterrupt();
00409                         }
00410                         break;
00411                 }
00412                 case 0x1: prb = byte; break;
00413 
00414                 case 0x2:
00415                         ddra = byte;
00416                         the_vic->ChangedVA(~(pra | ~ddra) & 3);
00417                         break;
00418                 case 0x3: ddrb = byte; break;
00419 
00420                 case 0x4: latcha = (latcha & 0xff00) | byte; break;
00421                 case 0x5:
00422                         latcha = (latcha & 0xff) | (byte << 8);
00423                         if (!(cra & 1)) // Reload timer if stopped
00424                                 ta = latcha;
00425                         break;
00426 
00427                 case 0x6: latchb = (latchb & 0xff00) | byte; break;
00428                 case 0x7:
00429                         latchb = (latchb & 0xff) | (byte << 8);
00430                         if (!(crb & 1)) // Reload timer if stopped
00431                                 tb = latchb;
00432                         break;
00433 
00434                 case 0x8:
00435                         if (crb & 0x80)
00436                                 alm_10ths = byte & 0x0f;
00437                         else
00438                                 tod_10ths = byte & 0x0f;
00439                         break;
00440                 case 0x9:
00441                         if (crb & 0x80)
00442                                 alm_sec = byte & 0x7f;
00443                         else
00444                                 tod_sec = byte & 0x7f;
00445                         break;
00446                 case 0xa:
00447                         if (crb & 0x80)
00448                                 alm_min = byte & 0x7f;
00449                         else
00450                                 tod_min = byte & 0x7f;
00451                         break;
00452                 case 0xb:
00453                         if (crb & 0x80)
00454                                 alm_hr = byte & 0x9f;
00455                         else
00456                                 tod_hr = byte & 0x9f;
00457                         break;
00458 
00459                 case 0xc:
00460                         sdr = byte;
00461                         TriggerInterrupt(8);    // Fake SDR interrupt for programs that need it
00462                         break;
00463 
00464                 case 0xd:
00465 //                      if (ThePrefs.CIAIRQHack)
00466                                 icr = 0;
00467                         if (byte & 0x80) {
00468                                 int_mask |= byte & 0x7f;
00469                                 if (icr & int_mask & 0x1f) { // Trigger NMI if pending
00470                                         icr |= 0x80;
00471                                         the_cpu->TriggerNMI();
00472                                 }
00473                         } else
00474                                 int_mask &= ~byte;
00475                         break;
00476 
00477                 case 0xe:
00478                         cra = byte & 0xef;
00479                         if (byte & 0x10) // Force load
00480                                 ta = latcha;
00481                         ta_cnt_phi2 = ((byte & 0x21) == 0x01);
00482                         break;
00483 
00484                 case 0xf:
00485                         crb = byte & 0xef;
00486                         if (byte & 0x10) // Force load
00487                                 tb = latchb;
00488                         tb_cnt_phi2 = ((byte & 0x61) == 0x01);
00489                         tb_cnt_ta = ((byte & 0x61) == 0x41);
00490                         break;
00491         }
00492 }
00493 
00494 
00495 /*
00496  *  Count CIA TOD clock (called during VBlank)
00497  */
00498 
00499 void MOS6526::CountTOD(void)
00500 {
00501         uint8 lo, hi;
00502 
00503         // Decrement frequency divider
00504         if (tod_divider)
00505                 tod_divider--;
00506         else {
00507 
00508                 // Reload divider according to 50/60 Hz flag
00509                 if (cra & 0x80)
00510                         tod_divider = 4;
00511                 else
00512                         tod_divider = 5;
00513 
00514                 // 1/10 seconds
00515                 tod_10ths++;
00516                 if (tod_10ths > 9) {
00517                         tod_10ths = 0;
00518 
00519                         // Seconds
00520                         lo = (tod_sec & 0x0f) + 1;
00521                         hi = tod_sec >> 4;
00522                         if (lo > 9) {
00523                                 lo = 0;
00524                                 hi++;
00525                         }
00526                         if (hi > 5) {
00527                                 tod_sec = 0;
00528 
00529                                 // Minutes
00530                                 lo = (tod_min & 0x0f) + 1;
00531                                 hi = tod_min >> 4;
00532                                 if (lo > 9) {
00533                                         lo = 0;
00534                                         hi++;
00535                                 }
00536                                 if (hi > 5) {
00537                                         tod_min = 0;
00538 
00539                                         // Hours
00540                                         lo = (tod_hr & 0x0f) + 1;
00541                                         hi = (tod_hr >> 4) & 1;
00542                                         tod_hr &= 0x80;         // Keep AM/PM flag
00543                                         if (lo > 9) {
00544                                                 lo = 0;
00545                                                 hi++;
00546                                         }
00547                                         tod_hr |= (hi << 4) | lo;
00548                                         if ((tod_hr & 0x1f) > 0x11)
00549                                                 tod_hr = tod_hr & 0x80 ^ 0x80;
00550                                 } else
00551                                         tod_min = (hi << 4) | lo;
00552                         } else
00553                                 tod_sec = (hi << 4) | lo;
00554                 }
00555 
00556                 // Alarm time reached? Trigger interrupt if enabled
00557                 if (tod_10ths == alm_10ths && tod_sec == alm_sec &&
00558                         tod_min == alm_min && tod_hr == alm_hr)
00559                         TriggerInterrupt(4);
00560         }
00561 }
00562 
00563 
00564 /*
00565  *  Trigger IRQ (CIA 1)
00566  */
00567 
00568 void MOS6526_1::TriggerInterrupt(int bit)
00569 {
00570         icr |= bit;
00571         if (int_mask & bit) {
00572                 icr |= 0x80;
00573                 the_cpu->TriggerCIAIRQ();
00574         }
00575 }
00576 
00577 
00578 /*
00579  *  Trigger NMI (CIA 2)
00580  */
00581 
00582 void MOS6526_2::TriggerInterrupt(int bit)
00583 {
00584         icr |= bit;
00585         if (int_mask & bit) {
00586                 icr |= 0x80;
00587                 the_cpu->TriggerNMI();
00588         }
00589 }

Generated on Tue Feb 8 04:07:26 2005 for E32frodo by doxygen 1.3.3