[NTVDM]
[reactos.git] / reactos / subsystems / ntvdm / hardware / pic.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: pic.c
5 * PURPOSE: Programmable Interrupt Controller emulation
6 * (Interrupt Controller Adapter (ICA) in Windows terminology)
7 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #define NDEBUG
13
14 #include "emulator.h"
15 #include "io.h"
16 #include "pic.h"
17
18 /* PRIVATE VARIABLES **********************************************************/
19
20 static PIC MasterPic, SlavePic;
21
22 /* PRIVATE FUNCTIONS **********************************************************/
23
24 static BYTE PicReadCommand(BYTE Port)
25 {
26 PPIC Pic;
27
28 /* Which PIC are we accessing? */
29 if (Port == PIC_MASTER_CMD) Pic = &MasterPic;
30 else Pic = &SlavePic;
31
32 if (Pic->ReadIsr)
33 {
34 /* Read the in-service register */
35 Pic->ReadIsr = FALSE;
36 return Pic->InServiceRegister;
37 }
38 else
39 {
40 /* Read the interrupt request register */
41 return Pic->IntRequestRegister;
42 }
43 }
44
45 static VOID PicWriteCommand(BYTE Port, BYTE Value)
46 {
47 PPIC Pic;
48
49 /* Which PIC are we accessing? */
50 if (Port == PIC_MASTER_CMD) Pic = &MasterPic;
51 else Pic = &SlavePic;
52
53 if (Value & PIC_ICW1)
54 {
55 /* Start initialization */
56 Pic->Initialization = TRUE;
57 Pic->IntOffset = 0xFF;
58 Pic->CascadeRegisterSet = FALSE;
59 Pic->ConfigRegister = Value;
60 return;
61 }
62
63 if (Value & PIC_OCW3)
64 {
65 /* This is an OCR3 */
66 if (Value == PIC_OCW3_READ_ISR)
67 {
68 /* Return the ISR on next read from command port */
69 Pic->ReadIsr = TRUE;
70 }
71
72 return;
73 }
74
75 /* This is an OCW2 */
76 if (Value & PIC_OCW2_EOI)
77 {
78 if (Value & PIC_OCW2_SL)
79 {
80 /* If the SL bit is set, clear a specific IRQ */
81 Pic->InServiceRegister &= ~(1 << (Value & PIC_OCW2_NUM_MASK));
82 }
83 else
84 {
85 /* Otherwise, clear all of them */
86 Pic->InServiceRegister = 0;
87 }
88
89 if (MasterPic.IntRequestRegister || SlavePic.IntRequestRegister)
90 {
91 /* Signal the next IRQ */
92 EmulatorInterruptSignal();
93 }
94 }
95 }
96
97 static BYTE PicReadData(BYTE Port)
98 {
99 /* Read the mask register */
100 if (Port == PIC_MASTER_DATA) return MasterPic.MaskRegister;
101 else return SlavePic.MaskRegister;
102 }
103
104 static VOID PicWriteData(BYTE Port, BYTE Value)
105 {
106 PPIC Pic;
107
108 /* Which PIC are we accessing? */
109 if (Port == PIC_MASTER_DATA) Pic = &MasterPic;
110 else Pic = &SlavePic;
111
112 /* Is the PIC ready? */
113 if (!Pic->Initialization)
114 {
115 /* Yes, this is an OCW1 */
116 Pic->MaskRegister = Value;
117 return;
118 }
119
120 /* Has the interrupt offset been set? */
121 if (Pic->IntOffset == 0xFF)
122 {
123 /* This is an ICW2, set the offset (last three bits always zero) */
124 Pic->IntOffset = Value & 0xF8;
125
126 /* Check if we are in single mode and don't need an ICW4 */
127 if ((Pic->ConfigRegister & PIC_ICW1_SINGLE)
128 && !(Pic->ConfigRegister & PIC_ICW1_ICW4))
129 {
130 /* Yes, done initializing */
131 Pic->Initialization = FALSE;
132 }
133 return;
134 }
135
136 /* Check if we are in cascade mode and the cascade register was not set */
137 if (!(Pic->ConfigRegister & PIC_ICW1_SINGLE) && !Pic->CascadeRegisterSet)
138 {
139 /* This is an ICW3 */
140 Pic->CascadeRegister = Value;
141 Pic->CascadeRegisterSet = TRUE;
142
143 /* Check if we need an ICW4 */
144 if (!(Pic->ConfigRegister & PIC_ICW1_ICW4))
145 {
146 /* No, done initializing */
147 Pic->Initialization = FALSE;
148 }
149 return;
150 }
151
152 /* This must be an ICW4, we will ignore the 8086 bit (assume always set) */
153 if (Value & PIC_ICW4_AEOI)
154 {
155 /* Use automatic end-of-interrupt */
156 Pic->AutoEoi = TRUE;
157 }
158
159 /* Done initializing */
160 Pic->Initialization = FALSE;
161 }
162
163 static BYTE WINAPI PicReadPort(USHORT Port)
164 {
165 switch (Port)
166 {
167 case PIC_MASTER_CMD:
168 case PIC_SLAVE_CMD:
169 {
170 return PicReadCommand(Port);
171 }
172
173 case PIC_MASTER_DATA:
174 case PIC_SLAVE_DATA:
175 {
176 return PicReadData(Port);
177 }
178 }
179
180 return 0;
181 }
182
183 static VOID WINAPI PicWritePort(USHORT Port, BYTE Data)
184 {
185 switch (Port)
186 {
187 case PIC_MASTER_CMD:
188 case PIC_SLAVE_CMD:
189 {
190 PicWriteCommand(Port, Data);
191 break;
192 }
193
194 case PIC_MASTER_DATA:
195 case PIC_SLAVE_DATA:
196 {
197 PicWriteData(Port, Data);
198 break;
199 }
200 }
201 }
202
203 /* PUBLIC FUNCTIONS ***********************************************************/
204
205 VOID PicInterruptRequest(BYTE Number)
206 {
207 BYTE i;
208
209 if (/* Number >= 0 && */ Number < 8)
210 {
211 /* Check if any of the higher-priority interrupts are busy */
212 for (i = 0; i <= Number; i++)
213 {
214 if (MasterPic.InServiceRegister & (1 << Number)) return;
215 }
216
217 /* Check if the interrupt is masked */
218 if (MasterPic.MaskRegister & (1 << Number)) return;
219
220 /* Set the appropriate bit in the IRR and interrupt the CPU */
221 MasterPic.IntRequestRegister |= 1 << Number;
222 EmulatorInterruptSignal();
223 }
224 else if (Number >= 8 && Number < 16)
225 {
226 Number -= 8;
227
228 /*
229 * The slave PIC is connected to IRQ 2, always! If the master PIC
230 * was misconfigured, don't do anything.
231 */
232 if (!(MasterPic.CascadeRegister & (1 << 2))
233 || SlavePic.CascadeRegister != 2)
234 {
235 return;
236 }
237
238 /* Check if any of the higher-priority interrupts are busy */
239 if (MasterPic.InServiceRegister != 0) return;
240 for (i = 0; i <= Number; i++)
241 {
242 if (SlavePic.InServiceRegister & (1 << Number)) return;
243 }
244
245 /* Check if the interrupt is masked */
246 if (SlavePic.MaskRegister & (1 << Number)) return;
247
248 /* Set the IRQ 2 bit in the master ISR */
249 if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << 2);
250
251 /* Set the appropriate bit in the IRR and interrupt the CPU */
252 SlavePic.IntRequestRegister |= 1 << Number;
253 EmulatorInterruptSignal();
254 }
255 }
256
257 BYTE PicGetInterrupt(VOID)
258 {
259 INT i;
260
261 /* Search the master PIC interrupts by priority */
262 for (i = 0; i < 8; i++)
263 {
264 if (MasterPic.IntRequestRegister & (1 << i))
265 {
266 /* Clear the IRR flag */
267 MasterPic.IntRequestRegister &= ~(1 << i);
268
269 /* Set the ISR flag, unless AEOI is enabled */
270 if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << i);
271
272 /* Return the interrupt number */
273 return MasterPic.IntOffset + i;
274 }
275 }
276
277 /* Search the slave PIC interrupts by priority */
278 for (i = 0; i < 8; i++)
279 {
280 if (SlavePic.IntRequestRegister & (1 << i))
281 {
282 /* Clear the IRR flag */
283 SlavePic.IntRequestRegister &= ~(1 << i);
284
285 if ((i == 1) && SlavePic.CascadeRegisterSet)
286 {
287 /* This interrupt is routed to the master PIC */
288 return MasterPic.IntOffset + SlavePic.CascadeRegister;
289 }
290 else
291 {
292 /* Set the ISR flag, unless AEOI is enabled */
293 if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= (1 << i);
294
295 /* Return the interrupt number */
296 return SlavePic.IntOffset + i;
297 }
298 }
299 }
300
301 /* Spurious interrupt */
302 if (MasterPic.InServiceRegister & (1 << 2)) return SlavePic.IntOffset + 7;
303 else return MasterPic.IntOffset + 7;
304 }
305
306 VOID PicInitialize(VOID)
307 {
308 /* Register the I/O Ports */
309 RegisterIoPort(PIC_MASTER_CMD , PicReadPort, PicWritePort);
310 RegisterIoPort(PIC_SLAVE_CMD , PicReadPort, PicWritePort);
311 RegisterIoPort(PIC_MASTER_DATA, PicReadPort, PicWritePort);
312 RegisterIoPort(PIC_SLAVE_DATA , PicReadPort, PicWritePort);
313 }
314
315
316
317 VOID
318 WINAPI
319 call_ica_hw_interrupt(INT ms,
320 BYTE line,
321 INT count)
322 {
323 BYTE InterruptNumber = line;
324
325 /* Check for PIC validity */
326 if (ms != ICA_MASTER && ms != ICA_SLAVE) return;
327
328 /*
329 * Adjust the interrupt request number according to the parameters,
330 * by adding an offset == 8 to the interrupt number.
331 *
332 * Indeed VDDs calling this function usually subtracts 8 so that they give:
333 *
334 * ms | line | corresponding interrupt number
335 * ------------+--------+--------------------------------
336 * ICA_MASTER | 0 -- 7 | 0 -- 7
337 * ICA_SLAVE | 0 -- 7 | 8 -- 15
338 *
339 * and PicInterruptRequest subtracts again 8 to the interrupt number
340 * if it is greater or equal than 8 (so that it determines which PIC
341 * to use via the interrupt number).
342 */
343 if (ms == ICA_SLAVE) InterruptNumber += 8;
344
345 /* Send the specified number of interrupt requests */
346 while (count-- > 0)
347 {
348 PicInterruptRequest(InterruptNumber);
349 }
350 }
351
352 /* EOF */