3f813fc3cbc57efc0dc489075109a084d4c160a3
[reactos.git] / subsystems / ntvdm / 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 /* PUBLIC FUNCTIONS ***********************************************************/
24
25 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 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 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 VOID PitDecrementCount(DWORD Count)
188 {
189 INT i;
190
191 for (i = 0; i < PIT_CHANNELS; i++)
192 {
193 switch (PitChannels[i].Mode)
194 {
195 case PIT_MODE_INT_ON_TERMINAL_COUNT:
196 {
197 /* Decrement the value */
198 if (Count > PitChannels[i].CurrentValue)
199 {
200 /* The value does not reload in this case */
201 PitChannels[i].CurrentValue = 0;
202 }
203 else PitChannels[i].CurrentValue -= Count;
204
205 /* Did it fall to the terminal count? */
206 if (PitChannels[i].CurrentValue == 0 && !PitChannels[i].Pulsed)
207 {
208 /* Yes, raise the output line */
209 if (i == 0) PicInterruptRequest(0);
210 PitChannels[i].Pulsed = TRUE;
211 }
212 break;
213 }
214
215 case PIT_MODE_RATE_GENERATOR:
216 {
217 BOOLEAN Reloaded = FALSE;
218
219 while (Count)
220 {
221 if ((Count > PitChannels[i].CurrentValue)
222 && (PitChannels[i].CurrentValue != 0))
223 {
224 /* Decrease the count */
225 Count -= PitChannels[i].CurrentValue;
226
227 /* Reload the value */
228 PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
229
230 /* Set the flag */
231 Reloaded = TRUE;
232 }
233 else
234 {
235 /* Decrease the value */
236 PitChannels[i].CurrentValue -= Count;
237
238 /* Clear the count */
239 Count = 0;
240
241 /* Did it fall to zero? */
242 if (PitChannels[i].CurrentValue == 0)
243 {
244 PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
245 Reloaded = TRUE;
246 }
247 }
248 }
249
250 /* If there was a reload on channel 0, raise IRQ 0 */
251 if ((i == 0) && Reloaded) PicInterruptRequest(0);
252
253 break;
254 }
255
256 case PIT_MODE_SQUARE_WAVE:
257 {
258 INT ReloadCount = 0;
259 WORD ReloadValue = PitChannels[i].ReloadValue;
260
261 /* The reload value must be even */
262 ReloadValue &= ~1;
263
264 while (Count)
265 {
266 if (((Count * 2) > PitChannels[i].CurrentValue)
267 && (PitChannels[i].CurrentValue != 0))
268 {
269 /* Decrease the count */
270 Count -= PitChannels[i].CurrentValue / 2;
271
272 /* Reload the value */
273 PitChannels[i].CurrentValue = ReloadValue;
274
275 /* Increment the reload count */
276 ReloadCount++;
277 }
278 else
279 {
280 /* Decrease the value */
281 PitChannels[i].CurrentValue -= Count * 2;
282
283 /* Clear the count */
284 Count = 0;
285
286 /* Did it fall to zero? */
287 if (PitChannels[i].CurrentValue == 0)
288 {
289 /* Reload the value */
290 PitChannels[i].CurrentValue = ReloadValue;
291
292 /* Increment the reload count */
293 ReloadCount++;
294 }
295 }
296 }
297
298 if (ReloadCount == 0) break;
299
300 /* Toggle the flip-flop if the number of reloads was odd */
301 if (ReloadCount & 1)
302 {
303 PitChannels[i].OutputFlipFlop = !PitChannels[i].OutputFlipFlop;
304 }
305
306 /* Was there any rising edge on channel 0 ? */
307 if (((PitChannels[i].OutputFlipFlop && (ReloadCount == 1))
308 || (ReloadCount > 1))
309 && (i == 0))
310 {
311 /* Yes, IRQ 0 */
312 PicInterruptRequest(0);
313 }
314
315 break;
316 }
317
318 case PIT_MODE_SOFTWARE_STROBE:
319 {
320 // TODO: NOT IMPLEMENTED
321 break;
322 }
323
324 case PIT_MODE_HARDWARE_ONE_SHOT:
325 case PIT_MODE_HARDWARE_STROBE:
326 {
327 /* These modes do not work on x86 PCs */
328 break;
329 }
330 }
331 }
332 }
333
334 DWORD PitGetResolution(VOID)
335 {
336 INT i;
337 DWORD MinReloadValue = 65536;
338
339 for (i = 0; i < PIT_CHANNELS; i++)
340 {
341 DWORD ReloadValue = PitChannels[i].ReloadValue;
342
343 /* 0 means 65536 */
344 if (ReloadValue == 0) ReloadValue = 65536;
345
346 if (ReloadValue < MinReloadValue) MinReloadValue = ReloadValue;
347 }
348
349 /* Return the frequency resolution */
350 return PIT_BASE_FREQUENCY / MinReloadValue;
351 }
352
353 BOOLEAN PitInitialize(VOID)
354 {
355 /* Register the I/O Ports */
356 RegisterIoPort(PIT_COMMAND_PORT, NULL , PitWritePort);
357 RegisterIoPort(PIT_DATA_PORT(0), PitReadPort, PitWritePort);
358 RegisterIoPort(PIT_DATA_PORT(1), PitReadPort, PitWritePort);
359 RegisterIoPort(PIT_DATA_PORT(2), PitReadPort, PitWritePort);
360
361 return TRUE;
362 }
363
364 /* EOF */