[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()
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 PitChannels[i].CurrentValue--;
161
162 /* Did it fall to the terminal count? */
163 if (PitChannels[i].CurrentValue == 0 && !PitChannels[i].Pulsed)
164 {
165 /* Yes, raise the output line */
166 if (i == 0) PicInterruptRequest(0);
167 PitChannels[i].Pulsed = TRUE;
168 }
169 break;
170 }
171
172 case PIT_MODE_RATE_GENERATOR:
173 {
174 /* Decrement the value */
175 PitChannels[i].CurrentValue--;
176
177 /* Did it fall to zero? */
178 if (PitChannels[i].CurrentValue != 0) break;
179
180 /* Yes, raise the output line and reload */
181 if (i == 0) PicInterruptRequest(0);
182 PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
183
184 break;
185 }
186
187 case PIT_MODE_SQUARE_WAVE:
188 {
189 /* Decrement the value by 2 */
190 PitChannels[i].CurrentValue -= 2;
191
192 /* Did it fall to zero? */
193 if (PitChannels[i].CurrentValue != 0) break;
194
195 /* Yes, toggle the flip-flop */
196 PitChannels[i].OutputFlipFlop = !PitChannels[i].OutputFlipFlop;
197
198 /* Did this create a rising edge in the signal? */
199 if (PitChannels[i].OutputFlipFlop)
200 {
201 /* Yes, IRQ 0 if this is channel 0 */
202 if (i == 0) PicInterruptRequest(0);
203 }
204
205 /* Reload the value, but make sure it's even */
206 if (PitChannels[i].ReloadValue % 2)
207 {
208 /* It's odd, reduce it by 1 */
209 PitChannels[i].CurrentValue = PitChannels[i].ReloadValue - 1;
210 }
211 else
212 {
213 /* It was even */
214 PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
215 }
216
217 break;
218 }
219
220 case PIT_MODE_SOFTWARE_STROBE:
221 {
222 // TODO: NOT IMPLEMENTED
223 break;
224 }
225
226 case PIT_MODE_HARDWARE_ONE_SHOT:
227 case PIT_MODE_HARDWARE_STROBE:
228 {
229 /* These modes do not work on x86 PCs */
230 break;
231 }
232 }
233 }
234 }
235
236 DWORD PitGetResolution(VOID)
237 {
238 INT i;
239 DWORD MinReloadValue = 65536;
240
241 for (i = 0; i < PIT_CHANNELS; i++)
242 {
243 DWORD ReloadValue = PitChannels[i].ReloadValue;
244
245 /* 0 means 65536 */
246 if (ReloadValue == 0) ReloadValue = 65536;
247
248 if (ReloadValue < MinReloadValue) MinReloadValue = ReloadValue;
249 }
250
251 /* Return the frequency resolution */
252 return PIT_BASE_FREQUENCY / MinReloadValue;
253 }
254
255 /* EOF */