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 */ 370