* Sync up to trunk head (r64921).
[reactos.git] / 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 }
90
91 static BYTE PicReadData(BYTE Port)
92 {
93 /* Read the mask register */
94 if (Port == PIC_MASTER_DATA) return MasterPic.MaskRegister;
95 else return SlavePic.MaskRegister;
96 }
97
98 static VOID PicWriteData(BYTE Port, BYTE Value)
99 {
100 PPIC Pic;
101
102 /* Which PIC are we accessing? */
103 if (Port == PIC_MASTER_DATA) Pic = &MasterPic;
104 else Pic = &SlavePic;
105
106 /* Is the PIC ready? */
107 if (!Pic->Initialization)
108 {
109 /* Yes, this is an OCW1 */
110 Pic->MaskRegister = Value;
111 return;
112 }
113
114 /* Has the interrupt offset been set? */
115 if (Pic->IntOffset == 0xFF)
116 {
117 /* This is an ICW2, set the offset (last three bits always zero) */
118 Pic->IntOffset = Value & 0xF8;
119
120 /* Check if we are in single mode and don't need an ICW4 */
121 if ((Pic->ConfigRegister & PIC_ICW1_SINGLE)
122 && !(Pic->ConfigRegister & PIC_ICW1_ICW4))
123 {
124 /* Yes, done initializing */
125 Pic->Initialization = FALSE;
126 }
127 return;
128 }
129
130 /* Check if we are in cascade mode and the cascade register was not set */
131 if (!(Pic->ConfigRegister & PIC_ICW1_SINGLE) && !Pic->CascadeRegisterSet)
132 {
133 /* This is an ICW3 */
134 Pic->CascadeRegister = Value;
135 Pic->CascadeRegisterSet = TRUE;
136
137 /* Check if we need an ICW4 */
138 if (!(Pic->ConfigRegister & PIC_ICW1_ICW4))
139 {
140 /* No, done initializing */
141 Pic->Initialization = FALSE;
142 }
143 return;
144 }
145
146 /* This must be an ICW4, we will ignore the 8086 bit (assume always set) */
147 if (Value & PIC_ICW4_AEOI)
148 {
149 /* Use automatic end-of-interrupt */
150 Pic->AutoEoi = TRUE;
151 }
152
153 /* Done initializing */
154 Pic->Initialization = FALSE;
155 }
156
157 static BYTE WINAPI PicReadPort(USHORT Port)
158 {
159 switch (Port)
160 {
161 case PIC_MASTER_CMD:
162 case PIC_SLAVE_CMD:
163 {
164 return PicReadCommand(Port);
165 }
166
167 case PIC_MASTER_DATA:
168 case PIC_SLAVE_DATA:
169 {
170 return PicReadData(Port);
171 }
172 }
173
174 return 0;
175 }
176
177 static VOID WINAPI PicWritePort(USHORT Port, BYTE Data)
178 {
179 switch (Port)
180 {
181 case PIC_MASTER_CMD:
182 case PIC_SLAVE_CMD:
183 {
184 PicWriteCommand(Port, Data);
185 break;
186 }
187
188 case PIC_MASTER_DATA:
189 case PIC_SLAVE_DATA:
190 {
191 PicWriteData(Port, Data);
192 break;
193 }
194 }
195 }
196
197 /* PUBLIC FUNCTIONS ***********************************************************/
198
199 VOID PicInterruptRequest(BYTE Number)
200 {
201 BYTE i;
202
203 if (/* Number >= 0 && */ Number < 8)
204 {
205 /* Check if any of the higher-priority interrupts are busy */
206 for (i = 0; i <= Number; i++)
207 {
208 if (MasterPic.InServiceRegister & (1 << Number)) return;
209 }
210
211 /* Check if the interrupt is masked */
212 if (MasterPic.MaskRegister & (1 << Number)) return;
213
214 /* Set the appropriate bit in the IRR and interrupt the CPU */
215 MasterPic.IntRequestRegister |= 1 << Number;
216 EmulatorInterruptSignal();
217 }
218 else if (Number >= 8 && Number < 16)
219 {
220 Number -= 8;
221
222 /*
223 * The slave PIC is connected to IRQ 2, always! If the master PIC
224 * was misconfigured, don't do anything.
225 */
226 if (!(MasterPic.CascadeRegister & (1 << 2))
227 || SlavePic.CascadeRegister != 2)
228 {
229 return;
230 }
231
232 /* Check if any of the higher-priority interrupts are busy */
233 if (MasterPic.InServiceRegister != 0) return;
234 for (i = 0; i <= Number; i++)
235 {
236 if (SlavePic.InServiceRegister & (1 << Number)) return;
237 }
238
239 /* Check if the interrupt is masked */
240 if (SlavePic.MaskRegister & (1 << Number)) return;
241
242 /* Set the IRQ 2 bit in the master ISR */
243 if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << 2);
244
245 /* Set the appropriate bit in the IRR and interrupt the CPU */
246 SlavePic.IntRequestRegister |= 1 << Number;
247 EmulatorInterruptSignal();
248 }
249 }
250
251 BYTE PicGetInterrupt(VOID)
252 {
253 INT i;
254
255 /* Search the master PIC interrupts by priority */
256 for (i = 0; i < 8; i++)
257 {
258 if (MasterPic.IntRequestRegister & (1 << i))
259 {
260 /* Clear the IRR flag */
261 MasterPic.IntRequestRegister &= ~(1 << i);
262
263 /* Set the ISR flag, unless AEOI is enabled */
264 if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << i);
265
266 /* Return the interrupt number */
267 return MasterPic.IntOffset + i;
268 }
269 }
270
271 /* Search the slave PIC interrupts by priority */
272 for (i = 0; i < 8; i++)
273 {
274 if (SlavePic.IntRequestRegister & (1 << i))
275 {
276 /* Clear the IRR flag */
277 SlavePic.IntRequestRegister &= ~(1 << i);
278
279 if ((i == 1) && SlavePic.CascadeRegisterSet)
280 {
281 /* This interrupt is routed to the master PIC */
282 return MasterPic.IntOffset + SlavePic.CascadeRegister;
283 }
284 else
285 {
286 /* Set the ISR flag, unless AEOI is enabled */
287 if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= (1 << i);
288
289 /* Return the interrupt number */
290 return SlavePic.IntOffset + i;
291 }
292 }
293 }
294
295 /* Spurious interrupt */
296 if (MasterPic.InServiceRegister & (1 << 2)) return SlavePic.IntOffset + 7;
297 else return MasterPic.IntOffset + 7;
298 }
299
300 VOID PicInitialize(VOID)
301 {
302 /* Register the I/O Ports */
303 RegisterIoPort(PIC_MASTER_CMD , PicReadPort, PicWritePort);
304 RegisterIoPort(PIC_SLAVE_CMD , PicReadPort, PicWritePort);
305 RegisterIoPort(PIC_MASTER_DATA, PicReadPort, PicWritePort);
306 RegisterIoPort(PIC_SLAVE_DATA , PicReadPort, PicWritePort);
307 }
308
309
310
311 VOID
312 WINAPI
313 call_ica_hw_interrupt(INT ms,
314 BYTE line,
315 INT count)
316 {
317 BYTE InterruptNumber = line;
318
319 /* Check for PIC validity */
320 if (ms != ICA_MASTER && ms != ICA_SLAVE) return;
321
322 /*
323 * Adjust the interrupt request number according to the parameters,
324 * by adding an offset == 8 to the interrupt number.
325 *
326 * Indeed VDDs calling this function usually subtracts 8 so that they give:
327 *
328 * ms | line | corresponding interrupt number
329 * ------------+--------+--------------------------------
330 * ICA_MASTER | 0 -- 7 | 0 -- 7
331 * ICA_SLAVE | 0 -- 7 | 8 -- 15
332 *
333 * and PicInterruptRequest subtracts again 8 to the interrupt number
334 * if it is greater or equal than 8 (so that it determines which PIC
335 * to use via the interrupt number).
336 */
337 if (ms == ICA_SLAVE) InterruptNumber += 8;
338
339 /* Send the specified number of interrupt requests */
340 while (count-- > 0)
341 {
342 PicInterruptRequest(InterruptNumber);
343 }
344 }
345
346 /* EOF */