[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 #include "timer.h"
12 #include "pic.h"
13
14 /* PRIVATE VARIABLES **********************************************************/
15
16 static PIT_CHANNEL PitChannels[PIT_CHANNELS];
17
18 /* PUBLIC FUNCTIONS ***********************************************************/
19
20 VOID PitWriteCommand(BYTE Value)
21 {
22 BYTE Channel = Value >> 6;
23 BYTE Mode = (Value >> 1) & 0x07;
24
25 /* Check if this is a counter latch command */
26 if (((Value >> 4) & 3) == 0)
27 {
28 PitChannels[Channel].LatchSet = TRUE;
29 PitChannels[Channel].LatchedValue = PitChannels[Channel].CurrentValue;
30 return;
31 }
32
33 /* Set the access mode and reset flip-flops */
34 PitChannels[Channel].AccessMode = (Value >> 4) & 3;
35 PitChannels[Channel].Pulsed = FALSE;
36 PitChannels[Channel].LatchSet = FALSE;
37 PitChannels[Channel].InputFlipFlop = FALSE;
38 PitChannels[Channel].OutputFlipFlop = FALSE;
39
40 switch (Mode)
41 {
42 case 0:
43 case 1:
44 case 2:
45 case 3:
46 case 4:
47 case 5:
48 {
49 PitChannels[Channel].Mode = Mode;
50 break;
51 }
52
53 case 6:
54 {
55 PitChannels[Channel].Mode = PIT_MODE_RATE_GENERATOR;
56 break;
57 }
58
59 case 7:
60 {
61 PitChannels[Channel].Mode = PIT_MODE_SQUARE_WAVE;
62 break;
63 }
64 }
65 }
66
67 BYTE PitReadData(BYTE Channel)
68 {
69 WORD CurrentValue = PitChannels[Channel].CurrentValue;
70 BYTE AccessMode = PitChannels[Channel].AccessMode;
71
72 /* Check if the value was latched */
73 if (PitChannels[Channel].LatchSet)
74 {
75 CurrentValue = PitChannels[Channel].LatchedValue;
76
77 if (AccessMode == 1 || AccessMode == 2)
78 {
79 /* The latched value was read as one byte */
80 PitChannels[Channel].LatchSet = FALSE;
81 }
82 }
83
84 /* Use the flip-flop for access mode 3 */
85 if (AccessMode == 3)
86 {
87 AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2;
88 PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop;
89
90 /* Check if this was the last read for the latched value */
91 if (!PitChannels[Channel].InputFlipFlop)
92 {
93 /* Yes, the latch value was read as two bytes */
94 PitChannels[Channel].LatchSet = FALSE;
95 }
96 }
97
98 switch (AccessMode)
99 {
100 case 1:
101 {
102 /* Low byte */
103 return CurrentValue & 0x00FF;
104 }
105
106 case 2:
107 {
108 /* High byte */
109 return CurrentValue >> 8;
110 }
111 }
112
113 /* Shouldn't get here */
114 return 0;
115 }
116
117 VOID PitWriteData(BYTE Channel, BYTE Value)
118 {
119 BYTE AccessMode = PitChannels[Channel].AccessMode;
120
121 /* Use the flip-flop for access mode 3 */
122 if (PitChannels[Channel].AccessMode == 3)
123 {
124 AccessMode = PitChannels[Channel].InputFlipFlop ? 1 : 2;
125 PitChannels[Channel].InputFlipFlop = !PitChannels[Channel].InputFlipFlop;
126 }
127
128 switch (AccessMode)
129 {
130 case 1:
131 {
132 /* Low byte */
133 PitChannels[Channel].ReloadValue &= 0xFF00;
134 PitChannels[Channel].ReloadValue |= Value;
135 break;
136 }
137
138 case 2:
139 {
140 /* High byte */
141 PitChannels[Channel].ReloadValue &= 0x00FF;
142 PitChannels[Channel].ReloadValue |= Value << 8;
143 }
144 }
145 }
146
147 VOID PitDecrementCount()
148 {
149 INT i;
150
151 for (i = 0; i < PIT_CHANNELS; i++)
152 {
153 switch (PitChannels[i].Mode)
154 {
155 case PIT_MODE_INT_ON_TERMINAL_COUNT:
156 {
157 /* Decrement the value */
158 PitChannels[i].CurrentValue--;
159
160 /* Did it fall to the terminal count? */
161 if (PitChannels[i].CurrentValue == 0 && !PitChannels[i].Pulsed)
162 {
163 /* Yes, raise the output line */
164 if (i == 0) PicInterruptRequest(0);
165 PitChannels[i].Pulsed = TRUE;
166 }
167 break;
168 }
169
170 case PIT_MODE_RATE_GENERATOR:
171 {
172 /* Decrement the value */
173 PitChannels[i].CurrentValue--;
174
175 /* Did it fall to zero? */
176 if (PitChannels[i].CurrentValue != 0) break;
177
178 /* Yes, raise the output line and reload */
179 if (i == 0) PicInterruptRequest(0);
180 PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
181
182 break;
183 }
184
185 case PIT_MODE_SQUARE_WAVE:
186 {
187 /* Decrement the value by 2 */
188 PitChannels[i].CurrentValue -= 2;
189
190 /* Did it fall to zero? */
191 if (PitChannels[i].CurrentValue != 0) break;
192
193 /* Yes, toggle the flip-flop */
194 PitChannels[i].OutputFlipFlop = !PitChannels[i].OutputFlipFlop;
195
196 /* Did this create a rising edge in the signal? */
197 if (PitChannels[i].OutputFlipFlop)
198 {
199 /* Yes, IRQ 0 if this is channel 0 */
200 if (i == 0) PicInterruptRequest(0);
201 }
202
203 /* Reload the value, but make sure it's even */
204 if (PitChannels[i].ReloadValue % 2)
205 {
206 /* It's odd, reduce it by 1 */
207 PitChannels[i].CurrentValue = PitChannels[i].ReloadValue - 1;
208 }
209 else
210 {
211 /* It was even */
212 PitChannels[i].CurrentValue = PitChannels[i].ReloadValue;
213 }
214
215 break;
216 }
217
218 case PIT_MODE_SOFTWARE_STROBE:
219 {
220 // TODO: NOT IMPLEMENTED
221 break;
222 }
223
224 case PIT_MODE_HARDWARE_ONE_SHOT:
225 case PIT_MODE_HARDWARE_STROBE:
226 {
227 /* These modes do not work on x86 PCs */
228 break;
229 }
230 }
231 }
232 }
233
234 /* EOF */
235