FabGL
ESP32 Display Controller and Graphics Library
VIA6522.cpp
1/*
2 Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3 Copyright (c) 2019-2022 Fabrizio Di Vittorio.
4 All rights reserved.
5
6
7* Please contact fdivitto2013@gmail.com if you need a commercial license.
8
9
10* This library and related software is available under GPL v3.
11
12 FabGL is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 FabGL is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26
27#include "VIA6522.h"
28
29
30#pragma GCC optimize ("O2")
31
32#if FABGL_ESP_IDF_VERSION > FABGL_ESP_IDF_VERSION_VAL(3, 3, 5)
33 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
34#endif
35
36
37
38namespace fabgl {
39
40
42// VIA (6522 - Versatile Interface Adapter)
43
44
45VIA6522::VIA6522(int tag)
46 : m_tag(tag)
47{
48 #if DEBUG6522
49 m_tick = 0;
50 #endif
51}
52
53
54void VIA6522::reset()
55{
56 m_timer1Counter = 0x0000;
57 m_timer1Latch = 0x0000;
58 m_timer2Counter = 0x0000;
59 m_timer2Latch = 0x00;
60 m_CA1 = 0;
61 m_CA1_prev = 0;
62 m_CA2 = 0;
63 m_CA2_prev = 0;
64 m_CB1 = 0;
65 m_CB1_prev = 0;
66 m_CB2 = 0;
67 m_CB2_prev = 0;
68 m_IFR = 0;
69 m_IER = 0;
70 m_ACR = 0;
71 m_timer1Triggered = false;
72 m_timer2Triggered = false;
73 m_DDRA = 0;
74 m_DDRB = 0;
75 m_PCR = 0;
76 m_PA = 0xff;
77 m_PB = 0xff;
78 m_SR = 0;
79 m_IRA = 0xff;
80 m_IRB = 0xff;
81 m_ORA = 0;
82 m_ORB = 0;
83}
84
85
86void VIA6522::setPA(int value)
87{
88 m_PA = value;
89 m_IRA = m_PA; // IRA is exactly what there is on port A
90}
91
92
93void VIA6522::setBitPA(int bit, bool value)
94{
95 uint8_t newPA = (m_PA & ~(1 << bit)) | ((int)value << bit);
96 setPA(newPA);
97}
98
99
100void VIA6522::openBitPA(int bit)
101{
102 uint8_t mask = (1 << bit);
103 if (m_DDRA & mask) {
104 // pin configured as output, set value of ORA
105 setBitPA(bit, m_ORA & mask);
106 } else {
107 // pin configured as input, pull it up
108 setBitPA(bit, true);
109 }
110}
111
112
113void VIA6522::setPB(int value)
114{
115 m_PB = value;
116 m_IRB = (m_PB & ~m_DDRB) | (m_ORB & m_DDRB); // IRB contains PB for inputs, and ORB for outputs
117}
118
119
120void VIA6522::setBitPB(int bit, bool value)
121{
122 uint8_t newPB = (m_PB & ~(1 << bit)) | ((int)value << bit);
123 setPB(newPB);
124}
125
126
127void VIA6522::openBitPB(int bit)
128{
129 uint8_t mask = (1 << bit);
130 if (m_DDRB & mask) {
131 // pin configured as output, set value of ORB
132 setBitPB(bit, m_ORB & mask);
133 } else {
134 // pin configured as input, pull it up
135 setBitPB(bit, true);
136 }
137}
138
139
140// reg must be 0..0x0f (not checked)
141void VIA6522::writeReg(int reg, int value)
142{
143 #if DEBUG6522
144 printf("tick %d VIA %d writeReg(%s, 0x%02x)\n", m_tick, m_tag, VIAREG2STR[reg], value);
145 #endif
146
147 switch (reg) {
148
149 // ORB: Output Register B
150 case VIA_REG_ORB_IRB:
151 m_ORB = value;
152 m_PB = (m_ORB & m_DDRB) | (m_PB & ~m_DDRB);
153 m_IRB = (m_PB & ~m_DDRB) | (m_ORB & m_DDRB); // IRB contains PB for inputs, and ORB for outputs
154 m_portOut(m_context, this, VIA6522Port::PB);
155 // clear CB1 and CB2 interrupt flags
156 m_IFR &= ~VIA_IER_CB1;
157 m_IFR &= ~VIA_IER_CB2;
158 break;
159
160 // ORA: Output Register A
161 case VIA_REG_ORA_IRA:
162 // clear CA1 and CA2 interrupt flags
163 m_IFR &= ~VIA_IER_CA1;
164 m_IFR &= ~VIA_IER_CA2;
165 // not break!
166 // ORA: Output Register A - no Handshake
167 case VIA_REG_ORA_IRA_NH:
168 m_ORA = value;
169 m_PA = (m_ORA & m_DDRA) | (m_PA & ~m_DDRA);
170 m_IRA = m_PA; // IRA is exactly what there is on port A
171 m_portOut(m_context, this, VIA6522Port::PA);
172 break;
173
174 // DDRB: Data Direction Register B
175 case VIA_REG_DDRB:
176 m_DDRB = value;
177 m_PB = (m_ORB & m_DDRB) | (m_PB & ~m_DDRB); // refresh Port B status
178 m_IRB = (m_PB & ~m_DDRB) | (m_ORB & m_DDRB); // IRB contains PB for inputs, and ORB for outputs
179 break;
180
181 // DDRA: Data Direction Register A
182 case VIA_REG_DDRA:
183 m_DDRA = value;
184 m_PA = (m_ORA & m_DDRA) | (m_PA & ~m_DDRA); // refresh Port A status
185 m_IRA = m_PA; // IRA is exactly what there is on port A
186 break;
187
188 // T1C-L: T1 Low-Order Latches
189 case VIA_REG_T1_C_LO:
190 m_timer1Latch = (m_timer1Latch & 0xff00) | value;
191 break;
192
193 // T1C-H: T1 High-Order Counter
194 case VIA_REG_T1_C_HI:
195 m_timer1Latch = (m_timer1Latch & 0x00ff) | (value << 8);
196 // timer1: write into high order counter
197 // timer1: transfer low order latch into low order counter
198 m_timer1Counter = (m_timer1Latch & 0x00ff) | (value << 8);
199 // clear T1 interrupt flag
200 m_IFR &= ~VIA_IER_T1;
201 m_timer1Triggered = false;
202 break;
203
204 // T1L-L: T1 Low-Order Latches
205 case VIA_REG_T1_L_LO:
206 m_timer1Latch = (m_timer1Latch & 0xff00) | value;
207 break;
208
209 // T1L-H: T1 High-Order Latches
210 case VIA_REG_T1_L_HI:
211 m_timer1Latch = (m_timer1Latch & 0x00ff) | (value << 8);
212 // clear T1 interrupt flag
213 m_IFR &= ~VIA_IER_T1;
214 break;
215
216 // T2C-L: T2 Low-Order Latches
217 case VIA_REG_T2_C_LO:
218 m_timer2Latch = value;
219 break;
220
221 // T2C-H: T2 High-Order Counter
222 case VIA_REG_T2_C_HI:
223 // timer2: copy low order latch into low order counter
224 m_timer2Counter = (value << 8) | m_timer2Latch;
225 // clear T2 interrupt flag
226 m_IFR &= ~VIA_IER_T2;
227 m_timer2Triggered = false;
228 break;
229
230 // SR: Shift Register
231 case VIA_REG_SR:
232 m_SR = value;
233 break;
234
235 // ACR: Auxliary Control Register
236 case VIA_REG_ACR:
237 m_ACR = value;
238 break;
239
240 // PCR: Peripheral Control Register
241 case VIA_REG_PCR:
242 {
243 m_PCR = value;
244 // CA2 control
245 switch ((m_PCR >> 1) & 0b111) {
246 case 0b110:
247 // manual output - low
248 m_CA2 = 0;
249 m_portOut(m_context, this, VIA6522Port::CA2);
250 break;
251 case 0b111:
252 // manual output - high
253 m_CA2 = 1;
254 m_portOut(m_context, this, VIA6522Port::CA2);
255 break;
256 default:
257 break;
258 }
259 // CB2 control
260 switch ((m_PCR >> 5) & 0b111) {
261 case 0b110:
262 // manual output - low
263 m_CB2 = 0;
264 m_portOut(m_context, this, VIA6522Port::CB2);
265 break;
266 case 0b111:
267 // manual output - high
268 m_CB2 = 1;
269 m_portOut(m_context, this, VIA6522Port::CB2);
270 break;
271 default:
272 break;
273 }
274 break;
275 }
276
277 // IFR: Interrupt Flag Register
278 case VIA_REG_IFR:
279 // reset each bit at 1
280 m_IFR &= ~value & 0x7f;
281 break;
282
283 // IER: Interrupt Enable Register
284 case VIA_REG_IER:
285 if (value & VIA_IER_CTRL) {
286 // set 0..6 bits
287 m_IER |= value & 0x7f;
288 } else {
289 // reset 0..6 bits
290 m_IER &= ~value & 0x7f;
291 }
292 break;
293
294 };
295}
296
297
298// reg must be 0..0x0f (not checked)
299int VIA6522::readReg(int reg)
300{
301 #if DEBUG6522
302 printf("tick %d VIA %d readReg(%s)\n", m_tick, m_tag, VIAREG2STR[reg]);
303 #endif
304
305 switch (reg) {
306
307 // IRB: Input Register B
308 case VIA_REG_ORB_IRB:
309 // clear CB1 and CB2 interrupt flags
310 m_IFR &= ~VIA_IER_CB1;
311 m_IFR &= ~VIA_IER_CB2;
312 // get updated PB status
313 m_portIn(m_context, this, VIA6522Port::PB);
314 return m_IRB;
315
316 // IRA: Input Register A
317 case VIA_REG_ORA_IRA:
318 // clear CA1 and CA2 interrupt flags
319 m_IFR &= ~VIA_IER_CA1;
320 m_IFR &= ~VIA_IER_CA2;
321 // IRA: Input Register A - no handshake
322 case VIA_REG_ORA_IRA_NH:
323 // get updated PA status
324 m_portIn(m_context, this, VIA6522Port::PA);
325 return m_IRA;
326
327 // DDRB: Data Direction Register B
328 case VIA_REG_DDRB:
329 return m_DDRB;
330
331 // DDRA: Data Direction Register A
332 case VIA_REG_DDRA:
333 return m_DDRA;
334
335 // T1C-L: T1 Low-Order Counter
336 case VIA_REG_T1_C_LO:
337 // clear T1 interrupt flag
338 m_IFR &= ~VIA_IER_T1;
339 // read T1 low order counter
340 return m_timer1Counter & 0xff;
341
342 // T1C-H: T1 High-Order Counter
343 case VIA_REG_T1_C_HI:
344 // read T1 high order counter
345 return m_timer1Counter >> 8;
346
347 // T1L-L: T1 Low-Order Latches
348 case VIA_REG_T1_L_LO:
349 // read T1 low order latch
350 return m_timer1Latch & 0xff;
351
352 // T1L-H: T1 High-Order Latches
353 case VIA_REG_T1_L_HI:
354 // read T1 high order latch
355 return m_timer1Latch >> 8;
356
357 // T2C-L: T2 Low-Order Counter
358 case VIA_REG_T2_C_LO:
359 // clear T2 interrupt flag
360 m_IFR &= ~VIA_IER_T2;
361 // read T2 low order counter
362 return m_timer2Counter & 0xff;
363
364 // T2C-H: T2 High-Order Counter
365 case VIA_REG_T2_C_HI:
366 // read T2 high order counter
367 return m_timer2Counter >> 8;
368
369 // SR: Shift Register
370 case VIA_REG_SR:
371 return m_SR;
372
373 // ACR: Auxiliary Control Register
374 case VIA_REG_ACR:
375 return m_ACR;
376
377 // PCR: Peripheral Control Register
378 case VIA_REG_PCR:
379 return m_PCR;
380
381 // IFR: Interrupt Flag Register
382 case VIA_REG_IFR:
383 return m_IFR | (m_IFR & m_IER ? 0x80 : 0);
384
385 // IER: Interrupt Enable Register
386 case VIA_REG_IER:
387 return m_IER | 0x80;
388
389 }
390 return 0;
391}
392
393
394// ret. true on interrupt
395bool VIA6522::tick(int cycles)
396{
397 #if DEBUG6522
398 m_tick += cycles;
399 #endif
400
401 // handle Timer 1
402 m_timer1Counter -= cycles;
403 if (m_timer1Counter <= 0) {
404 if (m_ACR & VIA_ACR_T1_FREERUN) {
405 // free run, reload from latch
406 m_timer1Counter += (m_timer1Latch - 1) + 3; // +2 delay before next start
407 m_IFR |= VIA_IER_T1; // set interrupt flag
408 } else if (!m_timer1Triggered) {
409 // one shot
410 m_timer1Counter += 0xFFFF;
411 m_timer1Triggered = true;
412 m_IFR |= VIA_IER_T1; // set interrupt flag
413 } else
414 m_timer1Counter = (uint16_t)m_timer1Counter; // restart from <0xffff
415 }
416
417 // handle Timer 2
418 if ((m_ACR & VIA_ACR_T2_COUNTPULSES) == 0) {
419 m_timer2Counter -= cycles;
420 if (m_timer2Counter <= 0 && !m_timer2Triggered) {
421 m_timer2Counter += 0xFFFF;
422 m_timer2Triggered = true;
423 m_IFR |= VIA_IER_T2; // set interrupt flag
424 }
425 }
426
427 // handle CA1 (RESTORE key)
428 if (m_CA1 != m_CA1_prev) {
429 // (interrupt on low->high transition) OR (interrupt on high->low transition)
430 if (((m_PCR & 1) && m_CA1) || (!(m_PCR & 1) && !m_CA1)) {
431 m_IFR |= VIA_IER_CA1;
432 }
433 m_CA1_prev = m_CA1; // set interrupt flag
434 }
435
436 // handle CB1
437 if (m_CB1 != m_CB1_prev) {
438 // (interrupt on low->high transition) OR (interrupt on high->low transition)
439 if (((m_PCR & 0x10) && m_CB1) || (!(m_PCR & 0x10) && !m_CB1)) {
440 m_IFR |= VIA_IER_CB1; // set interrupt flag
441 }
442 m_CB1_prev = m_CB1;
443 }
444
445 return m_IER & m_IFR & 0x7f;
446
447}
448
449
450
451}; // namespace fabgl
This file contains fabgl::VIA6522 definition.