ReactOS  0.4.14-dev-608-gd495a4f
pit.c
Go to the documentation of this file.
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 
29 
30 /* PRIVATE FUNCTIONS **********************************************************/
31 
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;BYTE NullCount = 0;
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 
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 
80 {
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 
92 {
93  switch (Channel->Mode)
94  {
96  PitSetOut(Channel, FALSE);
97  break;
98 
104  PitSetOut(Channel, TRUE);
105  break;
106  }
107 }
108 
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 */
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 PitChannels[Channel].FlipFlop = FALSE;
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 
193 static BYTE PitReadData(BYTE Channel)
194 {
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 
236 static VOID PitWriteData(BYTE Channel, BYTE Value)
237 {
239 
240  if (PitChannels[Channel].WriteStatus == 0x00)
241  {
242  PitChannels[Channel].WriteStatus = PitChannels[Channel].ReadWriteMode;
243  }
244 
245  ASSERT(PitChannels[Channel].WriteStatus != 0);
246 
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 
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 
302 {
303  switch (Port)
304  {
305  case PIT_COMMAND_PORT:
306  {
308  break;
309  }
310 
311  case PIT_DATA_PORT(0):
312  case PIT_DATA_PORT(1):
313  case PIT_DATA_PORT(2):
314  {
316  break;
317  }
318  }
319 }
320 
322 {
323  if (Count == 0) return;
324 
325  switch (Channel->Mode)
326  {
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 
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 
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 
449  {
450  // TODO: NOT IMPLEMENTED
451  break;
452  }
453 
456  {
457  /* These modes do not work on x86 PCs */
458  break;
459  }
460  }
461 }
462 
464 {
465  UCHAR i;
466 
467  for (i = 0; i < PIT_CHANNELS; i++)
468  {
469  // if (!PitChannels[i].Counting) continue;
471  }
472 }
473 
474 /* PUBLIC FUNCTIONS ***********************************************************/
475 
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 
485 {
486  if (Channel >= PIT_CHANNELS) return;
487  if (State == PitChannels[Channel].Gate) return;
488 
489  /* UNIMPLEMENTED */
490  PitChannels[Channel].Gate = State;
491 }
492 
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 
504 {
505  /* Set up the timers to their default value */
507  PitSetGate(0, TRUE);
509  PitSetGate(1, TRUE);
511  PitSetGate(2, FALSE);
512 
513  /* Register the I/O Ports */
518 
519  /* Register the hardware timer */
522  PitClock);
523 }
524 
525 /* EOF */
BOOLEAN Gate
Definition: pit.h:55
BOOLEAN LatchStatusSet
Definition: pit.h:51
_In_opt_ ULONG _Out_ PULONG Value
Definition: rtlfuncs.h:2343
BYTE ReadWriteMode
Definition: pit.h:44
CPPORT Port[4]
Definition: headless.c:34
static BYTE PitReadData(BYTE Channel)
Definition: pit.c:193
#define TRUE
Definition: types.h:120
static PIT_CHANNEL PitChannels[PIT_CHANNELS]
Definition: pit.c:27
BYTE ReadStatus
Definition: pit.h:47
static VOID PitWriteData(BYTE Channel, BYTE Value)
Definition: pit.c:236
PIT_OUT_FUNCTION OutFunction
Definition: pit.h:68
static VOID WINAPI PitWritePort(USHORT Port, BYTE Data)
Definition: pit.c:301
_In_ ULONG Mode
Definition: hubbusif.h:303
#define LOBYTE(W)
Definition: jmemdos.c:487
#define HIBYTE(W)
Definition: jmemdos.c:486
VOID(WINAPI * PIT_OUT_FUNCTION)(LPVOID Param, BOOLEAN State)
Definition: pit.h:37
PIT_MODE Mode
Definition: pit.h:42
_Inout_ __drv_aliasesMem PSLIST_ENTRY _Inout_ PSLIST_ENTRY _In_ ULONG Count
Definition: exfuncs.h:1015
#define READ_PIT_VALUE(PitChannel, Value)
Definition: pit.h:24
BYTE StatusLatch
Definition: pit.h:52
WORD ReloadValue
Definition: pit.h:61
#define FASTCALL
Definition: nt_native.h:50
int32_t INT
Definition: typedefs.h:56
VOID PitSetOutFunction(BYTE Channel, LPVOID Param, PIT_OUT_FUNCTION OutFunction)
Definition: pit.c:476
VOID RegisterIoPort(USHORT Port, EMULATOR_INB_PROC InHandler, EMULATOR_OUTB_PROC OutHandler)
Definition: io.c:320
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
static VOID PitLatchChannelStatus(BYTE Channel)
Definition: pit.c:32
unsigned char * LPBYTE
Definition: typedefs.h:52
static BYTE WINAPI PitReadPort(USHORT Port)
Definition: pit.c:286
WORD CurrentValue
Definition: pit.h:62
unsigned char BOOLEAN
PHARDWARE_TIMER CreateHardwareTimer(ULONG Flags, ULONGLONG Delay, PHARDWARE_TIMER_PROC Callback)
Definition: clock.c:144
LPVOID OutParam
Definition: pit.h:67
smooth NULL
Definition: ftsmooth.c:416
#define PIT_COMMAND_PORT
Definition: pit.h:19
BOOLEAN FlipFlop
Definition: pit.h:66
WORD PitGetReloadValue(BYTE Channel)
Definition: pit.c:493
static VOID NTAPI ReadWriteMode(IN UCHAR Mode)
Definition: vga.c:77
uint64_t ULONGLONG
Definition: typedefs.h:65
#define WRITE_PIT_VALUE(PitChannel, Value)
Definition: pit.h:21
#define PIT_CHANNELS
Definition: pit.h:16
#define HARDWARE_TIMER_PRECISE
Definition: clock.h:17
#define WINAPI
Definition: msvc.h:6
#define HARDWARE_TIMER_ENABLED
Definition: clock.h:15
unsigned short WORD
Definition: ntddk_ex.h:93
static VOID FASTCALL PitClock(ULONGLONG Count)
Definition: pit.c:463
unsigned long DWORD
Definition: ntddk_ex.h:95
static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count)
Definition: pit.c:321
#define PIT_BASE_FREQUENCY
Definition: pit.h:17
ASSERT((InvokeOnSuccess||InvokeOnError||InvokeOnCancel) ?(CompletionRoutine !=NULL) :TRUE)
BYTE WriteStatus
Definition: pit.h:48
unsigned char UCHAR
Definition: xmlstorage.h:181
unsigned char BYTE
Definition: mem.h:68
WORD OutputLatch
Definition: pit.h:58
WORD CountRegister
Definition: pit.h:57
uint16_t * LPWORD
Definition: typedefs.h:54
static VOID PitInitCounter(PPIT_CHANNEL Channel)
Definition: pit.c:91
enum State_ State
Definition: pofuncs.h:54
unsigned short USHORT
Definition: pedump.c:61
#define HZ_TO_NS(Freq)
Definition: clock.h:20
VOID PitInitialize(VOID)
Definition: pit.c:503
#define PIT_DATA_PORT(x)
Definition: pit.h:18
static USHORT ReadStatus(PUCHAR ReadDataPort)
Definition: hardware.c:116
BOOLEAN Out
Definition: pit.h:65
static VOID PitWriteCommand(BYTE Value)
Definition: pit.c:109
BOOLEAN Bcd
Definition: pit.h:43
static PHARDWARE_TIMER MasterClock
Definition: pit.c:28
static VOID PitLatchChannelCount(BYTE Channel)
Definition: pit.c:58
VOID PitSetGate(BYTE Channel, BOOLEAN State)
Definition: pit.c:484
static VOID PitSetOut(PPIT_CHANNEL Channel, BOOLEAN State)
Definition: pit.c:79