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