d9552201a688ae4d86625ce9b04e2505d404ff37
[reactos.git] / subsystems / ntvdm / 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 /* PUBLIC FUNCTIONS ***********************************************************/
23
24 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 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 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 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 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 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 VOID PicInterruptRequest(BYTE Number)
198 {
199 BYTE i;
200
201 if (Number >= 0 && Number < 8)
202 {
203 /* Check if any of the higher-priority interrupts are busy */
204 for (i = 0; i <= Number; i++)
205 {
206 if (MasterPic.InServiceRegister & (1 << Number)) return;
207 }
208
209 /* Check if the interrupt is masked */
210 if (MasterPic.MaskRegister & (1 << Number)) return;
211
212 /* Set the appropriate bit in the IRR and interrupt the CPU */
213 MasterPic.IntRequestRegister |= 1 << Number;
214 EmulatorInterruptSignal();
215 }
216 else if (Number >= 8 && Number < 16)
217 {
218 Number -= 8;
219
220 /*
221 * The slave PIC is connected to IRQ 2, always! If the master PIC
222 * was misconfigured, don't do anything.
223 */
224 if (!(MasterPic.CascadeRegister & (1 << 2))
225 || SlavePic.CascadeRegister != 2)
226 {
227 return;
228 }
229
230 /* Check if any of the higher-priority interrupts are busy */
231 if (MasterPic.InServiceRegister != 0) return;
232 for (i = 0; i <= Number; i++)
233 {
234 if (SlavePic.InServiceRegister & (1 << Number)) return;
235 }
236
237 /* Check if the interrupt is masked */
238 if (SlavePic.MaskRegister & (1 << Number)) return;
239
240 /* Set the IRQ 2 bit in the master ISR */
241 if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << 2);
242
243 /* Set the appropriate bit in the IRR and interrupt the CPU */
244 SlavePic.IntRequestRegister |= 1 << Number;
245 EmulatorInterruptSignal();
246 }
247 }
248
249 BYTE PicGetInterrupt(VOID)
250 {
251 INT i, j;
252
253 /* Search interrupts by priority */
254 for (i = 0; i < 8; i++)
255 {
256 /* Check if this line is cascaded to the slave PIC */
257 if ((i == 2)
258 && MasterPic.CascadeRegister & (1 << 2)
259 && SlavePic.Slave
260 && (SlavePic.CascadeRegister == 2))
261 {
262 /* Search the slave PIC interrupts by priority */
263 for (j = 0; j < 8; j++) if ((j != 1) && SlavePic.IntRequestRegister & (1 << j))
264 {
265 /* Clear the IRR flag */
266 SlavePic.IntRequestRegister &= ~(1 << j);
267
268 /* Set the ISR flag, unless AEOI is enabled */
269 if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= (1 << j);
270
271 /* Return the interrupt number */
272 return SlavePic.IntOffset + j;
273 }
274 }
275
276 if (MasterPic.IntRequestRegister & (1 << i))
277 {
278 /* Clear the IRR flag */
279 MasterPic.IntRequestRegister &= ~(1 << i);
280
281 /* Set the ISR flag, unless AEOI is enabled */
282 if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << i);
283
284 /* Return the interrupt number */
285 return MasterPic.IntOffset + i;
286 }
287 }
288
289 /* Spurious interrupt */
290 if (MasterPic.InServiceRegister & (1 << 2)) return SlavePic.IntOffset + 7;
291 else return MasterPic.IntOffset + 7;
292 }
293
294 BOOLEAN PicInitialize(VOID)
295 {
296 /* Register the I/O Ports */
297 RegisterIoPort(PIC_MASTER_CMD , PicReadPort, PicWritePort);
298 RegisterIoPort(PIC_SLAVE_CMD , PicReadPort, PicWritePort);
299 RegisterIoPort(PIC_MASTER_DATA, PicReadPort, PicWritePort);
300 RegisterIoPort(PIC_SLAVE_DATA , PicReadPort, PicWritePort);
301
302 return TRUE;
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 */