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