2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/hardware/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>
10 /* INCLUDES *******************************************************************/
22 /* PRIVATE VARIABLES **********************************************************/
26 BOOLEAN Initialization
;
28 BYTE IntRequestRegister
;
29 BYTE InServiceRegister
;
33 BOOLEAN CascadeRegisterSet
;
39 static PIC MasterPic
, SlavePic
;
41 /* PRIVATE FUNCTIONS **********************************************************/
43 static BYTE WINAPI
PicReadCommand(USHORT Port
)
47 /* Which PIC are we accessing? */
48 if (Port
== PIC_MASTER_CMD
)
50 else // if (Port == PIC_SLAVE_CMD)
55 /* Read the in-service register */
57 return Pic
->InServiceRegister
;
61 /* Read the interrupt request register */
62 return Pic
->IntRequestRegister
;
66 static VOID WINAPI
PicWriteCommand(USHORT Port
, BYTE Data
)
70 /* Which PIC are we accessing? */
71 if (Port
== PIC_MASTER_CMD
)
73 else // if (Port == PIC_SLAVE_CMD)
78 /* Start initialization */
79 Pic
->Initialization
= TRUE
;
80 Pic
->IntOffset
= 0xFF;
81 Pic
->CascadeRegisterSet
= FALSE
;
82 Pic
->ConfigRegister
= Data
;
89 if (Data
== PIC_OCW3_READ_ISR
)
91 /* Return the ISR on next read from command port */
99 if (Data
& PIC_OCW2_EOI
)
101 if (Data
& PIC_OCW2_SL
)
103 /* If the SL bit is set, clear a specific IRQ */
104 Pic
->InServiceRegister
&= ~(1 << (Data
& PIC_OCW2_NUM_MASK
));
108 /* Otherwise, clear all of them */
109 Pic
->InServiceRegister
= 0;
112 if (MasterPic
.IntRequestRegister
|| SlavePic
.IntRequestRegister
)
114 /* Signal the next IRQ */
115 EmulatorInterruptSignal();
120 static BYTE WINAPI
PicReadData(USHORT Port
)
122 /* Read the mask register */
123 if (Port
== PIC_MASTER_DATA
)
124 return MasterPic
.MaskRegister
;
125 else // if (Port == PIC_SLAVE_DATA)
126 return SlavePic
.MaskRegister
;
129 static VOID WINAPI
PicWriteData(USHORT Port
, BYTE Data
)
133 /* Which PIC are we accessing? */
134 if (Port
== PIC_MASTER_DATA
)
136 else // if (Port == PIC_SLAVE_DATA)
139 /* Is the PIC ready? */
140 if (!Pic
->Initialization
)
142 /* Yes, this is an OCW1 */
143 Pic
->MaskRegister
= Data
;
147 /* Has the interrupt offset been set? */
148 if (Pic
->IntOffset
== 0xFF)
150 /* This is an ICW2, set the offset (last three bits always zero) */
151 Pic
->IntOffset
= Data
& 0xF8;
153 /* Check if we are in single mode and don't need an ICW4 */
154 if ((Pic
->ConfigRegister
& PIC_ICW1_SINGLE
)
155 && !(Pic
->ConfigRegister
& PIC_ICW1_ICW4
))
157 /* Yes, done initializing */
158 Pic
->Initialization
= FALSE
;
163 /* Check if we are in cascade mode and the cascade register was not set */
164 if (!(Pic
->ConfigRegister
& PIC_ICW1_SINGLE
) && !Pic
->CascadeRegisterSet
)
166 /* This is an ICW3 */
167 Pic
->CascadeRegister
= Data
;
168 Pic
->CascadeRegisterSet
= TRUE
;
170 /* Check if we need an ICW4 */
171 if (!(Pic
->ConfigRegister
& PIC_ICW1_ICW4
))
173 /* No, done initializing */
174 Pic
->Initialization
= FALSE
;
179 /* This must be an ICW4, we will ignore the 8086 bit (assume always set) */
180 if (Data
& PIC_ICW4_AEOI
)
182 /* Use automatic end-of-interrupt */
186 /* Done initializing */
187 Pic
->Initialization
= FALSE
;
190 /* PUBLIC FUNCTIONS ***********************************************************/
192 VOID
PicInterruptRequest(BYTE Number
)
196 if (/* Number >= 0 && */ Number
< 8)
198 /* Check if any of the higher-priority interrupts are busy */
199 for (i
= 0; i
<= Number
; i
++)
201 if (MasterPic
.InServiceRegister
& (1 << Number
)) return;
204 /* Check if the interrupt is masked */
205 if (MasterPic
.MaskRegister
& (1 << Number
)) return;
207 /* Set the appropriate bit in the IRR and interrupt the CPU */
208 MasterPic
.IntRequestRegister
|= 1 << Number
;
209 EmulatorInterruptSignal();
211 else if (Number
>= 8 && Number
< 16)
216 * The slave PIC is connected to IRQ 2, always! If the master PIC
217 * was misconfigured, don't do anything.
219 if (!(MasterPic
.CascadeRegister
& (1 << 2))
220 || SlavePic
.CascadeRegister
!= 2)
225 /* Check if any of the higher-priority interrupts are busy */
226 if (MasterPic
.InServiceRegister
!= 0) return;
227 for (i
= 0; i
<= Number
; i
++)
229 if (SlavePic
.InServiceRegister
& (1 << Number
)) return;
232 /* Check if the interrupt is masked */
233 if (SlavePic
.MaskRegister
& (1 << Number
)) return;
235 /* Set the IRQ 2 bit in the master ISR */
236 if (!MasterPic
.AutoEoi
) MasterPic
.InServiceRegister
|= (1 << 2);
238 /* Set the appropriate bit in the IRR and interrupt the CPU */
239 SlavePic
.IntRequestRegister
|= 1 << Number
;
240 EmulatorInterruptSignal();
244 BYTE
PicGetInterrupt(VOID
)
248 /* Search the master PIC interrupts by priority */
249 for (i
= 0; i
< 8; i
++)
251 if (MasterPic
.IntRequestRegister
& (1 << i
))
253 /* Clear the IRR flag */
254 MasterPic
.IntRequestRegister
&= ~(1 << i
);
256 /* Set the ISR flag, unless AEOI is enabled */
257 if (!MasterPic
.AutoEoi
) MasterPic
.InServiceRegister
|= (1 << i
);
259 /* Return the interrupt number */
260 return MasterPic
.IntOffset
+ i
;
264 /* Search the slave PIC interrupts by priority */
265 for (i
= 0; i
< 8; i
++)
267 if (SlavePic
.IntRequestRegister
& (1 << i
))
269 /* Clear the IRR flag */
270 SlavePic
.IntRequestRegister
&= ~(1 << i
);
272 if ((i
== 1) && SlavePic
.CascadeRegisterSet
)
274 /* This interrupt is routed to the master PIC */
275 return MasterPic
.IntOffset
+ SlavePic
.CascadeRegister
;
279 /* Set the ISR flag, unless AEOI is enabled */
280 if (!SlavePic
.AutoEoi
) SlavePic
.InServiceRegister
|= (1 << i
);
282 /* Return the interrupt number */
283 return SlavePic
.IntOffset
+ i
;
288 /* Spurious interrupt */
289 if (MasterPic
.InServiceRegister
& (1 << 2))
290 return SlavePic
.IntOffset
+ 7;
292 return MasterPic
.IntOffset
+ 7;
295 VOID
PicInitialize(VOID
)
297 /* Register the I/O Ports */
298 RegisterIoPort(PIC_MASTER_CMD
, PicReadCommand
, PicWriteCommand
);
299 RegisterIoPort(PIC_SLAVE_CMD
, PicReadCommand
, PicWriteCommand
);
300 RegisterIoPort(PIC_MASTER_DATA
, PicReadData
, PicWriteData
);
301 RegisterIoPort(PIC_SLAVE_DATA
, PicReadData
, PicWriteData
);
308 call_ica_hw_interrupt(INT ms
,
312 BYTE InterruptNumber
= line
;
314 /* Check for PIC validity */
315 if (ms
!= ICA_MASTER
&& ms
!= ICA_SLAVE
) return;
318 * Adjust the interrupt request number according to the parameters,
319 * by adding an offset == 8 to the interrupt number.
321 * Indeed VDDs calling this function usually subtracts 8 so that they give:
323 * ms | line | corresponding interrupt number
324 * ------------+--------+--------------------------------
325 * ICA_MASTER | 0 -- 7 | 0 -- 7
326 * ICA_SLAVE | 0 -- 7 | 8 -- 15
328 * and PicInterruptRequest subtracts again 8 to the interrupt number
329 * if it is greater or equal than 8 (so that it determines which PIC
330 * to use via the interrupt number).
332 if (ms
== ICA_SLAVE
) InterruptNumber
+= 8;
334 /* Send the specified number of interrupt requests */
337 PicInterruptRequest(InterruptNumber
);
339 * FIXME: We should now restart 16-bit emulation and wait for its termination:
341 * "When the VDD calls VDDSimulateInterrupt, the address pointed to by
342 * the interrupt vector starts running in 16-bit mode. For an asynchronous
343 * interrupt, the VDD should create another thread and call VDDSimulateInterrupt
351 VDDReserveIrqLine(IN HANDLE hVdd
,
355 SetLastError(ERROR_INVALID_PARAMETER
);
361 VDDReleaseIrqLine(IN HANDLE hVdd
,
365 SetLastError(ERROR_INVALID_PARAMETER
);