📄 timer.c
字号:
/*====================================================================*/
/* */
/* NOTE: Ensure this program is compiled for LARGE model. */
/* Non-ANSI stuff is pertinent to Microsoft QuickC v2.5. */
/* Porting to another compiler should not be too hard! */
/* */
/*====================================================================*/
/* What we are going to demonstrate here is the use of two methods of */
/* hooking into the timer interrupt mechanism. All PCs have a timer */
/* chip with 3 channels. 1 channel is programmed to provide an tick */
/* interrupt 18.2 times a second. This is generally refered to as the */
/* hardware timer. This timer tick is serviced by an interrupt routine */
/* accessed by vector 0x08. DOS typically provides the service routine */
/* so that it can update the DOS clock, etc. What DOS also does is to */
/* also route each timer tick through another interrupt vector 0x1C. */
/* This is called the user timer. It toos counts at 18.2 times a sec */
/* with the idea that it is indenpendant of vector 0x08. */
/* */
/* What we will do is to use the user vector at 0x1C to control a very */
/* simple animated cursor (text mode) while the hardware timer at */
/* vector 0x08 is re-programmed to provide a faster interrupt rate */
/* that we will manage with our own interrupt handler before passing */
/* on control to the original DOS handler, and therefore our 0x1C */
/* handler, whenever we deem it appropriate. */
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <string.h>
#include <memory.h>
#define FALSE 0
#define TRUE 1
typedef unsigned char BOOL;
typedef unsigned char BYTE;
typedef unsigned int WORD;
BYTE *pVideo=(BYTE *)0xB8000000L; /* Ptr to base of VGA text RAM */
/* As with all interrupt service routines any variables used should be */
/* declared either globally or statically. It is NOT a good idea to use */
/* variables directly off of the stack - we won't know the state of the */
/* stack when we are invoked... */
/* Global place holders for original vector routines */
void (interrupt *OldDirectTimer)( void );
void (interrupt *OldUserTimer)( void );
long lCounter; /* Simple counter for more accurate delays */
int nTickRate; /* The actual rate we are getting interrupts */
int nTickLimit; /* The number of ticks before we call DOS ISR */
int nTickCount; /* Current DOS tick counter */
int nCursorX,nCursorY; /* Cursor position */
/*===================== Our Interrupt Service Routines ======================*/
void interrupt OurDirectTimer( void )
{
/* Decrement our count down timer */
if ( lCounter ) lCounter--; /* Don't go negative though! */
/* Determine if we call the original DOS handler */
if ( --nTickCount<=0 )
{
nTickCount = nTickLimit; /* Reset ticks to wait */
_chain_intr( OldDirectTimer ); /* Go to old handler! */
}
/* If we haven't called the original handler, we find ourselves here. */
/* The DOS handler would take care of telling the Programable Interrupt */
/* Controller (PIC) that the interrupt has been serviced. This is called */
/* acknowledging the interrupt and MUST be done. If not, the PIC cannot */
/* generate another interrupt for the timer chip or anything else... */
outp( 0x20,0x20 );
}
void interrupt OurUserTimer( void )
{
/* This routine is guaranteed to be called at approx 18 times per sec. */
/* We will use that fact to control the timing of our animated cursor */
#define NUM_OF_CURSORS 4 /* 4 cursors to display */
#define FRAME_DELAY 2 /* Approx 1/8 sec per frame */
static BYTE cbFrame=0, cbDelay=0, cbColour=0;
static BYTE cbCursors[NUM_OF_CURSORS]={ '-','/','|','\\' };
static WORD wOffset;
/* Are we ready to animate next frame... */
if ( cbDelay--==0 )
{
cbDelay = FRAME_DELAY;
if ( ++cbFrame>=NUM_OF_CURSORS ) cbFrame=0;
if ( ++cbColour>15 ) cbColour=1;
wOffset = nCursorY*160 + nCursorX*2;
*(pVideo+wOffset) = cbCursors[cbFrame];
*(pVideo+wOffset+1) = cbColour;
}
/* Call the original handler... */
_chain_intr( OldUserTimer );
}
void SetTimerRate( int nNewRate )
{
/* Reprogram the timer chip to give interrupts at the new rate. */
/* Bear in mind that 18.2 times per sec is the minimum... */
_asm cli /* Disable interrupts while we change these */
/* variables. They are used within the ISR */
/* itself so may cause problems! */
if ( nNewRate<18 ) nNewRate=18;
nTickRate = nNewRate;
nTickLimit = nNewRate/18; /* Number of ticks to give original */
/* 18 ticks per second */
nTickCount = 0;
/* Calculate the 'count down' value for the timer chip itself... */
nNewRate = (nNewRate==18) ? 0 : ((unsigned int) ( 1193180L / ((unsigned long)nNewRate) ));
/* Re-program the timer chip... */
_asm mov dx, 43H
_asm mov al, 34H
_asm out dx, al
_asm mov dx, 40H
_asm mov ax, nNewRate
_asm out dx, al
_asm mov al, ah
_asm out dx, al
/* Re-enable interrupts before we finish... */
_asm sti
}
void GetCursorPosition( int *col, int *row )
{
/* Ask the video BIOS to give us the current position of the cursor */
unsigned char r=0, c=0;
_asm mov ah, 03H
_asm mov bh, 00H
_asm int 10H
_asm mov r, dh
_asm mov c, dl
*row = (int) r;
*col = (int) c;
}
/*========================= main program ====================================*/
void main( void )
{
int i;
printf( "\n\nTimer Demo\n==========\n\nPress ESCape to abort, or\n\n" );
printf( "Press 0 to set timer to 18.2Hz\n" );
for ( i=1; i<10; i++ )
{
printf( "Press %d to set timer to %dHz\n",i,i*1000 );
}
printf( "\n" );
/* Determine the current position of the cursor. We only really want the */
/* row since we shall show it in the centre of the line... */
GetCursorPosition( &nCursorX,&nCursorY );
nCursorX = 39; /* Set to centre column */
/* Set things up by installing our handlers */
OldDirectTimer = _dos_getvect( 0x08 );
OldUserTimer = _dos_getvect( 0x1C );
SetTimerRate( 18 ); /* Initialise our variables */
_dos_setvect( 0x08,OurDirectTimer );
_dos_setvect( 0x1C,OurUserTimer );
/* Away we go */
while ( 1 )
{
/* Look for a get out */
i=0;
if ( kbhit() && (i=getch())==27 ) break;
/* Has something been pressed */
if ( i>='0' && i<='9' )
{
if ( i=='0' ) SetTimerRate(18); else SetTimerRate( (i-'0')*1000 );
lCounter = (long)nTickRate;
}
/* If the counter has reached zero, reset it... */
if ( lCounter==0L ) lCounter=(long)nTickRate;
/* Display message showing counter counting down and confirmation */
/* of tick rate. Note, that \r is used to not cause a line feed... */
printf( "\r%-8lu Timer Rate: %-6d",lCounter,nTickRate );
}
/* Reinstate the original handlers */
SetTimerRate( 18 ); /* Ensures correct rate! */
_dos_setvect( 0x08,OldDirectTimer );
_dos_setvect( 0x1C,OldUserTimer );
printf( "\n\nTimer Demo Coded by CyberFrog 8:)\n\n" );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -