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 2007-2010 Antti Lankila 6 * Copyright 2000 Simon White 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 */ 22 23 #ifndef INTERRUPT_H 24 #define INTERRUPT_H 25 26 #include "Event.h" 27 #include "EventScheduler.h" 28 #include "EventCallback.h" 29 30 #include <stdint.h> 31 32 #include "sidcxx11.h" 33 34 namespace libsidplayfp 35 { 36 37 class MOS652X; 38 39 /** 40 * This is the base class for the MOS6526 interrupt sources. 41 * Based on Denise emu code. 42 */ 43 class InterruptSource 44 { 45 public: 46 enum 47 { 48 INTERRUPT_NONE = 0, ///< no interrupt 49 INTERRUPT_UNDERFLOW_A = 1 << 0, ///< underflow Timer A 50 INTERRUPT_UNDERFLOW_B = 1 << 1, ///< underflow Timer B 51 INTERRUPT_ALARM = 1 << 2, ///< alarm clock 52 INTERRUPT_SP = 1 << 3, ///< serial port 53 INTERRUPT_FLAG = 1 << 4, ///< external flag 54 INTERRUPT_REQUEST = 1 << 7 ///< control bit 55 }; 56 57 private: 58 /// Pointer to the MOS6526 which this Interrupt belongs to. 59 MOS652X &parent; 60 61 protected: 62 /// Event scheduler. 63 EventScheduler &eventScheduler; 64 65 /// Clock when clear was called last 66 event_clock_t last_clear; 67 event_clock_t last_set; 68 69 /// Interrupt control register 70 uint8_t icr; 71 72 /// Interrupt data register 73 uint8_t idr; 74 75 uint8_t idrTemp; 76 77 /// Have we already scheduled CIA->CPU interrupt transition? 78 bool scheduled; 79 80 /// is the irq pin asserted? 81 bool asserted; 82 83 private: 84 EventCallback<InterruptSource> interruptEvent; 85 86 EventCallback<InterruptSource> updateIdrEvent; 87 88 EventCallback<InterruptSource> setIrqEvent; 89 90 EventCallback<InterruptSource> clearIrqEvent; 91 92 protected: interruptTriggered()93 inline bool interruptTriggered() const { return idr & INTERRUPT_REQUEST; } 94 interruptMasked(uint8_t interruptMask)95 inline bool interruptMasked(uint8_t interruptMask) const 96 { 97 return ((interruptMask != INTERRUPT_NONE) ? interruptMask : idr) & icr; 98 } 99 100 virtual void triggerInterrupt() =0; 101 102 /** 103 * Check if interrupts were ackowledged during previous cycle. 104 */ ack0()105 inline bool ack0() const { return eventScheduler.getTime(EVENT_CLOCK_PHI2) == (last_clear+1); } write0()106 inline bool write0() const { return eventScheduler.getTime(EVENT_CLOCK_PHI2) == (last_set+1); } 107 108 /** 109 * Signal interrupt to CPU. 110 */ 111 void interrupt(); 112 113 void updateIdr(); 114 115 void setIrq(); 116 117 void clearIrq(); 118 119 protected: 120 /** 121 * Create a new InterruptSource. 122 * 123 * @param scheduler event scheduler 124 * @param parent the MOS6526 which this Interrupt belongs to 125 */ InterruptSource(EventScheduler & scheduler,MOS652X & parent)126 InterruptSource(EventScheduler &scheduler, MOS652X &parent) : 127 parent(parent), 128 eventScheduler(scheduler), 129 last_clear(0), 130 last_set(0), 131 icr(0), 132 idr(0), 133 scheduled(false), 134 asserted(false), 135 interruptEvent("CIA Interrupt", *this, &InterruptSource::interrupt), 136 updateIdrEvent("CIA update ICR", *this, &InterruptSource::updateIdr), 137 setIrqEvent("CIA set IRQ", *this, &InterruptSource::setIrq), 138 clearIrqEvent("CIA clear IRQ", *this, &InterruptSource::clearIrq) 139 {} 140 141 /** 142 * Schedules an IRQ asserting state transition for next cycle. 143 */ schedule(int delay)144 void schedule(int delay) 145 { 146 if (!scheduled) 147 { 148 eventScheduler.schedule(interruptEvent, delay, EVENT_CLOCK_PHI1); 149 scheduled = true; 150 } 151 } 152 scheduleIrq()153 void scheduleIrq() 154 { 155 eventScheduler.schedule(setIrqEvent, 1, EVENT_CLOCK_PHI1); 156 } 157 158 bool isTriggered(uint8_t interruptMask); 159 160 public: ~InterruptSource()161 virtual ~InterruptSource() {} 162 163 /** 164 * Trigger an interrupt. 165 * 166 * @param interruptMask Interrupt flag number 167 */ 168 virtual void trigger(uint8_t interruptMask) =0; 169 170 /** 171 * Clear interrupt state. 172 * 173 * @return old interrupt state 174 */ 175 virtual uint8_t clear(); 176 177 /** 178 * Clear pending interrupts, but do not signal to CPU we lost them. 179 * It is assumed that all components get reset() calls in synchronous manner. 180 */ reset()181 virtual void reset() 182 { 183 last_clear = 0; 184 last_set = 0; 185 186 icr = 0; 187 idr = 0; 188 189 eventScheduler.cancel(updateIdrEvent); 190 eventScheduler.cancel(setIrqEvent); 191 eventScheduler.cancel(clearIrqEvent); 192 eventScheduler.cancel(interruptEvent); 193 scheduled = false; 194 195 asserted = false; 196 } 197 198 /** 199 * Set interrupt control mask bits. 200 * 201 * @param interruptMask control mask bits 202 */ 203 void set(uint8_t interruptMask); 204 }; 205 206 } 207 208 #endif // INTERRUPT_H 209