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