/* * PC/HW routine collection v1.5 for DOS/DJGPP * * Copyright (C) 2002 - Daniel Borca * Email : dborca@yahoo.com * Web : http://www.geocities.com/dborca */ #include #include #include "pc_hw.h" #define TIMER_IRQ 0 #define MAX_TIMERS 8 #define PIT_FREQ 0x1234DD #define ADJUST(timer, basefreq) timer.counter = PIT_FREQ * timer.freq / SQR(basefreq) #define unvolatile(__v, __t) __extension__ ({union { volatile __t __cp; __t __p; } __q; __q.__cp = __v; __q.__p;}) static int timer_installed; typedef struct { volatile unsigned int counter, clock_ticks, freq; volatile PFUNC func; volatile void *parm; } TIMER; static TIMER timer_main, timer_func[MAX_TIMERS]; /* Desc: main timer callback * * In : - * Out : 0 to bypass BIOS, 1 to chain to BIOS * * Note: - */ static int timer () { int i; for (i = 0; i < MAX_TIMERS; i++) { TIMER *t = &timer_func[i]; if (t->func) { t->clock_ticks += t->counter; if (t->clock_ticks >= timer_main.counter) { t->clock_ticks -= timer_main.counter; t->func(unvolatile(t->parm, void *)); } } } timer_main.clock_ticks += timer_main.counter; if (timer_main.clock_ticks >= 0x10000) { timer_main.clock_ticks -= 0x10000; return 1; } else { outportb(0x20, 0x20); return 0; } } ENDOFUNC(timer) /* Desc: uninstall timer engine * * In : - * Out : - * * Note: - */ void pc_remove_timer (void) { if (timer_installed) { timer_installed = FALSE; pc_clexit(pc_remove_timer); DISABLE(); outportb(0x43, 0x34); outportb(0x40, 0); outportb(0x40, 0); ENABLE(); pc_remove_irq(TIMER_IRQ); } } /* Desc: remove timerfunc * * In : timerfunc id * Out : 0 if success * * Note: tries to relax the main timer whenever possible */ int pc_remove_int (int fid) { int i; unsigned int freq = 0; /* are we installed? */ if (!timer_installed) { return -1; } /* sanity check */ if ((fid < 0) || (fid >= MAX_TIMERS) || (timer_func[fid].func == NULL)) { return -1; } timer_func[fid].func = NULL; /* scan for maximum frequency */ for (i = 0; i < MAX_TIMERS; i++) { TIMER *t = &timer_func[i]; if (t->func) { if (freq < t->freq) { freq = t->freq; } } } /* if there are no callbacks left, cleanup */ if (!freq) { pc_remove_timer(); return 0; } /* if we just lowered the maximum frequency, try to relax the timer engine */ if (freq < timer_main.freq) { unsigned int new_counter = PIT_FREQ / freq; DISABLE(); for (i = 0; i < MAX_TIMERS; i++) { if (timer_func[i].func) { ADJUST(timer_func[i], freq); } } outportb(0x43, 0x34); outportb(0x40, (unsigned char)new_counter); outportb(0x40, (unsigned char)(new_counter>>8)); timer_main.clock_ticks = 0; timer_main.counter = new_counter; timer_main.freq = freq; ENABLE(); } return 0; } ENDOFUNC(pc_remove_int) /* Desc: adjust timerfunc * * In : timerfunc id, new frequency (Hz) * Out : 0 if success * * Note: might change the main timer frequency */ int pc_adjust_int (int fid, unsigned int freq) { int i; /* are we installed? */ if (!timer_installed) { return -1; } /* sanity check */ if ((fid < 0) || (fid >= MAX_TIMERS) || (timer_func[fid].func == NULL)) { return -1; } timer_func[fid].freq = freq; /* scan for maximum frequency */ freq = 0; for (i = 0; i < MAX_TIMERS; i++) { TIMER *t = &timer_func[i]; if (t->func) { if (freq < t->freq) { freq = t->freq; } } } /* update main timer / sons to match highest frequency */ DISABLE(); /* using '>' is correct still (and avoids updating * the HW timer too often), but doesn't relax the timer! */ if (freq != timer_main.freq) { unsigned int new_counter = PIT_FREQ / freq; for (i = 0; i < MAX_TIMERS; i++) { if (timer_func[i].func) { ADJUST(timer_func[i], freq); } } outportb(0x43, 0x34); outportb(0x40, (unsigned char)new_counter); outportb(0x40, (unsigned char)(new_counter>>8)); timer_main.clock_ticks = 0; timer_main.counter = new_counter; timer_main.freq = freq; } else { ADJUST(timer_func[fid], timer_main.freq); } ENABLE(); return 0; } ENDOFUNC(pc_adjust_int) /* Desc: install timer engine * * In : - * Out : 0 for success * * Note: initial frequency is 18.2 Hz */ static int install_timer (void) { if (timer_installed || pc_install_irq(TIMER_IRQ, timer)) { return -1; } else { memset(timer_func, 0, sizeof(timer_func)); LOCKDATA(timer_func); LOCKDATA(timer_main); LOCKFUNC(timer); LOCKFUNC(pc_adjust_int); LOCKFUNC(pc_remove_int); timer_main.counter = 0x10000; DISABLE(); outportb(0x43, 0x34); outportb(0x40, 0); outportb(0x40, 0); timer_main.clock_ticks = 0; ENABLE(); pc_atexit(pc_remove_timer); timer_installed = TRUE; return 0; } } /* Desc: install timerfunc * * In : callback function, opaque pointer to be passed to callee, freq (Hz) * Out : timerfunc id (0 .. MAX_TIMERS-1) * * Note: returns -1 if error */ int pc_install_int (PFUNC func, void *parm, unsigned int freq) { int i; TIMER *t = NULL; /* ensure the timer engine is set up */ if (!timer_installed) { if (install_timer()) { return -1; } } /* find an empty slot */ for (i = 0; i < MAX_TIMERS; i++) { if (!timer_func[i].func) { t = &timer_func[i]; break; } } if (t == NULL) { return -1; } DISABLE(); t->func = func; t->parm = parm; t->freq = freq; t->clock_ticks = 0; /* update main timer / sons to match highest frequency */ if (freq > timer_main.freq) { unsigned int new_counter = PIT_FREQ / freq; for (i = 0; i < MAX_TIMERS; i++) { if (timer_func[i].func) { ADJUST(timer_func[i], freq); } } outportb(0x43, 0x34); outportb(0x40, (unsigned char)new_counter); outportb(0x40, (unsigned char)(new_counter>>8)); timer_main.clock_ticks = 0; timer_main.counter = new_counter; timer_main.freq = freq; } else { /* t == &timer_func[i] */ ADJUST(timer_func[i], timer_main.freq); } i = t - timer_func; ENABLE(); return i; }