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
->OutFunction
) Channel
->OutFunction(Channel
->OutParam
, State
);
85 static VOID
PitInitCounter(PPIT_CHANNEL Channel
)
87 switch (Channel
->Mode
)
89 case PIT_MODE_INT_ON_TERMINAL_COUNT
:
90 PitSetOut(Channel
, FALSE
);
93 case PIT_MODE_HARDWARE_ONE_SHOT
:
94 case PIT_MODE_RATE_GENERATOR
:
95 case PIT_MODE_SQUARE_WAVE
:
96 case PIT_MODE_SOFTWARE_STROBE
:
97 case PIT_MODE_HARDWARE_STROBE
:
98 PitSetOut(Channel
, TRUE
);
103 static VOID
PitWriteCommand(BYTE Value
)
105 BYTE Channel
= (Value
>> 6) & 0x03;
106 BYTE ReadWriteMode
= (Value
>> 4) & 0x03;
107 BYTE Mode
= (Value
>> 1) & 0x07;
108 BOOLEAN IsBcd
= Value
& 0x01;
111 * Check for valid PIT channel - Possible values: 0, 1, 2.
112 * A value of 3 is for Read-Back Command.
114 if (Channel
> PIT_CHANNELS
) return;
116 /* Read-Back Command */
117 if (Channel
== PIT_CHANNELS
)
119 if ((Value
& 0x20) == 0) // Bit 5 (Count) == 0: We latch multiple counters' counts
121 if (Value
& 0x02) PitLatchChannelCount(0);
122 if (Value
& 0x04) PitLatchChannelCount(1);
123 if (Value
& 0x08) PitLatchChannelCount(2);
125 if ((Value
& 0x10) == 0) // Bit 4 (Status) == 0: We latch multiple counters' statuses
127 if (Value
& 0x02) PitLatchChannelStatus(0);
128 if (Value
& 0x04) PitLatchChannelStatus(1);
129 if (Value
& 0x08) PitLatchChannelStatus(2);
134 /* Check if this is a counter latch command... */
135 if (ReadWriteMode
== 0)
137 PitLatchChannelCount(Channel
);
141 /* ... otherwise, set the modes and reset flip-flops */
142 PitChannels
[Channel
].ReadWriteMode
= ReadWriteMode
;
143 PitChannels
[Channel
].ReadStatus
= 0x00;
144 PitChannels
[Channel
].WriteStatus
= 0x00;
146 PitChannels
[Channel
].LatchStatusSet
= FALSE
;
147 PitChannels
[Channel
].StatusLatch
= 0x00;
149 PitChannels
[Channel
].CountRegister
= 0x00;
150 PitChannels
[Channel
].OutputLatch
= 0x00;
152 /** HACK!! **/PitChannels
[Channel
].FlipFlop
= FALSE
;/** HACK!! **/
154 /* Fix the current value if we switch to BCD counting */
155 PitChannels
[Channel
].Bcd
= IsBcd
;
156 if (IsBcd
&& PitChannels
[Channel
].CurrentValue
> 9999)
157 PitChannels
[Channel
].CurrentValue
= 9999;
168 PitChannels
[Channel
].Mode
= Mode
;
176 * Modes 6 and 7 become PIT_MODE_RATE_GENERATOR
177 * and PIT_MODE_SQUARE_WAVE respectively.
179 PitChannels
[Channel
].Mode
= Mode
- 4;
184 PitInitCounter(&PitChannels
[Channel
]);
187 static BYTE
PitReadData(BYTE Channel
)
189 LPBYTE ReadWriteMode
= NULL
;
190 LPWORD CurrentValue
= NULL
;
193 * If the status was latched, the first read operation will return the
194 * latched status, whichever value (count or status) was latched first.
196 if (PitChannels
[Channel
].LatchStatusSet
)
198 PitChannels
[Channel
].LatchStatusSet
= FALSE
;
199 return PitChannels
[Channel
].StatusLatch
;
202 /* To be able to read the count asynchronously, latch it first if needed */
203 if (PitChannels
[Channel
].ReadStatus
== 0) PitLatchChannelCount(Channel
);
205 /* The count is now latched */
206 ASSERT(PitChannels
[Channel
].ReadStatus
!= 0);
208 ReadWriteMode
= &PitChannels
[Channel
].ReadStatus
;
209 CurrentValue
= &PitChannels
[Channel
].OutputLatch
;
211 if (*ReadWriteMode
& 1)
214 *ReadWriteMode
&= ~1;
215 return LOBYTE(*CurrentValue
);
218 if (*ReadWriteMode
& 2)
221 *ReadWriteMode
&= ~2;
222 return HIBYTE(*CurrentValue
);
225 /* Shouldn't get here */
230 static VOID
PitWriteData(BYTE Channel
, BYTE Value
)
232 LPBYTE ReadWriteMode
= NULL
;
234 if (PitChannels
[Channel
].WriteStatus
== 0x00)
236 PitChannels
[Channel
].WriteStatus
= PitChannels
[Channel
].ReadWriteMode
;
239 ASSERT(PitChannels
[Channel
].WriteStatus
!= 0);
241 ReadWriteMode
= &PitChannels
[Channel
].WriteStatus
;
243 if (*ReadWriteMode
& 1)
246 *ReadWriteMode
&= ~1;
247 PitChannels
[Channel
].CountRegister
&= 0xFF00;
248 PitChannels
[Channel
].CountRegister
|= Value
;
250 else if (*ReadWriteMode
& 2)
253 *ReadWriteMode
&= ~2;
254 PitChannels
[Channel
].CountRegister
&= 0x00FF;
255 PitChannels
[Channel
].CountRegister
|= Value
<< 8;
258 /* ReadWriteMode went to zero: we are going to load the new count */
259 if (*ReadWriteMode
== 0x00)
261 if (PitChannels
[Channel
].CountRegister
== 0x0000)
263 /* Wrap around to the highest count */
264 if (PitChannels
[Channel
].Bcd
)
265 PitChannels
[Channel
].CountRegister
= 9999;
267 PitChannels
[Channel
].CountRegister
= 0xFFFF; // 0x10000; // 65536
270 /* Convert the current value from BCD if needed */
271 PitChannels
[Channel
].CountRegister
=
272 WRITE_PIT_VALUE(PitChannels
[Channel
], PitChannels
[Channel
].CountRegister
);
273 PitChannels
[Channel
].ReloadValue
= PitChannels
[Channel
].CountRegister
;
277 static BYTE WINAPI
PitReadPort(ULONG Port
)
281 case PIT_DATA_PORT(0):
282 case PIT_DATA_PORT(1):
283 case PIT_DATA_PORT(2):
285 return PitReadData(Port
- PIT_DATA_PORT(0));
292 static VOID WINAPI
PitWritePort(ULONG Port
, BYTE Data
)
296 case PIT_COMMAND_PORT
:
298 PitWriteCommand(Data
);
302 case PIT_DATA_PORT(0):
303 case PIT_DATA_PORT(1):
304 case PIT_DATA_PORT(2):
306 PitWriteData(Port
- PIT_DATA_PORT(0), Data
);
312 static VOID
PitDecrementCount(PPIT_CHANNEL Channel
, DWORD Count
)
314 if (Count
== 0) return;
316 switch (Channel
->Mode
)
318 case PIT_MODE_INT_ON_TERMINAL_COUNT
:
320 /* Decrement the value */
321 if (Count
> Channel
->CurrentValue
)
323 /* The value does not reload in this case */
324 Channel
->CurrentValue
= 0;
326 else Channel
->CurrentValue
-= Count
;
328 /* Did it fall to the terminal count? */
329 if (Channel
->CurrentValue
== 0 && !Channel
->Out
)
331 /* Yes, raise the output line */
332 PitSetOut(Channel
, TRUE
);
337 case PIT_MODE_RATE_GENERATOR
:
339 BOOLEAN Reloaded
= FALSE
;
343 if ((Count
> Channel
->CurrentValue
)
344 && (Channel
->CurrentValue
!= 0))
346 /* Decrement the count */
347 Count
-= Channel
->CurrentValue
;
349 /* Reload the value */
350 Channel
->CurrentValue
= Channel
->ReloadValue
;
357 /* Decrement the value */
358 Channel
->CurrentValue
-= Count
;
360 /* Clear the count */
363 /* Did it fall to zero? */
364 if (Channel
->CurrentValue
== 0)
366 Channel
->CurrentValue
= Channel
->ReloadValue
;
372 /* If there was a reload, raise the output line */
373 if (Reloaded
) PitSetOut(Channel
, TRUE
);
378 case PIT_MODE_SQUARE_WAVE
:
381 WORD ReloadValue
= Channel
->ReloadValue
;
383 /* The reload value must be even */
388 if (((Count
* 2) > Channel
->CurrentValue
)
389 && (Channel
->CurrentValue
!= 0))
391 /* Decrement the count */
392 Count
-= Channel
->CurrentValue
/ 2;
394 /* Reload the value */
395 Channel
->CurrentValue
= ReloadValue
;
397 /* Increment the reload count */
402 /* Decrement the value */
403 Channel
->CurrentValue
-= Count
* 2;
405 /* Clear the count */
408 /* Did it fall to zero? */
409 if (Channel
->CurrentValue
== 0)
411 /* Reload the value */
412 Channel
->CurrentValue
= ReloadValue
;
414 /* Increment the reload count */
420 if (ReloadCount
== 0) break;
422 /* Toggle the flip-flop if the number of reloads was odd */
425 Channel
->FlipFlop
= !Channel
->FlipFlop
;
426 // PitSetOut(Channel, !Channel->Out);
429 /* Was there any rising edge? */
430 if ((Channel
->FlipFlop
&& (ReloadCount
== 1)) || (ReloadCount
> 1))
432 /* Yes, raise the output line */
433 PitSetOut(Channel
, TRUE
);
439 case PIT_MODE_SOFTWARE_STROBE
:
441 // TODO: NOT IMPLEMENTED
445 case PIT_MODE_HARDWARE_ONE_SHOT
:
446 case PIT_MODE_HARDWARE_STROBE
:
448 /* These modes do not work on x86 PCs */
454 /* PUBLIC FUNCTIONS ***********************************************************/
456 VOID
PitSetOutFunction(BYTE Channel
, LPVOID Param
, PIT_OUT_FUNCTION OutFunction
)
458 if (Channel
>= PIT_CHANNELS
) return;
460 PitChannels
[Channel
].OutParam
= Param
;
461 PitChannels
[Channel
].OutFunction
= OutFunction
;
464 VOID
PitSetGate(BYTE Channel
, BOOLEAN State
)
466 if (Channel
>= PIT_CHANNELS
) return;
467 if (State
== PitChannels
[Channel
].Gate
) return;
470 PitChannels
[Channel
].Gate
= State
;
473 VOID
PitClock(DWORD Count
)
477 if (Count
== 0) return;
479 for (i
= 0; i
< PIT_CHANNELS
; i
++)
481 // if (!PitChannels[i].Counting) continue;
482 PitDecrementCount(&PitChannels
[i
], Count
);
486 DWORD
PitGetResolution(VOID
)
489 DWORD MinReloadValue
= 65536;
491 for (i
= 0; i
< PIT_CHANNELS
; i
++)
493 DWORD ReloadValue
= PitChannels
[i
].ReloadValue
;
496 if (ReloadValue
== 0) ReloadValue
= 65536;
498 if (ReloadValue
< MinReloadValue
) MinReloadValue
= ReloadValue
;
501 /* Return the frequency resolution */
502 return PIT_BASE_FREQUENCY
/ MinReloadValue
;
505 VOID
PitInitialize(VOID
)
507 /* Set up the timers to their default value */
508 PitSetOutFunction(0, NULL
, NULL
);
510 PitSetOutFunction(1, NULL
, NULL
);
512 PitSetOutFunction(2, NULL
, NULL
);
513 PitSetGate(2, FALSE
);
515 /* Register the I/O Ports */
516 RegisterIoPort(PIT_COMMAND_PORT
, NULL
, PitWritePort
);
517 RegisterIoPort(PIT_DATA_PORT(0), PitReadPort
, PitWritePort
);
518 RegisterIoPort(PIT_DATA_PORT(1), PitReadPort
, PitWritePort
);
519 RegisterIoPort(PIT_DATA_PORT(2), PitReadPort
, PitWritePort
);