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;
91 static BYTE
PicReadData(BYTE Port
)
93 /* Read the mask register */
94 if (Port
== PIC_MASTER_DATA
) return MasterPic
.MaskRegister
;
95 else return SlavePic
.MaskRegister
;
98 static VOID
PicWriteData(BYTE Port
, BYTE Value
)
102 /* Which PIC are we accessing? */
103 if (Port
== PIC_MASTER_DATA
) Pic
= &MasterPic
;
104 else Pic
= &SlavePic
;
106 /* Is the PIC ready? */
107 if (!Pic
->Initialization
)
109 /* Yes, this is an OCW1 */
110 Pic
->MaskRegister
= Value
;
114 /* Has the interrupt offset been set? */
115 if (Pic
->IntOffset
== 0xFF)
117 /* This is an ICW2, set the offset (last three bits always zero) */
118 Pic
->IntOffset
= Value
& 0xF8;
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
))
124 /* Yes, done initializing */
125 Pic
->Initialization
= FALSE
;
130 /* Check if we are in cascade mode and the cascade register was not set */
131 if (!(Pic
->ConfigRegister
& PIC_ICW1_SINGLE
) && !Pic
->CascadeRegisterSet
)
133 /* This is an ICW3 */
134 Pic
->CascadeRegister
= Value
;
135 Pic
->CascadeRegisterSet
= TRUE
;
137 /* Check if we need an ICW4 */
138 if (!(Pic
->ConfigRegister
& PIC_ICW1_ICW4
))
140 /* No, done initializing */
141 Pic
->Initialization
= FALSE
;
146 /* This must be an ICW4, we will ignore the 8086 bit (assume always set) */
147 if (Value
& PIC_ICW4_AEOI
)
149 /* Use automatic end-of-interrupt */
153 /* Done initializing */
154 Pic
->Initialization
= FALSE
;
157 static BYTE WINAPI
PicReadPort(ULONG Port
)
164 return PicReadCommand(Port
);
167 case PIC_MASTER_DATA
:
170 return PicReadData(Port
);
177 static VOID WINAPI
PicWritePort(ULONG Port
, BYTE Data
)
184 PicWriteCommand(Port
, Data
);
188 case PIC_MASTER_DATA
:
191 PicWriteData(Port
, Data
);
197 /* PUBLIC FUNCTIONS ***********************************************************/
199 VOID
PicInterruptRequest(BYTE Number
)
203 if (/* Number >= 0 && */ Number
< 8)
205 /* Check if any of the higher-priority interrupts are busy */
206 for (i
= 0; i
<= Number
; i
++)
208 if (MasterPic
.InServiceRegister
& (1 << Number
)) return;
211 /* Check if the interrupt is masked */
212 if (MasterPic
.MaskRegister
& (1 << Number
)) return;
214 /* Set the appropriate bit in the IRR and interrupt the CPU */
215 MasterPic
.IntRequestRegister
|= 1 << Number
;
216 EmulatorInterruptSignal();
218 else if (Number
>= 8 && Number
< 16)
223 * The slave PIC is connected to IRQ 2, always! If the master PIC
224 * was misconfigured, don't do anything.
226 if (!(MasterPic
.CascadeRegister
& (1 << 2))
227 || SlavePic
.CascadeRegister
!= 2)
232 /* Check if any of the higher-priority interrupts are busy */
233 if (MasterPic
.InServiceRegister
!= 0) return;
234 for (i
= 0; i
<= Number
; i
++)
236 if (SlavePic
.InServiceRegister
& (1 << Number
)) return;
239 /* Check if the interrupt is masked */
240 if (SlavePic
.MaskRegister
& (1 << Number
)) return;
242 /* Set the IRQ 2 bit in the master ISR */
243 if (!MasterPic
.AutoEoi
) MasterPic
.InServiceRegister
|= (1 << 2);
245 /* Set the appropriate bit in the IRR and interrupt the CPU */
246 SlavePic
.IntRequestRegister
|= 1 << Number
;
247 EmulatorInterruptSignal();
251 BYTE
PicGetInterrupt(VOID
)
255 /* Search interrupts by priority */
256 for (i
= 0; i
< 8; i
++)
258 /* Check if this line is cascaded to the slave PIC */
260 && MasterPic
.CascadeRegister
& (1 << 2)
262 && (SlavePic
.CascadeRegister
== 2))
264 /* Search the slave PIC interrupts by priority */
265 for (j
= 0; j
< 8; j
++) if ((j
!= 1) && SlavePic
.IntRequestRegister
& (1 << j
))
267 /* Clear the IRR flag */
268 SlavePic
.IntRequestRegister
&= ~(1 << j
);
270 /* Set the ISR flag, unless AEOI is enabled */
271 if (!SlavePic
.AutoEoi
) SlavePic
.InServiceRegister
|= (1 << j
);
273 /* Return the interrupt number */
274 return SlavePic
.IntOffset
+ j
;
278 if (MasterPic
.IntRequestRegister
& (1 << i
))
280 /* Clear the IRR flag */
281 MasterPic
.IntRequestRegister
&= ~(1 << i
);
283 /* Set the ISR flag, unless AEOI is enabled */
284 if (!MasterPic
.AutoEoi
) MasterPic
.InServiceRegister
|= (1 << i
);
286 /* Return the interrupt number */
287 return MasterPic
.IntOffset
+ i
;
291 /* Spurious interrupt */
292 if (MasterPic
.InServiceRegister
& (1 << 2)) return SlavePic
.IntOffset
+ 7;
293 else return MasterPic
.IntOffset
+ 7;
296 VOID
PicInitialize(VOID
)
298 /* Register the I/O Ports */
299 RegisterIoPort(PIC_MASTER_CMD
, PicReadPort
, PicWritePort
);
300 RegisterIoPort(PIC_SLAVE_CMD
, PicReadPort
, PicWritePort
);
301 RegisterIoPort(PIC_MASTER_DATA
, PicReadPort
, PicWritePort
);
302 RegisterIoPort(PIC_SLAVE_DATA
, PicReadPort
, PicWritePort
);
309 call_ica_hw_interrupt(INT ms
,
313 BYTE InterruptNumber
= line
;
315 /* Check for PIC validity */
316 if (ms
!= ICA_MASTER
|| ms
!= ICA_SLAVE
) return;
319 * Adjust the interrupt request number according to the parameters,
320 * by adding an offset == 8 to the interrupt number.
322 * Indeed VDDs calling this function usually subtracts 8 so that they give:
324 * ms | line | corresponding interrupt number
325 * ------------+--------+--------------------------------
326 * ICA_MASTER | 0 -- 7 | 0 -- 7
327 * ICA_SLAVE | 0 -- 7 | 8 -- 15
329 * and PicInterruptRequest subtracts again 8 to the interrupt number
330 * if it is greater or equal than 8 (so that it determines which PIC
331 * to use via the interrupt number).
333 if (ms
== ICA_SLAVE
) InterruptNumber
+= 8;
335 /* Send the specified number of interrupt requests */
338 PicInterruptRequest(InterruptNumber
);