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 
29 #include <stdint.h>
30 
31 #include "sidcxx11.h"
32 
33 namespace libsidplayfp
34 {
35 
36 class MOS6526;
37 
38 /**
39  * This is the base class for the MOS6526 interrupt sources.
40  */
41 class InterruptSource : protected Event
42 {
43 public:
44     enum
45     {
46         INTERRUPT_NONE         = 0,      ///< no interrupt
47         INTERRUPT_UNDERFLOW_A  = 1 << 0, ///< underflow Timer A
48         INTERRUPT_UNDERFLOW_B  = 1 << 1, ///< underflow Timer B
49         INTERRUPT_ALARM        = 1 << 2, ///< alarm clock
50         INTERRUPT_SP           = 1 << 3, ///< serial port
51         INTERRUPT_FLAG         = 1 << 4, ///< external flag
52         INTERRUPT_REQUEST      = 1 << 7  ///< control bit
53     };
54 
55 private:
56     /// Pointer to the MOS6526 which this Interrupt belongs to.
57     MOS6526 &parent;
58 
59 protected:
60     /// Event scheduler.
61     EventScheduler &eventScheduler;
62 
63     /// Clock when clear was called last
64     event_clock_t last_clear;
65 
66     /// Interrupt control register
67     uint8_t icr;
68 
69     /// Interrupt data register
70     uint8_t idr;
71 
72 private:
73     /// Have we already scheduled CIA->CPU interrupt transition?
74     bool scheduled;
75 
76 protected:
interruptMasked()77     bool interruptMasked() const { return icr & idr; }
78 
interruptTriggered()79     bool interruptTriggered() const { return idr & INTERRUPT_REQUEST; }
80 
triggerInterrupt()81     void triggerInterrupt() { idr |= INTERRUPT_REQUEST; }
82 
83 protected:
84     /**
85      * Create a new InterruptSource.
86      *
87      * @param scheduler event scheduler
88      * @param parent the MOS6526 which this Interrupt belongs to
89      */
InterruptSource(EventScheduler & scheduler,MOS6526 & parent)90     InterruptSource(EventScheduler &scheduler, MOS6526 &parent) :
91         Event("CIA Interrupt"),
92         parent(parent),
93         eventScheduler(scheduler),
94         last_clear(0),
95         icr(0),
96         idr(0),
97         scheduled(false)
98     {}
99 
100     /**
101      * Schedules an IRQ asserting state transition for next cycle.
102      */
schedule()103     void schedule()
104     {
105         if (!scheduled)
106         {
107             eventScheduler.schedule(*this, 1, EVENT_CLOCK_PHI1);
108             scheduled = true;
109         }
110     }
111 
112     void interrupt(bool state);
113 
114 public:
~InterruptSource()115     virtual ~InterruptSource() {}
116 
117     /**
118      * Trigger an interrupt.
119      *
120      * @param interruptMask Interrupt flag number
121      */
trigger(uint8_t interruptMask)122     virtual void trigger(uint8_t interruptMask) { idr |= interruptMask; }
123 
124     /**
125      * Clear interrupt state.
126      *
127      * @return old interrupt state
128      */
129     virtual uint8_t clear();
130 
131     /**
132      * Clear pending interrupts, but do not signal to CPU we lost them.
133      * It is assumed that all components get reset() calls in synchronous manner.
134      */
reset()135     virtual void reset()
136     {
137         icr = 0;
138         idr = 0;
139         eventScheduler.cancel(*this);
140         scheduled = false;
141     }
142 
143     /**
144      * Set interrupt control mask bits.
145      *
146      * @param interruptMask control mask bits
147      */
set(uint8_t interruptMask)148     void set(uint8_t interruptMask)
149     {
150         if (interruptMask & 0x80)
151         {
152             icr |= interruptMask & ~INTERRUPT_REQUEST;
153             trigger(INTERRUPT_NONE);
154         }
155         else
156         {
157             icr &= ~interruptMask;
158         }
159     }
160 
161     /**
162      * Signal interrupt to CPU.
163      */
164     void event() override;
165 };
166 
167 }
168 
169 #endif // INTERRUPT_H
170