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