[SHELL-EXPERIMENTS]
[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 /* Latch the counter's status */
48 PitChannels[Channel].LatchStatusSet = TRUE;
49 PitChannels[Channel].StatusLatch = StatusLatch;
50 }
51 }
52
53 static VOID PitLatchChannelCount(BYTE Channel)
54 {
55 if (Channel >= PIT_CHANNELS) return;
56
57 /*
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.
62 */
63 if (PitChannels[Channel].ReadStatus == 0x00)
64 {
65 /* Latch the counter's value */
66 PitChannels[Channel].ReadStatus = PitChannels[Channel].ReadWriteMode;
67
68 /* Convert the current value to BCD if needed */
69 PitChannels[Channel].OutputLatch =
70 READ_PIT_VALUE(PitChannels[Channel], PitChannels[Channel].CurrentValue);
71 }
72 }
73
74 static VOID PitSetOut(PPIT_CHANNEL Channel, BOOLEAN State)
75 {
76 /** HACK!! **\ if (State == Channel->Out) return; \** HACK!! **/
77
78 /* Set the new state of the OUT pin */
79 Channel->Out = State;
80
81 /* Call the callback */
82 if (Channel->OutFunction) Channel->OutFunction(Channel->OutParam, State);
83 }
84
85 static VOID PitInitCounter(PPIT_CHANNEL Channel)
86 {
87 switch (Channel->Mode)
88 {
89 case PIT_MODE_INT_ON_TERMINAL_COUNT:
90 PitSetOut(Channel, FALSE);
91 break;
92
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);
99 break;
100 }
101 }
102
103 static VOID PitWriteCommand(BYTE Value)
104 {
105 BYTE Channel = (Value >> 6) & 0x03;
106 BYTE ReadWriteMode = (Value >> 4) & 0x03;
107 BYTE Mode = (Value >> 1) & 0x07;
108 BOOLEAN IsBcd = Value & 0x01;
109
110 /*
111 * Check for valid PIT channel - Possible values: 0, 1, 2.
112 * A value of 3 is for Read-Back Command.
113 */
114 if (Channel > PIT_CHANNELS) return;
115
116 /* Read-Back Command */
117 if (Channel == PIT_CHANNELS)
118 {
119 if ((Value & 0x20) == 0) // Bit 5 (Count) == 0: We latch multiple counters' counts
120 {
121 if (Value & 0x02) PitLatchChannelCount(0);
122 if (Value & 0x04) PitLatchChannelCount(1);
123 if (Value & 0x08) PitLatchChannelCount(2);
124 }
125 if ((Value & 0x10) == 0) // Bit 4 (Status) == 0: We latch multiple counters' statuses
126 {
127 if (Value & 0x02) PitLatchChannelStatus(0);
128 if (Value & 0x04) PitLatchChannelStatus(1);
129 if (Value & 0x08) PitLatchChannelStatus(2);
130 }
131 return;
132 }
133
134 /* Check if this is a counter latch command... */
135 if (ReadWriteMode == 0)
136 {
137 PitLatchChannelCount(Channel);
138 return;
139 }
140
141 /* ... otherwise, set the modes and reset flip-flops */
142 PitChannels[Channel].ReadWriteMode = ReadWriteMode;
143 PitChannels[Channel].ReadStatus = 0x00;
144 PitChannels[Channel].WriteStatus = 0x00;
145
146 PitChannels[Channel].LatchStatusSet = FALSE;
147 PitChannels[Channel].StatusLatch = 0x00;
148
149 PitChannels[Channel].CountRegister = 0x00;
150 PitChannels[Channel].OutputLatch = 0x00;
151
152 /** HACK!! **/PitChannels[Channel].FlipFlop = FALSE;/** HACK!! **/
153
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;
158
159 switch (Mode)
160 {
161 case 0:
162 case 1:
163 case 2:
164 case 3:
165 case 4:
166 case 5:
167 {
168 PitChannels[Channel].Mode = Mode;
169 break;
170 }
171
172 case 6:
173 case 7:
174 {
175 /*
176 * Modes 6 and 7 become PIT_MODE_RATE_GENERATOR
177 * and PIT_MODE_SQUARE_WAVE respectively.
178 */
179 PitChannels[Channel].Mode = Mode - 4;
180 break;
181 }
182 }
183
184 PitInitCounter(&PitChannels[Channel]);
185 }
186
187 static BYTE PitReadData(BYTE Channel)
188 {
189 LPBYTE ReadWriteMode = NULL;
190 LPWORD CurrentValue = NULL;
191
192 /*
193 * If the status was latched, the first read operation will return the
194 * latched status, whichever value (count or status) was latched first.
195 */
196 if (PitChannels[Channel].LatchStatusSet)
197 {
198 PitChannels[Channel].LatchStatusSet = FALSE;
199 return PitChannels[Channel].StatusLatch;
200 }
201
202 /* To be able to read the count asynchronously, latch it first if needed */
203 if (PitChannels[Channel].ReadStatus == 0) PitLatchChannelCount(Channel);
204
205 /* The count is now latched */
206 ASSERT(PitChannels[Channel].ReadStatus != 0);
207
208 ReadWriteMode = &PitChannels[Channel].ReadStatus ;
209 CurrentValue = &PitChannels[Channel].OutputLatch;
210
211 if (*ReadWriteMode & 1)
212 {
213 /* Read LSB */
214 *ReadWriteMode &= ~1;
215 return LOBYTE(*CurrentValue);
216 }
217
218 if (*ReadWriteMode & 2)
219 {
220 /* Read MSB */
221 *ReadWriteMode &= ~2;
222 return HIBYTE(*CurrentValue);
223 }
224
225 /* Shouldn't get here */
226 ASSERT(FALSE);
227 return 0;
228 }
229
230 static VOID PitWriteData(BYTE Channel, BYTE Value)
231 {
232 LPBYTE ReadWriteMode = NULL;
233
234 if (PitChannels[Channel].WriteStatus == 0x00)
235 {
236 PitChannels[Channel].WriteStatus = PitChannels[Channel].ReadWriteMode;
237 }
238
239 ASSERT(PitChannels[Channel].WriteStatus != 0);
240
241 ReadWriteMode = &PitChannels[Channel].WriteStatus;
242
243 if (*ReadWriteMode & 1)
244 {
245 /* Write LSB */
246 *ReadWriteMode &= ~1;
247 PitChannels[Channel].CountRegister &= 0xFF00;
248 PitChannels[Channel].CountRegister |= Value;
249 }
250 else if (*ReadWriteMode & 2)
251 {
252 /* Write MSB */
253 *ReadWriteMode &= ~2;
254 PitChannels[Channel].CountRegister &= 0x00FF;
255 PitChannels[Channel].CountRegister |= Value << 8;
256 }
257
258 /* ReadWriteMode went to zero: we are going to load the new count */
259 if (*ReadWriteMode == 0x00)
260 {
261 if (PitChannels[Channel].CountRegister == 0x0000)
262 {
263 /* Wrap around to the highest count */
264 if (PitChannels[Channel].Bcd)
265 PitChannels[Channel].CountRegister = 9999;
266 else
267 PitChannels[Channel].CountRegister = 0xFFFF; // 0x10000; // 65536
268 }
269
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;
274 }
275 }
276
277 static BYTE WINAPI PitReadPort(ULONG Port)
278 {
279 switch (Port)
280 {
281 case PIT_DATA_PORT(0):
282 case PIT_DATA_PORT(1):
283 case PIT_DATA_PORT(2):
284 {
285 return PitReadData(Port - PIT_DATA_PORT(0));
286 }
287 }
288
289 return 0;
290 }
291
292 static VOID WINAPI PitWritePort(ULONG Port, BYTE Data)
293 {
294 switch (Port)
295 {
296 case PIT_COMMAND_PORT:
297 {
298 PitWriteCommand(Data);
299 break;
300 }
301
302 case PIT_DATA_PORT(0):
303 case PIT_DATA_PORT(1):
304 case PIT_DATA_PORT(2):
305 {
306 PitWriteData(Port - PIT_DATA_PORT(0), Data);
307 break;
308 }
309 }
310 }
311
312 static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count)
313 {
314 if (Count == 0) return;
315
316 switch (Channel->Mode)
317 {
318 case PIT_MODE_INT_ON_TERMINAL_COUNT:
319 {
320 /* Decrement the value */
321 if (Count > Channel->CurrentValue)
322 {
323 /* The value does not reload in this case */
324 Channel->CurrentValue = 0;
325 }
326 else Channel->CurrentValue -= Count;
327
328 /* Did it fall to the terminal count? */
329 if (Channel->CurrentValue == 0 && !Channel->Out)
330 {
331 /* Yes, raise the output line */
332 PitSetOut(Channel, TRUE);
333 }
334 break;
335 }
336
337 case PIT_MODE_RATE_GENERATOR:
338 {
339 BOOLEAN Reloaded = FALSE;
340
341 while (Count)
342 {
343 if ((Count > Channel->CurrentValue)
344 && (Channel->CurrentValue != 0))
345 {
346 /* Decrement the count */
347 Count -= Channel->CurrentValue;
348
349 /* Reload the value */
350 Channel->CurrentValue = Channel->ReloadValue;
351
352 /* Set the flag */
353 Reloaded = TRUE;
354 }
355 else
356 {
357 /* Decrement the value */
358 Channel->CurrentValue -= Count;
359
360 /* Clear the count */
361 Count = 0;
362
363 /* Did it fall to zero? */
364 if (Channel->CurrentValue == 0)
365 {
366 Channel->CurrentValue = Channel->ReloadValue;
367 Reloaded = TRUE;
368 }
369 }
370 }
371
372 /* If there was a reload, raise the output line */
373 if (Reloaded) PitSetOut(Channel, TRUE);
374
375 break;
376 }
377
378 case PIT_MODE_SQUARE_WAVE:
379 {
380 INT ReloadCount = 0;
381 WORD ReloadValue = Channel->ReloadValue;
382
383 /* The reload value must be even */
384 ReloadValue &= ~1;
385
386 while (Count)
387 {
388 if (((Count * 2) > Channel->CurrentValue)
389 && (Channel->CurrentValue != 0))
390 {
391 /* Decrement the count */
392 Count -= Channel->CurrentValue / 2;
393
394 /* Reload the value */
395 Channel->CurrentValue = ReloadValue;
396
397 /* Increment the reload count */
398 ReloadCount++;
399 }
400 else
401 {
402 /* Decrement the value */
403 Channel->CurrentValue -= Count * 2;
404
405 /* Clear the count */
406 Count = 0;
407
408 /* Did it fall to zero? */
409 if (Channel->CurrentValue == 0)
410 {
411 /* Reload the value */
412 Channel->CurrentValue = ReloadValue;
413
414 /* Increment the reload count */
415 ReloadCount++;
416 }
417 }
418 }
419
420 if (ReloadCount == 0) break;
421
422 /* Toggle the flip-flop if the number of reloads was odd */
423 if (ReloadCount & 1)
424 {
425 Channel->FlipFlop = !Channel->FlipFlop;
426 // PitSetOut(Channel, !Channel->Out);
427 }
428
429 /* Was there any rising edge? */
430 if ((Channel->FlipFlop && (ReloadCount == 1)) || (ReloadCount > 1))
431 {
432 /* Yes, raise the output line */
433 PitSetOut(Channel, TRUE);
434 }
435
436 break;
437 }
438
439 case PIT_MODE_SOFTWARE_STROBE:
440 {
441 // TODO: NOT IMPLEMENTED
442 break;
443 }
444
445 case PIT_MODE_HARDWARE_ONE_SHOT:
446 case PIT_MODE_HARDWARE_STROBE:
447 {
448 /* These modes do not work on x86 PCs */
449 break;
450 }
451 }
452 }
453
454 /* PUBLIC FUNCTIONS ***********************************************************/
455
456 VOID PitSetOutFunction(BYTE Channel, LPVOID Param, PIT_OUT_FUNCTION OutFunction)
457 {
458 if (Channel >= PIT_CHANNELS) return;
459
460 PitChannels[Channel].OutParam = Param;
461 PitChannels[Channel].OutFunction = OutFunction;
462 }
463
464 VOID PitSetGate(BYTE Channel, BOOLEAN State)
465 {
466 if (Channel >= PIT_CHANNELS) return;
467 if (State == PitChannels[Channel].Gate) return;
468
469 /* UNIMPLEMENTED */
470 PitChannels[Channel].Gate = State;
471 }
472
473 VOID PitClock(DWORD Count)
474 {
475 UINT i;
476
477 if (Count == 0) return;
478
479 for (i = 0; i < PIT_CHANNELS; i++)
480 {
481 // if (!PitChannels[i].Counting) continue;
482 PitDecrementCount(&PitChannels[i], Count);
483 }
484 }
485
486 DWORD PitGetResolution(VOID)
487 {
488 INT i;
489 DWORD MinReloadValue = 65536;
490
491 for (i = 0; i < PIT_CHANNELS; i++)
492 {
493 DWORD ReloadValue = PitChannels[i].ReloadValue;
494
495 /* 0 means 65536 */
496 if (ReloadValue == 0) ReloadValue = 65536;
497
498 if (ReloadValue < MinReloadValue) MinReloadValue = ReloadValue;
499 }
500
501 /* Return the frequency resolution */
502 return PIT_BASE_FREQUENCY / MinReloadValue;
503 }
504
505 VOID PitInitialize(VOID)
506 {
507 /* Set up the timers to their default value */
508 PitSetOutFunction(0, NULL, NULL);
509 PitSetGate(0, TRUE);
510 PitSetOutFunction(1, NULL, NULL);
511 PitSetGate(1, TRUE);
512 PitSetOutFunction(2, NULL, NULL);
513 PitSetGate(2, FALSE);
514
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);
520 }
521
522 /* EOF */