1 /* 2 * This file is part of libsidplayfp, a SID player engine. 3 * 4 * Copyright 2011-2020 Leandro Nini <drfiemost@users.sourceforge.net> 5 * Copyright 2009-2014 VICE Project 6 * Copyright 2007-2010 Antti Lankila 7 * Copyright 2000 Simon White 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 */ 23 24 #ifndef MOS6526_H 25 #define MOS6526_H 26 27 #include <memory> 28 29 #include <stdint.h> 30 31 #include "interrupt.h" 32 #include "timer.h" 33 #include "tod.h" 34 #include "SerialPort.h" 35 #include "EventScheduler.h" 36 37 #include "sidcxx11.h" 38 39 class EventContext; 40 41 namespace libsidplayfp 42 { 43 44 class MOS6526; 45 46 /** 47 * This is the timer A of this CIA. 48 * 49 * @author Ken Händel 50 * 51 */ 52 class TimerA final : public Timer 53 { 54 private: 55 /** 56 * Signal underflows of Timer A to Timer B. 57 */ 58 void underFlow() override; 59 60 void serialPort() override; 61 62 public: 63 /** 64 * Create timer A. 65 */ TimerA(EventScheduler & scheduler,MOS6526 & parent)66 TimerA(EventScheduler &scheduler, MOS6526 &parent) : 67 Timer("CIA Timer A", scheduler, parent) {} 68 }; 69 70 /** 71 * This is the timer B of this CIA. 72 * 73 * @author Ken Händel 74 * 75 */ 76 class TimerB final : public Timer 77 { 78 private: 79 void underFlow() override; 80 81 public: 82 /** 83 * Create timer B. 84 */ TimerB(EventScheduler & scheduler,MOS6526 & parent)85 TimerB(EventScheduler &scheduler, MOS6526 &parent) : 86 Timer("CIA Timer B", scheduler, parent) {} 87 88 /** 89 * Receive an underflow from Timer A. 90 */ cascade()91 void cascade() 92 { 93 // we pretend that we are CPU doing a write to ctrl register 94 syncWithCpu(); 95 state |= CIAT_STEP; 96 wakeUpAfterSyncWithCpu(); 97 } 98 99 /** 100 * Check if start flag is set. 101 * 102 * @return true if start flag is set, false otherwise 103 */ started()104 bool started() const { return (state & CIAT_CR_START) != 0; } 105 }; 106 107 /** 108 * InterruptSource that acts like new CIA 109 */ 110 class InterruptSource8521 final : public InterruptSource 111 { 112 public: InterruptSource8521(EventScheduler & scheduler,MOS6526 & parent)113 InterruptSource8521(EventScheduler &scheduler, MOS6526 &parent) : 114 InterruptSource(scheduler, parent) 115 {} 116 117 void trigger(uint8_t interruptMask) override; 118 119 uint8_t clear() override; 120 }; 121 122 /** 123 * InterruptSource that acts like old CIA 124 */ 125 class InterruptSource6526 final : public InterruptSource 126 { 127 private: 128 /// Timer B bug 129 bool tbBug; 130 131 private: triggerBug()132 void triggerBug() { idr &= ~INTERRUPT_UNDERFLOW_B; } 133 134 public: InterruptSource6526(EventScheduler & scheduler,MOS6526 & parent)135 InterruptSource6526(EventScheduler &scheduler, MOS6526 &parent) : 136 InterruptSource(scheduler, parent), 137 tbBug(false) 138 {} 139 140 void trigger(uint8_t interruptMask) override; 141 142 uint8_t clear() override; 143 144 void reset() override; 145 }; 146 147 /** 148 * This class is heavily based on the ciacore/ciatimer source code from VICE. 149 * The CIA state machine is lifted as-is. Big thanks to VICE project! 150 * The Serial Port emulation is based on Denise emu code. 151 */ 152 class MOS6526 153 { 154 friend class InterruptSource; 155 friend class SerialPort; 156 friend class TimerA; 157 friend class TimerB; 158 friend class Tod; 159 160 private: 161 static const char *credit; 162 163 protected: 164 /// Event context. 165 EventScheduler &eventScheduler; 166 167 /// Ports 168 //@{ 169 uint8_t &pra, &prb, &ddra, &ddrb; 170 //@} 171 172 /// These are all CIA registers. 173 uint8_t regs[0x10]; 174 175 /// Timers A and B. 176 //@{ 177 TimerA timerA; 178 TimerB timerB; 179 //@} 180 181 /// Interrupt Source 182 std::unique_ptr<InterruptSource> interruptSource; 183 184 /// TOD 185 Tod tod; 186 187 /// Serial Data Registers 188 SerialPort serialPort; 189 190 /// Events 191 //@{ 192 EventCallback<MOS6526> bTickEvent; 193 //@} 194 195 private: 196 /** 197 * Trigger an interrupt from TOD. 198 */ 199 void todInterrupt(); 200 201 /** 202 * Trigger an interrupt from Serial Port. 203 */ 204 void spInterrupt(); 205 206 /** 207 * This event exists solely to break the ambiguity of what scheduling on 208 * top of PHI1 causes, because there is no ordering between events on 209 * same phase. Thus it is scheduled in PHI2 to ensure the b.event() is 210 * run once before the value changes. 211 * 212 * - PHI1 a.event() (which calls underFlow()) 213 * - PHI1 b.event() 214 * - PHI2 bTick.event() 215 * - PHI1 a.event() 216 * - PHI1 b.event() 217 */ 218 void bTick(); 219 220 /** 221 * Timer A underflow. 222 */ 223 void underflowA(); 224 225 /** Timer B underflow. */ 226 void underflowB(); 227 228 /** 229 * Handle the serial port. 230 */ 231 void handleSerialPort(); 232 233 protected: 234 /** 235 * Create a new CIA. 236 * 237 * @param context the event context 238 */ 239 MOS6526(EventScheduler &scheduler); 240 241 /** 242 * Signal interrupt. 243 * 244 * @param state 245 * interrupt state 246 */ 247 virtual void interrupt(bool state) = 0; 248 portA()249 virtual void portA() {} portB()250 virtual void portB() {} 251 252 /** 253 * Timers can appear on the port. 254 */ 255 uint8_t adjustDataPort(uint8_t data); 256 257 /** 258 * Read CIA register. 259 * 260 * @param addr 261 * register address to read (lowest 4 bits) 262 */ 263 uint8_t read(uint_least8_t addr); 264 265 /** 266 * Write CIA register. 267 * 268 * @param addr 269 * register address to write (lowest 4 bits) 270 * @param data 271 * value to write 272 */ 273 void write(uint_least8_t addr, uint8_t data); 274 275 public: 276 /** 277 * Select chip model. 278 * 279 * @param newModel true for new model 8521, false for old 6526 280 */ 281 void setModel(bool newModel); 282 283 /** 284 * Reset CIA. 285 */ 286 virtual void reset(); 287 288 /** 289 * Get the credits. 290 * 291 * @return the credits 292 */ 293 static const char *credits(); 294 295 /** 296 * Set day-of-time event occurence of rate. 297 * 298 * @param clock 299 */ setDayOfTimeRate(unsigned int clock)300 void setDayOfTimeRate(unsigned int clock) { tod.setPeriod(clock); } 301 }; 302 303 } 304 305 #endif // MOS6526_H 306