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 #include "mos6526.h"
25 
26 #include <cstring>
27 
28 #include "sidendian.h"
29 
30 namespace libsidplayfp
31 {
32 
33 enum
34 {
35     PRA     = 0,
36     PRB     = 1,
37     DDRA    = 2,
38     DDRB    = 3,
39     TAL     = 4,
40     TAH     = 5,
41     TBL     = 6,
42     TBH     = 7,
43     TOD_TEN = 8,
44     TOD_SEC = 9,
45     TOD_MIN = 10,
46     TOD_HR  = 11,
47     SDR     = 12,
48     ICR     = 13,
49     IDR     = 13,
50     CRA     = 14,
51     CRB     = 15
52 };
53 
54 // Timer A
55 
underFlow()56 void TimerA::underFlow()
57 {
58     parent.underflowA();
59 }
60 
serialPort()61 void TimerA::serialPort()
62 {
63     parent.handleSerialPort();
64 }
65 
66 // Timer B
67 
underFlow()68 void TimerB::underFlow()
69 {
70     parent.underflowB();
71 }
72 
73 // Interrupt Source 8521
74 
trigger(uint8_t interruptMask)75 void InterruptSource8521::trigger(uint8_t interruptMask)
76 {
77     InterruptSource::trigger(interruptMask);
78 
79     if (interruptMasked() && !interruptTriggered())
80     {
81         if (eventScheduler.getTime(EVENT_CLOCK_PHI2) == last_clear+1)
82         {
83             // Interrupt delayed by 1/2 cycle if acknowledged on assert
84             schedule();
85         }
86         else
87         {
88             triggerInterrupt();
89             interrupt(true);
90         }
91     }
92 }
93 
clear()94 uint8_t InterruptSource8521::clear()
95 {
96     if (interruptTriggered())
97     {
98         interrupt(false);
99     }
100 
101     return InterruptSource::clear();
102 }
103 
104 // Interrupt Source 6526
105 
trigger(uint8_t interruptMask)106 void InterruptSource6526::trigger(uint8_t interruptMask)
107 {
108     // timer b bug
109     if (interruptMask == InterruptSource::INTERRUPT_UNDERFLOW_B)
110     {
111         tbBug = (eventScheduler.getTime(EVENT_CLOCK_PHI2) == last_clear+1);
112     }
113 
114     InterruptSource::trigger(interruptMask);
115 
116     if (!interruptMasked())
117         return;
118 
119     if (eventScheduler.getTime(EVENT_CLOCK_PHI2) == last_clear)
120     {
121         return;
122     }
123 
124     if (tbBug)
125     {
126         triggerBug();
127         tbBug = false;
128     }
129 
130     if (!interruptTriggered())
131     {
132         // interrupts are delayed by 1 clk on old CIAs
133         schedule();
134     }
135 }
136 
clear()137 uint8_t InterruptSource6526::clear()
138 {
139     if (tbBug)
140     {
141         triggerBug();
142         tbBug = false;
143     }
144 
145     if (interruptTriggered())
146     {
147         interrupt(false);
148     }
149 
150     return InterruptSource::clear();
151 }
152 
reset()153 void InterruptSource6526::reset()
154 {
155     InterruptSource::reset();
156 
157     tbBug = false;
158 }
159 
credits()160 const char *MOS6526::credits()
161 {
162     return
163             "MOS6526/8521 (CIA) Emulation:\n"
164             "\tCopyright (C) 2001-2004 Simon White\n"
165             "\tCopyright (C) 2007-2010 Antti S. Lankila\n"
166             "\tCopyright (C) 2009-2014 VICE Project\n"
167             "\tCopyright (C) 2011-2020 Leandro Nini\n";
168 }
169 
MOS6526(EventScheduler & scheduler)170 MOS6526::MOS6526(EventScheduler &scheduler) :
171     eventScheduler(scheduler),
172     pra(regs[PRA]),
173     prb(regs[PRB]),
174     ddra(regs[DDRA]),
175     ddrb(regs[DDRB]),
176     timerA(scheduler, *this),
177     timerB(scheduler, *this),
178     interruptSource(new InterruptSource6526(scheduler, *this)),
179     tod(scheduler, *this, regs),
180     serialPort(scheduler, *this),
181     bTickEvent("CIA B counts A", *this, &MOS6526::bTick)
182 {
183     reset();
184 }
185 
handleSerialPort()186 void MOS6526::handleSerialPort()
187 {
188     if (regs[CRA] & 0x40)
189     {
190         serialPort.handle();
191     }
192 }
193 
reset()194 void MOS6526::reset()
195 {
196     memset(regs, 0, sizeof(regs));
197 
198     serialPort.reset();
199 
200     // Reset timers
201     timerA.reset();
202     timerB.reset();
203 
204     // Reset interruptSource
205     interruptSource->reset();
206 
207     // Reset tod
208     tod.reset();
209 
210     eventScheduler.cancel(bTickEvent);
211 }
212 
adjustDataPort(uint8_t data)213 uint8_t MOS6526::adjustDataPort(uint8_t data)
214 {
215     if (regs[CRA] & 0x02)
216     {
217         data &= 0xbf;
218         if (timerA.getPb(regs[CRA]))
219             data |= 0x40;
220     }
221     if (regs[CRB] & 0x02)
222     {
223         data &= 0x7f;
224         if (timerB.getPb(regs[CRB]))
225             data |= 0x80;
226     }
227     return data;
228 }
229 
read(uint_least8_t addr)230 uint8_t MOS6526::read(uint_least8_t addr)
231 {
232     addr &= 0x0f;
233 
234     timerA.syncWithCpu();
235     timerA.wakeUpAfterSyncWithCpu();
236     timerB.syncWithCpu();
237     timerB.wakeUpAfterSyncWithCpu();
238 
239     switch (addr)
240     {
241     case PRA: // Simulate a serial port
242         return (regs[PRA] | ~regs[DDRA]);
243     case PRB:
244         return adjustDataPort(regs[PRB] | ~regs[DDRB]);
245     case TAL:
246         return endian_16lo8(timerA.getTimer());
247     case TAH:
248         return endian_16hi8(timerA.getTimer());
249     case TBL:
250         return endian_16lo8(timerB.getTimer());
251     case TBH:
252         return endian_16hi8(timerB.getTimer());
253     case TOD_TEN:
254     case TOD_SEC:
255     case TOD_MIN:
256     case TOD_HR:
257         return tod.read(addr - TOD_TEN);
258     case IDR:
259         return interruptSource->clear();
260     case CRA:
261         return (regs[CRA] & 0xee) | (timerA.getState() & 1);
262     case CRB:
263         return (regs[CRB] & 0xee) | (timerB.getState() & 1);
264     default:
265         return regs[addr];
266     }
267 }
268 
write(uint_least8_t addr,uint8_t data)269 void MOS6526::write(uint_least8_t addr, uint8_t data)
270 {
271     addr &= 0x0f;
272 
273     timerA.syncWithCpu();
274     timerB.syncWithCpu();
275 
276     const uint8_t oldData = regs[addr];
277     regs[addr] = data;
278 
279     switch (addr)
280     {
281     case PRA:
282     case DDRA:
283         portA();
284         break;
285     case PRB:
286     case DDRB:
287         portB();
288         break;
289     case TAL:
290         timerA.latchLo(data);
291         break;
292     case TAH:
293         timerA.latchHi(data);
294         break;
295     case TBL:
296         timerB.latchLo(data);
297         break;
298     case TBH:
299         timerB.latchHi(data);
300         break;
301     case TOD_TEN:
302     case TOD_SEC:
303     case TOD_MIN:
304     case TOD_HR:
305         tod.write(addr - TOD_TEN, data);
306         break;
307     case SDR:
308         serialPort.startSdr();
309         break;
310     case ICR:
311         interruptSource->set(data);
312         break;
313     case CRA:
314         if ((data ^ oldData) & 0x40)
315             serialPort.switchSerialDirection((data & 0x40) == 0);
316         if ((data & 1) && !(oldData & 1))
317         {
318             // Reset the underflow flipflop for the data port
319             timerA.setPbToggle(true);
320         }
321         timerA.setControlRegister(data);
322         break;
323     case CRB:
324         if ((data & 1) && !(oldData & 1))
325         {
326             // Reset the underflow flipflop for the data port
327             timerB.setPbToggle(true);
328         }
329         timerB.setControlRegister(data | (data & 0x40) >> 1);
330         break;
331     }
332 
333     timerA.wakeUpAfterSyncWithCpu();
334     timerB.wakeUpAfterSyncWithCpu();
335 }
336 
bTick()337 void MOS6526::bTick()
338 {
339     timerB.cascade();
340 }
341 
underflowA()342 void MOS6526::underflowA()
343 {
344     interruptSource->trigger(InterruptSource::INTERRUPT_UNDERFLOW_A);
345 
346     if ((regs[CRB] & 0x41) == 0x41)
347     {
348         if (timerB.started())
349         {
350             eventScheduler.schedule(bTickEvent, 0, EVENT_CLOCK_PHI2);
351         }
352     }
353 }
354 
underflowB()355 void MOS6526::underflowB()
356 {
357     interruptSource->trigger(InterruptSource::INTERRUPT_UNDERFLOW_B);
358 }
359 
todInterrupt()360 void MOS6526::todInterrupt()
361 {
362     interruptSource->trigger(InterruptSource::INTERRUPT_ALARM);
363 }
364 
spInterrupt()365 void MOS6526::spInterrupt()
366 {
367     interruptSource->trigger(InterruptSource::INTERRUPT_SP);
368 }
369 
setModel(bool newModel)370 void MOS6526::setModel(bool newModel)
371 {
372     if (newModel)
373         interruptSource.reset(new InterruptSource8521(eventScheduler, *this));
374     else
375         interruptSource.reset(new InterruptSource6526(eventScheduler, *this));
376 
377     //serialPort.setNewModel(newModel);
378 }
379 
380 }
381