added VGM playback
This commit is contained in:
243
src/lib/pctimer/gccint8.c
Normal file
243
src/lib/pctimer/gccint8.c
Normal file
@@ -0,0 +1,243 @@
|
||||
/* PCTIMER 1.4
|
||||
Millisecond Resolution Timing Library for DJGPP V2
|
||||
Release Date: 3/15/1998
|
||||
Chih-Hao Tsai (hao520@yahoo.com)
|
||||
*/
|
||||
|
||||
#include <dos.h>
|
||||
#include <dpmi.h>
|
||||
#include <pc.h>
|
||||
#include <go32.h>
|
||||
#include <bios.h>
|
||||
#include <string.h>
|
||||
#include "gccint8.h"
|
||||
|
||||
#define IRQ0 0x8
|
||||
#define PIT0 0x40
|
||||
#define PIT1 0x41
|
||||
#define PIT2 0x42
|
||||
#define PITMODE 0x43
|
||||
#define PITCONST 1193180L
|
||||
#define PIT0DEF 18.2067597
|
||||
#define KBCTRL 0x61
|
||||
#define NEW8H 1
|
||||
#define TRUE_FAILURE 0
|
||||
#define CHAIN_TERMINATION 1
|
||||
#define OUTPORTB_TERMINATION 2
|
||||
|
||||
static float tick_per_ms = 0.0182068;
|
||||
static float ms_per_tick = 54.9246551;
|
||||
static float freq8h = 18.2067597;
|
||||
static unsigned char flag8h = 0;
|
||||
static _go32_dpmi_seginfo rm_old_handler, rm_new_handler, pm_old_handler, pm_new_handler;
|
||||
static _go32_dpmi_registers r, r1;
|
||||
int counter_8h;
|
||||
int counter_reset;
|
||||
unsigned long int ticks_8h = 0;
|
||||
unsigned char flag_pm_termination = TRUE_FAILURE;
|
||||
|
||||
static void (*hook_function)();
|
||||
|
||||
void pctimer_init(unsigned int Hz) {
|
||||
unsigned int pit0_set, pit0_value;
|
||||
|
||||
if (flag8h != NEW8H) {
|
||||
disable();
|
||||
|
||||
lock_rm_new8h();
|
||||
lock_pm_new8h();
|
||||
|
||||
_go32_dpmi_lock_data(&hook_function, sizeof(hook_function));
|
||||
_go32_dpmi_lock_data(&ticks_8h, sizeof(ticks_8h));
|
||||
_go32_dpmi_lock_data(&counter_8h, sizeof(counter_8h));
|
||||
_go32_dpmi_lock_data(&counter_8h, sizeof(counter_reset));
|
||||
_go32_dpmi_lock_data(&flag_pm_termination, sizeof(flag_pm_termination));
|
||||
_go32_dpmi_lock_data(&r, sizeof(r));
|
||||
_go32_dpmi_lock_data(&r1, sizeof(r1));
|
||||
|
||||
_go32_dpmi_get_protected_mode_interrupt_vector(8, &pm_old_handler);
|
||||
pm_new_handler.pm_offset = (int)pm_new8h;
|
||||
pm_new_handler.pm_selector = _go32_my_cs();
|
||||
_go32_dpmi_chain_protected_mode_interrupt_vector(8, &pm_new_handler);
|
||||
|
||||
_go32_dpmi_get_real_mode_interrupt_vector(8, &rm_old_handler);
|
||||
rm_new_handler.pm_offset = (int)rm_new8h;
|
||||
_go32_dpmi_allocate_real_mode_callback_iret(&rm_new_handler, &r1);
|
||||
_go32_dpmi_set_real_mode_interrupt_vector(8, &rm_new_handler);
|
||||
|
||||
outportb(PITMODE, 0x36);
|
||||
pit0_value = PITCONST / Hz;
|
||||
pit0_set = (pit0_value & 0x00ff);
|
||||
outportb(PIT0, pit0_set);
|
||||
pit0_set = (pit0_value >> 8);
|
||||
outportb(PIT0, pit0_set);
|
||||
|
||||
ticks_8h = 0;
|
||||
flag8h = NEW8H;
|
||||
freq8h = Hz;
|
||||
counter_8h = 0;
|
||||
counter_reset = freq8h / PIT0DEF;
|
||||
tick_per_ms = freq8h / 1000;
|
||||
ms_per_tick = 1000 / freq8h;
|
||||
|
||||
enable();
|
||||
}
|
||||
}
|
||||
|
||||
void pctimer_exit(void) {
|
||||
// unsigned int pit0_set, pit0_value;
|
||||
unsigned long tick;
|
||||
char *cmostime;
|
||||
|
||||
if (flag8h == NEW8H) {
|
||||
disable();
|
||||
|
||||
outportb(PITMODE, 0x36);
|
||||
outportb(PIT0, 0x00);
|
||||
outportb(PIT0, 0x00);
|
||||
|
||||
_go32_dpmi_set_protected_mode_interrupt_vector(8, &pm_old_handler);
|
||||
|
||||
_go32_dpmi_set_real_mode_interrupt_vector(8, &rm_old_handler);
|
||||
_go32_dpmi_free_real_mode_callback(&rm_new_handler);
|
||||
|
||||
enable();
|
||||
|
||||
cmostime = get_cmostime();
|
||||
tick = PIT0DEF * ((((float)*cmostime) * 3600) + (((float)*(cmostime + 1)) * 60) + (((float)*(cmostime + 2))));
|
||||
biostime(1, tick);
|
||||
|
||||
flag8h = 0;
|
||||
freq8h = PIT0DEF;
|
||||
counter_reset = freq8h / PIT0DEF;
|
||||
tick_per_ms = freq8h / 1000;
|
||||
ms_per_tick = 1000 / freq8h;
|
||||
}
|
||||
}
|
||||
|
||||
void rm_new8h(void) {
|
||||
disable();
|
||||
|
||||
if (flag_pm_termination == TRUE_FAILURE) {
|
||||
ticks_8h++;
|
||||
counter_8h++;
|
||||
}
|
||||
|
||||
if ((counter_8h == counter_reset) || (flag_pm_termination == CHAIN_TERMINATION)) {
|
||||
flag_pm_termination = TRUE_FAILURE;
|
||||
counter_8h = 0;
|
||||
memset(&r, 0, sizeof(r));
|
||||
r.x.cs = rm_old_handler.rm_segment;
|
||||
r.x.ip = rm_old_handler.rm_offset;
|
||||
r.x.ss = r.x.sp = 0;
|
||||
_go32_dpmi_simulate_fcall_iret(&r);
|
||||
enable();
|
||||
} else {
|
||||
flag_pm_termination = TRUE_FAILURE;
|
||||
outportb(0x20, 0x20);
|
||||
enable();
|
||||
}
|
||||
}
|
||||
|
||||
void lock_rm_new8h(void) { _go32_dpmi_lock_code(rm_new8h, (unsigned long)(lock_rm_new8h - rm_new8h)); }
|
||||
|
||||
void pm_new8h(void) {
|
||||
disable();
|
||||
|
||||
ticks_8h++;
|
||||
counter_8h++;
|
||||
|
||||
flag_pm_termination = TRUE_FAILURE;
|
||||
|
||||
if (hook_function) {
|
||||
hook_function();
|
||||
}
|
||||
|
||||
if (counter_8h == counter_reset) {
|
||||
flag_pm_termination = CHAIN_TERMINATION;
|
||||
counter_8h = 0;
|
||||
enable();
|
||||
} else {
|
||||
flag_pm_termination = OUTPORTB_TERMINATION;
|
||||
outportb(0x20, 0x20);
|
||||
enable();
|
||||
}
|
||||
}
|
||||
|
||||
void lock_pm_new8h(void) { _go32_dpmi_lock_code(pm_new8h, (unsigned long)(lock_pm_new8h - pm_new8h)); }
|
||||
|
||||
unsigned long pctimer_get_ticks(void) { return ticks_8h; }
|
||||
|
||||
unsigned long pctimer_time(unsigned long start, unsigned long finish) {
|
||||
unsigned long duration, millisec;
|
||||
|
||||
if (finish < start)
|
||||
return 0;
|
||||
else {
|
||||
duration = finish - start;
|
||||
millisec = duration * ms_per_tick;
|
||||
return millisec;
|
||||
}
|
||||
}
|
||||
|
||||
void pctimer_sleep(unsigned int delayms) {
|
||||
unsigned long int delaybegin = 0;
|
||||
unsigned long int delayend = 0;
|
||||
unsigned int delaytick;
|
||||
|
||||
delaytick = delayms * tick_per_ms;
|
||||
|
||||
if (flag8h == NEW8H)
|
||||
delaybegin = pctimer_get_ticks();
|
||||
else
|
||||
delaybegin = biostime(0, 0);
|
||||
|
||||
do {
|
||||
if (flag8h == NEW8H)
|
||||
delayend = pctimer_get_ticks();
|
||||
else
|
||||
delayend = biostime(0, 0);
|
||||
} while ((delayend - delaybegin) < delaytick);
|
||||
}
|
||||
|
||||
void pctimer_sound(int freq, int duration) {
|
||||
int byte;
|
||||
unsigned int freq1;
|
||||
|
||||
freq1 = PITCONST / freq;
|
||||
outportb(PITMODE, 0xb6);
|
||||
byte = (freq1 & 0xff);
|
||||
outportb(PIT2, byte);
|
||||
byte = (freq1 >> 8);
|
||||
outportb(PIT2, byte);
|
||||
byte = inportb(KBCTRL);
|
||||
outportb(KBCTRL, (byte | 3));
|
||||
|
||||
pctimer_sleep(duration);
|
||||
outportb(KBCTRL, (byte & 0xfc));
|
||||
}
|
||||
|
||||
char *get_cmostime(void) {
|
||||
char *buff;
|
||||
static char buffer[6];
|
||||
char ch;
|
||||
|
||||
buff = buffer;
|
||||
memset(&r, 0, sizeof(r));
|
||||
r.h.ah = 0x02;
|
||||
_go32_dpmi_simulate_int(0x1a, &r);
|
||||
|
||||
ch = r.h.ch;
|
||||
buffer[0] = (char)((int)(ch & 0x0f) + (int)((ch >> 4) & 0x0f) * 10);
|
||||
ch = r.h.cl;
|
||||
buffer[1] = (char)((int)(ch & 0x0f) + (int)((ch >> 4) & 0x0f) * 10);
|
||||
ch = r.h.dh;
|
||||
buffer[2] = (char)((int)(ch & 0x0f) + (int)((ch >> 4) & 0x0f) * 10);
|
||||
buffer[3] = r.h.dl;
|
||||
buffer[4] = (char)(r.x.flags & 0x0001);
|
||||
buffer[5] = 0x00;
|
||||
|
||||
return (buff);
|
||||
}
|
||||
|
||||
void pctimer_set_hook(void (*proc)()) { hook_function = proc; }
|
||||
19
src/lib/pctimer/gccint8.h
Normal file
19
src/lib/pctimer/gccint8.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/* PCTIMER 1.4
|
||||
Millisecond Resolution Timing Library for DJGPP V2
|
||||
Release Date: 3/15/1998
|
||||
Chih-Hao Tsai (hao520@yahoo.com)
|
||||
*/
|
||||
|
||||
void pctimer_init(unsigned int);
|
||||
void pctimer_exit(void);
|
||||
void rm_new8h(void);
|
||||
void lock_rm_new8h(void);
|
||||
void pm_new8h(void);
|
||||
void lock_pm_new8h(void);
|
||||
unsigned long pctimer_get_ticks(void);
|
||||
unsigned long pctimer_time(unsigned long, unsigned long);
|
||||
void pctimer_sleep(unsigned int);
|
||||
void pctimer_sound(int, int);
|
||||
char *get_cmostime(void);
|
||||
|
||||
void pctimer_set_hook(void (*proc)());
|
||||
157
src/lib/pctimer/readme.txt
Normal file
157
src/lib/pctimer/readme.txt
Normal file
@@ -0,0 +1,157 @@
|
||||
PCTIMER: Millisecond Resolution Timing Library for DJGPP V2
|
||||
Version 1.4 Release Notes
|
||||
March 15, 1998
|
||||
Status: Freeware.
|
||||
Distribution status: Has to be distributed as this archive.
|
||||
Send comments to: Chih-Hao Tsai (hao520@yahoo.com).
|
||||
|
||||
|
||||
==== A FEW WORDS ON WIN95
|
||||
|
||||
Although I have only tested PCTIMER with Win95 and CWSDPMI,
|
||||
PCTIMER should run on most DPMI servers.
|
||||
|
||||
However, theoretically, applications running under Win95
|
||||
should not touch hardware interrupts. The "correct" method
|
||||
of doing millisecond resolution timing under Win95 is to call
|
||||
Windows API. The standard Multimedia Timer API can do
|
||||
millisecond resolution timing. (You'll need to include
|
||||
<mmsystem.h> and link with winmm.lib. But, as far as I know,
|
||||
RSXNT does not provide Multimedia API access.)
|
||||
|
||||
If you need an example on using Windows API to do millisecond
|
||||
resolution timing, check this out (I used Visual C++ 4.0):
|
||||
|
||||
Test Report: Millisecond Resolution Timing with Win95/VC4
|
||||
http://www.geocities.com/hao510/w98timer/
|
||||
|
||||
|
||||
==== BASIC LOGIC
|
||||
|
||||
PCTIMER reprograms the 8454 IC (Programmable Interrupt Timer)
|
||||
to get high frequency of System Timer Interrupt (IRQ0) whose
|
||||
default frequency is 18.2 Hz. Since the operating systems
|
||||
rely heavily on the System Timer Interrupt, increasing its
|
||||
frequency could cause instability. PCTIMER hooks both
|
||||
protected and real mode int 8h handlers (default handler for
|
||||
System Timer Interrupt) reprograms 8254 to get higher
|
||||
frequency of interrupts, but calls the original handlers at
|
||||
a fixed frequency of 18.2 Hz.
|
||||
|
||||
|
||||
==== RELATIONSHIP BETWEEN PROTECTED & REAL MODE INT 8H
|
||||
|
||||
According to DJGPP V2 FAQ, hardware interrupts are always
|
||||
passed to protected mode first, and only if unhandled are
|
||||
they reflected to real mode. In other words, we must at least
|
||||
hook protected mode int 8h. To avoid potential loss of ticks
|
||||
when the protected mode fails to handle the hardware
|
||||
interrupt, we should also hook real mode int 8h.
|
||||
|
||||
In actual implementation of the two handlers, things become
|
||||
much more complex than that.
|
||||
|
||||
|
||||
==== PCTIMER PROTECTED MODE INT8H HANDLER
|
||||
|
||||
Here is PCTIMER's protected mode int 8h in pseudo code. The
|
||||
meanings of pm_termination_flag's values are:
|
||||
|
||||
TRUE_FAILURE: The handler failed to handle the hardware
|
||||
interrupt.
|
||||
|
||||
CHAIN_TERMINATION: The handler terminated with chaining to
|
||||
the old handler. Note that after chaining the old protected
|
||||
mode int 8h handler, the *real* mode int 8h *will* get called.
|
||||
We need to take care of this.
|
||||
|
||||
OUTPORTB_TERMINATION: The handler terminated with an
|
||||
outportb (0x20, 0x20) instruction. This instruction sends
|
||||
a hardware request to the Interrupt Controller to terminate
|
||||
the interrupt. This works (although intrusive), but will
|
||||
cause the DPMI server to believe that the protected mode
|
||||
handler failed to do its job. Therefore, the real mode
|
||||
handler *will* get called. We need to take care of this,
|
||||
too.
|
||||
|
||||
(Read the real code for details.)
|
||||
|
||||
PCTIMER Protected Mode Int 8h Handler (Pseudo-code)
|
||||
* pm_termination_flag = TRUE_FAILURE
|
||||
* counter++
|
||||
* if it is time to chain old handler
|
||||
- pm_termination_flag = CHAIN_TERMINATION
|
||||
- let the wrapper chains the old handler
|
||||
* else
|
||||
- pm_termination_flag = OUTPORTB_TERMINATION
|
||||
- outportb (0x20, 0x20)
|
||||
|
||||
|
||||
==== PCTIMER REAL MODE INT8H HANDLER
|
||||
|
||||
The real mode handler is considerably more complex than the
|
||||
protected mode one. It depends on pm_termination_flag to
|
||||
determine how it should behave. Always set
|
||||
pm_termination_flag to TRUE_FAILURE before we leave, so in
|
||||
case the protected mode handler should fail, the real mode
|
||||
handler can detect it next time.
|
||||
|
||||
(Read the real code for details.)
|
||||
|
||||
PCTIMER Real Mode Int 8h Handler (Pseudo-code)
|
||||
* if pm_termination_flag = TRUE_FAILURE
|
||||
- counter++
|
||||
* if it is time to chain the old handler, or if the protected
|
||||
mode handler decided to do that (i.e.,
|
||||
pm_termination_flag = CHAIN_TERMINATION)
|
||||
- call old real mode handler
|
||||
- pm_termination_flag = TRUE_FAILURE
|
||||
* else
|
||||
- outportb (0x20, 0x20)
|
||||
- pm_termination_flag = TRUE_FAILURE
|
||||
|
||||
|
||||
==== Example of Usage
|
||||
|
||||
#include <gccint8.h>
|
||||
|
||||
void main (void)
|
||||
{
|
||||
unsigned long int start, finish, elapsed_time;
|
||||
|
||||
/* initiate the timer with 1/1000 s resolution */
|
||||
/* you can use different resolution, but resolution */
|
||||
/* higher than 1000 is not recommended. */
|
||||
|
||||
pctimer_init (1000);
|
||||
|
||||
start = pctimer_get_ticks ();
|
||||
/* do some stuff here */
|
||||
finish = pctimer_get_ticks ();
|
||||
elapsed_time = pctimer_time (start, finish);
|
||||
/* always returns elapsed time in the unit of ms. */
|
||||
|
||||
pctimer_sleep (200); /* sleep 200 ms */
|
||||
pctimer_sound (800, 200); /* 800 Hz sound for 200 ms */
|
||||
|
||||
/* never forget to exit the timer!!! */
|
||||
/* otherwise, the system WILL crash!!! */
|
||||
|
||||
pctimer_exit ();
|
||||
}
|
||||
|
||||
|
||||
==== HISTORY
|
||||
3/15/98 Version 1.4
|
||||
11/26/95 Version 1.3
|
||||
1/29/95 Version 1.2
|
||||
1/16/95 Version 1.1
|
||||
11/5/94 Version 1.0
|
||||
|
||||
|
||||
==== DISCLAIMER
|
||||
|
||||
I am not at all responsible for any damages, consequential
|
||||
or incidental, and by using PCTIMER, you are agreeing not
|
||||
to hold either of the above responsible for any problems
|
||||
whatsoever.
|
||||
Reference in New Issue
Block a user