[NTVDM]: Timer
[reactos.git] / reactos / subsystems / ntvdm / hardware / timer.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: timer.c
5 * PURPOSE: Programmable Interval Timer emulation -
6 * i82C54/8254 compatible
7 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
8 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9 */
10
11 /* INCLUDES *******************************************************************/
12
13 #define NDEBUG
14
15 #include "emulator.h"
16 #include "io.h"
17 #include "timer.h"
18 #include "pic.h"
19
20 /* PRIVATE VARIABLES **********************************************************/
21
22 static PIT_CHANNEL PitChannels[PIT_CHANNELS];
23 PPIT_CHANNEL PitChannel2 = &PitChannels[2];
24
25 /* PRIVATE FUNCTIONS **********************************************************/
26
27 static VOID PitLatchChannelStatus(BYTE Channel)
28 {
29 if (Channel >= PIT_CHANNELS) return;
30
31 /*
32 * A given counter can be latched only one time until it gets unlatched.
33 * If the counter is latched and then is latched again later before the
34 * value is read, then this last latch command is ignored and the value
35 * will be the value at the time the first command was issued.
36 */
37 if (PitChannels[Channel].LatchStatusSet == FALSE)
38 {
39 BYTE StatusLatch = 0;
40 /** HACK!! **/BYTE NullCount = 0;/** HACK!! **/
41
42 StatusLatch = PitChannels[Channel].Out << 7 | NullCount << 6;
43 StatusLatch |= (PitChannels[Channel].ReadWriteMode & 0x03) << 4;
44 StatusLatch |= (PitChannels[Channel].Mode & 0x07) << 1;
45 StatusLatch |= (PitChannels[Channel].Bcd & 0x01);
46
47 /* Latch the counter's status */
48 PitChannels[Channel].LatchStatusSet = TRUE;
49 PitChannels[Channel].StatusLatch = StatusLatch;
50 }
51 }
52
53 static VOID PitLatchChannelCount(BYTE Channel)
54 {
55 if (Channel >= PIT_CHANNELS) return;
56
57 /*
58 * A given counter can be latched only one time until it gets unlatched.
59 * If the counter is latched and then is latched again later before the
60 * value is read, then this last latch command is ignored and the value
61 * will be the value at the time the first command was issued.
62 */
63 if (PitChannels[Channel].ReadStatus == 0x00)
64 {
65 /* Latch the counter's value */
66 PitChannels[Channel].ReadStatus = PitChannels[Channel].ReadWriteMode;
67
68 /* Convert the current value to BCD if needed */
69 PitChannels[Channel].OutputLatch =
70 READ_PIT_VALUE(PitChannels[Channel], PitChannels[Channel].CurrentValue);
71 }
72 }
73
74 static VOID PitSetOut(PPIT_CHANNEL Channel, BOOLEAN State)
75 {
76 /** HACK!! **\ if (State == Channel->Out) return; \** HACK!! **/
77
78 /* Set the new state of the OUT pin */
79 Channel->Out = State;
80
81 /* Call the callback */
82 if (!Channel->Gate) return; // HACK: This is a HACK until gates are properly used (needed for the speaker to work properly).
83 if (Channel->OutFunction) Channel->OutFunction(Channel->OutParam, State);
84 }
85
86 static VOID PitInitCounter(PPIT_CHANNEL Channel)
87 {
88 switch (Channel->Mode)
89 {
90 case PIT_MODE_INT_ON_TERMINAL_COUNT:
91 PitSetOut(Channel, FALSE);
92 break;
93
94 case PIT_MODE_HARDWARE_ONE_SHOT:
95 case PIT_MODE_RATE_GENERATOR:
96 case PIT_MODE_SQUARE_WAVE:
97 case PIT_MODE_SOFTWARE_STROBE:
98 case PIT_MODE_HARDWARE_STROBE:
99 PitSetOut(Channel, TRUE);
100 break;
101 }
102 }
103
104 static VOID PitWriteCommand(BYTE Value)
105 {
106 BYTE Channel = (Value >> 6) & 0x03;
107 BYTE ReadWriteMode = (Value >> 4) & 0x03;
108 BYTE Mode = (Value >> 1) & 0x07;
109 BOOLEAN IsBcd = Value & 0x01;
110
111 /*
112 * Check for valid PIT channel - Possible values: 0, 1, 2.
113 * A value of 3 is for Read-Back Command.
114 */
115 if (Channel > PIT_CHANNELS) return;
116
117 /* Read-Back Command */
118 if (Channel == PIT_CHANNELS)
119 {
120 if ((Value & 0x20) == 0) // Bit 5 (Count) == 0: We latch multiple counters' counts
121 {
122 if (Value & 0x02) PitLatchChannelCount(0);
123 if (Value & 0x04) PitLatchChannelCount(1);
124 if (Value & 0x08) PitLatchChannelCount(2);
125 }
126 if ((Value & 0x10) == 0) // Bit 4 (Status) == 0: We latch multiple counters' statuses
127 {
128 if (Value & 0x02) PitLatchChannelStatus(0);
129 if (Value & 0x04) PitLatchChannelStatus(1);
130 if (Value & 0x08) PitLatchChannelStatus(2);
131 }
132 return;
133 }
134
135 /* Check if this is a counter latch command... */
136 if (ReadWriteMode == 0)
137 {
138 PitLatchChannelCount(Channel);
139 return;
140 }
141
142 /* ... otherwise, set the modes and reset flip-flops */
143 PitChannels[Channel].ReadWriteMode = ReadWriteMode;
144 PitChannels[Channel].ReadStatus = 0x00;
145 PitChannels[Channel].WriteStatus = 0x00;
146
147 PitChannels[Channel].LatchStatusSet = FALSE;
148 PitChannels[Channel].StatusLatch = 0x00;
149
150 PitChannels[Channel].CountRegister = 0x00;
151 PitChannels[Channel].OutputLatch = 0x00;
152
153 /** HACK!! **/PitChannels[Channel].FlipFlop = FALSE;/** HACK!! **/
154
155 /* Fix the current value if we switch to BCD counting */
156 PitChannels[Channel].Bcd = IsBcd;
157 if (IsBcd && PitChannels[Channel].CurrentValue > 9999)
158 PitChannels[Channel].CurrentValue = 9999;
159
160 switch (Mode)
161 {
162 case 0:
163 case 1:
164 case 2:
165 case 3:
166 case 4:
167 case 5:
168 {
169 PitChannels[Channel].Mode = Mode;
170 break;
171 }
172
173 case 6:
174 case 7:
175 {
176 /*
177 * Modes 6 and 7 become PIT_MODE_RATE_GENERATOR
178 * and PIT_MODE_SQUARE_WAVE respectively.
179 */
180 PitChannels[Channel].Mode = Mode - 4;
181 break;
182 }
183 }
184
185 PitInitCounter(&PitChannels[Channel]);
186 }
187
188 static BYTE PitReadData(BYTE Channel)
189 {
190 LPBYTE ReadWriteMode = NULL;
191 LPWORD CurrentValue = NULL;
192
193 /*
194 * If the status was latched, the first read operation will return the
195 * latched status, whichever value (count or status) was latched first.
196 */
197 if (PitChannels[Channel].LatchStatusSet)
198 {
199 PitChannels[Channel].LatchStatusSet = FALSE;
200 return PitChannels[Channel].StatusLatch;
201 }
202
203 /* To be able to read the count asynchronously, latch it first if needed */
204 if (PitChannels[Channel].ReadStatus == 0) PitLatchChannelCount(Channel);
205
206 /* The count is now latched */
207 ASSERT(PitChannels[Channel].ReadStatus != 0);
208
209 ReadWriteMode = &PitChannels[Channel].ReadStatus ;
210 CurrentValue = &PitChannels[Channel].OutputLatch;
211
212 if (*ReadWriteMode & 1)
213 {
214 /* Read LSB */
215 *ReadWriteMode &= ~1;
216 return LOBYTE(*CurrentValue);
217 }
218
219 if (*ReadWriteMode & 2)
220 {
221 /* Read MSB */
222 *ReadWriteMode &= ~2;
223 return HIBYTE(*CurrentValue);
224 }
225
226 /* Shouldn't get here */
227 ASSERT(FALSE);
228 return 0;
229 }
230
231 static VOID PitWriteData(BYTE Channel, BYTE Value)
232 {
233 LPBYTE ReadWriteMode = NULL;
234
235 if (PitChannels[Channel].WriteStatus == 0x00)
236 {
237 PitChannels[Channel].WriteStatus = PitChannels[Channel].ReadWriteMode;
238 }
239
240 ASSERT(PitChannels[Channel].WriteStatus != 0);
241
242 ReadWriteMode = &PitChannels[Channel].WriteStatus;
243
244 if (*ReadWriteMode & 1)
245 {
246 /* Write LSB */
247 *ReadWriteMode &= ~1;
248 PitChannels[Channel].CountRegister &= 0xFF00;
249 PitChannels[Channel].CountRegister |= Value;
250 }
251 else if (*ReadWriteMode & 2)
252 {
253 /* Write MSB */
254 *ReadWriteMode &= ~2;
255 PitChannels[Channel].CountRegister &= 0x00FF;
256 PitChannels[Channel].CountRegister |= Value << 8;
257 }
258
259 /* ReadWriteMode went to zero: we are going to load the new count */
260 if (*ReadWriteMode == 0x00)
261 {
262 if (PitChannels[Channel].CountRegister == 0x0000)
263 {
264 /* Wrap around to the highest count */
265 if (PitChannels[Channel].Bcd)
266 PitChannels[Channel].CountRegister = 9999;
267 else
268 PitChannels[Channel].CountRegister = 0xFFFF; // 0x10000; // 65536
269 }
270
271 /* Convert the current value from BCD if needed */
272 PitChannels[Channel].CountRegister =
273 WRITE_PIT_VALUE(PitChannels[Channel], PitChannels[Channel].CountRegister);
274 PitChannels[Channel].ReloadValue = PitChannels[Channel].CountRegister;
275 }
276 }
277
278 static BYTE WINAPI PitReadPort(ULONG Port)
279 {
280 switch (Port)
281 {
282 case PIT_DATA_PORT(0):
283 case PIT_DATA_PORT(1):
284 case PIT_DATA_PORT(2):
285 {
286 return PitReadData(Port - PIT_DATA_PORT(0));
287 }
288 }
289
290 return 0;
291 }
292
293 static VOID WINAPI PitWritePort(ULONG Port, BYTE Data)
294 {
295 switch (Port)
296 {
297 case PIT_COMMAND_PORT:
298 {
299 PitWriteCommand(Data);
300 break;
301 }
302
303 case PIT_DATA_PORT(0):
304 case PIT_DATA_PORT(1):
305 case PIT_DATA_PORT(2):
306 {
307 PitWriteData(Port - PIT_DATA_PORT(0), Data);
308 break;
309 }
310 }
311 }
312
313 static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count)
314 {
315 if (Count == 0) return;
316
317 switch (Channel->Mode)
318 {
319 case PIT_MODE_INT_ON_TERMINAL_COUNT:
320 {
321 /* Decrement the value */
322 if (Count > Channel->CurrentValue)
323 {
324 /* The value does not reload in this case */
325 Channel->CurrentValue = 0;
326 }
327 else Channel->CurrentValue -= Count;
328
329 /* Did it fall to the terminal count? */
330 if (Channel->CurrentValue == 0 && !Channel->Out)
331 {
332 /* Yes, raise the output line */
333 PitSetOut(Channel, TRUE);
334 }
335 break;
336 }
337
338 case PIT_MODE_RATE_GENERATOR:
339 {
340 BOOLEAN Reloaded = FALSE;
341
342 while (Count)
343 {
344 if ((Count > Channel->CurrentValue)
345 && (Channel->CurrentValue != 0))
346 {
347 /* Decrement the count */
348 Count -= Channel->CurrentValue;
349
350 /* Reload the value */
351 Channel->CurrentValue = Channel->ReloadValue;
352
353 /* Set the flag */
354 Reloaded = TRUE;
355 }
356 else
357 {
358 /* Decrement the value */
359 Channel->CurrentValue -= Count;
360
361 /* Clear the count */
362 Count = 0;
363
364 /* Did it fall to zero? */
365 if (Channel->CurrentValue == 0)
366 {
367 Channel->CurrentValue = Channel->ReloadValue;
368 Reloaded = TRUE;
369 }
370 }
371 }
372
373 /* If there was a reload, raise the output line */
374 if (Reloaded) PitSetOut(Channel, TRUE);
375
376 break;
377 }
378
379 case PIT_MODE_SQUARE_WAVE:
380 {
381 INT ReloadCount = 0;
382 WORD ReloadValue = Channel->ReloadValue;
383
384 /* The reload value must be even */
385 ReloadValue &= ~1;
386
387 while (Count)
388 {
389 if (((Count * 2) > Channel->CurrentValue)
390 && (Channel->CurrentValue != 0))
391 {
392 /* Decrement the count */
393 Count -= Channel->CurrentValue / 2;
394
395 /* Reload the value */
396 Channel->CurrentValue = ReloadValue;
397
398 /* Increment the reload count */
399 ReloadCount++;
400 }
401 else
402 {
403 /* Decrement the value */
404 Channel->CurrentValue -= Count * 2;
405
406 /* Clear the count */
407 Count = 0;
408
409 /* Did it fall to zero? */
410 if (Channel->CurrentValue == 0)
411 {
412 /* Reload the value */
413 Channel->CurrentValue = ReloadValue;
414
415 /* Increment the reload count */
416 ReloadCount++;
417 }
418 }
419 }
420
421 if (ReloadCount == 0) break;
422
423 /* Toggle the flip-flop if the number of reloads was odd */
424 if (ReloadCount & 1)
425 {
426 Channel->FlipFlop = !Channel->FlipFlop;
427 PitSetOut(Channel, !Channel->Out);
428 }
429
430 /* Was there any rising edge? */
431 if ((Channel->FlipFlop && (ReloadCount == 1)) || (ReloadCount > 1))
432 {
433 /* Yes, raise the output line */
434 PitSetOut(Channel, TRUE);
435 }
436
437 break;
438 }
439
440 case PIT_MODE_SOFTWARE_STROBE:
441 {
442 // TODO: NOT IMPLEMENTED
443 break;
444 }
445
446 case PIT_MODE_HARDWARE_ONE_SHOT:
447 case PIT_MODE_HARDWARE_STROBE:
448 {
449 /* These modes do not work on x86 PCs */
450 break;
451 }
452 }
453 }
454
455 /* PUBLIC FUNCTIONS ***********************************************************/
456
457 VOID PitSetOutFunction(BYTE Channel, LPVOID Param, PIT_OUT_FUNCTION OutFunction)
458 {
459 if (Channel >= PIT_CHANNELS) return;
460
461 PitChannels[Channel].OutParam = Param;
462 PitChannels[Channel].OutFunction = OutFunction;
463 }
464
465 VOID PitSetGate(BYTE Channel, BOOLEAN State)
466 {
467 if (Channel >= PIT_CHANNELS) return;
468 if (State == PitChannels[Channel].Gate) return;
469
470 /* UNIMPLEMENTED */
471 PitChannels[Channel].Gate = State;
472 }
473
474 VOID PitClock(DWORD Count)
475 {
476 UINT i;
477
478 if (Count == 0) return;
479
480 for (i = 0; i < PIT_CHANNELS; i++)
481 {
482 // if (!PitChannels[i].Counting) continue;
483 PitDecrementCount(&PitChannels[i], Count);
484 }
485 }
486
487 DWORD PitGetResolution(VOID)
488 {
489 INT i;
490 DWORD MinReloadValue = 65536;
491
492 for (i = 0; i < PIT_CHANNELS; i++)
493 {
494 DWORD ReloadValue = PitChannels[i].ReloadValue;
495
496 /* 0 means 65536 */
497 if (ReloadValue == 0) ReloadValue = 65536;
498
499 if (ReloadValue < MinReloadValue) MinReloadValue = ReloadValue;
500 }
501
502 /* Return the frequency resolution */
503 return PIT_BASE_FREQUENCY / MinReloadValue;
504 }
505
506 VOID PitInitialize(VOID)
507 {
508 /* Set up the timers to their default value */
509 PitSetOutFunction(0, NULL, NULL);
510 PitSetGate(0, TRUE);
511 PitSetOutFunction(1, NULL, NULL);
512 PitSetGate(1, TRUE);
513 PitSetOutFunction(2, NULL, NULL);
514 PitSetGate(2, FALSE);
515
516 /* Register the I/O Ports */
517 RegisterIoPort(PIT_COMMAND_PORT, NULL , PitWritePort);
518 RegisterIoPort(PIT_DATA_PORT(0), PitReadPort, PitWritePort);
519 RegisterIoPort(PIT_DATA_PORT(1), PitReadPort, PitWritePort);
520 RegisterIoPort(PIT_DATA_PORT(2), PitReadPort, PitWritePort);
521 }
522
523 /* EOF */