* Sync with trunk r64401.
[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(ULONG 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(ULONG 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, j;
254
255 /* Search interrupts by priority */
256 for (i = 0; i < 8; i++)
257 {
258 /* Check if this line is cascaded to the slave PIC */
259 if ((i == 2)
260 && MasterPic.CascadeRegister & (1 << 2)
261 && SlavePic.Slave
262 && (SlavePic.CascadeRegister == 2))
263 {
264 /* Search the slave PIC interrupts by priority */
265 for (j = 0; j < 8; j++) if ((j != 1) && SlavePic.IntRequestRegister & (1 << j))
266 {
267 /* Clear the IRR flag */
268 SlavePic.IntRequestRegister &= ~(1 << j);
269
270 /* Set the ISR flag, unless AEOI is enabled */
271 if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= (1 << j);
272
273 /* Return the interrupt number */
274 return SlavePic.IntOffset + j;
275 }
276 }
277
278 if (MasterPic.IntRequestRegister & (1 << i))
279 {
280 /* Clear the IRR flag */
281 MasterPic.IntRequestRegister &= ~(1 << i);
282
283 /* Set the ISR flag, unless AEOI is enabled */
284 if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << i);
285
286 /* Return the interrupt number */
287 return MasterPic.IntOffset + i;
288 }
289 }
290
291 /* Spurious interrupt */
292 if (MasterPic.InServiceRegister & (1 << 2)) return SlavePic.IntOffset + 7;
293 else return MasterPic.IntOffset + 7;
294 }
295
296 VOID PicInitialize(VOID)
297 {
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);
303 }
304
305
306
307 VOID
308 WINAPI
309 call_ica_hw_interrupt(INT ms,
310 BYTE line,
311 INT count)
312 {
313 BYTE InterruptNumber = line;
314
315 /* Check for PIC validity */
316 if (ms != ICA_MASTER && ms != ICA_SLAVE) return;
317
318 /*
319 * Adjust the interrupt request number according to the parameters,
320 * by adding an offset == 8 to the interrupt number.
321 *
322 * Indeed VDDs calling this function usually subtracts 8 so that they give:
323 *
324 * ms | line | corresponding interrupt number
325 * ------------+--------+--------------------------------
326 * ICA_MASTER | 0 -- 7 | 0 -- 7
327 * ICA_SLAVE | 0 -- 7 | 8 -- 15
328 *
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).
332 */
333 if (ms == ICA_SLAVE) InterruptNumber += 8;
334
335 /* Send the specified number of interrupt requests */
336 while (count-- > 0)
337 {
338 PicInterruptRequest(InterruptNumber);
339 }
340 }
341
342 /* EOF */