diff --git a/reactos/hal/halx86/generic/beep.c b/reactos/hal/halx86/generic/beep.c index 4b82f2b9ea6..70612cd0be5 100644 --- a/reactos/hal/halx86/generic/beep.c +++ b/reactos/hal/halx86/generic/beep.c @@ -1,10 +1,9 @@ /* * PROJECT: ReactOS HAL - * LICENSE: GPL - See COPYING in the top level directory + * LICENSE: BSD - See COPYING.ARM in the top level directory * FILE: hal/halx86/generic/beep.c * PURPOSE: Speaker support (beeping) - * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) - * Eric Kohl (ekohl@abo.rhein-zeitung.de) + * PROGRAMMERS: ReactOS Portable Systems Group */ /* INCLUDES ******************************************************************/ @@ -13,13 +12,6 @@ #define NDEBUG #include -/* CONSTANTS *****************************************************************/ - -#define TIMER2 (PUCHAR)0x42 -#define TIMER3 (PUCHAR)0x43 -#define PORT_B (PUCHAR)0x61 -#define CLOCKFREQ 1193167 - /* FUNCTIONS *****************************************************************/ /* @@ -29,42 +21,96 @@ BOOLEAN NTAPI HalMakeBeep(IN ULONG Frequency) { - UCHAR Data; + SYSTEM_CONTROL_PORT_B_REGISTER SystemControl; + TIMER_CONTROL_PORT_REGISTER TimerControl; ULONG Divider; + BOOLEAN Result = FALSE; - /* Acquire CMOS Lock */ + // + // Acquire CMOS Lock + // HalpAcquireSystemHardwareSpinLock(); - /* Turn the register off */ - Data = READ_PORT_UCHAR(PORT_B); - WRITE_PORT_UCHAR(PORT_B, Data & 0xFC); + // + // Turn the timer off by disconnecting its output pin and speaker gate + // + SystemControl.Bits = __inbyte(SYSTEM_CONTROL_PORT_B); + SystemControl.SpeakerDataEnable = FALSE; + SystemControl.Timer2GateToSpeaker = FALSE; + __outbyte(SYSTEM_CONTROL_PORT_B, SystemControl.Bits); - /* Check if we have a frequency */ + // + // Check if we have a frequency + // if (Frequency) { - /* Set the divider */ - Divider = CLOCKFREQ / Frequency; + // + // Set the divider + // + Divider = PIT_FREQUENCY / Frequency; - /* Check if it's too large */ - if (Divider > 0x10000) + // + // Check if it's too large + // + if (Divider <= 0x10000) { - /* Fail */ - HalpReleaseCmosSpinLock(); - return FALSE; + // + // Program the PIT for binary mode + // + TimerControl.BcdMode = FALSE; + + // + // Program the PIT to generate a square wave (Mode 3) on channel 2. + // Channel 0 is used for the IRQ0 clock interval timer, and channel + // 1 is used for DRAM refresh. + // + // Mode 2 gives much better accuracy, but generates an output signal + // that drops to low for each input signal cycle at 0.8381 useconds. + // This is too fast for the PC speaker to process and would result + // in no sound being emitted. + // + // Mode 3 will generate a high pulse that is a bit longer and will + // allow the PC speaker to notice. Additionally, take note that on + // channel 2, when input goes low the counter will stop and output + // will go to high. + // + TimerControl.OperatingMode = PitOperatingMode3; + TimerControl.Channel = PitChannel2; + + // + // Set the access mode that we'll use to program the reload value. + // + TimerControl.AccessMode = PitAccessModeLowHigh; + + // + // Now write the programming bits + // + __outbyte(TIMER_CONTROL_PORT, TimerControl.Bits); + + // + // Next we write the reload value for channel 2 + // + __outbyte(TIMER_CHANNEL2_DATA_PORT, Divider & 0xFF); + __outbyte(TIMER_CHANNEL2_DATA_PORT, Divider >> 8); + + // + // Reconnect the speaker to the timer and re-enable the output pin + // + SystemControl.Bits = __inbyte(SYSTEM_CONTROL_PORT_B); + SystemControl.SpeakerDataEnable = TRUE; + SystemControl.Timer2GateToSpeaker = TRUE; + __outbyte(SYSTEM_CONTROL_PORT_B, SystemControl.Bits); + Result = TRUE; } - - /* Set timer divider */ - WRITE_PORT_UCHAR(TIMER3, 0xB6); - WRITE_PORT_UCHAR(TIMER2, (UCHAR)(Divider & 0xFF)); - WRITE_PORT_UCHAR(TIMER2, (UCHAR)((Divider>>8) & 0xFF)); - - /* Turn speaker on */ - WRITE_PORT_UCHAR(PORT_B, READ_PORT_UCHAR(PORT_B) | 0x03); } - /* Release CMOS lock */ + // + // Release CMOS lock + // HalpReleaseCmosSpinLock(); - /* Return success */ - return TRUE; + // + // Return success + // + return Result; } diff --git a/reactos/hal/halx86/generic/misc.c b/reactos/hal/halx86/generic/misc.c index 794ee6a9c6f..e6021d894b0 100644 --- a/reactos/hal/halx86/generic/misc.c +++ b/reactos/hal/halx86/generic/misc.c @@ -88,7 +88,7 @@ HalpFlushTLB(VOID) Cr4 = __readcr4(); // - // Disable global pit + // Disable global bit // __writecr4(Cr4 & ~CR4_PGE); @@ -121,7 +121,7 @@ VOID NTAPI HalHandleNMI(IN PVOID NmiInfo) { - UCHAR NmiStatus; + SYSTEM_CONTROL_PORT_B_REGISTER SystemControl; // // Don't recurse @@ -129,9 +129,9 @@ HalHandleNMI(IN PVOID NmiInfo) if (HalpNMIInProgress++) while (TRUE); // - // Get the NMI Flag + // Read the system control register B // - NmiStatus = READ_PORT_UCHAR((PUCHAR)0x61); + SystemControl.Bits = __inbyte(SYSTEM_CONTROL_PORT_B); // // Switch to boot vieo @@ -161,29 +161,29 @@ HalHandleNMI(IN PVOID NmiInfo) // // Display NMI failure string // - HalDisplayString("\n*** Hardware Malfunction\n\n"); - HalDisplayString("Call your hardware vendor for support\n\n"); + InbvDisplayString("\n*** Hardware Malfunction\n\n"); + InbvDisplayString("Call your hardware vendor for support\n\n"); // // Check for parity error // - if (NmiStatus & 0x80) + if (SystemControl.ParityCheck) { // // Display message // - HalDisplayString("NMI: Parity Check / Memory Parity Error\n"); + InbvDisplayString("NMI: Parity Check / Memory Parity Error\n"); } // // Check for I/O failure // - if (NmiStatus & 0x40) + if (SystemControl.ChannelCheck) { // // Display message // - HalDisplayString("NMI: Channel Check / IOCHK\n"); + InbvDisplayString("NMI: Channel Check / IOCHK\n"); } // @@ -200,12 +200,12 @@ HalHandleNMI(IN PVOID NmiInfo) // // Halt the system // - HalDisplayString("\n*** The system has halted ***\n"); + InbvDisplayString("\n*** The system has halted ***\n"); // // Enter the debugger if possible // - //if (!KdDebuggerNotPresent && KdDebuggerEnabled) KeEnterKernelDebugger(); + //if (!(KdDebuggerNotPresent) && (KdDebuggerEnabled)) KeEnterKernelDebugger(); // // Freeze the system diff --git a/reactos/hal/halx86/generic/timer.c b/reactos/hal/halx86/generic/timer.c index e7ef223f506..239db03bb94 100644 --- a/reactos/hal/halx86/generic/timer.c +++ b/reactos/hal/halx86/generic/timer.c @@ -49,6 +49,7 @@ HalpInitializeClock(VOID) ULONG Increment; USHORT RollOver; ULONG_PTR Flags; + TIMER_CONTROL_PORT_REGISTER TimerControl; /* Check the CPU Type */ if (Prcb->CpuType <= 4) @@ -59,7 +60,7 @@ HalpInitializeClock(VOID) } /* Get increment and rollover for the largest time clock ms possible */ - Increment= HalpRolloverTable[HalpLargestClockMS - 1].HighPart; + Increment = HalpRolloverTable[HalpLargestClockMS - 1].HighPart; RollOver = (USHORT)HalpRolloverTable[HalpLargestClockMS - 1].LowPart; /* Set the maximum and minimum increment with the kernel */ @@ -69,11 +70,37 @@ HalpInitializeClock(VOID) /* Disable interrupts */ Flags = __readeflags(); _disable(); + + // + // Program the PIT for binary mode + // + TimerControl.BcdMode = FALSE; - /* Set the rollover */ - __outbyte(TIMER_CONTROL_PORT, TIMER_SC0 | TIMER_BOTH | TIMER_MD2); - __outbyte(TIMER_DATA_PORT0, RollOver & 0xFF); - __outbyte(TIMER_DATA_PORT0, RollOver >> 8); + // + // Program the PIT to generate a normal rate wave (Mode 3) on channel 0. + // Channel 0 is used for the IRQ0 clock interval timer, and channel + // 1 is used for DRAM refresh. + // + // Mode 2 gives much better accuracy than Mode 3. + // + TimerControl.OperatingMode = PitOperatingMode2; + TimerControl.Channel = PitChannel0; + + // + // Set the access mode that we'll use to program the reload value. + // + TimerControl.AccessMode = PitAccessModeLowHigh; + + // + // Now write the programming bits + // + __outbyte(TIMER_CONTROL_PORT, TimerControl.Bits); + + // + // Next we write the reload value for channel 0 + // + __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver & 0xFF); + __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver >> 8); /* Restore interrupts if they were previously enabled */ __writeeflags(Flags); diff --git a/reactos/hal/halx86/include/halp.h b/reactos/hal/halx86/include/halp.h index 639283e5c23..6fe0daab24d 100644 --- a/reactos/hal/halx86/include/halp.h +++ b/reactos/hal/halx86/include/halp.h @@ -16,13 +16,6 @@ #define RTC_REG_A_UIP 0x80 #define RTC_REGISTER_CENTURY 0x32 -/* Timer Registers and Ports */ -#define TIMER_CONTROL_PORT 0x43 -#define TIMER_DATA_PORT0 0x40 -#define TIMER_SC0 0 -#define TIMER_BOTH 0x30 -#define TIMER_MD2 0x4 - /* Usage flags */ #define IDT_REGISTERED 0x01 #define IDT_LATCHED 0x02 @@ -34,7 +27,106 @@ (((bcd & 0xF0) >> 4) * 10 + (bcd & 0x0F)) #define INT_BCD(int) \ (UCHAR)(((int / 10) << 4) + (int % 10)) - + +// +// Commonly stated as being 1.19318MHz +// +// See ISA System Architecture 3rd Edition (Tom Shanley, Don Anderson, John Swindle) +// P. 471 +// +// However, the true value is closer to 1.19318181[...]81MHz since this is 1/3rd +// of the NTSC color subcarrier frequency which runs at 3.57954545[...]45MHz. +// +// Note that Windows uses 1.193167MHz which seems to have no basis. However, if +// one takes the NTSC color subcarrier frequency as being 3.579545 (trimming the +// infinite series) and divides it by three, one obtains 1.19318167. +// +// It may be that the original NT HAL source code introduced a typo and turned +// 119318167 into 1193167 by ommitting the "18". This is very plausible as the +// number is quite long. +// +#define PIT_FREQUENCY 1193182 + +// +// These ports are controlled by the i8254 Programmable Interrupt Timer (PIT) +// +#define TIMER_CHANNEL0_DATA_PORT 0x40 +#define TIMER_CHANNEL1_DATA_PORT 0x41 +#define TIMER_CHANNEL2_DATA_PORT 0x42 +#define TIMER_CONTROL_PORT 0x43 + +// +// Mode 0 - Interrupt On Terminal Count +// Mode 1 - Hardware Re-triggerable One-Shot +// Mode 2 - Rate Generator +// Mode 3 - Square Wave Generator +// Mode 4 - Software Triggered Strobe +// Mode 5 - Hardware Triggered Strobe +// +typedef enum _TIMER_OPERATING_MODES +{ + PitOperatingMode0, + PitOperatingMode1, + PitOperatingMode2, + PitOperatingMode3, + PitOperatingMode4, + PitOperatingMode5, + PitOperatingMode2Reserved, + PitOperatingMode5Reserved +} TIMER_OPERATING_MODES; + +typedef enum _TIMER_ACCESS_MODES +{ + PitAccessModeCounterLatch, + PitAccessModeLow, + PitAccessModeHigh, + PitAccessModeLowHigh +} TIMER_ACCESS_MODES; + +typedef enum _TIMER_CHANNELS +{ + PitChannel0, + PitChannel1, + PitChannel2, + PitReadBack +} TIMER_CHANNELS; + +typedef union _TIMER_CONTROL_PORT_REGISTER +{ + struct + { + UCHAR BcdMode:1; + TIMER_OPERATING_MODES OperatingMode:3; + TIMER_ACCESS_MODES AccessMode:2; + TIMER_CHANNELS Channel:2; + }; + UCHAR Bits; +} TIMER_CONTROL_PORT_REGISTER, *PTIMER_CONTROL_PORT_REGISTER; + +// +// See ISA System Architecture 3rd Edition (Tom Shanley, Don Anderson, John Swindle) +// P. 400 +// +// This port is controled by the i8255 Programmable Peripheral Interface (PPI) +// +#define SYSTEM_CONTROL_PORT_A 0x92 +#define SYSTEM_CONTROL_PORT_B 0x61 +typedef union _SYSTEM_CONTROL_PORT_B_REGISTER +{ + struct + { + UCHAR Timer2GateToSpeaker:1; + UCHAR SpeakerDataEnable:1; + UCHAR ParityCheckEnable:1; + UCHAR ChannelCheckEnable:1; + UCHAR RefreshRequest:1; + UCHAR Timer2Output:1; + UCHAR ChannelCheck:1; + UCHAR ParityCheck:1; + }; + UCHAR Bits; +} SYSTEM_CONTROL_PORT_B_REGISTER, *PSYSTEM_CONTROL_PORT_B_REGISTER; + // // Mm PTE/PDE to Hal PTE/PDE //