2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
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>
10 /* INCLUDES *******************************************************************/
18 /* PRIVATE VARIABLES **********************************************************/
20 static PIC MasterPic
, SlavePic
;
22 /* PRIVATE FUNCTIONS **********************************************************/
24 static BYTE
PicReadCommand(BYTE Port
)
28 /* Which PIC are we accessing? */
29 if (Port
== PIC_MASTER_CMD
) Pic
= &MasterPic
;
34 /* Read the in-service register */
36 return Pic
->InServiceRegister
;
40 /* Read the interrupt request register */
41 return Pic
->IntRequestRegister
;
45 static VOID
PicWriteCommand(BYTE Port
, BYTE Value
)
49 /* Which PIC are we accessing? */
50 if (Port
== PIC_MASTER_CMD
) Pic
= &MasterPic
;
55 /* Start initialization */
56 Pic
->Initialization
= TRUE
;
57 Pic
->IntOffset
= 0xFF;
58 Pic
->CascadeRegisterSet
= FALSE
;
59 Pic
->ConfigRegister
= Value
;
66 if (Value
== PIC_OCW3_READ_ISR
)
68 /* Return the ISR on next read from command port */
76 if (Value
& PIC_OCW2_EOI
)
78 if (Value
& PIC_OCW2_SL
)
80 /* If the SL bit is set, clear a specific IRQ */
81 Pic
->InServiceRegister
&= ~(1 << (Value
& PIC_OCW2_NUM_MASK
));
85 /* Otherwise, clear all of them */
86 Pic
->InServiceRegister
= 0;
89 if (MasterPic
.IntRequestRegister
|| SlavePic
.IntRequestRegister
)
91 /* Signal the next IRQ */
92 EmulatorInterruptSignal();
97 static BYTE
PicReadData(BYTE Port
)
99 /* Read the mask register */
100 if (Port
== PIC_MASTER_DATA
) return MasterPic
.MaskRegister
;
101 else return SlavePic
.MaskRegister
;
104 static VOID
PicWriteData(BYTE Port
, BYTE Value
)
108 /* Which PIC are we accessing? */
109 if (Port
== PIC_MASTER_DATA
) Pic
= &MasterPic
;
110 else Pic
= &SlavePic
;
112 /* Is the PIC ready? */
113 if (!Pic
->Initialization
)
115 /* Yes, this is an OCW1 */
116 Pic
->MaskRegister
= Value
;
120 /* Has the interrupt offset been set? */
121 if (Pic
->IntOffset
== 0xFF)
123 /* This is an ICW2, set the offset (last three bits always zero) */
124 Pic
->IntOffset
= Value
& 0xF8;
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
))
130 /* Yes, done initializing */
131 Pic
->Initialization
= FALSE
;
136 /* Check if we are in cascade mode and the cascade register was not set */
137 if (!(Pic
->ConfigRegister
& PIC_ICW1_SINGLE
) && !Pic
->CascadeRegisterSet
)
139 /* This is an ICW3 */
140 Pic
->CascadeRegister
= Value
;
141 Pic
->CascadeRegisterSet
= TRUE
;
143 /* Check if we need an ICW4 */
144 if (!(Pic
->ConfigRegister
& PIC_ICW1_ICW4
))
146 /* No, done initializing */
147 Pic
->Initialization
= FALSE
;
152 /* This must be an ICW4, we will ignore the 8086 bit (assume always set) */
153 if (Value
& PIC_ICW4_AEOI
)
155 /* Use automatic end-of-interrupt */
159 /* Done initializing */
160 Pic
->Initialization
= FALSE
;
163 static BYTE WINAPI
PicReadPort(USHORT Port
)
170 return PicReadCommand(Port
);
173 case PIC_MASTER_DATA
:
176 return PicReadData(Port
);
183 static VOID WINAPI
PicWritePort(USHORT Port
, BYTE Data
)
190 PicWriteCommand(Port
, Data
);
194 case PIC_MASTER_DATA
:
197 PicWriteData(Port
, Data
);
203 /* PUBLIC FUNCTIONS ***********************************************************/
205 VOID
PicInterruptRequest(BYTE Number
)
209 if (/* Number >= 0 && */ Number
< 8)
211 /* Check if any of the higher-priority interrupts are busy */
212 for (i
= 0; i
<= Number
; i
++)
214 if (MasterPic
.InServiceRegister
& (1 << Number
)) return;
217 /* Check if the interrupt is masked */
218 if (MasterPic
.MaskRegister
& (1 << Number
)) return;
220 /* Set the appropriate bit in the IRR and interrupt the CPU */
221 MasterPic
.IntRequestRegister
|= 1 << Number
;
222 EmulatorInterruptSignal();
224 else if (Number
>= 8 && Number
< 16)
229 * The slave PIC is connected to IRQ 2, always! If the master PIC
230 * was misconfigured, don't do anything.
232 if (!(MasterPic
.CascadeRegister
& (1 << 2))
233 || SlavePic
.CascadeRegister
!= 2)
238 /* Check if any of the higher-priority interrupts are busy */
239 if (MasterPic
.InServiceRegister
!= 0) return;
240 for (i
= 0; i
<= Number
; i
++)
242 if (SlavePic
.InServiceRegister
& (1 << Number
)) return;
245 /* Check if the interrupt is masked */
246 if (SlavePic
.MaskRegister
& (1 << Number
)) return;
248 /* Set the IRQ 2 bit in the master ISR */
249 if (!MasterPic
.AutoEoi
) MasterPic
.InServiceRegister
|= (1 << 2);
251 /* Set the appropriate bit in the IRR and interrupt the CPU */
252 SlavePic
.IntRequestRegister
|= 1 << Number
;
253 EmulatorInterruptSignal();
257 BYTE
PicGetInterrupt(VOID
)
261 /* Search the master PIC interrupts by priority */
262 for (i
= 0; i
< 8; i
++)
264 if (MasterPic
.IntRequestRegister
& (1 << i
))
266 /* Clear the IRR flag */
267 MasterPic
.IntRequestRegister
&= ~(1 << i
);
269 /* Set the ISR flag, unless AEOI is enabled */
270 if (!MasterPic
.AutoEoi
) MasterPic
.InServiceRegister
|= (1 << i
);
272 /* Return the interrupt number */
273 return MasterPic
.IntOffset
+ i
;
277 /* Search the slave PIC interrupts by priority */
278 for (i
= 0; i
< 8; i
++)
280 if (SlavePic
.IntRequestRegister
& (1 << i
))
282 /* Clear the IRR flag */
283 SlavePic
.IntRequestRegister
&= ~(1 << i
);
285 if ((i
== 1) && SlavePic
.CascadeRegisterSet
)
287 /* This interrupt is routed to the master PIC */
288 return MasterPic
.IntOffset
+ SlavePic
.CascadeRegister
;
292 /* Set the ISR flag, unless AEOI is enabled */
293 if (!SlavePic
.AutoEoi
) SlavePic
.InServiceRegister
|= (1 << i
);
295 /* Return the interrupt number */
296 return SlavePic
.IntOffset
+ i
;
301 /* Spurious interrupt */
302 if (MasterPic
.InServiceRegister
& (1 << 2)) return SlavePic
.IntOffset
+ 7;
303 else return MasterPic
.IntOffset
+ 7;
306 VOID
PicInitialize(VOID
)
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
);
319 call_ica_hw_interrupt(INT ms
,
323 BYTE InterruptNumber
= line
;
325 /* Check for PIC validity */
326 if (ms
!= ICA_MASTER
&& ms
!= ICA_SLAVE
) return;
329 * Adjust the interrupt request number according to the parameters,
330 * by adding an offset == 8 to the interrupt number.
332 * Indeed VDDs calling this function usually subtracts 8 so that they give:
334 * ms | line | corresponding interrupt number
335 * ------------+--------+--------------------------------
336 * ICA_MASTER | 0 -- 7 | 0 -- 7
337 * ICA_SLAVE | 0 -- 7 | 8 -- 15
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).
343 if (ms
== ICA_SLAVE
) InterruptNumber
+= 8;
345 /* Send the specified number of interrupt requests */
348 PicInterruptRequest(InterruptNumber
);