added VGM playback

This commit is contained in:
Ilu
2023-05-13 19:36:43 +02:00
parent 49af8d6c03
commit cbada2817f
10 changed files with 888 additions and 1 deletions

243
src/lib/pctimer/gccint8.c Normal file
View 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
View 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
View 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.