1 /* 2 * COPYRIGHT: GPL - See COPYING in the top level directory 3 * PROJECT: ReactOS Virtual DOS Machine 4 * FILE: subsystems/mvdm/ntvdm/hardware/pit.c 5 * PURPOSE: Programmable Interval Timer emulation - 6 * i82C54/8254 compatible 7 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org> 8 * Hermes Belusca-Maito (hermes.belusca@sfr.fr) 9 */ 10 11 /* INCLUDES *******************************************************************/ 12 13 #include "ntvdm.h" 14 15 #define NDEBUG 16 #include <debug.h> 17 18 #include "emulator.h" 19 #include "pit.h" 20 21 #include "io.h" 22 #include "pic.h" 23 #include "clock.h" 24 25 /* PRIVATE VARIABLES **********************************************************/ 26 27 static PIT_CHANNEL PitChannels[PIT_CHANNELS]; 28 static PHARDWARE_TIMER MasterClock; 29 30 /* PRIVATE FUNCTIONS **********************************************************/ 31 32 static VOID PitLatchChannelStatus(BYTE Channel) 33 { 34 if (Channel >= PIT_CHANNELS) return; 35 36 /* 37 * A given counter can be latched only one time until it gets unlatched. 38 * If the counter is latched and then is latched again later before the 39 * value is read, then this last latch command is ignored and the value 40 * will be the value at the time the first command was issued. 41 */ 42 if (PitChannels[Channel].LatchStatusSet == FALSE) 43 { 44 BYTE StatusLatch = 0; 45 /** HACK!! **/BYTE NullCount = 0;/** HACK!! **/ 46 47 StatusLatch = PitChannels[Channel].Out << 7 | NullCount << 6; 48 StatusLatch |= (PitChannels[Channel].ReadWriteMode & 0x03) << 4; 49 StatusLatch |= (PitChannels[Channel].Mode & 0x07) << 1; 50 StatusLatch |= (PitChannels[Channel].Bcd & 0x01); 51 52 /* Latch the counter's status */ 53 PitChannels[Channel].LatchStatusSet = TRUE; 54 PitChannels[Channel].StatusLatch = StatusLatch; 55 } 56 } 57 58 static VOID PitLatchChannelCount(BYTE Channel) 59 { 60 if (Channel >= PIT_CHANNELS) return; 61 62 /* 63 * A given counter can be latched only one time until it gets unlatched. 64 * If the counter is latched and then is latched again later before the 65 * value is read, then this last latch command is ignored and the value 66 * will be the value at the time the first command was issued. 67 */ 68 if (PitChannels[Channel].ReadStatus == 0x00) 69 { 70 /* Latch the counter's value */ 71 PitChannels[Channel].ReadStatus = PitChannels[Channel].ReadWriteMode; 72 73 /* Convert the current value to BCD if needed */ 74 PitChannels[Channel].OutputLatch = 75 READ_PIT_VALUE(PitChannels[Channel], PitChannels[Channel].CurrentValue); 76 } 77 } 78 79 static VOID PitSetOut(PPIT_CHANNEL Channel, BOOLEAN State) 80 { 81 /** HACK!! **\ if (State == Channel->Out) return; \** HACK!! **/ 82 83 /* Set the new state of the OUT pin */ 84 Channel->Out = State; 85 86 /* Call the callback */ 87 if (!Channel->Gate) return; // HACK: This is a HACK until gates are properly used (needed for the speaker to work properly). 88 if (Channel->OutFunction) Channel->OutFunction(Channel->OutParam, State); 89 } 90 91 static VOID PitInitCounter(PPIT_CHANNEL Channel) 92 { 93 switch (Channel->Mode) 94 { 95 case PIT_MODE_INT_ON_TERMINAL_COUNT: 96 PitSetOut(Channel, FALSE); 97 break; 98 99 case PIT_MODE_HARDWARE_ONE_SHOT: 100 case PIT_MODE_RATE_GENERATOR: 101 case PIT_MODE_SQUARE_WAVE: 102 case PIT_MODE_SOFTWARE_STROBE: 103 case PIT_MODE_HARDWARE_STROBE: 104 PitSetOut(Channel, TRUE); 105 break; 106 } 107 } 108 109 static VOID PitWriteCommand(BYTE Value) 110 { 111 BYTE Channel = (Value >> 6) & 0x03; 112 BYTE ReadWriteMode = (Value >> 4) & 0x03; 113 BYTE Mode = (Value >> 1) & 0x07; 114 BOOLEAN IsBcd = Value & 0x01; 115 116 /* 117 * Check for valid PIT channel - Possible values: 0, 1, 2. 118 * A value of 3 is for Read-Back Command. 119 */ 120 if (Channel > PIT_CHANNELS) return; 121 122 /* Read-Back Command */ 123 if (Channel == PIT_CHANNELS) 124 { 125 if ((Value & 0x20) == 0) // Bit 5 (Count) == 0: We latch multiple counters' counts 126 { 127 if (Value & 0x02) PitLatchChannelCount(0); 128 if (Value & 0x04) PitLatchChannelCount(1); 129 if (Value & 0x08) PitLatchChannelCount(2); 130 } 131 if ((Value & 0x10) == 0) // Bit 4 (Status) == 0: We latch multiple counters' statuses 132 { 133 if (Value & 0x02) PitLatchChannelStatus(0); 134 if (Value & 0x04) PitLatchChannelStatus(1); 135 if (Value & 0x08) PitLatchChannelStatus(2); 136 } 137 return; 138 } 139 140 /* Check if this is a counter latch command... */ 141 if (ReadWriteMode == 0) 142 { 143 PitLatchChannelCount(Channel); 144 return; 145 } 146 147 /* ... otherwise, set the modes and reset flip-flops */ 148 PitChannels[Channel].ReadWriteMode = ReadWriteMode; 149 PitChannels[Channel].ReadStatus = 0x00; 150 PitChannels[Channel].WriteStatus = 0x00; 151 152 PitChannels[Channel].LatchStatusSet = FALSE; 153 PitChannels[Channel].StatusLatch = 0x00; 154 155 PitChannels[Channel].CountRegister = 0x00; 156 PitChannels[Channel].OutputLatch = 0x00; 157 158 /** HACK!! **/PitChannels[Channel].FlipFlop = FALSE;/** HACK!! **/ 159 160 /* Fix the current value if we switch to BCD counting */ 161 PitChannels[Channel].Bcd = IsBcd; 162 if (IsBcd && PitChannels[Channel].CurrentValue > 9999) 163 PitChannels[Channel].CurrentValue = 9999; 164 165 switch (Mode) 166 { 167 case 0: 168 case 1: 169 case 2: 170 case 3: 171 case 4: 172 case 5: 173 { 174 PitChannels[Channel].Mode = Mode; 175 break; 176 } 177 178 case 6: 179 case 7: 180 { 181 /* 182 * Modes 6 and 7 become PIT_MODE_RATE_GENERATOR 183 * and PIT_MODE_SQUARE_WAVE respectively. 184 */ 185 PitChannels[Channel].Mode = Mode - 4; 186 break; 187 } 188 } 189 190 PitInitCounter(&PitChannels[Channel]); 191 } 192 193 static BYTE PitReadData(BYTE Channel) 194 { 195 LPBYTE ReadWriteMode = NULL; 196 LPWORD CurrentValue = NULL; 197 198 /* 199 * If the status was latched, the first read operation will return the 200 * latched status, whichever value (count or status) was latched first. 201 */ 202 if (PitChannels[Channel].LatchStatusSet) 203 { 204 PitChannels[Channel].LatchStatusSet = FALSE; 205 return PitChannels[Channel].StatusLatch; 206 } 207 208 /* To be able to read the count asynchronously, latch it first if needed */ 209 if (PitChannels[Channel].ReadStatus == 0) PitLatchChannelCount(Channel); 210 211 /* The count is now latched */ 212 ASSERT(PitChannels[Channel].ReadStatus != 0); 213 214 ReadWriteMode = &PitChannels[Channel].ReadStatus ; 215 CurrentValue = &PitChannels[Channel].OutputLatch; 216 217 if (*ReadWriteMode & 1) 218 { 219 /* Read LSB */ 220 *ReadWriteMode &= ~1; 221 return LOBYTE(*CurrentValue); 222 } 223 224 if (*ReadWriteMode & 2) 225 { 226 /* Read MSB */ 227 *ReadWriteMode &= ~2; 228 return HIBYTE(*CurrentValue); 229 } 230 231 /* Shouldn't get here */ 232 ASSERT(FALSE); 233 return 0; 234 } 235 236 static VOID PitWriteData(BYTE Channel, BYTE Value) 237 { 238 LPBYTE ReadWriteMode = NULL; 239 240 if (PitChannels[Channel].WriteStatus == 0x00) 241 { 242 PitChannels[Channel].WriteStatus = PitChannels[Channel].ReadWriteMode; 243 } 244 245 ASSERT(PitChannels[Channel].WriteStatus != 0); 246 247 ReadWriteMode = &PitChannels[Channel].WriteStatus; 248 249 if (*ReadWriteMode & 1) 250 { 251 /* Write LSB */ 252 *ReadWriteMode &= ~1; 253 PitChannels[Channel].CountRegister &= 0xFF00; 254 PitChannels[Channel].CountRegister |= Value; 255 } 256 else if (*ReadWriteMode & 2) 257 { 258 /* Write MSB */ 259 *ReadWriteMode &= ~2; 260 PitChannels[Channel].CountRegister &= 0x00FF; 261 PitChannels[Channel].CountRegister |= Value << 8; 262 } 263 264 /* ReadWriteMode went to zero: we are going to load the new count */ 265 if (*ReadWriteMode == 0x00) 266 { 267 if (PitChannels[Channel].CountRegister == 0x0000) 268 { 269 /* Wrap around to the highest count */ 270 if (PitChannels[Channel].Bcd) 271 PitChannels[Channel].CountRegister = 9999; 272 else 273 PitChannels[Channel].CountRegister = 0xFFFF; // 0x10000; // 65536 274 } 275 276 /* Convert the current value from BCD if needed */ 277 PitChannels[Channel].CountRegister = 278 WRITE_PIT_VALUE(PitChannels[Channel], PitChannels[Channel].CountRegister); 279 PitChannels[Channel].ReloadValue = PitChannels[Channel].CountRegister; 280 281 /* Reload now the new count */ 282 PitChannels[Channel].CurrentValue = PitChannels[Channel].ReloadValue; 283 } 284 } 285 286 static BYTE WINAPI PitReadPort(USHORT Port) 287 { 288 switch (Port) 289 { 290 case PIT_DATA_PORT(0): 291 case PIT_DATA_PORT(1): 292 case PIT_DATA_PORT(2): 293 { 294 return PitReadData(Port - PIT_DATA_PORT(0)); 295 } 296 } 297 298 return 0; 299 } 300 301 static VOID WINAPI PitWritePort(USHORT Port, BYTE Data) 302 { 303 switch (Port) 304 { 305 case PIT_COMMAND_PORT: 306 { 307 PitWriteCommand(Data); 308 break; 309 } 310 311 case PIT_DATA_PORT(0): 312 case PIT_DATA_PORT(1): 313 case PIT_DATA_PORT(2): 314 { 315 PitWriteData(Port - PIT_DATA_PORT(0), Data); 316 break; 317 } 318 } 319 } 320 321 static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count) 322 { 323 if (Count == 0) return; 324 325 switch (Channel->Mode) 326 { 327 case PIT_MODE_INT_ON_TERMINAL_COUNT: 328 { 329 /* Decrement the value */ 330 if (Count > Channel->CurrentValue) 331 { 332 /* The value does not reload in this case */ 333 Channel->CurrentValue = 0; 334 } 335 else Channel->CurrentValue -= Count; 336 337 /* Did it fall to the terminal count? */ 338 if (Channel->CurrentValue == 0 && !Channel->Out) 339 { 340 /* Yes, raise the output line */ 341 PitSetOut(Channel, TRUE); 342 } 343 break; 344 } 345 346 case PIT_MODE_RATE_GENERATOR: 347 { 348 BOOLEAN Reloaded = FALSE; 349 350 while (Count) 351 { 352 if ((Count > Channel->CurrentValue) 353 && (Channel->CurrentValue != 0)) 354 { 355 /* Decrement the count */ 356 Count -= Channel->CurrentValue; 357 358 /* Reload the value */ 359 Channel->CurrentValue = Channel->ReloadValue; 360 361 /* Set the flag */ 362 Reloaded = TRUE; 363 } 364 else 365 { 366 /* Decrement the value */ 367 Channel->CurrentValue -= Count; 368 369 /* Clear the count */ 370 Count = 0; 371 372 /* Did it fall to zero? */ 373 if (Channel->CurrentValue == 0) 374 { 375 Channel->CurrentValue = Channel->ReloadValue; 376 Reloaded = TRUE; 377 } 378 } 379 } 380 381 /* If there was a reload, raise the output line */ 382 if (Reloaded) PitSetOut(Channel, TRUE); 383 384 break; 385 } 386 387 case PIT_MODE_SQUARE_WAVE: 388 { 389 INT ReloadCount = 0; 390 WORD ReloadValue = Channel->ReloadValue; 391 392 /* The reload value must be even */ 393 ReloadValue &= ~1; 394 395 while (Count) 396 { 397 if (((Count * 2) > Channel->CurrentValue) 398 && (Channel->CurrentValue != 0)) 399 { 400 /* Decrement the count */ 401 Count -= Channel->CurrentValue / 2; 402 403 /* Reload the value */ 404 Channel->CurrentValue = ReloadValue; 405 406 /* Increment the reload count */ 407 ReloadCount++; 408 } 409 else 410 { 411 /* Decrement the value */ 412 Channel->CurrentValue -= Count * 2; 413 414 /* Clear the count */ 415 Count = 0; 416 417 /* Did it fall to zero? */ 418 if (Channel->CurrentValue == 0) 419 { 420 /* Reload the value */ 421 Channel->CurrentValue = ReloadValue; 422 423 /* Increment the reload count */ 424 ReloadCount++; 425 } 426 } 427 } 428 429 if (ReloadCount == 0) break; 430 431 /* Toggle the flip-flop if the number of reloads was odd */ 432 if (ReloadCount & 1) 433 { 434 Channel->FlipFlop = !Channel->FlipFlop; 435 PitSetOut(Channel, !Channel->Out); 436 } 437 438 /* Was there any rising edge? */ 439 if ((Channel->FlipFlop && (ReloadCount == 1)) || (ReloadCount > 1)) 440 { 441 /* Yes, raise the output line */ 442 PitSetOut(Channel, TRUE); 443 } 444 445 break; 446 } 447 448 case PIT_MODE_SOFTWARE_STROBE: 449 { 450 // TODO: NOT IMPLEMENTED 451 break; 452 } 453 454 case PIT_MODE_HARDWARE_ONE_SHOT: 455 case PIT_MODE_HARDWARE_STROBE: 456 { 457 /* These modes do not work on x86 PCs */ 458 break; 459 } 460 } 461 } 462 463 static VOID FASTCALL PitClock(ULONGLONG Count) 464 { 465 UCHAR i; 466 467 for (i = 0; i < PIT_CHANNELS; i++) 468 { 469 // if (!PitChannels[i].Counting) continue; 470 PitDecrementCount(&PitChannels[i], Count); 471 } 472 } 473 474 /* PUBLIC FUNCTIONS ***********************************************************/ 475 476 VOID PitSetOutFunction(BYTE Channel, LPVOID Param, PIT_OUT_FUNCTION OutFunction) 477 { 478 if (Channel >= PIT_CHANNELS) return; 479 480 PitChannels[Channel].OutParam = Param; 481 PitChannels[Channel].OutFunction = OutFunction; 482 } 483 484 VOID PitSetGate(BYTE Channel, BOOLEAN State) 485 { 486 if (Channel >= PIT_CHANNELS) return; 487 if (State == PitChannels[Channel].Gate) return; 488 489 /* UNIMPLEMENTED */ 490 PitChannels[Channel].Gate = State; 491 } 492 493 WORD PitGetReloadValue(BYTE Channel) 494 { 495 if (Channel >= PIT_CHANNELS) return 0xFFFF; 496 497 if (PitChannels[Channel].ReloadValue == 0) 498 return 0xFFFF; 499 else 500 return PitChannels[Channel].ReloadValue; 501 } 502 503 VOID PitInitialize(VOID) 504 { 505 /* Set up the timers to their default value */ 506 PitSetOutFunction(0, NULL, NULL); 507 PitSetGate(0, TRUE); 508 PitSetOutFunction(1, NULL, NULL); 509 PitSetGate(1, TRUE); 510 PitSetOutFunction(2, NULL, NULL); 511 PitSetGate(2, FALSE); 512 513 /* Register the I/O Ports */ 514 RegisterIoPort(PIT_COMMAND_PORT, NULL, PitWritePort); 515 RegisterIoPort(PIT_DATA_PORT(0), PitReadPort, PitWritePort); 516 RegisterIoPort(PIT_DATA_PORT(1), PitReadPort, PitWritePort); 517 RegisterIoPort(PIT_DATA_PORT(2), PitReadPort, PitWritePort); 518 519 /* Register the hardware timer */ 520 MasterClock = CreateHardwareTimer(HARDWARE_TIMER_ENABLED | HARDWARE_TIMER_PRECISE, 521 HZ_TO_NS(PIT_BASE_FREQUENCY), 522 PitClock); 523 } 524 525 /* EOF */ 526