FabGL
ESP32 Display Controller and Graphics Library
PIT8253.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 <string.h>
28
29#include "PIT8253.h"
30
31
32
33namespace fabgl {
34
35
36
37PIT8253::PIT8253()
38{
39 FRC1Timer_init(PIT_FRC1_PRESCALER);
40}
41
42
43PIT8253::~PIT8253()
44{
45}
46
47
48void PIT8253::reset()
49{
50 for (int i = 0; i < 3; ++i) {
51 memset(&m_timer[i], 0, sizeof(TimerInfo));
52 m_timer[i].mode = 3;
53 m_timer[i].RLMode = 3;
54 m_timer[i].latch = -1;
55 m_timer[i].LSBToggle = true;
56 }
57 m_lastTickTime = FRC1Timer();
58 m_acc = 0;
59}
60
61
62void PIT8253::write(int reg, uint8_t value)
63{
64 // just in case ticks are needed
65 tick();
66
67 if (reg == 3) {
68
69 // write control register
70
71 int timerIndex = (value >> 6) & 0x03;
72 auto & timer = m_timer[timerIndex];
73
74 int RLMode = (value >> 4) & 0x03;
75
76 if (RLMode == 0) {
77 // counter latching operation (doesn't change BCD or mode)
78 timer.latch = timer.count;
79 timer.LSBToggle = true;
80 timer.ctrlSet = false;
81 //printf("%lld, PIT8253: write ctrl reg, %02X (timer=%d, latched=%04X)\n", esp_timer_get_time(), value, timerIndex, timer.latch);
82 } else {
83 // read / load
84 timer.mode = (value >> 1) & 0x07;
85 timer.BCD = (value & 1) == 1;
86 timer.RLMode = RLMode;
87 timer.ctrlSet = true;
88 if (RLMode == 3)
89 timer.LSBToggle = true;
90 //printf("%lld, PIT8253: write ctrl reg, %02X (timer=%d, mode=%d)\n", esp_timer_get_time(), value, timerIndex, timer.mode);
91 }
92
93 if (value >> 6 == 3)
94 printf("8253, read back. Required 8254?\n");
95
96 } else {
97
98 // write timers registers
99
100 int timerIndex = reg;
101 auto & timer = m_timer[timerIndex];
102
103 bool writeLSB = false;
104
105 switch (timer.RLMode) {
106 case 1:
107 writeLSB = true;
108 break;
109 case 3:
110 writeLSB = timer.LSBToggle;
111 timer.LSBToggle = !timer.LSBToggle;
112 break;
113 }
114
115 if (writeLSB) {
116 // LSB
117 //printf("%lld, PIT8253: timer %d write LSB, %02X\n", esp_timer_get_time(), timerIndex, value);
118 timer.resetHolding = (timer.resetHolding & 0xFF00) | value;
119 } else {
120 // MSB
121 //printf("%lld, PIT8253: timer %d write MSB, %02X\n", esp_timer_get_time(), timerIndex, value);
122 timer.resetHolding = (timer.resetHolding & 0x00FF) | (((int)value) << 8);
123 timer.resetCount = timer.resetHolding;
124 if (timer.ctrlSet) {
125 timer.count = (uint16_t)(timer.resetCount - 1);
126 timer.ctrlSet = false;
127 }
128 }
129
130 // OUT: with mode 0 it starts low, other modes it starts high
131 changeOut(timerIndex, timer.mode != 0);
132
133 }
134
135}
136
137
138uint8_t PIT8253::read(int reg)
139{
140 // just in case ticks are needed
141 tick();
142
143 uint8_t value = 0;
144
145 if (reg < 3) {
146 // read timers registers
147 auto & timer = m_timer[reg];
148
149 int readValue = timer.latch != -1 ? timer.latch : timer.count;
150
151 bool readLSB = false;
152 if (timer.RLMode == 1) {
153 readLSB = true;
154 } else if (timer.RLMode == 3) {
155 readLSB = timer.LSBToggle;
156 timer.LSBToggle = !timer.LSBToggle;
157 }
158
159 if (readLSB) {
160 value = readValue & 0xFF;
161 } else {
162 value = (readValue >> 8) & 0xFF;
163 timer.latch = -1;
164 }
165 //printf("read reg %d => %02X (%04X)\n", reg, value, readValue);
166 }
167
168 return value;
169}
170
171
172void PIT8253::setGate(int timerIndex, bool value)
173{
174 // just in case ticks are needed
175 tick();
176
177 auto & timer = m_timer[timerIndex];
178
179 switch (timer.mode) {
180 case 0:
181 case 2:
182 case 3:
183 // running when gate is high
184 timer.running = value;
185 break;
186 case 1:
187 case 5:
188 // switch to running when gate changes to high
189 if (timer.gate == false && value == true)
190 timer.running = true;
191 break;
192 }
193 switch (timer.mode) {
194 case 2:
195 case 3:
196 if (value == false)
197 changeOut(timerIndex, true);
198 break;
199 }
200 if (!timer.gate && value)
201 timer.count = timer.resetCount;
202 timer.gate = value;
203 //printf("setGate(%d, %d) [gate=%d running=%d]\n", timerIndex, value, timer.gate, timer.running);
204}
205
206
207void PIT8253::changeOut(int timer, bool value)
208{
209 if (value != m_timer[timer].out) {
210 //printf("timer %d, out = %d\n", timer, value);
211 m_timer[timer].out = value;
212 m_changeOut(m_context, timer);
213 }
214}
215
216
217void PIT8253::tick()
218{
219 uint32_t now = FRC1Timer();
220 int32_t diff = now - m_lastTickTime;
221 if (diff < 0)
222 diff = FRC1TimerMax - m_lastTickTime + now;
223
224 constexpr uint32_t BITS = 10;
225 constexpr uint32_t INC = (PIT_TICK_FREQ << BITS) / PIT_FRC1_FREQUENCY;
226
227 m_acc += INC * diff;
228 int ticks = m_acc >> BITS;
229 m_acc &= (1 << BITS) - 1;
230
231 m_lastTickTime = now;
232
233 if (ticks == 0)
234 return;
235
236 if (ticks > 65535) {
237 //printf("Too much ticks! (%d)\n", ticks);
238 m_acc += (ticks - 65535) << BITS;
239 ticks = 65535;
240 }
241
242 for (int timerIndex = 0; timerIndex < 3; ++timerIndex) {
243
244 auto & timer = m_timer[timerIndex];
245
246 if (timer.running) {
247
248 // modes 4 or 5: end of ending low pulse?
249 if (timer.mode >= 4 && timer.out == false) {
250 // mode 4, end of low pulse
251 changeOut(timerIndex, true);
252 timer.running = false;
253 timer.count = 65535;
254 continue;
255 }
256
257 timer.count -= ticks;
258
259 // in mode 3 each tick subtract 2 instead of 1
260 if (timer.mode == 3)
261 timer.count -= ticks;
262
263 if (timer.count <= 0) {
264 // count terminated
265 timer.count += timer.resetCount == 0 ? 65536 : timer.resetCount;
266 switch (timer.mode) {
267 case 0:
268 case 1:
269 // at the end OUT goes high
270 changeOut(timerIndex, true);
271 break;
272 case 2:
273 changeOut(timerIndex, false);
274 break;
275 case 3:
276 changeOut(timerIndex, !timer.out);
277 break;
278 }
279 } else {
280 // count running
281 switch (timer.mode) {
282 case 1:
283 case 4:
284 case 5:
285 // start low pulse
286 changeOut(timerIndex, false);
287 break;
288 case 2:
289 changeOut(timerIndex, true);
290 break;
291 }
292 }
293
294 //printf("running: timer %d [count = %04X, ticks = %d]\n", timerIndex, timer.count, ticks);
295
296 }
297
298 }
299
300}
301
302
303} // namespace fabgl