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