VirtualC64 v5.0 beta
Commodore 64 Emulator
Loading...
Searching...
No Matches
VIA.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 "SubComponent.h"
16
17namespace vc64 {
18
19/*
20 * This implementation is mainly based on the document
21 *
22 * "R6522 VERSATILE INTERFACE ADAPTER" by Frank Kontros [F. K.]
23 *
24 * and the Hoxs64 implementation by David Horrocks.
25 */
26
27#define VIACountA0 (1ULL << 0) // Forces timer 1 to decrement every cycle
28#define VIACountA1 (1ULL << 1)
29#define VIACountB0 (1ULL << 2) // Forces timer 2 to decrement every cycle
30#define VIACountB1 (1ULL << 3)
31#define VIAReloadA0 (1ULL << 4) // Forces timer 1 to reload
32#define VIAReloadA1 (1ULL << 5)
33#define VIAReloadA2 (1ULL << 6)
34#define VIAReloadB0 (1ULL << 7) // Forces timer 2 to reload
35#define VIAReloadB1 (1ULL << 8)
36#define VIAReloadB2 (1ULL << 9)
37#define VIAPostOneShotA0 (1ULL << 10) // Timer 1 fired in one shot mode
38#define VIAPostOneShotB0 (1ULL << 11) // Ttimer 2 fired in one shot mode
39#define VIAInterrupt0 (1ULL << 12) // Holds down the interrupt line
40#define VIAInterrupt1 (1ULL << 13)
41#define VIASetCA1out0 (1ULL << 14) // Sets CA2 pin high
42#define VIASetCA1out1 (1ULL << 15)
43#define VIAClearCA1out0 (1ULL << 16) // Sets CA2 pin low
44#define VIAClearCA1out1 (1ULL << 17)
45#define VIASetCA2out0 (1ULL << 18) // Sets CA2 pin high
46#define VIASetCA2out1 (1ULL << 19)
47#define VIAClearCA2out0 (1ULL << 20) // Sets CA2 pin low
48#define VIAClearCA2out1 (1ULL << 21)
49#define VIASetCB2out0 (1ULL << 22) // Sets CB2 pin high
50#define VIASetCB2out1 (1ULL << 23)
51#define VIAClearCB2out0 (1ULL << 24) // Sets CB2 pin low
52#define VIAClearCB2out1 (1ULL << 25)
53#define VIAPB7out0 (1ULL << 26) // Value of PB7 pin (if output is enabled)
54#define VIAClrInterrupt0 (1ULL << 27) // Releases the interrupt line
55#define VIAClrInterrupt1 (1ULL << 28)
56
57#define VIAClearBits ~((1ULL << 29) | VIACountA0 | VIACountB0 | VIAReloadA0 | VIAReloadB0 | VIAPostOneShotA0 | VIAPostOneShotB0 | VIAInterrupt0 | VIASetCA1out0 | VIAClearCA1out0 | VIASetCA2out0 | VIAClearCA2out0 | VIASetCB2out0 | VIAClearCB2out0 | VIAPB7out0 | VIAClrInterrupt0)
58
59class VIA6522 : public SubComponent {
60
61 Descriptions descriptions = {{
62
63 .name = "VIA",
64 .description = "VIA 6522"
65 }};
66
67 friend class Drive;
68 friend class ParCable;
69
70protected:
71
72 // Owner of this VIA
73 Drive &drive;
74
75
76 //
77 // Peripheral interface
78 //
79
80 /* Peripheral port A: "The Peripheral A port consists of 8 lines which can
81 * be individually programmed to act as an input or an output under control
82 * of a Data Direction Register. The polarity of output pins is controlled
83 * by an Output Register and input data can be latched into an internal
84 * register under control of the CA1 line."
85 */
86 u8 pa;
87
88 /* Peripheral A control lines: "The two peripheral A control lines act as
89 * interrupt inputs or as handshake outputs. Each line controls an internal
90 * interrupt flag with a corresponding interrupt enable bit. In addition,
91 * CA1controls the latching of data on Peripheral A Port input lines. The
92 * various modes of operation are controlled by the system processor
93 * through the internal control registers."
94 */
95 bool ca1;
96 bool ca2;
97
98 /* Peripheral port B: "The Peripheral B port consists of 8 lines which can
99 * be individually programmed to act as an input or an output under control
100 * of a Data Direction Register. The polarity of output pins is controlled
101 * by an Output Register and input data can be latched into an internal
102 * register under control of the CA1 line."
103 */
104 u8 pb;
105
106 /* Peripheral B control lines: "The Peripheral B control lines act as
107 * interrupt inputs or as handshake outputs. As with CA1 and CA2, each line
108 * controls an interrupt flag with a corresponding interrupt enable bit. In
109 * addition, these lines act as a serial port under control of the Shift
110 * Register."
111 */
112 bool cb1;
113 bool cb2;
114
115
116 //
117 // Port registers
118 //
119
120 /* Data direction registers: "Each port has a Data Direction Register
121 * (DDRA, DDRB) for specifying whether the peripheral pins are to act as
122 * inputs or outputs. A 0 in a bit of the Data Direction Register causes
123 * the corresponding peripheral pin to act as an input. A 1 causes the pin
124 * to act as an output."
125 */
126 u8 ddra;
127 u8 ddrb;
128
129 /* Output registers: "Each peripheral pin is also controlled by a bit in
130 * the Output Register (ORA, ORB) and an Input Register (IRA, IRB). When
131 * the pin is programmed to act as an output, the voltage on the pin is
132 * controlled by the correĀ­sponding bit of the Output Register. A 1 in the
133 * Output Register causes the pin to go high, and a 0 causes the pin to go
134 * low. Data can be written into Output Register bits corresponding to pins
135 * which are programmed to act as inputs; however, the pin will be
136 * unaffected.
137 */
138 u8 ora;
139 u8 orb;
140
141 /* Input registers: "Reading a peripheral port causes the contents of the
142 * Input Register (IRA, IRB) to be transferred onto the Data Bus. With
143 * input latching disabled, IRA will always reflect the data on the PA pins.
144 * With input latching enabled, IRA will reflect the contents of the Port A
145 * prior to setting the CA1 Interrupt Flag (IFRl) by an active transition
146 * on CA1.
147 */
148 u8 ira;
149 u8 irb;
150
151
152 //
153 // Timers
154 //
155
156 /* VIA timer 1: "Interval Timer T1 consists of two 8-bit latches and a
157 * 16-bit counter. The latches store data which is to be loaded into the
158 * counter. After loading, the counter decrements at 02 clock rate. Upon
159 * reaching zero, an interrupt flag is set, and IRQ goes low if the T1
160 * interrupt is enabled. Timer 1 then disables any further interrupts or
161 * automatically transfers the contents of the latches into the counter and
162 * continues to decrement. In addition, the timer may be programmed to
163 * invert the output signal on a peripheral pin (PB7) each time it
164 * times-out."
165 */
166 u16 t1; // T1C
167 u8 t1_latch_lo; // T1L_L
168 u8 t1_latch_hi; // T1L_H
169
170 /* VIA timer 2: "Timer 2 operates as an interval timer (in the 'one-shot'
171 * mode only), or as a counter for counting negative pulses on the PB6
172 * peripheral pin. A single control bit in the Auxiliary Control Register
173 * selects between these two modes. This timer is comprised of a
174 * 'write-only' low-order latch (T2L-L), a 'read-only' low-order counter
175 * (T2C-L) and a read/write high order counter (T2C-H). The counter
176 * registers act as a 16-bit counter which decrements at 02 rate."
177 */
178 u16 t2; // T1C
179 u8 t2_latch_lo; // T2L_L
180
181 // Peripheral control register
182 u8 pcr;
183
184 // Auxiliary register
185 u8 acr;
186
187 // Interrupt enable register
188 u8 ier;
189
190 // Interrupt flag register
191 u8 ifr;
192
193 // Shift register
194 u8 sr;
195
196 // Event triggering queue
197 u64 delay;
198
199 /* New bits to feed in. Bits set in this variable makes a trigger event
200 * persistent.
201 */
202 u64 feed;
203
204
205 //
206 // Speeding up emulation (sleep logic)
207 //
208
209 /* Idle counter. When the VIA state does not change during execution, this
210 * variable is increased by one. If it exceeds a certain threshhold, the
211 * chip is put into idle state via sleep().
212 */
213 u8 tiredness;
214
215 // Wakeup cycle
216 i64 wakeUpCycle;
217
218 // Number of skipped executions
219 i64 idleCounter;
220
221
222 //
223 // Initializing
224 //
225
226public:
227
228 VIA6522(C64 &ref, Drive &drvref);
229 virtual bool isVia1() const = 0;
230
231 VIA6522& operator= (const VIA6522& other) {
232
233 CLONE(pa)
234 CLONE(ca1)
235 CLONE(ca2)
236 CLONE(pb)
237 CLONE(cb1)
238 CLONE(cb2)
239 CLONE(ddra)
240 CLONE(ddrb)
241 CLONE(ora)
242 CLONE(orb)
243 CLONE(ira)
244 CLONE(irb)
245 CLONE(t1)
246 CLONE(t1_latch_lo)
247 CLONE(t1_latch_hi)
248 CLONE(t2)
249 CLONE(t2_latch_lo)
250 CLONE(pcr)
251 CLONE(acr)
252 CLONE(ier)
253 CLONE(ifr)
254 CLONE(sr)
255 CLONE(delay)
256 CLONE(feed)
257 CLONE(tiredness)
258 CLONE(wakeUpCycle)
259 CLONE(idleCounter)
260
261 return *this;
262 }
263
264
265 //
266 // Methods from Serializable
267 //
268
269public:
270
271 template <class T>
272 void serialize(T& worker)
273 {
274 worker
275
276 << pa
277 << ca1
278 << ca2
279 << pb
280 << cb1
281 << cb2
282 << ddra
283 << ddrb
284 << ora
285 << orb
286 << ira
287 << irb
288 << t1
289 << t1_latch_lo
290 << t1_latch_hi
291 << t2
292 << t2_latch_lo
293 << pcr
294 << acr
295 << ier
296 << ifr
297 << sr
298 << delay
299 << feed
300 << tiredness
301 << wakeUpCycle
302 << idleCounter;
303
304 } SERIALIZERS(serialize);
305
306
307 //
308 // Methods from CoreComponent
309 //
310
311public:
312
313 const Descriptions &getDescriptions() const override { return descriptions; }
314 void prefix() const override;
315
316private:
317
318 void _dump(Category category, std::ostream& os) const override;
319 void _reset(bool hard) override;
320
321
322 //
323 //
324 //
325
326public:
327
328 // Returns true if this object emulates is VIA2
329 bool isVia2() const;
330
331 // Getters for the data directon registers
332 u8 getDDRA() const { return ddra; }
333 u8 getDDRB() const { return ddrb; }
334
335 // Getters for peripheral ports
336 u8 getPA() const { return pa; }
337 u8 getPB() const { return pb; }
338
339 // Getter for the peripheral control pins
340 bool getCA2() const { return ca2; }
341 bool getCB2() const { return cb2; }
342
343 // Emulates the virtual VIA for one cycle
344 void execute();
345
346private:
347
348 // Emulates a timer for one cycle
349 void executeTimer1();
350 void executeTimer2();
351
352public:
353
354 /* Special peek function for the I/O memory range. The peek function only
355 * handles those registers that are treated similarly by both VIA chips.
356 */
357 virtual u8 peek(u16 addr);
358
359protected:
360
361 /* Special peek function for output register A. Variable handshake is
362 * needed to distiguish if ORA is read via address 0x1 (handshake enabled)
363 * or address 0xF (no handshake).
364 */
365 virtual u8 peekORA(bool handshake);
366
367 // Special peek function for output register B
368 u8 peekORB();
369
370public:
371
372 // Same as peek, but without side effects
373 u8 spypeek(u16 addr) const;
374
375 /* Special poke function for the I/O memory range. The poke function only
376 * handles those registers that are treated similarly by both VIA chips.
377 */
378 void poke(u16 addr, u8 value);
379
380protected:
381
382 /* Special poke function for output register A. Variable handshake is
383 * needed to distiguish if ORA is written via address 0x1 (handshake
384 * enabled) or address 0xF (no handshake).
385 */
386 virtual void pokeORA(u8 value, bool handshake);
387
388 // Special poke function for output register B
389 void pokeORB(u8 value);
390
391 // Special poke function for the PCR register
392 void pokePCR(u8 value);
393
394
395 //
396 // Internal Configuration
397 //
398
399 // Returns true iff timer 1 is in free-run mode (continous interrupts)
400 bool freeRun() const { return (acr & 0x40) != 0; }
401
402 // Returns true iff timer 2 counts pulses on pin PB6
403 bool countPulses() const { return (acr & 0x20) != 0; }
404
405 // Returns true iff an output pulse is generated on each T1 load operation
406 bool PB7OutputEnabled() const { return (acr & 0x80) != 0; }
407
408 // Checks if input latching is enabled
409 bool inputLatchingEnabledA() const { return (GET_BIT(acr,0)); }
410 bool inputLatchingEnabledB() const { return (GET_BIT(acr,1)); }
411
412
413 //
414 // Peripheral Control Register (PCR)
415 //
416
417protected:
418
419 // Reads the control bits from the peripheral control register
420 u8 ca1Control() const { return pcr & 0x01; }
421 u8 ca2Control() const { return (pcr >> 1) & 0x07; }
422 u8 cb1Control() const { return (pcr >> 4) & 0x01; }
423 u8 cb2Control() const { return (pcr >> 5) & 0x07; }
424
425
426 //
427 // Ports
428 //
429
430protected:
431
432 // Bit values driving port A from inside the chip
433 u8 portAinternal() const;
434
435 // Bit values driving port A from outside the chip
436 virtual u8 portAexternal() const = 0;
437
438 // Updates variable pa with bit values visible at port A
439 virtual void updatePA();
440
441 // Bit values driving port B from inside the chip
442 u8 portBinternal() const;
443
444 // Bit values driving port B from outside the chip
445 virtual u8 portBexternal() const = 0;
446
447 // Updates variable pa with bit values visible at port B
448 virtual void updatePB();
449
450
451 //
452 // Peripheral control lines
453 //
454
455public:
456
457 // Schedules a transition on the CA1 pin for the next cycle
458 void CA1action(bool value);
459
460private:
461
462 // Performs a transition on the CA1 pin
463 void setCA1(bool value);
464
465
466 //
467 // Interrupt handling
468 //
469
470public:
471
472 // Pulls down or releases the IRQ line
473 virtual void pullDownIrqLine() = 0;
474 virtual void releaseIrqLine() = 0;
475
476 /* Releases the IRQ line if IFR and IER have no matching bits. This method
477 * is invoked whenever a bit in the IFR or IER is is cleared.
478 */
479 void releaseIrqLineIfNeeded() { if ((ifr & ier) == 0) delay |= VIAClrInterrupt0; }
480
481 /* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
482 * ---------------------------------------------------------------------------------
483 * | IRQ | Timer 1 | Timer 2 | CB1 | CB2 |Shift Reg| CA1 | CA2 |
484 *
485 * Timer 1 - Set by: Time-out of T1
486 * Cleared by: Read t1 low or write t1 high
487 * Timer 2 - Set by: Time-out of T2
488 * Cleared by: Read t2 low or write t2 high
489 * CB1 - Set by: Active edge on CB1
490 * Cleared by: Read or write to register 0 (ORB)
491 * CB2 - Set by: Active edge on CB2
492 * Cleared by: Read or write to register 0 (ORB),
493 * if CB2 is not selected as "INDEPENDENT".
494 * Shift Reg - Set by: 8 shifts completed
495 * Cleared by: Read or write to register 10 (0xA)
496 * CA1 - Set by: Active edge on CA1
497 * Cleared by: Read or write to register 1 (ORA)
498 * CA2 - Set by: Active edge on CA2
499 * Cleared by: Read or write to register 1 (ORA),
500 * if CA2 is not selected as "INDEPENDENT".
501 */
502
503 // Sets or clears the Timer 1 interrupt flag
504 void setInterruptFlag_T1() {
505 if (!GET_BIT(ifr, 6) && GET_BIT(ier, 6)) delay |= VIAInterrupt0;
506 SET_BIT(ifr, 6);
507 }
508 void clearInterruptFlag_T1() { CLR_BIT(ifr, 6); releaseIrqLineIfNeeded(); }
509
510 // Sets or clears the Timer 2 interrupt flag
511 void setInterruptFlag_T2() {
512 if (!GET_BIT(ifr, 5) && GET_BIT(ier, 5)) delay |= VIAInterrupt0;
513 SET_BIT(ifr, 5);
514 }
515 void clearInterruptFlag_T2() { CLR_BIT(ifr, 5); releaseIrqLineIfNeeded(); }
516
517 // Sets or clears the CB1 interrupt flag
518 void setInterruptFlag_CB1() {
519 if (!GET_BIT(ifr, 4) && GET_BIT(ier, 4)) delay |= VIAInterrupt0;
520 SET_BIT(ifr, 4);
521 }
522 void clearInterruptFlag_CB1() { CLR_BIT(ifr, 4); releaseIrqLineIfNeeded(); }
523
524 // Clears the CB2 interrupt flag
525 void clearInterruptFlag_CB2() { CLR_BIT(ifr, 3); releaseIrqLineIfNeeded(); }
526
527 // Clears the Shift Register interrupt flag
528 void clearInterruptFlag_SR() { CLR_BIT(ifr, 2); releaseIrqLineIfNeeded(); }
529
530 // Clears the CB1 interrupt flag
531 void clearInterruptFlag_CA1() { CLR_BIT(ifr, 1); releaseIrqLineIfNeeded(); }
532
533 // Clears the CA2 interrupt flag
534 void clearInterruptFlag_CA2() { CLR_BIT(ifr, 0); releaseIrqLineIfNeeded(); }
535
536
537 //
538 // Speeding up emulation
539 //
540
541 // Puts the VIA into idle state
542 void sleep();
543
544 // Emulates all previously skipped cycles
545 void wakeUp();
546};
547
548
549/* First virtual VIA6522 controller. VIA1 serves as hardware interface between
550 * the VC1541 CPU and the serial port (IEC bus).
551 */
552class VIA1 final : public VIA6522 {
553
554public:
555
556 VIA1(C64 &ref, Drive &drvref) : VIA6522(ref, drvref) { }
557 ~VIA1() { }
558 bool isVia1() const override { return true; }
559
560 u8 peekORA(bool handshake) override;
561 void pokeORA(u8 value, bool handshake) override;
562 u8 portAexternal() const override;
563 u8 portBexternal() const override;
564 void updatePA() override;
565 void updatePB() override;
566 void pullDownIrqLine() override;
567 void releaseIrqLine() override;
568};
569
570/* Second virtual VIA6522 controller. VIA2 serves as hardware interface between
571 * the VC1541 CPU and the drive logic.
572 */
573class VIA2 final : public VIA6522 {
574
575public:
576
577 VIA2(C64 &ref, Drive &drvref) : VIA6522(ref, drvref) { }
578 ~VIA2() { }
579 bool isVia1() const override { return false; }
580
581 u8 portAexternal() const override;
582 u8 portBexternal() const override;
583 void updatePB() override;
584 void pullDownIrqLine() override;
585 void releaseIrqLine() override;
586};
587
588}
VirtualC64 project namespace.
Definition CmdQueue.cpp:16