/* * (C) 2006 Dirk W. Hoffmann. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "C64.h" CIA::CIA() { name = "CIA"; // Register sub components VirtualComponent *subcomponents[] = { &tod, NULL }; registerSubComponents(subcomponents, sizeof(subcomponents)); // Register snapshot items SnapshotItem items[] = { { &delay, sizeof(delay), CLEAR_ON_RESET }, { &feed, sizeof(feed), CLEAR_ON_RESET }, { &CRA, sizeof(CRA), CLEAR_ON_RESET }, { &CRB, sizeof(CRB), CLEAR_ON_RESET }, { &ICR, sizeof(ICR), CLEAR_ON_RESET }, { &IMR, sizeof(IMR), CLEAR_ON_RESET }, { &PB67TimerMode, sizeof(PB67TimerMode), CLEAR_ON_RESET }, { &PB67TimerOut, sizeof(PB67TimerOut), CLEAR_ON_RESET }, { &PB67Toggle, sizeof(PB67Toggle), CLEAR_ON_RESET }, { &PALatch, sizeof(PALatch), CLEAR_ON_RESET }, { &PBLatch, sizeof(PBLatch), CLEAR_ON_RESET }, { &DDRA, sizeof(DDRA), CLEAR_ON_RESET }, { &DDRB, sizeof(DDRB), CLEAR_ON_RESET }, { &PA, sizeof(PA), CLEAR_ON_RESET }, { &PB, sizeof(PB), CLEAR_ON_RESET }, { &CNT, sizeof(CNT), CLEAR_ON_RESET }, { &INT, sizeof(INT), CLEAR_ON_RESET }, { &readICR, sizeof(readICR), CLEAR_ON_RESET }, { &counterA, sizeof(counterA), CLEAR_ON_RESET }, { &latchA, sizeof(latchA), CLEAR_ON_RESET }, { &counterB, sizeof(counterB), CLEAR_ON_RESET }, { &latchB, sizeof(latchB), CLEAR_ON_RESET }, { NULL, 0, 0 }}; registerSnapshotItems(items, sizeof(items)); } CIA::~CIA() { } void CIA::reset() { VirtualComponent::reset(); // Establish bindings cpu = c64->cpu; vic = c64->vic; clearInterruptLine(); PA = 0xff; PB = 0xff; CNT = true; // CNT line is high by default INT = 1; latchA = 0xFFFF; latchB = 0xFFFF; } #if 0 void CIA::setFlagPin(uint8_t value) { if (value) // Note: FLAG pin is inverted ICR &= ~0x10; else ICR |= 0x10; } #endif void CIA::triggerRisingEdgeOnFlagPin() { // ICR &= ~0x10; // Note: FLAG pin is inverted } void CIA::triggerFallingEdgeOnFlagPin() { ICR |= 0x10; // Note: FLAG pin is inverted // Trigger interrupt, if enabled if (IMR & 0x10) { INT = 0; ICR |= 0x80; raiseInterruptLine(); } } uint8_t CIA::peek(uint16_t addr) { uint8_t result; switch(addr) { case CIA_DATA_DIRECTION_A: result = DDRA; break; case CIA_DATA_DIRECTION_B: result = DDRB; break; case CIA_TIMER_A_LOW: result = getCounterALo(); break; case CIA_TIMER_A_HIGH: result = getCounterAHi(); break; case CIA_TIMER_B_LOW: result = getCounterBLo(); break; case CIA_TIMER_B_HIGH: result = getCounterBHi(); break; case CIA_TIME_OF_DAY_SEC_FRAC: result = tod.getTodTenth(); tod.defreeze(); break; case CIA_TIME_OF_DAY_SECONDS: result = tod.getTodSeconds(); break; case CIA_TIME_OF_DAY_MINUTES: result = tod.getTodMinutes(); break; case CIA_TIME_OF_DAY_HOURS: tod.freeze(); result = tod.getTodHours(); break; case CIA_SERIAL_IO_BUFFER: result = 0x00; break; case CIA_INTERRUPT_CONTROL: result = ICR; // get status of the Int line into bit 7 and draw Int high if (INT == 0) { result |= 0x80; INT = 1; clearInterruptLine(); // really? } // discard pending interrupts delay &= ~(Interrupt0 | Interrupt1); // Remember read access readICR = true; // set all events to 0 ICR = 0; break; case CIA_CONTROL_REG_A: result = (uint8_t)(CRA & ~0x10); // Bit 4 is always 0 when read break; case CIA_CONTROL_REG_B: result = (uint8_t)(CRB & ~0x10); // Bit 4 is always 0 when read break; default: result = 0; panic("Unknown CIA address %04X\n", addr); break; } return result; } void CIA::poke(uint16_t addr, uint8_t value) { switch(addr) { case CIA_TIMER_A_LOW: setLatchALo(value); // If timer A is currently in LOAD state, this value goes directly into the counter if (delay & LoadA2) { setCounterALo(value); } return; case CIA_TIMER_A_HIGH: setLatchAHi(value); // load counter if timer is stopped if ((CRA & 0x01) == 0) { delay |= LoadA0; } // If timer A is currently in LOAD state, this value goes directly into the counter if (delay & LoadA2) { setCounterAHi(value); } return; case CIA_TIMER_B_LOW: setLatchBLo(value); // If timer B is currently in LOAD state, this value goes directly into the counter if (delay & LoadB2) { setCounterBLo(value); } return; case CIA_TIMER_B_HIGH: setLatchBHi(value); // load counter if timer is stopped if ((CRB & 0x01) == 0) { delay |= LoadB0; } // If timer B is currently in LOAD state, this value goes directly into the counter if (delay & LoadB2) { setCounterBHi(value); } return; case CIA_TIME_OF_DAY_SEC_FRAC: if (CRB & 0x80) { tod.setAlarmTenth(value); } else { tod.setTodTenth(value); tod.cont(); } return; case CIA_TIME_OF_DAY_SECONDS: if (CRB & 0x80) tod.setAlarmSeconds(value); else tod.setTodSeconds(value); return; case CIA_TIME_OF_DAY_MINUTES: if (CRB & 0x80) tod.setAlarmMinutes(value); else tod.setTodMinutes(value); return; case CIA_TIME_OF_DAY_HOURS: if (CRB & 0x80) { tod.setAlarmHours(value); } else { // Note: A real C64 shows strange behaviour when writing 0x12 or 0x92 // into this register. In this case, the AM/PM flag is inverted if ((value & 0x1F) == 0x12) value ^= 0x80; tod.setTodHours(value); tod.stop(); } return; case CIA_SERIAL_IO_BUFFER: // Serial I/O communication is not (yet) implemented //triggerInterrupt(0x08); // debug("poke CIA_SERIAL_IO_BUFFER: %0x2X\n", value); return; case CIA_INTERRUPT_CONTROL: //if ((value & 0x84) == 0x84) // debug("SETTING TIME OF DAY ALARM (%02X)\n", value); // bit 7 means set (1) or clear (0) the other bits if ((value & 0x80) != 0) { IMR |= (value & 0x1F); } else { IMR &= ~(value & 0x1F); } // raise an interrupt in the next cycle if condition matches if ((IMR & ICR) != 0) { if (INT) { delay |= Interrupt0; } } return; case CIA_CONTROL_REG_A: { // // Adapted from PC64Win by Wolfgang Lorenz // // set clock in o2 mode // todo cnt if ((value & 0x21) == 0x01) { delay |= CountA1 | CountA0; feed |= CountA0; } else { delay &= ~(CountA1 | CountA0); feed &= ~CountA0; } // set one shot mode if ((value & 0x08) != 0) { feed |= OneShotA0; } else { feed &= ~OneShotA0; } // set force load if ((value & 0x10) != 0) { delay |= LoadA0; } // set toggle high on rising edge of Start if ((value & 0x01) != 0 && (CRA & 0x01) == 0) { PB67Toggle |= 0x40; } // timer A output to PB6 if ((value & 0x02) == 0) { PB67TimerMode &= ~0x40; } else { PB67TimerMode |= 0x40; if ((value & 0x04) == 0) { if ((delay & PB7Low1) == 0) { PB67TimerOut &= ~0x40; } else { PB67TimerOut |= 0x40; } } else { PB67TimerOut = (PB67TimerOut & ~0x40) | (PB67Toggle & 0x40); } } // write PB67 PB = ((PBLatch | ~DDRB) & ~PB67TimerMode) | (PB67TimerOut & PB67TimerMode); // set the register CRA = value; return; } case CIA_CONTROL_REG_B: { // // Adapted from PC64Win by Wolfgang Lorenz // // set clock in o2 mode // todo cnt if ((value & 0x61) == 0x01) { delay |= CountB1 | CountB0; feed |= CountB0; } else { delay &= ~(CountB1 | CountB0); feed &= ~CountB0; } // set one shot mode if ((value & 0x08) != 0) { feed |= OneShotB0; } else { feed &= ~OneShotB0; } // set force load if ((value & 0x10) != 0) { delay |= LoadB0; } // set toggle high on rising edge of Start if ((value & 0x01) != 0 && (CRB & 0x01) == 0) { PB67Toggle |= 0x80; } // timer B output to PB7 if ((value & 0x02) == 0) { PB67TimerMode &= ~0x80; } else { PB67TimerMode |= 0x80; if ((value & 0x04) == 0) { if ((delay & PB7Low1) == 0) { PB67TimerOut &= ~0x80; } else { PB67TimerOut |= 0x80; } } else { PB67TimerOut = (PB67TimerOut & ~0x80) | (PB67Toggle & 0x80); } } // write PB67 PB = ((PBLatch | ~DDRB) & ~PB67TimerMode) | (PB67TimerOut & PB67TimerMode); // set the register CRB = value; return; } default: panic("PANIC: Unknown CIA address (poke) %04X\n", addr); } } void CIA::incrementTOD() { if (tod.increment()) { // Set interrupt source ICR |= 0x04; // Trigger interrupt, if enabled if (IMR & 0x04) { // The uppermost bit indicates that an interrupt occured // printf("Triggering CIA interrupt (source = %02X) at cycle %d\n", source, (int)cpu->getCycles()); ICR |= 0x80; raiseInterruptLine(); } } } void CIA::dumpTrace() { const char *indent = " "; if (!tracingEnabled()) return; debug(1, "%sICR: %02X IMR: %02X ", indent, ICR, IMR); debug(1, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", delay & CountA0 ? "CntA0 " : "", delay & CountA1 ? "CntA1 " : "", delay & CountA2 ? "CntA2 " : "", delay & CountA3 ? "CntA3 " : "", delay & CountB0 ? "CntB0 " : "", delay & CountB1 ? "CntB1 " : "", delay & CountB2 ? "CntB2 " : "", delay & CountB3 ? "CntB3 " : "", delay & LoadA0 ? "LdA0 " : "", delay & LoadA1 ? "LdA1 " : "", delay & LoadA2 ? "LdA2 " : "", delay & LoadB0 ? "LdB0 " : "", delay & LoadB1 ? "LdB1 " : "", delay & LoadB1 ? "LdB2 " : "", delay & PB6Low0 ? "PB6Lo0 " : "", delay & PB6Low1 ? "PB6Lo1 " : "", delay & PB7Low0 ? "PB7Lo0 " : "", delay & PB7Low1 ? "PB7Lo1 " : "", delay & Interrupt0 ? "Int0 " : "", delay & Interrupt1 ? "Int1 " : "", delay & OneShotA0 ? "1ShotA0 " : "", delay & OneShotB0 ? "1ShotB0 " : ""); debug(1, "%sA: %04X (%04X) PA: %02X (%02X) DDRA: %02X CRA: %02X\n", indent, counterA, latchA, PA, PALatch, DDRA, CRA); debug(1, "%sB: %04X (%04X) PB: %02X (%02X) DDRB: %02X CRB: %02X\n", indent, counterB, latchB, PB, PBLatch, DDRB, CRB); } void CIA::dumpState() { // assert(0); msg(" Counter A : %02X\n", getCounterA()); msg(" Latch A : %02X\n", getLatchA()); msg(" Data port A : %02X\n", getDataPortA()); msg(" Data port direction A : %02X\n", getDataPortDirectionA()); msg(" Control register A : %02X\n", getControlRegA()); msg(" Timer A interrupts : %s\n", isInterruptEnabledA() ? "enabled" : "disabled"); msg("\n"); msg(" Counter B : %02X\n", getCounterB()); msg(" Latch B : %02X\n", getLatchB()); msg(" Data port B : %02X\n", getDataPortB()); msg(" Data port direction B : %02X\n", getDataPortDirectionB()); msg(" Control register B : %02X\n", getControlRegB()); msg(" Timer B interrupts : %s\n", isInterruptEnabledB() ? "enabled" : "disabled"); msg("\n"); msg(" Interrupt control reg : %02X\n", ICR); msg(" Interrupt mask reg : %02X\n", IMR); msg("\n"); tod.dumpState(); } void CIA::executeOneCycle() { // // Layout of timer (A and B) // // Source: "A Software Model of the CIA6526" by Wolfgang Lorenz // // Phi2 Phi2 Phi2 // | | | // timerA ----- ------v------ ------v------ ----------v----------- // input ---->| & |------>| dwDelay & |-X-| dwDelay & |---->| decrement counter | // --->| | | CountA2 | | | CountA3 | | (1) | // | ----- ------------- | ------------- | | // ----------------- ^ Clr | | | // | bCRA & 0x01 | | | ------------------| new counter == 0? | // | timer A start |<---- | | | | | // ----------------- | | v v | | // ----- | ----- | timer A | // | & | | | & | | 16 bit counter | // | | | | | | and latch | // ----- | ----- | | // ^ ^ | |(2) | | // | | ---------|------------- | | // | | | | | | // timer A | | | ----- | | | // output <-----------|-X----------------X--->|>=1|---X---->| load from latch | // | --->| | | (4) | // ----- | ----- ---------------------- // |>=1| | // | | | Phi2 // ----- | | // ^ ^ | ------v------ ---------------- // | | (3) ---| dwDelay & |<-----| bcRA & 0x10 | // | ----------------- | LoadA1 | | force load | // | Phi2 | ------------- ---------------- // | | | ^ Clr // ----------------- | ------v------ | | // | bCRA & 0x08 | | | dwDelay & | | Phi2 // | one shot |---X->| oneShotA0 |--- // ----------------- ------------- // Timer A // Decrement counter if (delay & CountA3) counterA--; // (1) // Check underflow condition bool timerAOutput = (counterA == 0 && (delay & CountA2)); // (2) if (timerAOutput) { // Stop timer in one shot mode if ((delay | feed) & OneShotA0) { // (3) CRA &= ~0x01; delay &= ~(CountA2 | CountA1 | CountA0); feed &= ~CountA0; } // Timer A output to timer B in cascade mode if ((CRB & 0x61) == 0x41 || ((CRB & 0x61) == 0x61 && CNT)) { delay |= CountB1; } delay |= LoadA1; } // Load counter if (delay & LoadA1) // (4) reloadTimerA(); // Timer B // Decrement counter if (delay & CountB3) { counterB--; // (1) // debug("Counter B down to %04X \n", counterB); } // Check underflow condition bool timerBOutput = (counterB == 0 && (delay & CountB2)); // (2) if (timerBOutput) { // Stop timer in one shot mode if ((delay | feed) & OneShotB0) { // (3) CRB &= ~0x01; delay &= ~(CountB2 | CountB1 | CountB0); feed &= ~CountB0; } delay |= LoadB1; } // Load counter if (delay & LoadB1) // (4) reloadTimerB(); // // Timer output to PB6 (timer A) and PB7 (timer B) // // Source: "A Software Model of the CIA6526" by Wolfgang Lorenz // // (7) ----------------- // --------------------------->| 0x00 (pulse) | // | | | ---------------- // | | bCRA & 0x04 |------>| 0x02 (timer) | // timerA | Flip --------------- (8) | timer mode | | | // output -X------>| bPB67Toggle |---->| 0x04 (toggle) | | bCRA & 0x02 | // (5) | ^ 0x04 | | (6) | | output mode |----> PB6 output // --------------- ----------------- | | // ^ Set | 0x00 (port) | // | ---->| | // ----------------- 0->1 | ----------------- | ---------------- // | bCRA & 0x01 |------- | port B bit 6 |--- // | timer A start | | output | // ----------------- ----------------- // Timer A output to PB6 if (timerAOutput) { PB67Toggle ^= 0x40; // (5) toggle underflow counter bit if (CRA & 0x02) { // (6) if ((CRA & 0x04) == 0) { // (7) set PB6 high for one clock cycle PB67TimerOut |= 0x40; delay |= PB6Low0; delay &= ~PB6Low1; } else { // (8) toggle PB6 (copy bit 6 from PB67Toggle) PB67TimerOut = (PB67TimerOut & 0xBF) | (PB67Toggle & 0x40); } } } // Timer B output to PB7 if (timerBOutput) { PB67Toggle ^= 0x80; // (5) toggle underflow counter bit if (CRB & 0x02) { // (6) if ((CRB & 0x04) == 0) { // (7) set PB7 high for one clock cycle PB67TimerOut |= 0x80; delay |= PB7Low0; delay &= ~PB7Low1; } else { // (8) toggle PB7 (copy bit 7 from PB67Toggle) PB67TimerOut = (PB67TimerOut & 0x7F) | (PB67Toggle & 0x80); } } } // Set PB67 back to low if (delay & PB6Low1) PB67TimerOut &= ~0x40; if (delay & PB7Low1) PB67TimerOut &= ~0x80; // Write new PB PB = ((PBLatch | ~DDRB) & ~PB67TimerMode) | (PB67TimerOut & PB67TimerMode); // // Interrupt logic // // Source: "A Software Model of the CIA6526" by Wolfgang Lorenz // // ---------- // | bIMR & |---- // | 0x01 | | ----- // ---------- ---->| & |---- // timerA (9) Set ---------- ---->| | | // output ------------>| bICR & | | ----- | // ---------->| 0x01 |---- | ----- // | Clr ---------- -->|>=1|--- // | ---------- -->| | | // | | bIMR & |---- | ----- | // | | 0x02 | | ----- | | // | ---------- ---->| & |---- | // timerB | (10) Set ---------- ---->| | | // output --|--------->| bICR & | | ----- | // X--------->| 0x01 |---- | // | Clr ---------- | // read | | // ICR ------X-------------X---------------- | // | | | // v Clr v Clr | // ------ ---------- ---------------- | (11) // Int <--| -1 |<---| bICR & |<---| dwDelay & |<------- // ouptput | | | 0x80 |Set | Interrupt1 | // ------ ---------- -------^-------- // | // Phi2 // Set interrupt register and interrupt line if (delay & Interrupt1) { INT = 0; raiseInterruptLine(); } if (timerAOutput) { // (9) // On a real C64, there is a race condition here. If ICR is currently read, // the read access occurs *before* timer A sets bit 1. Hence, bit 1 always shows up. ICR |= 0x01; } if (timerBOutput && !readICR) { // (10) // On a real C64, there is a race condition here. If ICR is currently read, // the read access occurs *after* timer B sets bit 2. Hence, bit 2 won't show up. ICR |= 0x02; } if ((timerAOutput && (IMR & 0x01)) || (timerBOutput && (IMR & 0x02))) // (11) delay |= Interrupt0; readICR = false; // move delay flags left and feed in new bits delay = ((delay << 1) & DelayMask) | feed; } // ----------------------------------------------------------------------------------------- // Complex Interface Adapter 1 // ----------------------------------------------------------------------------------------- CIA1::CIA1() { name = "CIA1"; debug(2, " Creating CIA1 at address %p...\n", this); } CIA1::~CIA1() { this->c64 = c64; debug(2, " Releasing CIA1\n"); } void CIA1::reset() { keyboard = c64->keyboard; joy[0] = c64->joystick1; joy[1] = c64->joystick2; joystick[0] = 0xff; joystick[1] = 0xff; CIA::reset(); } void CIA1::dumpState() { msg("CIA 1:\n"); msg("------\n\n"); CIA::dumpState(); } void CIA1::raiseInterruptLine() { cpu->setIRQLineCIA(); } void CIA1::clearInterruptLine() { cpu->clearIRQLineCIA(); } uint8_t CIA1::getInterruptLine() { return cpu->getIRQLineCIA(); } void CIA1::pollJoystick(Joystick *joy, int joyDevNo) { JoystickAxisState leftRightState = joy->GetAxisX(); JoystickAxisState upDownState = joy->GetAxisY(); bool buttonState = joy->GetButtonPressed(); assert (joy != NULL); // up/down // set the down bit: 2, 2 and clear up bit: 2, 1 // Remember: clearJoystickBits(x, y) means pressed // setJoystickBits( x, y ) means released if(upDownState == JOYSTICK_AXIS_Y_UP) { clearJoystickBits(joyDevNo, 1); setJoystickBits(joyDevNo, 2); } else if(upDownState == JOYSTICK_AXIS_Y_DOWN) { clearJoystickBits(joyDevNo, 2); setJoystickBits(joyDevNo, 1); } else { setJoystickBits(joyDevNo, 1); setJoystickBits(joyDevNo, 2); } // left/right if(leftRightState == JOYSTICK_AXIS_X_LEFT) { clearJoystickBits(joyDevNo, 4); setJoystickBits(joyDevNo, 8); } else if(leftRightState == JOYSTICK_AXIS_X_RIGHT) { clearJoystickBits(joyDevNo, 8); setJoystickBits(joyDevNo, 4); } else { setJoystickBits(joyDevNo, 4); setJoystickBits(joyDevNo, 8); } // fire if(buttonState) { clearJoystickBits(joyDevNo, 16); } else { setJoystickBits(joyDevNo, 16); } } uint8_t CIA1::peek(uint16_t addr) { uint8_t result; assert(addr <= CIA1_END_ADDR - CIA1_START_ADDR); switch(addr) { case CIA_DATA_PORT_A: pollJoystick(joy[1], 2); // We change only those bits that are configured as outputs, all input bits are 1 result = PA; // iomem[addr] | ~iomem[CIA_DATA_DIRECTION_A]; // The external port lines can pull down any bit, even if it configured as output // result &= portLinesA; // Check joystick movement result &= joystick[1]; break; case CIA_DATA_PORT_B: { uint8_t bitmask = CIA1::peek(CIA_DATA_PORT_A); uint8_t keyboardBits = keyboard->getRowValues(bitmask); pollJoystick(joy[0], 1); result = PB; // Check joystick movement result &= joystick[0]; // Check for pressed keys result &= keyboardBits; break; } default: result = CIA::peek(addr); break; } // log("PEEKING %04X: %02X\n", 0xDC00 + addr, result); return result; } void CIA1::poke(uint16_t addr, uint8_t value) { uint8_t PBold; assert(addr <= CIA1_END_ADDR - CIA1_START_ADDR); // log("Poking %02X to %04X\n", value, 0xDC00 + addr); // The following registers need special handling switch(addr) { case CIA_DATA_PORT_A: PALatch = value; PA = PALatch | ~DDRA; return; case CIA_DATA_DIRECTION_A: DDRA = value; PA = PALatch | ~DDRA; return; case CIA_DATA_PORT_B: PBold = PB; PBLatch = value; PB = ((PBLatch | ~DDRB) & ~PB67TimerMode) | (PB67TimerOut & PB67TimerMode); if ((PBold & 0x10) != (PB & 0x10)) { // edge on lightpen bit? vic->triggerLightPenInterrupt(); } return; case CIA_DATA_DIRECTION_B: PBold = PB; DDRB = value; PB = ((PBLatch | ~DDRB) & ~PB67TimerMode) | (PB67TimerOut & PB67TimerMode); if ((PBold & 0x10) != (PB & 0x10)) { // edge on lightpen bit? vic->triggerLightPenInterrupt(); } return; default: CIA::poke(addr, value); } } void CIA1::setJoystickBits(int nr, uint8_t mask) { assert(nr == 1 || nr == 2); if (nr == 1) joystick[0] |= mask; else if (nr == 2) joystick[1] |= mask; } void CIA1::clearJoystickBits(int nr, uint8_t mask) { assert(nr == 1 || nr == 2); if (nr == 1) joystick[0] &= (0xff-mask); else if (nr == 2) joystick[1] &= (0xff-mask); } // ----------------------------------------------------------------------------------------- // Complex Interface Adapter 2 // ----------------------------------------------------------------------------------------- CIA2::CIA2() { name = "CIA2"; debug(2, " Creating CIA2 at address %p...\n", this); } CIA2::~CIA2() { debug(2, " Releasing CIA2...\n"); } void CIA2::reset() { this->c64 = c64; iec = c64->iec; CIA::reset(); } void CIA2::dumpState() { msg("CIA 2:\n"); msg("------\n\n"); CIA::dumpState(); } void CIA2::raiseInterruptLine() { cpu->setNMILineCIA(); } void CIA2::clearInterruptLine() { cpu->clearNMILineCIA(); } uint8_t CIA2::getInterruptLine() { return cpu->getNMILineCIA(); } uint8_t CIA2::peek(uint16_t addr) { uint8_t result; assert(addr <= CIA_END_ADDR - CIA_START_ADDR); switch(addr) { case CIA_DATA_PORT_A: result = PA; // The two upper bits are connected to the clock line and the data line result &= 0x3F; result |= (iec->getClockLine() ? 0x40 : 0x00); result |= (iec->getDataLine() ? 0x80 : 0x00); // The external port lines can pull down any bit, even if it configured as output. // Note that bits 0 and 1 are not connected to the bus and determine the memory bank seen by the VIC chip // result &= (portLinesB | 0x03); return result; case CIA_DATA_PORT_B: result = PB; return result; default: return CIA::peek(addr); } } void CIA2::poke(uint16_t addr, uint8_t value) { assert(addr <= CIA2_END_ADDR - CIA2_START_ADDR); switch(addr) { case CIA_DATA_PORT_A: PALatch = value; PA = PALatch | ~DDRA; // Bits 0 and 1 determine the memory bank seen the VIC vic->setMemoryBankAddr((~PA & 0x03) << 14); // Bits 3 to 5 of PA are connected to the IEC bus iec->updateCiaPins(PALatch, DDRA); return; case CIA_DATA_DIRECTION_A: DDRA = value; PA = PALatch | ~DDRA; // Bits 0 and 1 determine the memory bank seen the VIC vic->setMemoryBankAddr((~PA & 0x03) << 14); // Bits 3 to 5 of PA are connected to the IEC bus iec->updateCiaPins(PALatch, DDRA); return; case CIA_DATA_PORT_B: PBLatch = value; PB = ((PBLatch | ~DDRB) & ~PB67TimerMode) | (PB67TimerOut & PB67TimerMode); // oldPB = PB; return; case CIA_DATA_DIRECTION_B: DDRB = value; PB = ((PBLatch | ~DDRB) & ~PB67TimerMode) | (PB67TimerOut & PB67TimerMode); // oldPB = PB; return; default: CIA::poke(addr, value); } }