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