📄 timer.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//------------------------------------------------------------------------------
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
//------------------------------------------------------------------------------
#include <windows.h>
#include <pc.h>
#include <nkintr.h>
#include <timer.h>
extern volatile DWORD CurMSec;
extern DWORD dwReschedTime;
DWORD g_dwOALTimerCount;
static DWORD dwOALTicksPerMs;
volatile ULARGE_INTEGER CurTicks = { 0, 0 };
//
// Kernel global variables used by GetIdleTime() to determine CPU utilization.
//
extern DWORD idleconv; // translation constant in 1 ms units.
extern DWORD curridlehigh, curridlelow; // 64-bit idle time in ms
//
// Profiling
//
DWORD dwReschedCount;
extern DWORD GetEPC(void);
extern int (*PProfileInterrupt)(void); // pointer to profiler ISR,
extern void ProfilerHit(DWORD);
DWORD dwProfilingMultiple;
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void
SetTimer0(
WORD wVal
)
{
__asm {
// configure counter for correct mode
mov al, 00110100b ; counter 0, 16-bit, mode 2, binary
out 043h, al
jmp short $+2
// load the timer with correct count value
mov ax, wVal
out 040h, al
jmp short $+2
mov al,ah
out 040h, al
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
int
ProfileInterrupt(void)
{
ProfilerHit(GetEPC());
dwReschedCount++;
if (dwReschedCount < dwProfilingMultiple)
return SYSINTR_NOP;
dwReschedCount=0;
return SYSINTR_RESCHED;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void
OEMProfileTimerEnable(
DWORD dwUSec
)
{
DWORD dwUSecAdjusted;
if (dwUSec) {
//
// Rate specified (limit boundary)
//
if (dwUSec < 50) {
dwUSec = 50;
}
if (dwUSec > 1000) {
dwUSec = 1000;
}
} else {
//
// Use default rate (100 uS)
//
dwUSec = 100;
}
//
// We can only program the timer such that 1ms is a direct multiple
//
dwProfilingMultiple = 1000 * g_dwBSPMsPerIntr / dwUSec;
dwUSecAdjusted = 1000 * g_dwBSPMsPerIntr / dwProfilingMultiple;
RETAILMSG(1, (TEXT("Starting profile timer at %d uS rate\r\n"), dwUSecAdjusted));
dwReschedCount = 0;
PProfileInterrupt = ProfileInterrupt;
SetTimer0((WORD) (g_dwOALTimerCount / dwProfilingMultiple));
PICEnableInterrupt(INTR_TIMER0, TRUE);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void
OEMProfileTimerDisable(void)
{
PProfileInterrupt = NULL;
SetTimer0 ((WORD) g_dwOALTimerCount);
PICEnableInterrupt(INTR_TIMER0, TRUE);
}
extern BOOL x86InitPerfCounter (void);
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void
InitClock(void)
{
BYTE cData;
//
// Set up translation constant for GetIdleTime() (1 ms units).
// Note: Since curridlehigh, curridlelow is counting in ms, and GetIdleTime( )
// reports in ms, the conversion ratio is one. If curridlehigh, curridlelow
// were using other units (like ticks), then the conversion would be calculated
// from the clock frequency.
//
idleconv = 1;
x86InitPerfCounter ();
//
// Setup Timer0 to fire every TICK_RATE mS and generate interrupt
//
g_dwOALTimerCount = (g_dwBSPMsPerIntr * TIMER_FREQ) / 1000;
dwOALTicksPerMs = TIMER_FREQ / 1000;
SetTimer0 ((WORD) g_dwOALTimerCount);
PICEnableInterrupt(INTR_TIMER0, TRUE);
//
// Set up Timer2 to use its full range for kcall profiling
//
__asm {
// configure counter for correct mode
mov al, 10110100b ; counter 2, 16-bit, mode 2, binary
out 043h, al
jmp short $+2
// Start counter at highest value, it's a countdown.
// It's confusing, but 0 is largest initial value. Read the manual.
xor eax, eax ; 0x00
out 042h, al
jmp short $+2
out 042h, al
// Enable bit 0 of Port B to start counter2 counting
in al, 61h ;Read Current value of Port B
or al, 00000001b ;Set bit 0 to enable gate
out 61h, al ;Store new value to Port B
}
do {
cData = CMOS_Read( RTC_STATUS_A);
} while ( cData & RTC_SRA_UIP );
cData = CMOS_Read( RTC_STATUS_B );
CMOS_Write( RTC_STATUS_B, (BYTE)(cData|RTC_SRB_24HR) );
cData = CMOS_Read( (BYTE)(RTC_STATUS_B) );
RETAILMSG(1, (TEXT("RTC - Status Reg B - 0x%2.2X\r\n"), cData));
PICEnableInterrupt(INTR_RTC, TRUE);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void
CPUEnterIdle(
DWORD dwIdleParam
)
{
_asm {
sti
hlt
}
}
//------------------------------------------------------------------------------
//
// This routine is called by the kernel when there are no threads ready to
// run. The CPU should be put into a reduced power mode and halted. It is
// important to be able to resume execution quickly upon receiving an interrupt.
//
// In general the PIT (8254) does not support immediate reprogramming of the counter
// (the 8254 waits until the current countdown cycle is finished before reloading the
// counter with the new value) so OEMIdle never re-program the counter. If this
// OAL is being adapted to a system with a reprogrammable countdown timer or a
// free running counter with compare registers, see other platforms for example
//------------------------------------------------------------------------------
void
OEMIdle(
DWORD dwIdleParam
)
{
DWORD dwIdleMs = dwReschedTime - CurMSec;
if ((int) dwIdleMs > 0) {
// Use for 64-bit math
ULARGE_INTEGER currIdle = {
curridlelow,
curridlehigh
};
DWORD dwPrevMSec = CurMSec;
// max idle time is the system tick
if (dwIdleMs > g_dwBSPMsPerIntr)
dwIdleMs = g_dwBSPMsPerIntr;
// enter idle
CPUEnterIdle (dwIdleParam);
INTERRUPTS_OFF ();
if (dwPrevMSec != CurMSec)
{
// waked up by timer interrupt, update global idle time
currIdle.QuadPart += dwIdleMs;
curridlelow = currIdle.LowPart;
curridlehigh = currIdle.HighPart;
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
DWORD SC_GetTickCount(void)
{
return CurMSec;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -