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