xref: /reactos/subsystems/mvdm/ntvdm/hardware/pic.c (revision c2c66aff)
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 
PicReadCommand(USHORT Port)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 
PicWriteCommand(USHORT Port,BYTE Data)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 
PicReadData(USHORT Port)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 
PicWriteData(USHORT Port,BYTE Data)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 
PicInterruptRequest(BYTE Number)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 
PicGetInterrupt(VOID)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 
PicInitialize(VOID)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
call_ica_hw_interrupt(INT ms,BYTE line,INT count)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
VDDReserveIrqLine(IN HANDLE hVdd,IN WORD IrqLine)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
VDDReleaseIrqLine(IN HANDLE hVdd,IN WORD IrqLine)361 VDDReleaseIrqLine(IN HANDLE hVdd,
362                   IN WORD   IrqLine)
363 {
364     UNIMPLEMENTED;
365     SetLastError(ERROR_INVALID_PARAMETER);
366     return FALSE;
367 }
368 
369 /* EOF */
370