VirtualC64 v5.0 beta
Commodore 64 Emulator
Loading...
Searching...
No Matches
CIA.h
1// -----------------------------------------------------------------------------
2// This file is part of VirtualC64
3//
4// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de
5// This FILE is dual-licensed. You are free to choose between:
6//
7// - The GNU General Public License v3 (or any later version)
8// - The Mozilla Public License v2
9//
10// SPDX-License-Identifier: GPL-3.0-or-later OR MPL-2.0
11// -----------------------------------------------------------------------------
12
13#pragma once
14
15#include "CIATypes.h"
16#include "C64Types.h"
17#include "TOD.h"
18
19namespace vc64 {
20
21class CIA : public SubComponent, public Inspectable<CIAInfo, CIAStats> {
22
23 friend class TOD;
24 friend class ParCable;
25
26 Descriptions descriptions = {
27 {
28 .name = "CIA1",
29 .description = "Complex Interface Adapter 1"
30 },
31 {
32 .name = "CIA2",
33 .description = "Complex Interface Adapter 2"
34 }
35 };
36
37 ConfigOptions options = {
38
41 };
42
43
44 //
45 // Action flags
46 //
47
48 // Decrements timer A
49 static constexpr u64 CIACountA0 = (1ULL << 0);
50 static constexpr u64 CIACountA1 = (1ULL << 1);
51 static constexpr u64 CIACountA2 = (1ULL << 2);
52 static constexpr u64 CIACountA3 = (1ULL << 3);
53
54 // Decrements timer B
55 static constexpr u64 CIACountB0 = (1ULL << 4);
56 static constexpr u64 CIACountB1 = (1ULL << 5);
57 static constexpr u64 CIACountB2 = (1ULL << 6);
58 static constexpr u64 CIACountB3 = (1ULL << 7);
59
60 // Loads timer A
61 static constexpr u64 CIALoadA0 = (1ULL << 8);
62 static constexpr u64 CIALoadA1 = (1ULL << 9);
63 static constexpr u64 CIALoadA2 = (1ULL << 10);
64
65 // Loads timer B
66 static constexpr u64 CIALoadB0 = (1ULL << 11);
67 static constexpr u64 CIALoadB1 = (1ULL << 12);
68 static constexpr u64 CIALoadB2 = (1ULL << 13);
69
70 // Sets pin PB6 low
71 static constexpr u64 CIAPB6Low0 = (1ULL << 14);
72 static constexpr u64 CIAPB6Low1 = (1ULL << 15);
73
74 // Sets pin PB7 low
75 static constexpr u64 CIAPB7Low0 = (1ULL << 16);
76 static constexpr u64 CIAPB7Low1 = (1ULL << 17);
77
78 // Triggers an interrupt
79 static constexpr u64 CIASetInt0 = (1ULL << 18);
80 static constexpr u64 CIASetInt1 = (1ULL << 19);
81
82 // Releases the interrupt line
83 static constexpr u64 CIAClearInt0 = (1ULL << 20);
84 static constexpr u64 CIAOneShotA0 = (1ULL << 21);
85 static constexpr u64 CIAOneShotB0 = (1ULL << 22);
86
87 // Indicates that ICR was read recently
88 static constexpr u64 CIAReadIcr0 = (1ULL << 23);
89 static constexpr u64 CIAReadIcr1 = (1ULL << 24);
90
91 // Clears bit 8 in ICR register
92 static constexpr u64 CIAClearIcr0 = (1ULL << 25);
93 static constexpr u64 CIAClearIcr1 = (1ULL << 26);
94 static constexpr u64 CIAClearIcr2 = (1ULL << 27);
95
96 // Clears bit 0 - 7 in ICR register
97 static constexpr u64 CIAAckIcr0 = (1ULL << 28);
98 static constexpr u64 CIAAckIcr1 = (1ULL << 29);
99
100 // Sets bit 8 in ICR register
101 static constexpr u64 CIASetIcr0 = (1ULL << 30);
102 static constexpr u64 CIASetIcr1 = (1ULL << 31);
103
104 // Triggers an IRQ with TOD as source
105 static constexpr u64 CIATODInt0 = (1ULL << 32);
106
107 // Triggers an IRQ with serial reg as source
108 static constexpr u64 CIASerInt0 = (1ULL << 33);
109 static constexpr u64 CIASerInt1 = (1ULL << 34);
110 static constexpr u64 CIASerInt2 = (1ULL << 35);
111
112 // Loads the serial shift register
113 static constexpr u64 CIASerLoad0 = (1ULL << 36);
114 static constexpr u64 CIASerLoad1 = (1ULL << 37);
115
116 // Clock signal driving the serial register
117 static constexpr u64 CIASerClk0 = (1ULL << 38);
118 static constexpr u64 CIASerClk1 = (1ULL << 39);
119 static constexpr u64 CIASerClk2 = (1ULL << 40);
120 static constexpr u64 CIASerClk3 = (1ULL << 41);
121
122 static constexpr u64 CIALast = (1ULL << 42);
123
124 static constexpr u64 CIADelayMask = ~CIALast
125 & ~CIACountA0 & ~CIACountB0 & ~CIALoadA0 & ~CIALoadB0 & ~CIAPB6Low0
126 & ~CIAPB7Low0 & ~CIASetInt0 & ~CIAClearInt0 & ~CIAOneShotA0 & ~CIAOneShotB0
127 & ~CIAReadIcr0 & ~CIAClearIcr0 & ~CIAAckIcr0 & ~CIASetIcr0 & ~CIATODInt0
128 & ~CIASerInt0 & ~CIASerLoad0 & ~CIASerClk0;
129
130 // Current configuration
131 CIAConfig config = { };
132
133
134 //
135 // Sub components
136 //
137
138public:
139
140 TOD tod = TOD(c64, *this);
141
142
143 //
144 // Internals
145 //
146
147protected:
148
149 // Timer A counter
150 u16 counterA;
151
152 // Timer B counter
153 u16 counterB;
154
155 // Timer A latch
156 u16 latchA;
157
158 // Timer B latch
159 u16 latchB;
160
161
162 //
163 // Control
164 //
165
166 // Action flags
167 u64 delay;
168 u64 feed;
169
170 // Control registers
171 u8 CRA;
172 u8 CRB;
173
174 // Interrupt control register
175 u8 icr;
176
177 // ICR bits to be deleted when CIAAckIcr1 hits
178 u8 icrAck;
179
180 // Interrupt mask register
181 u8 imr;
182
183protected:
184
185 // Bit mask for PB outputs (0 = port register, 1 = timer)
186 u8 PB67TimerMode;
187
188 // PB outputs bits 6 and 7 in timer mode
189 u8 PB67TimerOut;
190
191 // PB outputs bits 6 and 7 in toggle mode
192 u8 PB67Toggle;
193
194
195 //
196 // Port registers
197 //
198
199protected:
200
201 // Peripheral data registers
202 u8 PRA;
203 u8 PRB;
204
205 // Data directon registers
206 u8 DDRA;
207 u8 DDRB;
208
209 // Peripheral ports
210 u8 PA;
211 u8 PB;
212
213
214 //
215 // Shift register logic
216 //
217
218private:
219
220 /* Serial data register
221 * http://unusedino.de/ec64/technical/misc/cia6526/serial.html
222 * "The serial port is a buffered, 8-bit synchronous shift register system.
223 * A control bit selects input or output mode. In input mode, data on the
224 * SP pin is shifted into the shift register on the rising edge of the
225 * signal applied to the CNT pin. After 8 CNT pulses, the data in the shift
226 * register is dumped into the Serial Data Register and an interrupt is
227 * generated. In the output mode, TIMER A is used for the baud rate
228 * generator. Data is shifted out on the SP pin at 1/2 the underflow rate
229 * of TIMER A. [...] Transmission will start following a write to the
230 * Serial Data Register (provided TIMER A is running and in continuous
231 * mode). The clock signal derived from TIMER A appears as an output on the
232 * CNT pin. The data in the Serial Data Register will be loaded into the
233 * shift register then shift out to the SP pin when a CNT pulse occurs.
234 * Data shifted out becomes valid on the falling edge of CNT and remains
235 * valid until the next falling edge. After 8 CNT pulses, an interrupt is
236 * generated to indicate more data can be sent. If the Serial Data Register
237 * was loaded with new information prior to this interrupt, the new data
238 * will automatically be loaded into the shift register and transmission
239 * will continue. If the microprocessor stays one byte ahead of the shift
240 * register, transmission will be continuous. If no further data is to be
241 * transmitted, after the 8th CNT pulse, CNT will return high and SP will
242 * remain at the level of the last data bit transmitted. SDR data is
243 * shifted out MSB first and serial input data should also appear in this
244 * format.
245 */
246 u8 sdr;
247
248 // Clock signal for driving the serial register
249 bool serClk;
250
251 /* Shift register counter
252 * The counter is set to 8 when the shift register is loaded and decremented
253 * when a bit is shifted out.
254 */
255 u8 serCounter;
256
257 //
258 // Port pins
259 //
260
261 bool CNT;
262 bool INT;
263
264
265 //
266 // Sleep logic
267 //
268
269 /* Idle counter. When the CIA's state does not change during execution,
270 * this variable is increased by one. If it exceeds a certain threshhold,
271 * the chip is put into idle state via sleep().
272 */
273 u8 tiredness;
274
275 // Total number of skipped cycles (used by the debugger, only)
276 Cycle idleCycles;
277
278public:
279
280 // Indicates if the CIA is currently idle
281 bool sleeping;
282
283 /* The last executed cycle before the chip went idle
284 * The variable is set in sleep()
285 */
286 Cycle sleepCycle;
287
288 /* The first cycle to be executed after the chip went idle
289 * The variable is set in sleep()
290 */
291 Cycle wakeUpCycle;
292
293
294 //
295 // Initializing
296 //
297
298public:
299
300 CIA(C64 &ref, isize id);
301
302 bool isCIA1() const { return objid == 0; }
303 bool isCIA2() const { return objid == 1; }
304
305 CIA& operator= (const CIA& other) {
306
307 CLONE(tod)
308
309 CLONE(counterA)
310 CLONE(counterB)
311 CLONE(latchA)
312 CLONE(latchB)
313 CLONE(delay)
314 CLONE(feed)
315 CLONE(CRA)
316 CLONE(CRB)
317 CLONE(icr)
318 CLONE(icrAck)
319 CLONE(imr)
320 CLONE(PB67TimerMode)
321 CLONE(PB67TimerOut)
322 CLONE(PB67Toggle)
323 CLONE(PRA)
324 CLONE(PRB)
325 CLONE(DDRA)
326 CLONE(DDRB)
327 CLONE(PA)
328 CLONE(PB)
329 CLONE(sdr)
330 CLONE(serClk)
331 CLONE(serCounter)
332 CLONE(CNT)
333 CLONE(INT)
334 CLONE(tiredness)
335 CLONE(idleCycles)
336 CLONE(sleeping)
337 CLONE(sleepCycle)
338 CLONE(wakeUpCycle)
339
340 CLONE(config)
341
342 return *this;
343 }
344
345
346 //
347 // Methods from Serializable
348 //
349
350public:
351
352 template <class T>
353 void serialize(T& worker)
354 {
355 worker
356
357 << tod
358
359 << counterA
360 << counterB
361 << latchA
362 << latchB
363 << delay
364 << feed
365 << CRA
366 << CRB
367 << icr
368 << icrAck
369 << imr
370 << PB67TimerMode
371 << PB67TimerOut
372 << PB67Toggle
373 << PRA
374 << PRB
375 << DDRA
376 << DDRB
377 << PA
378 << PB
379 << sdr
380 << serClk
381 << serCounter
382 << CNT
383 << INT
384 << tiredness
385 << idleCycles
386 << sleeping
387 << sleepCycle
388 << wakeUpCycle;
389
390 if (isResetter(worker)) return;
391
392 worker
393
394 << config.revision
395 << config.timerBBug;
396
397 } SERIALIZERS(serialize);
398
399
400 //
401 // Methods from CoreComponent
402 //
403
404public:
405
406 const Descriptions &getDescriptions() const override { return descriptions; }
407
408private:
409
410 void _dump(Category category, std::ostream& os) const override;
411 void _reset(bool hard) override;
412
413
414 //
415 // Methods from Inspectable
416 //
417
418public:
419
420 void cacheInfo(CIAInfo &result) const override;
421 void cacheStats(CIAStats &result) const override;
422
423
424 //
425 // Methods from Configurable
426 //
427
428public:
429
430 const CIAConfig &getConfig() const { return config; }
431 const ConfigOptions &getOptions() const override { return options; }
432 i64 getOption(Option opt) const override;
433 void checkOption(Option opt, i64 value) override;
434 void setOption(Option opt, i64 value) override;
435
436
437 //
438 // Accessing the I/O register space
439 //
440
441public:
442
443 // Reads a value from a CIA register
444 u8 peek(u16 addr);
445
446 // Reads a value from a CIA register without causing side effects
447 u8 spypeek(u16 addr) const;
448
449 // Writes a value into a CIA register
450 void poke(u16 addr, u8 value);
451
452
453 //
454 // Accessing the port registers
455 //
456
457public:
458
459 // Returns the data registers (call updatePA() or updatePB() first)
460 u8 getPA() const { return PA; }
461 u8 getPB() const { return PB; }
462
463private:
464
465 // Returns the data direction register
466 u8 getDDRA() const { return DDRA; }
467 u8 getDDRB() const { return DDRB; }
468
469 // Computes the value we currently see at port A
470 virtual void updatePA() = 0;
471 virtual u8 computePA() const = 0;
472
473 // Returns the value driving port A from inside the chip
474 virtual u8 portAinternal() const = 0;
475
476 // Returns the value driving port A from outside the chip
477 virtual u8 portAexternal() const = 0;
478
479 // Computes the value we currently see at port B
480 virtual void updatePB() = 0;
481 virtual u8 computePB() const = 0;
482
483 // Returns the value driving port B from inside the chip
484 virtual u8 portBinternal() const = 0;
485
486 // Returns the value driving port B from outside the chip
487 virtual u8 portBexternal() const = 0;
488
489protected:
490
491 // Action method for peeking the port registers
492 virtual u8 peekPA() { updatePA(); return PA; }
493 virtual u8 peekPB() { updatePB(); return PB; }
494
495 // Action method for poking the port registers
496 virtual void pokePRA(u8 value) { PRA = value; updatePA(); }
497 virtual void pokePRB(u8 value) { PRB = value; updatePB(); }
498
499 // Action method for poking the port direction registers
500 virtual void pokeDDRA(u8 value) { DDRA = value; updatePA(); }
501 virtual void pokeDDRB(u8 value) { DDRB = value; updatePB(); }
502
503
504 //
505 // Accessing the port pins
506 //
507
508public:
509
510 // Simulates an edge on the flag pin
511 void triggerRisingEdgeOnFlagPin();
512 void triggerFallingEdgeOnFlagPin();
513
514 // Emulates a pulse on the PC pin
515 virtual void pulsePC() { };
516
517
518 //
519 // Handling interrupts
520 //
521
522private:
523
524 // Requests the CPU to interrupt
525 virtual void pullDownInterruptLine() = 0;
526
527 // Removes the interrupt requests
528 virtual void releaseInterruptLine() = 0;
529
530 // Loads a latched value into timer
531 void reloadTimerA(u64 *delay);
532 void reloadTimerB(u64 *delay);
533
534 // Triggers an interrupt (invoked inside execute())
535 void triggerTimerIrq(u64 *delay);
536 void triggerTodIrq(u64 *delay);
537 void triggerSerialIrq(u64 *delay);
538
539public:
540
541 // Handles an interrupt request from TOD
542 void todInterrupt();
543
544
545 //
546 // Handling events
547 //
548
549public:
550
551 // Services an event in the CIA slot
552 void serviceEvent(EventID id);
553
554 // Schedules the next execution event
555 void scheduleNextExecution();
556
557 // Schedules the next wakeup event
558 void scheduleWakeUp();
559
560
561 //
562 // Executing
563 //
564
565public:
566
567 // Executes the CIA for one cycle
568 void executeOneCycle();
569
570
571 //
572 // Speeding up (sleep logic)
573 //
574
575private:
576
577 // Puts the CIA into idle state
578 void sleep();
579
580public:
581
582 // Emulates all previously skipped cycles
583 void wakeUp();
584
585 // Returns true if the CIA is in idle state
586 bool isSleeping() const { return sleeping; }
587
588 // Returns true if the CIA is awake
589 bool isAwake() const { return !sleeping; }
590
591 // The CIA is idle since this number of cycles
592 Cycle idleSince() const;
593
594 // Total number of cycles the CIA was idle
595 Cycle idleTotal() const { return idleCycles; }
596};
597
598
599//
600// CIA1
601//
602
603class CIA1 final : public CIA {
604
605public:
606
607 CIA1(C64 &ref) : CIA(ref, 0) { };
608
609private:
610
611 void pullDownInterruptLine() override;
612 void releaseInterruptLine() override;
613
614 u8 portAinternal() const override;
615 u8 portAexternal() const override;
616 void updatePA() override;
617 u8 computePA() const override;
618
619 u8 portBinternal() const override;
620 u8 portBexternal() const override;
621 void updatePB() override;
622 u8 computePB() const override;
623};
624
625
626//
627// CIA2
628//
629
630class CIA2 final : public CIA {
631
632 friend class ParCable;
633
634public:
635
636 CIA2(C64 &ref) : CIA(ref, 1) { };
637
638private:
639
640 void pullDownInterruptLine() override;
641 void releaseInterruptLine() override;
642
643 u8 portAinternal() const override;
644 u8 portAexternal() const override;
645
646public:
647
648 void updatePA() override;
649 u8 computePA() const override;
650
651private:
652
653 u8 portBinternal() const override;
654 u8 portBexternal() const override;
655 void updatePB() override;
656 u8 computePB() const override;
657 void pokePRA(u8 value) override;
658 void pokePRB(u8 value) override;
659 void pokeDDRA(u8 value) override;
660 void pulsePC() override;
661};
662
663}
VirtualC64 project namespace.
Definition CmdQueue.cpp:16
@ OPT_CIA_TIMER_B_BUG
Emulate timer B bug.
Definition OptionTypes.h:105
@ OPT_CIA_REVISION
Chip revision.
Definition OptionTypes.h:104