/* * This file is part of libsidplayfp, a SID player engine. * * Copyright 2011-2014 Leandro Nini * Copyright 2009-2014 VICE Project * Copyright 2007-2010 Antti Lankila * Copyright 2000 Simon White * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MOS6526_H #define MOS6526_H #include #include "timer.h" #include "tod.h" #include "EventScheduler.h" #include "c64/component.h" #include "sidcxx11.h" class EventContext; namespace libsidplayfp { class MOS6526; /** * This is the timer A of this CIA. * * @author Ken Händel * */ class TimerA : public Timer { private: /** * Signal underflows of Timer A to Timer B. */ void underFlow() override; void serialPort() override; public: /** * Create timer A. */ TimerA(EventContext *context, MOS6526* parent) : Timer("CIA Timer A", context, parent) {} }; /** * This is the timer B of this CIA. * * @author Ken Händel * */ class TimerB : public Timer { private: void underFlow() override; public: /** * Create timer B. */ TimerB(EventContext *context, MOS6526* parent) : Timer("CIA Timer B", context, parent) {} /** * Receive an underflow from Timer A. */ void cascade() { /* we pretend that we are CPU doing a write to ctrl register */ syncWithCpu(); state |= CIAT_STEP; wakeUpAfterSyncWithCpu(); } /** * Check if start flag is set. * * @return true if start flag is set, false otherwise */ bool started() const { return (state & CIAT_CR_START) != 0; } }; /** * This class is heavily based on the ciacore/ciatimer source code from VICE. * The CIA state machine is lifted as-is. Big thanks to VICE project! * * @author alankila */ class MOS6526 : public component { friend class TimerA; friend class TimerB; friend class Tod; private: static const char *credit; protected: /// Event context. EventContext &event_context; /// These are all CIA registers. uint8_t regs[0x10]; /// Ports //@{ uint8_t &pra, &prb, &ddra, &ddrb; //@} /// Timers A and B. //@{ TimerA timerA; TimerB timerB; //@} /// TOD Tod tod; /// Serial Data Registers //@{ uint8_t sdr_out; bool sdr_buffered; int sdr_count; //@} /// Interrupt control register uint8_t icr; /// Interrupt data register uint8_t idr; /// Have we already scheduled CIA->CPU interrupt transition? bool triggerScheduled; /// Events //@{ EventCallback bTickEvent; EventCallback triggerEvent; //@} private: /** * TOD alarm interrupt. */ void todInterrupt(); /** * This event exists solely to break the ambiguity of what scheduling on * top of PHI1 causes, because there is no ordering between events on * same phase. Thus it is scheduled in PHI2 to ensure the b.event() is * run once before the value changes. * * - PHI1 a.event() (which calls underFlow()) * - PHI1 b.event() * - PHI2 bTick.event() * - PHI1 a.event() * - PHI1 b.event() */ void bTick(); /** * Signal interrupt to CPU. */ void trigger(); /** * Timer A underflow. */ void underflowA(); /** Timer B underflow. */ void underflowB(); /** * Trigger an interrupt. * * @param interruptMask Interrupt flag number */ void trigger(uint8_t interruptMask); /** * Clear interrupt state. */ void clear(); /** * Handle the serial port. */ void serialPort(); protected: /** * Create a new CIA. * * @param context the event context */ MOS6526(EventContext *context); ~MOS6526() {} /** * Signal interrupt. * * @param state * interrupt state */ virtual void interrupt(bool state) = 0; virtual void portA() {} virtual void portB() {} /** * Read CIA register. * * @param addr * register address to read (lowest 4 bits) */ uint8_t read(uint_least8_t addr) override; /** * Write CIA register. * * @param addr * register address to write (lowest 4 bits) * @param data * value to write */ void write(uint_least8_t addr, uint8_t data) override; public: /** * Reset CIA. */ virtual void reset() override; /** * Get the credits. * * @return the credits */ static const char *credits() { return credit; } /** * Set day-of-time event occurence of rate. * * @param clock */ void setDayOfTimeRate(unsigned int clock) { tod.setPeriod(clock); } }; } #endif // MOS6526_H