51d2febbb735b519354614ec964e49aae735c184
[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 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "pic.h"
14 #include "emulator.h"
15
16 /* PRIVATE VARIABLES **********************************************************/
17
18 static PIC MasterPic, SlavePic;
19
20 /* PUBLIC FUNCTIONS ***********************************************************/
21
22 BYTE PicReadCommand(BYTE Port)
23 {
24 PPIC Pic;
25
26 /* Which PIC are we accessing? */
27 if (Port == PIC_MASTER_CMD) Pic = &MasterPic;
28 else Pic = &SlavePic;
29
30 if (Pic->ReadIsr)
31 {
32 /* Read the in-service register */
33 Pic->ReadIsr = FALSE;
34 return Pic->InServiceRegister;
35 }
36 else
37 {
38 /* The IRR is always 0, as the emulated CPU receives the interrupt instantly */
39 return 0;
40 }
41 }
42
43 VOID PicWriteCommand(BYTE Port, BYTE Value)
44 {
45 PPIC Pic;
46
47 /* Which PIC are we accessing? */
48 if (Port == PIC_MASTER_CMD) Pic = &MasterPic;
49 else Pic = &SlavePic;
50
51 if (Value & PIC_ICW1)
52 {
53 /* Start initialization */
54 Pic->Initialization = TRUE;
55 Pic->IntOffset = 0xFF;
56 Pic->CascadeRegisterSet = FALSE;
57 Pic->ConfigRegister = Value;
58 return;
59 }
60
61 if (Value & PIC_OCW3)
62 {
63 /* This is an OCR3 */
64 if (Value == PIC_OCW3_READ_ISR)
65 {
66 /* Return the ISR on next read from command port */
67 Pic->ReadIsr = TRUE;
68 }
69
70 return;
71 }
72
73 /* This is an OCW2 */
74 if (Value & PIC_OCW2_EOI)
75 {
76 if (Value & PIC_OCW2_SL)
77 {
78 /* If the SL bit is set, clear a specific IRQ */
79 Pic->InServiceRegister &= ~(1 << (Value & PIC_OCW2_NUM_MASK));
80 }
81 else
82 {
83 /* Otherwise, clear all of them */
84 Pic->InServiceRegister = 0;
85 }
86 }
87 }
88
89 BYTE PicReadData(BYTE Port)
90 {
91 /* Read the mask register */
92 if (Port == PIC_MASTER_DATA) return MasterPic.MaskRegister;
93 else return SlavePic.MaskRegister;
94 }
95
96 VOID PicWriteData(BYTE Port, BYTE Value)
97 {
98 PPIC Pic;
99
100 /* Which PIC are we accessing? */
101 if (Port == PIC_MASTER_DATA) Pic = &MasterPic;
102 else Pic = &SlavePic;
103
104 /* Is the PIC ready? */
105 if (!Pic->Initialization)
106 {
107 /* Yes, this is an OCW1 */
108 Pic->MaskRegister = Value;
109 return;
110 }
111
112 /* Has the interrupt offset been set? */
113 if (Pic->IntOffset == 0xFF)
114 {
115 /* This is an ICW2, set the offset (last three bits always zero) */
116 Pic->IntOffset = Value & 0xF8;
117
118 /* Check if we are in single mode and don't need an ICW4 */
119 if ((Pic->ConfigRegister & PIC_ICW1_SINGLE)
120 && !(Pic->ConfigRegister & PIC_ICW1_ICW4))
121 {
122 /* Yes, done initializing */
123 Pic->Initialization = FALSE;
124 }
125 return;
126 }
127
128 /* Check if we are in cascade mode and the cascade register was not set */
129 if (!(Pic->ConfigRegister & PIC_ICW1_SINGLE) && !Pic->CascadeRegisterSet)
130 {
131 /* This is an ICW3 */
132 Pic->CascadeRegister = Value;
133 Pic->CascadeRegisterSet = TRUE;
134
135 /* Check if we need an ICW4 */
136 if (!(Pic->ConfigRegister & PIC_ICW1_ICW4))
137 {
138 /* No, done initializing */
139 Pic->Initialization = FALSE;
140 }
141 return;
142 }
143
144 /* This must be an ICW4, we will ignore the 8086 bit (assume always set) */
145 if (Value & PIC_ICW4_AEOI)
146 {
147 /* Use automatic end-of-interrupt */
148 Pic->AutoEoi = TRUE;
149 }
150
151 /* Done initializing */
152 Pic->Initialization = FALSE;
153 }
154
155 VOID PicInterruptRequest(BYTE Number)
156 {
157 BYTE i;
158
159 if (Number >= 0 && Number < 8)
160 {
161 /* Check if any of the higher-priorirty interrupts are busy */
162 for (i = 0; i <= Number ; i++)
163 {
164 if (MasterPic.InServiceRegister & (1 << Number)) return;
165 }
166
167 /* Check if the interrupt is masked */
168 if (MasterPic.MaskRegister & (1 << Number)) return;
169
170 /* Set the appropriate bit in the ISR and interrupt the CPU */
171 if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << Number;
172 EmulatorExternalInterrupt(MasterPic.IntOffset + Number);
173 }
174 else if (Number >= 8 && Number < 16)
175 {
176 Number -= 8;
177
178 /*
179 * The slave PIC is connected to IRQ 2, always! If the master PIC
180 * was misconfigured, don't do anything.
181 */
182 if (!(MasterPic.CascadeRegister & (1 << 2))
183 || SlavePic.CascadeRegister != 2)
184 {
185 return;
186 }
187
188 /* Check if any of the higher-priorirty interrupts are busy */
189 if (MasterPic.InServiceRegister != 0) return;
190 for (i = 0; i <= Number ; i++)
191 {
192 if (SlavePic.InServiceRegister & (1 << Number)) return;
193 }
194
195 /* Check if the interrupt is masked */
196 if (SlavePic.MaskRegister & (1 << Number)) return;
197
198 /* Set the IRQ 2 bit in the master ISR */
199 if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << 2;
200
201 /* Set the appropriate bit in the ISR and interrupt the CPU */
202 if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= 1 << Number;
203 EmulatorExternalInterrupt(SlavePic.IntOffset + Number);
204 }
205 }
206
207 /* EOF */