b46dcb406061d8ccde168dcfc8d2c07a963f5556
[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 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "emulator.h"
14 #include "io.h"
15 #include "timer.h"
16 #include "pic.h"
17
18 /* PRIVATE VARIABLES **********************************************************/
19
20 static PIT_CHANNEL PitChannels[PIT_CHANNELS];
21 PPIT_CHANNEL PitChannel2 = &PitChannels[2];
22
23 /* PRIVATE FUNCTIONS **********************************************************/
24
25 static VOID PitWriteCommand(BYTE Value)
26 {
27 BYTE Channel = Value >> 6;
28 BYTE Mode = (Value >> 1) & 0x07;
29
30 /* Check if this is a counter latch command */
31 if (((Value >> 4) & 3) == 0)
32 {
33 PitChannels[Channel].LatchSet = TRUE;
34 PitChannels[Channel].LatchedValue = PitChannels[Channel].CurrentValue;
35 return;
36 }
37
38 /* Set the access mode and reset flip-flops */
39 PitChannels[Channel].AccessMode = (Value >> 4) & 3;
40 PitChannels[Channel].Pulsed = FALSE;
41 PitChannels[Channel].LatchSet = FALSE;
42 PitChannels[Channel].InputFlipFlop = FALSE;
43 PitChannels[Channel].OutputFlipFlop = FALSE;
44
45 switch (Mode)
46 {
47 case 0:
48 case 1:
49 case 2:
50 case 3:
51 case 4:
52 case 5:
53 {
54 PitChannels[Channel].Mode = Mode;
55 break;
56 }
57
58 case 6:
59 {
60 PitChannels[Channel].Mode = PIT_MODE_RATE_GENERATOR;
61 break;
62 }
63
64 case 7:
65 {
66 PitChannels[Channel].Mode = PIT_MODE_SQUARE_WAVE;
67 break;
68 }
69 }
70 }
71
72 static BYTE PitReadData(BYTE Channel)
73 {
74 WORD CurrentValue = PitChannels[Channel].CurrentValue;
75 BYTE AccessMode = PitChannels[Channel].AccessMode;
76
77 /* Check if the value was latched */
78 if (PitChannels[Channel].LatchSet)
79 {
80 CurrentValue = PitChannels[Channel].LatchedValue;
81
82 if (AccessMode == 1 || AccessMode == 2)
83 {
84 /* The latched value was read as one byte */
85 PitChannels[Channel].LatchSet = FALSE;
86 }
87 }
88
89 /* Use the flip-flop for access mode 3 */
90 if (AccessMode == 3)
91 {
92 AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2;
93 PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop;
94
95 /* Check if this was the last read for the latched value */
96 if (!PitChannels[Channel].InputFlipFlop)
97 {
98 /* Yes, the latch value was read as two bytes */
99 PitChannels[Channel].LatchSet = FALSE;
100 }
101 }
102
103 switch (AccessMode)
104 {
105 case 1:
106 {
107 /* Low byte */
108 return CurrentValue & 0x00FF;
109 }
110
111 case 2:
112 {
113 /* High byte */
114 return CurrentValue >> 8;
115 }
116 }
117
118 /* Shouldn't get here */
119 return 0;
120 }
121
122 static VOID PitWriteData(BYTE Channel, BYTE Value)
123 {
124 BYTE AccessMode = PitChannels[Channel].AccessMode;
125
126 /* Use the flip-flop for access mode 3 */
127 if (PitChannels[Channel].AccessMode == 3)
128 {
129 AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2;
130 PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop;
131 }
132
133 switch (AccessMode)
134 {
135 case 1:
136 {
137 /* Low byte */
138 PitChannels[Channel].ReloadValue &= 0xFF00;
139 PitChannels[Channel].ReloadValue |= Value;
140 break;
141 }
142
143 case 2:
144 {
145 /* High byte */
146 PitChannels[Channel].ReloadValue &= 0x00FF;
147 PitChannels[Channel].ReloadValue |= Value << 8;
148 }
149 }
150 }
151
152 static BYTE WINAPI PitReadPort(ULONG Port)
153 {
154 switch (Port)
155 {
156 case PIT_DATA_PORT(0):
157 case PIT_DATA_PORT(1):
158 case PIT_DATA_PORT(2):
159 {
160 return PitReadData(Port - PIT_DATA_PORT(0));
161 }
162 }
163
164 return 0;
165 }
166
167 static VOID WINAPI PitWritePort(ULONG Port, BYTE Data)
168 {
169 switch (Port)
170 {
171 case PIT_COMMAND_PORT:
172 {
173 PitWriteCommand(Data);
174 break;
175 }
176
177 case PIT_DATA_PORT(0):
178 case PIT_DATA_PORT(1):
179 case PIT_DATA_PORT(2):
180 {
181 PitWriteData(Port - PIT_DATA_PORT(0), Data);
182 break;
183 }
184 }
185 }
186
187 /* PUBLIC FUNCTIONS ***********************************************************/
188
189 VOID PitDecrementCount(DWORD Count)
190 {
191 INT i;
192
193 for (i = 0; i < PIT_CHANNELS; i++)
194 {
195 switch (PitChannels[i].Mode)
196 {
197 case PIT_MODE_INT_ON_TERMINAL_COUNT:
198 {
199 /* Decrement the value */
200 if (Count > PitChannels[i].CurrentValue)
201 {
202 /* The value does not reload in this case */
203 PitChannels[i].CurrentValue = 0;
204 }
205 else PitChannels[i].CurrentValue -= Count;
206
207 /* Did it fall to the terminal count? */
208 if (PitChannels[i].CurrentValue == 0 && !PitChannels[i].Pulsed)
209 {
210 /* Yes, raise the output line */
211 if (i == 0) PicInterruptRequest(0);
212 PitChannels[i].Pulsed = TRUE;
213 }
214 break;
215 }
216
217 case PIT_MODE_RATE_GENERATOR:
218 {
219 BOOLEAN Reloaded = FALSE;
220
221 while (Count)
222 {
223 if ((Count > PitChannels[i].CurrentValue)
224 && (PitChannels[i].CurrentValue != 0))
225 {
226 /* Decrease the count */
227 Count -= PitChannels[i].CurrentValue;
228
229 /* Reload the value */
230 PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
231
232 /* Set the flag */
233 Reloaded = TRUE;
234 }
235 else
236 {
237 /* Decrease the value */
238 PitChannels[i].CurrentValue -= Count;
239
240 /* Clear the count */
241 Count = 0;
242
243 /* Did it fall to zero? */
244 if (PitChannels[i].CurrentValue == 0)
245 {
246 PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
247 Reloaded = TRUE;
248 }
249 }
250 }
251
252 /* If there was a reload on channel 0, raise IRQ 0 */
253 if ((i == 0) && Reloaded) PicInterruptRequest(0);
254
255 break;
256 }
257
258 case PIT_MODE_SQUARE_WAVE:
259 {
260 INT ReloadCount = 0;
261 WORD ReloadValue = PitChannels[i].ReloadValue;
262
263 /* The reload value must be even */
264 ReloadValue &= ~1;
265
266 while (Count)
267 {
268 if (((Count * 2) > PitChannels[i].CurrentValue)
269 && (PitChannels[i].CurrentValue != 0))
270 {
271 /* Decrease the count */
272 Count -= PitChannels[i].CurrentValue / 2;
273
274 /* Reload the value */
275 PitChannels[i].CurrentValue = ReloadValue;
276
277 /* Increment the reload count */
278 ReloadCount++;
279 }
280 else
281 {
282 /* Decrease the value */
283 PitChannels[i].CurrentValue -= Count * 2;
284
285 /* Clear the count */
286 Count = 0;
287
288 /* Did it fall to zero? */
289 if (PitChannels[i].CurrentValue == 0)
290 {
291 /* Reload the value */
292 PitChannels[i].CurrentValue = ReloadValue;
293
294 /* Increment the reload count */
295 ReloadCount++;
296 }
297 }
298 }
299
300 if (ReloadCount == 0) break;
301
302 /* Toggle the flip-flop if the number of reloads was odd */
303 if (ReloadCount & 1)
304 {
305 PitChannels[i].OutputFlipFlop = !PitChannels[i].OutputFlipFlop;
306 }
307
308 /* Was there any rising edge on channel 0 ? */
309 if (((PitChannels[i].OutputFlipFlop && (ReloadCount == 1))
310 || (ReloadCount > 1))
311 && (i == 0))
312 {
313 /* Yes, IRQ 0 */
314 PicInterruptRequest(0);
315 }
316
317 break;
318 }
319
320 case PIT_MODE_SOFTWARE_STROBE:
321 {
322 // TODO: NOT IMPLEMENTED
323 break;
324 }
325
326 case PIT_MODE_HARDWARE_ONE_SHOT:
327 case PIT_MODE_HARDWARE_STROBE:
328 {
329 /* These modes do not work on x86 PCs */
330 break;
331 }
332 }
333 }
334 }
335
336 DWORD PitGetResolution(VOID)
337 {
338 INT i;
339 DWORD MinReloadValue = 65536;
340
341 for (i = 0; i < PIT_CHANNELS; i++)
342 {
343 DWORD ReloadValue = PitChannels[i].ReloadValue;
344
345 /* 0 means 65536 */
346 if (ReloadValue == 0) ReloadValue = 65536;
347
348 if (ReloadValue < MinReloadValue) MinReloadValue = ReloadValue;
349 }
350
351 /* Return the frequency resolution */
352 return PIT_BASE_FREQUENCY / MinReloadValue;
353 }
354
355 VOID PitInitialize(VOID)
356 {
357 /* Register the I/O Ports */
358 RegisterIoPort(PIT_COMMAND_PORT, NULL , PitWritePort);
359 RegisterIoPort(PIT_DATA_PORT(0), PitReadPort, PitWritePort);
360 RegisterIoPort(PIT_DATA_PORT(1), PitReadPort, PitWritePort);
361 RegisterIoPort(PIT_DATA_PORT(2), PitReadPort, PitWritePort);
362 }
363
364 /* EOF */