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 *******************************************************************/
23 /* PRIVATE VARIABLES **********************************************************/
25 static PIT_CHANNEL PitChannels
[PIT_CHANNELS
];
26 static PHARDWARE_TIMER MasterClock
;
28 /* PRIVATE FUNCTIONS **********************************************************/
30 static VOID
PitLatchChannelStatus(BYTE Channel
)
32 if (Channel
>= PIT_CHANNELS
) return;
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.
40 if (PitChannels
[Channel
].LatchStatusSet
== FALSE
)
43 /** HACK!! **/BYTE NullCount
= 0;/** HACK!! **/
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);
50 /* Latch the counter's status */
51 PitChannels
[Channel
].LatchStatusSet
= TRUE
;
52 PitChannels
[Channel
].StatusLatch
= StatusLatch
;
56 static VOID
PitLatchChannelCount(BYTE Channel
)
58 if (Channel
>= PIT_CHANNELS
) return;
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.
66 if (PitChannels
[Channel
].ReadStatus
== 0x00)
68 /* Latch the counter's value */
69 PitChannels
[Channel
].ReadStatus
= PitChannels
[Channel
].ReadWriteMode
;
71 /* Convert the current value to BCD if needed */
72 PitChannels
[Channel
].OutputLatch
=
73 READ_PIT_VALUE(PitChannels
[Channel
], PitChannels
[Channel
].CurrentValue
);
77 static VOID
PitSetOut(PPIT_CHANNEL Channel
, BOOLEAN State
)
79 /** HACK!! **\ if (State == Channel->Out) return; \** HACK!! **/
81 /* Set the new state of the OUT pin */
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
);
89 static VOID
PitInitCounter(PPIT_CHANNEL Channel
)
91 switch (Channel
->Mode
)
93 case PIT_MODE_INT_ON_TERMINAL_COUNT
:
94 PitSetOut(Channel
, FALSE
);
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
);
107 static VOID
PitWriteCommand(BYTE Value
)
109 BYTE Channel
= (Value
>> 6) & 0x03;
110 BYTE ReadWriteMode
= (Value
>> 4) & 0x03;
111 BYTE Mode
= (Value
>> 1) & 0x07;
112 BOOLEAN IsBcd
= Value
& 0x01;
115 * Check for valid PIT channel - Possible values: 0, 1, 2.
116 * A value of 3 is for Read-Back Command.
118 if (Channel
> PIT_CHANNELS
) return;
120 /* Read-Back Command */
121 if (Channel
== PIT_CHANNELS
)
123 if ((Value
& 0x20) == 0) // Bit 5 (Count) == 0: We latch multiple counters' counts
125 if (Value
& 0x02) PitLatchChannelCount(0);
126 if (Value
& 0x04) PitLatchChannelCount(1);
127 if (Value
& 0x08) PitLatchChannelCount(2);
129 if ((Value
& 0x10) == 0) // Bit 4 (Status) == 0: We latch multiple counters' statuses
131 if (Value
& 0x02) PitLatchChannelStatus(0);
132 if (Value
& 0x04) PitLatchChannelStatus(1);
133 if (Value
& 0x08) PitLatchChannelStatus(2);
138 /* Check if this is a counter latch command... */
139 if (ReadWriteMode
== 0)
141 PitLatchChannelCount(Channel
);
145 /* ... otherwise, set the modes and reset flip-flops */
146 PitChannels
[Channel
].ReadWriteMode
= ReadWriteMode
;
147 PitChannels
[Channel
].ReadStatus
= 0x00;
148 PitChannels
[Channel
].WriteStatus
= 0x00;
150 PitChannels
[Channel
].LatchStatusSet
= FALSE
;
151 PitChannels
[Channel
].StatusLatch
= 0x00;
153 PitChannels
[Channel
].CountRegister
= 0x00;
154 PitChannels
[Channel
].OutputLatch
= 0x00;
156 /** HACK!! **/PitChannels
[Channel
].FlipFlop
= FALSE
;/** HACK!! **/
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;
172 PitChannels
[Channel
].Mode
= Mode
;
180 * Modes 6 and 7 become PIT_MODE_RATE_GENERATOR
181 * and PIT_MODE_SQUARE_WAVE respectively.
183 PitChannels
[Channel
].Mode
= Mode
- 4;
188 PitInitCounter(&PitChannels
[Channel
]);
191 static BYTE
PitReadData(BYTE Channel
)
193 LPBYTE ReadWriteMode
= NULL
;
194 LPWORD CurrentValue
= NULL
;
197 * If the status was latched, the first read operation will return the
198 * latched status, whichever value (count or status) was latched first.
200 if (PitChannels
[Channel
].LatchStatusSet
)
202 PitChannels
[Channel
].LatchStatusSet
= FALSE
;
203 return PitChannels
[Channel
].StatusLatch
;
206 /* To be able to read the count asynchronously, latch it first if needed */
207 if (PitChannels
[Channel
].ReadStatus
== 0) PitLatchChannelCount(Channel
);
209 /* The count is now latched */
210 ASSERT(PitChannels
[Channel
].ReadStatus
!= 0);
212 ReadWriteMode
= &PitChannels
[Channel
].ReadStatus
;
213 CurrentValue
= &PitChannels
[Channel
].OutputLatch
;
215 if (*ReadWriteMode
& 1)
218 *ReadWriteMode
&= ~1;
219 return LOBYTE(*CurrentValue
);
222 if (*ReadWriteMode
& 2)
225 *ReadWriteMode
&= ~2;
226 return HIBYTE(*CurrentValue
);
229 /* Shouldn't get here */
234 static VOID
PitWriteData(BYTE Channel
, BYTE Value
)
236 LPBYTE ReadWriteMode
= NULL
;
238 if (PitChannels
[Channel
].WriteStatus
== 0x00)
240 PitChannels
[Channel
].WriteStatus
= PitChannels
[Channel
].ReadWriteMode
;
243 ASSERT(PitChannels
[Channel
].WriteStatus
!= 0);
245 ReadWriteMode
= &PitChannels
[Channel
].WriteStatus
;
247 if (*ReadWriteMode
& 1)
250 *ReadWriteMode
&= ~1;
251 PitChannels
[Channel
].CountRegister
&= 0xFF00;
252 PitChannels
[Channel
].CountRegister
|= Value
;
254 else if (*ReadWriteMode
& 2)
257 *ReadWriteMode
&= ~2;
258 PitChannels
[Channel
].CountRegister
&= 0x00FF;
259 PitChannels
[Channel
].CountRegister
|= Value
<< 8;
262 /* ReadWriteMode went to zero: we are going to load the new count */
263 if (*ReadWriteMode
== 0x00)
265 if (PitChannels
[Channel
].CountRegister
== 0x0000)
267 /* Wrap around to the highest count */
268 if (PitChannels
[Channel
].Bcd
)
269 PitChannels
[Channel
].CountRegister
= 9999;
271 PitChannels
[Channel
].CountRegister
= 0xFFFF; // 0x10000; // 65536
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
;
281 static BYTE WINAPI
PitReadPort(USHORT Port
)
285 case PIT_DATA_PORT(0):
286 case PIT_DATA_PORT(1):
287 case PIT_DATA_PORT(2):
289 return PitReadData(Port
- PIT_DATA_PORT(0));
296 static VOID WINAPI
PitWritePort(USHORT Port
, BYTE Data
)
300 case PIT_COMMAND_PORT
:
302 PitWriteCommand(Data
);
306 case PIT_DATA_PORT(0):
307 case PIT_DATA_PORT(1):
308 case PIT_DATA_PORT(2):
310 PitWriteData(Port
- PIT_DATA_PORT(0), Data
);
316 static VOID
PitDecrementCount(PPIT_CHANNEL Channel
, DWORD Count
)
318 if (Count
== 0) return;
320 switch (Channel
->Mode
)
322 case PIT_MODE_INT_ON_TERMINAL_COUNT
:
324 /* Decrement the value */
325 if (Count
> Channel
->CurrentValue
)
327 /* The value does not reload in this case */
328 Channel
->CurrentValue
= 0;
330 else Channel
->CurrentValue
-= Count
;
332 /* Did it fall to the terminal count? */
333 if (Channel
->CurrentValue
== 0 && !Channel
->Out
)
335 /* Yes, raise the output line */
336 PitSetOut(Channel
, TRUE
);
341 case PIT_MODE_RATE_GENERATOR
:
343 BOOLEAN Reloaded
= FALSE
;
347 if ((Count
> Channel
->CurrentValue
)
348 && (Channel
->CurrentValue
!= 0))
350 /* Decrement the count */
351 Count
-= Channel
->CurrentValue
;
353 /* Reload the value */
354 Channel
->CurrentValue
= Channel
->ReloadValue
;
361 /* Decrement the value */
362 Channel
->CurrentValue
-= Count
;
364 /* Clear the count */
367 /* Did it fall to zero? */
368 if (Channel
->CurrentValue
== 0)
370 Channel
->CurrentValue
= Channel
->ReloadValue
;
376 /* If there was a reload, raise the output line */
377 if (Reloaded
) PitSetOut(Channel
, TRUE
);
382 case PIT_MODE_SQUARE_WAVE
:
385 WORD ReloadValue
= Channel
->ReloadValue
;
387 /* The reload value must be even */
392 if (((Count
* 2) > Channel
->CurrentValue
)
393 && (Channel
->CurrentValue
!= 0))
395 /* Decrement the count */
396 Count
-= Channel
->CurrentValue
/ 2;
398 /* Reload the value */
399 Channel
->CurrentValue
= ReloadValue
;
401 /* Increment the reload count */
406 /* Decrement the value */
407 Channel
->CurrentValue
-= Count
* 2;
409 /* Clear the count */
412 /* Did it fall to zero? */
413 if (Channel
->CurrentValue
== 0)
415 /* Reload the value */
416 Channel
->CurrentValue
= ReloadValue
;
418 /* Increment the reload count */
424 if (ReloadCount
== 0) break;
426 /* Toggle the flip-flop if the number of reloads was odd */
429 Channel
->FlipFlop
= !Channel
->FlipFlop
;
430 PitSetOut(Channel
, !Channel
->Out
);
433 /* Was there any rising edge? */
434 if ((Channel
->FlipFlop
&& (ReloadCount
== 1)) || (ReloadCount
> 1))
436 /* Yes, raise the output line */
437 PitSetOut(Channel
, TRUE
);
443 case PIT_MODE_SOFTWARE_STROBE
:
445 // TODO: NOT IMPLEMENTED
449 case PIT_MODE_HARDWARE_ONE_SHOT
:
450 case PIT_MODE_HARDWARE_STROBE
:
452 /* These modes do not work on x86 PCs */
458 static VOID FASTCALL
PitClock(ULONGLONG Count
)
462 for (i
= 0; i
< PIT_CHANNELS
; i
++)
464 // if (!PitChannels[i].Counting) continue;
465 PitDecrementCount(&PitChannels
[i
], Count
);
469 /* PUBLIC FUNCTIONS ***********************************************************/
471 VOID
PitSetOutFunction(BYTE Channel
, LPVOID Param
, PIT_OUT_FUNCTION OutFunction
)
473 if (Channel
>= PIT_CHANNELS
) return;
475 PitChannels
[Channel
].OutParam
= Param
;
476 PitChannels
[Channel
].OutFunction
= OutFunction
;
479 VOID
PitSetGate(BYTE Channel
, BOOLEAN State
)
481 if (Channel
>= PIT_CHANNELS
) return;
482 if (State
== PitChannels
[Channel
].Gate
) return;
485 PitChannels
[Channel
].Gate
= State
;
488 WORD
PitGetReloadValue(BYTE Channel
)
490 if (Channel
>= PIT_CHANNELS
) return 0xFFFF;
492 if (PitChannels
[Channel
].ReloadValue
== 0)
495 return PitChannels
[Channel
].ReloadValue
;
498 VOID
PitInitialize(VOID
)
500 /* Set up the timers to their default value */
501 PitSetOutFunction(0, NULL
, NULL
);
503 PitSetOutFunction(1, NULL
, NULL
);
505 PitSetOutFunction(2, NULL
, NULL
);
506 PitSetGate(2, FALSE
);
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
);
514 /* Register the hardware timer */
515 MasterClock
= CreateHardwareTimer(HARDWARE_TIMER_ENABLED
| HARDWARE_TIMER_PRECISE
,
516 HZ_TO_NS(PIT_BASE_FREQUENCY
),