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