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