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-2021 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 
38 namespace fabgl {
39 
40 
42 // VIA (6522 - Versatile Interface Adapter)
43 
44 
45 VIA6522::VIA6522(int tag)
46  : m_tag(tag)
47 {
48  #if DEBUG6522
49  m_tick = 0;
50  #endif
51 }
52 
53 
54 void 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 
86 void 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 
93 void VIA6522::setBitPA(int bit, bool value)
94 {
95  uint8_t newPA = (m_PA & ~(1 << bit)) | ((int)value << bit);
96  setPA(newPA);
97 }
98 
99 
100 void 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 
113 void 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 
120 void VIA6522::setBitPB(int bit, bool value)
121 {
122  uint8_t newPB = (m_PB & ~(1 << bit)) | ((int)value << bit);
123  setPB(newPB);
124 }
125 
126 
127 void 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)
141 void 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)
299 int 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
395 bool 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
Definition: canvas.cpp:36
This file contains fabgl::VIA6522 definition.