[TRANSLATION] Update Romanian translation (#851)
[reactos.git] / subsystems / mvdm / ntvdm / hardware / pic.c
1 /*
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>
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include "ntvdm.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 #include "emulator.h"
18 #include "pic.h"
19
20 #include "io.h"
21
22 /* PRIVATE VARIABLES **********************************************************/
23
24 typedef struct _PIC
25 {
26 BOOLEAN Initialization;
27 BYTE MaskRegister;
28 BYTE IntRequestRegister;
29 BYTE InServiceRegister;
30 BYTE IntOffset;
31 BYTE ConfigRegister;
32 BYTE CascadeRegister;
33 BOOLEAN CascadeRegisterSet;
34 BOOLEAN AutoEoi;
35 BOOLEAN Slave;
36 BOOLEAN ReadIsr;
37 } PIC, *PPIC;
38
39 static PIC MasterPic, SlavePic;
40
41 /* PRIVATE FUNCTIONS **********************************************************/
42
43 static BYTE WINAPI PicReadCommand(USHORT Port)
44 {
45 PPIC Pic;
46
47 /* Which PIC are we accessing? */
48 if (Port == PIC_MASTER_CMD)
49 Pic = &MasterPic;
50 else // if (Port == PIC_SLAVE_CMD)
51 Pic = &SlavePic;
52
53 if (Pic->ReadIsr)
54 {
55 /* Read the in-service register */
56 Pic->ReadIsr = FALSE;
57 return Pic->InServiceRegister;
58 }
59 else
60 {
61 /* Read the interrupt request register */
62 return Pic->IntRequestRegister;
63 }
64 }
65
66 static VOID WINAPI PicWriteCommand(USHORT Port, BYTE Data)
67 {
68 PPIC Pic;
69
70 /* Which PIC are we accessing? */
71 if (Port == PIC_MASTER_CMD)
72 Pic = &MasterPic;
73 else // if (Port == PIC_SLAVE_CMD)
74 Pic = &SlavePic;
75
76 if (Data & PIC_ICW1)
77 {
78 /* Start initialization */
79 Pic->Initialization = TRUE;
80 Pic->IntOffset = 0xFF;
81 Pic->CascadeRegisterSet = FALSE;
82 Pic->ConfigRegister = Data;
83 return;
84 }
85
86 if (Data & PIC_OCW3)
87 {
88 /* This is an OCR3 */
89 if (Data == PIC_OCW3_READ_ISR)
90 {
91 /* Return the ISR on next read from command port */
92 Pic->ReadIsr = TRUE;
93 }
94
95 return;
96 }
97
98 /* This is an OCW2 */
99 if (Data & PIC_OCW2_EOI)
100 {
101 if (Data & PIC_OCW2_SL)
102 {
103 /* If the SL bit is set, clear a specific IRQ */
104 Pic->InServiceRegister &= ~(1 << (Data & PIC_OCW2_NUM_MASK));
105 }
106 else
107 {
108 /* Otherwise, clear all of them */
109 Pic->InServiceRegister = 0;
110 }
111
112 if (MasterPic.IntRequestRegister || SlavePic.IntRequestRegister)
113 {
114 /* Signal the next IRQ */
115 EmulatorInterruptSignal();
116 }
117 }
118 }
119
120 static BYTE WINAPI PicReadData(USHORT Port)
121 {
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;
127 }
128
129 static VOID WINAPI PicWriteData(USHORT Port, BYTE Data)
130 {
131 PPIC Pic;
132
133 /* Which PIC are we accessing? */
134 if (Port == PIC_MASTER_DATA)
135 Pic = &MasterPic;
136 else // if (Port == PIC_SLAVE_DATA)
137 Pic = &SlavePic;
138
139 /* Is the PIC ready? */
140 if (!Pic->Initialization)
141 {
142 /* Yes, this is an OCW1 */
143 Pic->MaskRegister = Data;
144 return;
145 }
146
147 /* Has the interrupt offset been set? */
148 if (Pic->IntOffset == 0xFF)
149 {
150 /* This is an ICW2, set the offset (last three bits always zero) */
151 Pic->IntOffset = Data & 0xF8;
152
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))
156 {
157 /* Yes, done initializing */
158 Pic->Initialization = FALSE;
159 }
160 return;
161 }
162
163 /* Check if we are in cascade mode and the cascade register was not set */
164 if (!(Pic->ConfigRegister & PIC_ICW1_SINGLE) && !Pic->CascadeRegisterSet)
165 {
166 /* This is an ICW3 */
167 Pic->CascadeRegister = Data;
168 Pic->CascadeRegisterSet = TRUE;
169
170 /* Check if we need an ICW4 */
171 if (!(Pic->ConfigRegister & PIC_ICW1_ICW4))
172 {
173 /* No, done initializing */
174 Pic->Initialization = FALSE;
175 }
176 return;
177 }
178
179 /* This must be an ICW4, we will ignore the 8086 bit (assume always set) */
180 if (Data & PIC_ICW4_AEOI)
181 {
182 /* Use automatic end-of-interrupt */
183 Pic->AutoEoi = TRUE;
184 }
185
186 /* Done initializing */
187 Pic->Initialization = FALSE;
188 }
189
190 /* PUBLIC FUNCTIONS ***********************************************************/
191
192 VOID PicInterruptRequest(BYTE Number)
193 {
194 BYTE i;
195
196 if (/* Number >= 0 && */ Number < 8)
197 {
198 /* Check if any of the higher-priority interrupts are busy */
199 for (i = 0; i <= Number; i++)
200 {
201 if (MasterPic.InServiceRegister & (1 << Number)) return;
202 }
203
204 /* Check if the interrupt is masked */
205 if (MasterPic.MaskRegister & (1 << Number)) return;
206
207 /* Set the appropriate bit in the IRR and interrupt the CPU */
208 MasterPic.IntRequestRegister |= 1 << Number;
209 EmulatorInterruptSignal();
210 }
211 else if (Number >= 8 && Number < 16)
212 {
213 Number -= 8;
214
215 /*
216 * The slave PIC is connected to IRQ 2, always! If the master PIC
217 * was misconfigured, don't do anything.
218 */
219 if (!(MasterPic.CascadeRegister & (1 << 2))
220 || SlavePic.CascadeRegister != 2)
221 {
222 return;
223 }
224
225 /* Check if any of the higher-priority interrupts are busy */
226 if (MasterPic.InServiceRegister != 0) return;
227 for (i = 0; i <= Number; i++)
228 {
229 if (SlavePic.InServiceRegister & (1 << Number)) return;
230 }
231
232 /* Check if the interrupt is masked */
233 if (SlavePic.MaskRegister & (1 << Number)) return;
234
235 /* Set the IRQ 2 bit in the master ISR */
236 if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << 2);
237
238 /* Set the appropriate bit in the IRR and interrupt the CPU */
239 SlavePic.IntRequestRegister |= 1 << Number;
240 EmulatorInterruptSignal();
241 }
242 }
243
244 BYTE PicGetInterrupt(VOID)
245 {
246 UINT i;
247
248 /* Search the master PIC interrupts by priority */
249 for (i = 0; i < 8; i++)
250 {
251 if (MasterPic.IntRequestRegister & (1 << i))
252 {
253 /* Clear the IRR flag */
254 MasterPic.IntRequestRegister &= ~(1 << i);
255
256 /* Set the ISR flag, unless AEOI is enabled */
257 if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= (1 << i);
258
259 /* Return the interrupt number */
260 return MasterPic.IntOffset + i;
261 }
262 }
263
264 /* Search the slave PIC interrupts by priority */
265 for (i = 0; i < 8; i++)
266 {
267 if (SlavePic.IntRequestRegister & (1 << i))
268 {
269 /* Clear the IRR flag */
270 SlavePic.IntRequestRegister &= ~(1 << i);
271
272 if ((i == 1) && SlavePic.CascadeRegisterSet)
273 {
274 /* This interrupt is routed to the master PIC */
275 return MasterPic.IntOffset + SlavePic.CascadeRegister;
276 }
277 else
278 {
279 /* Set the ISR flag, unless AEOI is enabled */
280 if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= (1 << i);
281
282 /* Return the interrupt number */
283 return SlavePic.IntOffset + i;
284 }
285 }
286 }
287
288 /* Spurious interrupt */
289 if (MasterPic.InServiceRegister & (1 << 2))
290 return SlavePic.IntOffset + 7;
291 else
292 return MasterPic.IntOffset + 7;
293 }
294
295 VOID PicInitialize(VOID)
296 {
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 );
302 }
303
304
305
306 VOID
307 WINAPI
308 call_ica_hw_interrupt(INT ms,
309 BYTE line,
310 INT count)
311 {
312 BYTE InterruptNumber = line;
313
314 /* Check for PIC validity */
315 if (ms != ICA_MASTER && ms != ICA_SLAVE) return;
316
317 /*
318 * Adjust the interrupt request number according to the parameters,
319 * by adding an offset == 8 to the interrupt number.
320 *
321 * Indeed VDDs calling this function usually subtracts 8 so that they give:
322 *
323 * ms | line | corresponding interrupt number
324 * ------------+--------+--------------------------------
325 * ICA_MASTER | 0 -- 7 | 0 -- 7
326 * ICA_SLAVE | 0 -- 7 | 8 -- 15
327 *
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).
331 */
332 if (ms == ICA_SLAVE) InterruptNumber += 8;
333
334 /* Send the specified number of interrupt requests */
335 while (count-- > 0)
336 {
337 PicInterruptRequest(InterruptNumber);
338 /*
339 * FIXME: We should now restart 16-bit emulation and wait for its termination:
340 *
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
344 * from that thread."
345 */
346 }
347 }
348
349 WORD
350 WINAPI
351 VDDReserveIrqLine(IN HANDLE hVdd,
352 IN WORD IrqLine)
353 {
354 UNIMPLEMENTED;
355 SetLastError(ERROR_INVALID_PARAMETER);
356 return 0xFFFF;
357 }
358
359 BOOL
360 WINAPI
361 VDDReleaseIrqLine(IN HANDLE hVdd,
362 IN WORD IrqLine)
363 {
364 UNIMPLEMENTED;
365 SetLastError(ERROR_INVALID_PARAMETER);
366 return FALSE;
367 }
368
369 /* EOF */