2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
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)
11 /* INCLUDES *******************************************************************/
20 /* PRIVATE VARIABLES **********************************************************/
22 static PIT_CHANNEL PitChannels
[PIT_CHANNELS
];
23 PPIT_CHANNEL PitChannel2
= &PitChannels
[2];
25 /* PRIVATE FUNCTIONS **********************************************************/
27 static VOID
PitLatchChannelStatus(BYTE Channel
)
29 if (Channel
>= PIT_CHANNELS
) return;
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.
37 if (PitChannels
[Channel
].LatchStatusSet
== FALSE
)
40 /** HACK!! **/BYTE NullCount
= 0;/** HACK!! **/
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);
47 /* Latch the counter's status */
48 PitChannels
[Channel
].LatchStatusSet
= TRUE
;
49 PitChannels
[Channel
].StatusLatch
= StatusLatch
;
53 static VOID
PitLatchChannelCount(BYTE Channel
)
55 if (Channel
>= PIT_CHANNELS
) return;
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.
63 if (PitChannels
[Channel
].ReadStatus
== 0x00)
65 /* Latch the counter's value */
66 PitChannels
[Channel
].ReadStatus
= PitChannels
[Channel
].ReadWriteMode
;
68 /* Convert the current value to BCD if needed */
69 PitChannels
[Channel
].OutputLatch
=
70 READ_PIT_VALUE(PitChannels
[Channel
], PitChannels
[Channel
].CurrentValue
);
74 static VOID
PitSetOut(PPIT_CHANNEL Channel
, BOOLEAN State
)
76 /** HACK!! **\ if (State == Channel->Out) return; \** HACK!! **/
78 /* Set the new state of the OUT pin */
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
);
86 static VOID
PitInitCounter(PPIT_CHANNEL Channel
)
88 switch (Channel
->Mode
)
90 case PIT_MODE_INT_ON_TERMINAL_COUNT
:
91 PitSetOut(Channel
, FALSE
);
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
);
104 static VOID
PitWriteCommand(BYTE Value
)
106 BYTE Channel
= (Value
>> 6) & 0x03;
107 BYTE ReadWriteMode
= (Value
>> 4) & 0x03;
108 BYTE Mode
= (Value
>> 1) & 0x07;
109 BOOLEAN IsBcd
= Value
& 0x01;
112 * Check for valid PIT channel - Possible values: 0, 1, 2.
113 * A value of 3 is for Read-Back Command.
115 if (Channel
> PIT_CHANNELS
) return;
117 /* Read-Back Command */
118 if (Channel
== PIT_CHANNELS
)
120 if ((Value
& 0x20) == 0) // Bit 5 (Count) == 0: We latch multiple counters' counts
122 if (Value
& 0x02) PitLatchChannelCount(0);
123 if (Value
& 0x04) PitLatchChannelCount(1);
124 if (Value
& 0x08) PitLatchChannelCount(2);
126 if ((Value
& 0x10) == 0) // Bit 4 (Status) == 0: We latch multiple counters' statuses
128 if (Value
& 0x02) PitLatchChannelStatus(0);
129 if (Value
& 0x04) PitLatchChannelStatus(1);
130 if (Value
& 0x08) PitLatchChannelStatus(2);
135 /* Check if this is a counter latch command... */
136 if (ReadWriteMode
== 0)
138 PitLatchChannelCount(Channel
);
142 /* ... otherwise, set the modes and reset flip-flops */
143 PitChannels
[Channel
].ReadWriteMode
= ReadWriteMode
;
144 PitChannels
[Channel
].ReadStatus
= 0x00;
145 PitChannels
[Channel
].WriteStatus
= 0x00;
147 PitChannels
[Channel
].LatchStatusSet
= FALSE
;
148 PitChannels
[Channel
].StatusLatch
= 0x00;
150 PitChannels
[Channel
].CountRegister
= 0x00;
151 PitChannels
[Channel
].OutputLatch
= 0x00;
153 /** HACK!! **/PitChannels
[Channel
].FlipFlop
= FALSE
;/** HACK!! **/
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;
169 PitChannels
[Channel
].Mode
= Mode
;
177 * Modes 6 and 7 become PIT_MODE_RATE_GENERATOR
178 * and PIT_MODE_SQUARE_WAVE respectively.
180 PitChannels
[Channel
].Mode
= Mode
- 4;
185 PitInitCounter(&PitChannels
[Channel
]);
188 static BYTE
PitReadData(BYTE Channel
)
190 LPBYTE ReadWriteMode
= NULL
;
191 LPWORD CurrentValue
= NULL
;
194 * If the status was latched, the first read operation will return the
195 * latched status, whichever value (count or status) was latched first.
197 if (PitChannels
[Channel
].LatchStatusSet
)
199 PitChannels
[Channel
].LatchStatusSet
= FALSE
;
200 return PitChannels
[Channel
].StatusLatch
;
203 /* To be able to read the count asynchronously, latch it first if needed */
204 if (PitChannels
[Channel
].ReadStatus
== 0) PitLatchChannelCount(Channel
);
206 /* The count is now latched */
207 ASSERT(PitChannels
[Channel
].ReadStatus
!= 0);
209 ReadWriteMode
= &PitChannels
[Channel
].ReadStatus
;
210 CurrentValue
= &PitChannels
[Channel
].OutputLatch
;
212 if (*ReadWriteMode
& 1)
215 *ReadWriteMode
&= ~1;
216 return LOBYTE(*CurrentValue
);
219 if (*ReadWriteMode
& 2)
222 *ReadWriteMode
&= ~2;
223 return HIBYTE(*CurrentValue
);
226 /* Shouldn't get here */
231 static VOID
PitWriteData(BYTE Channel
, BYTE Value
)
233 LPBYTE ReadWriteMode
= NULL
;
235 if (PitChannels
[Channel
].WriteStatus
== 0x00)
237 PitChannels
[Channel
].WriteStatus
= PitChannels
[Channel
].ReadWriteMode
;
240 ASSERT(PitChannels
[Channel
].WriteStatus
!= 0);
242 ReadWriteMode
= &PitChannels
[Channel
].WriteStatus
;
244 if (*ReadWriteMode
& 1)
247 *ReadWriteMode
&= ~1;
248 PitChannels
[Channel
].CountRegister
&= 0xFF00;
249 PitChannels
[Channel
].CountRegister
|= Value
;
251 else if (*ReadWriteMode
& 2)
254 *ReadWriteMode
&= ~2;
255 PitChannels
[Channel
].CountRegister
&= 0x00FF;
256 PitChannels
[Channel
].CountRegister
|= Value
<< 8;
259 /* ReadWriteMode went to zero: we are going to load the new count */
260 if (*ReadWriteMode
== 0x00)
262 if (PitChannels
[Channel
].CountRegister
== 0x0000)
264 /* Wrap around to the highest count */
265 if (PitChannels
[Channel
].Bcd
)
266 PitChannels
[Channel
].CountRegister
= 9999;
268 PitChannels
[Channel
].CountRegister
= 0xFFFF; // 0x10000; // 65536
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
;
278 static BYTE WINAPI
PitReadPort(ULONG Port
)
282 case PIT_DATA_PORT(0):
283 case PIT_DATA_PORT(1):
284 case PIT_DATA_PORT(2):
286 return PitReadData(Port
- PIT_DATA_PORT(0));
293 static VOID WINAPI
PitWritePort(ULONG Port
, BYTE Data
)
297 case PIT_COMMAND_PORT
:
299 PitWriteCommand(Data
);
303 case PIT_DATA_PORT(0):
304 case PIT_DATA_PORT(1):
305 case PIT_DATA_PORT(2):
307 PitWriteData(Port
- PIT_DATA_PORT(0), Data
);
313 static VOID
PitDecrementCount(PPIT_CHANNEL Channel
, DWORD Count
)
315 if (Count
== 0) return;
317 switch (Channel
->Mode
)
319 case PIT_MODE_INT_ON_TERMINAL_COUNT
:
321 /* Decrement the value */
322 if (Count
> Channel
->CurrentValue
)
324 /* The value does not reload in this case */
325 Channel
->CurrentValue
= 0;
327 else Channel
->CurrentValue
-= Count
;
329 /* Did it fall to the terminal count? */
330 if (Channel
->CurrentValue
== 0 && !Channel
->Out
)
332 /* Yes, raise the output line */
333 PitSetOut(Channel
, TRUE
);
338 case PIT_MODE_RATE_GENERATOR
:
340 BOOLEAN Reloaded
= FALSE
;
344 if ((Count
> Channel
->CurrentValue
)
345 && (Channel
->CurrentValue
!= 0))
347 /* Decrement the count */
348 Count
-= Channel
->CurrentValue
;
350 /* Reload the value */
351 Channel
->CurrentValue
= Channel
->ReloadValue
;
358 /* Decrement the value */
359 Channel
->CurrentValue
-= Count
;
361 /* Clear the count */
364 /* Did it fall to zero? */
365 if (Channel
->CurrentValue
== 0)
367 Channel
->CurrentValue
= Channel
->ReloadValue
;
373 /* If there was a reload, raise the output line */
374 if (Reloaded
) PitSetOut(Channel
, TRUE
);
379 case PIT_MODE_SQUARE_WAVE
:
382 WORD ReloadValue
= Channel
->ReloadValue
;
384 /* The reload value must be even */
389 if (((Count
* 2) > Channel
->CurrentValue
)
390 && (Channel
->CurrentValue
!= 0))
392 /* Decrement the count */
393 Count
-= Channel
->CurrentValue
/ 2;
395 /* Reload the value */
396 Channel
->CurrentValue
= ReloadValue
;
398 /* Increment the reload count */
403 /* Decrement the value */
404 Channel
->CurrentValue
-= Count
* 2;
406 /* Clear the count */
409 /* Did it fall to zero? */
410 if (Channel
->CurrentValue
== 0)
412 /* Reload the value */
413 Channel
->CurrentValue
= ReloadValue
;
415 /* Increment the reload count */
421 if (ReloadCount
== 0) break;
423 /* Toggle the flip-flop if the number of reloads was odd */
426 Channel
->FlipFlop
= !Channel
->FlipFlop
;
427 PitSetOut(Channel
, !Channel
->Out
);
430 /* Was there any rising edge? */
431 if ((Channel
->FlipFlop
&& (ReloadCount
== 1)) || (ReloadCount
> 1))
433 /* Yes, raise the output line */
434 PitSetOut(Channel
, TRUE
);
440 case PIT_MODE_SOFTWARE_STROBE
:
442 // TODO: NOT IMPLEMENTED
446 case PIT_MODE_HARDWARE_ONE_SHOT
:
447 case PIT_MODE_HARDWARE_STROBE
:
449 /* These modes do not work on x86 PCs */
455 /* PUBLIC FUNCTIONS ***********************************************************/
457 VOID
PitSetOutFunction(BYTE Channel
, LPVOID Param
, PIT_OUT_FUNCTION OutFunction
)
459 if (Channel
>= PIT_CHANNELS
) return;
461 PitChannels
[Channel
].OutParam
= Param
;
462 PitChannels
[Channel
].OutFunction
= OutFunction
;
465 VOID
PitSetGate(BYTE Channel
, BOOLEAN State
)
467 if (Channel
>= PIT_CHANNELS
) return;
468 if (State
== PitChannels
[Channel
].Gate
) return;
471 PitChannels
[Channel
].Gate
= State
;
474 VOID
PitClock(DWORD Count
)
478 if (Count
== 0) return;
480 for (i
= 0; i
< PIT_CHANNELS
; i
++)
482 // if (!PitChannels[i].Counting) continue;
483 PitDecrementCount(&PitChannels
[i
], Count
);
487 DWORD
PitGetResolution(VOID
)
490 DWORD MinReloadValue
= 65536;
492 for (i
= 0; i
< PIT_CHANNELS
; i
++)
494 DWORD ReloadValue
= PitChannels
[i
].ReloadValue
;
497 if (ReloadValue
== 0) ReloadValue
= 65536;
499 if (ReloadValue
< MinReloadValue
) MinReloadValue
= ReloadValue
;
502 /* Return the frequency resolution */
503 return PIT_BASE_FREQUENCY
/ MinReloadValue
;
506 VOID
PitInitialize(VOID
)
508 /* Set up the timers to their default value */
509 PitSetOutFunction(0, NULL
, NULL
);
511 PitSetOutFunction(1, NULL
, NULL
);
513 PitSetOutFunction(2, NULL
, NULL
);
514 PitSetGate(2, FALSE
);
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
);