home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C!T ROM 2
/
ctrom_ii_b.zip
/
ctrom_ii_b
/
PROGRAM
/
C
/
TSR100JE
/
TSR.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1993-03-22
|
34KB
|
983 lines
//--------------------------------------------------------------------------
//
// TSR.CPP: body of DOS TSR class.
// Copyright (c) J.English 1993.
// Author's address: je@unix.brighton.ac.uk
//
// Permission is granted to use copy and distribute the
// information contained in this file provided that this
// copyright notice is retained intact and that any software
// or other document incorporating this file or parts thereof
// makes the source code for the TSR class of which this file
// is a part freely available.
//
//--------------------------------------------------------------------------
//
// Note: this class is highly DOS specific and hence non-portable.
// It also involves the use of assembly language and interrupt
// functions, so it is also very compiler-specific. Will require
// modifications for use with compilers other than Borland C++ 3.0
// or later. If Borland compilers prior to version to version 3.0
// are used, note that they do not support nesting of types within
// classes (so TSR::F1 etc. will need changing to a global name F1)
// and that the "_chain_intr" function used here was introduced with
// version 3.0.
//
// Revision history:
// 1.0 March 1993 Initial coding
//
//--------------------------------------------------------------------------
#include "tsr.h"
#include <stdio.h>
#include <string.h>
#include <dos.h>
#include <bios.h>
//--------------------------------------------------------------------------
//
// Constants.
//
const int MIN_STACK = 512; // minimum stack size in bytes
const int ALT_FKEY = 0x68 - TSR::F1; // hotkey change for Alt-Fn
const int CTRL_FKEY = 0x5E - TSR::F1; // hotkey change for Ctrl-Fn
const int SHIFT_FKEY = 0x54 - TSR::F1; // hotkey change for Shift-Fn
const long DAY_LENGTH = 1573040L; // length of day in timer ticks
const char far* COPYRIGHT = "C++ TSR class, Copyright (c) J.English 1993";
// copyright notice for self-identify
//--------------------------------------------------------------------------
//
// Global (static) variables.
//
static TSR* instance = 0; // pointer to the TSR instance
static char TSRname [32]; // TSR identifying string
static unsigned char hotshift; // shift state for hotkey
static unsigned char hotscan; // scancode for hotkey
static int interval; // length of timeslice in timer ticks
static int TSRfunction; // Int 2F function used to find TSR
static char far* stack; // stack for TSR execution
static unsigned stacklen; // size of TSR stack in bytes
static unsigned pspseg; // saved PSP segment
static unsigned dtaseg; // saved DTA segment
static unsigned dtaoff; // saved DTA offset
static volatile char far* indos; // saved INDOS flag address
static volatile char far* critical; // saved critical error flag address
static volatile int diskflag = 0; // set if disk interrupt in progress
static volatile int breakflag = 0; // set if ctrl-break has been pressed
static volatile int hotkeyflag = 0; // set if hotkey pressed
static volatile int TSRrequest = 0; // set if TSR should wake up
static volatile int TSRactive = 0; // set if TSR already active
static volatile long nexttick = 0; // next timer tick to wake up at
static volatile long far* timer = (volatile long far*) MK_FP(0x40,0x6C);
static volatile char far* midnight = (volatile char far*) MK_FP(0x40,0x70);
// timer values in BIOS data area
//--------------------------------------------------------------------------
//
// Functions and function pointers.
//
typedef void interrupt (*Handler)(...); // interrupt handler address type
static Handler oldint8; // old int 8 vector (timer)
static Handler oldint9; // old int 9 vector (keyboard)
static Handler oldint13; // old int 13 vector (disk)
static Handler oldint21; // old int 21 vector (DOS services)
static Handler oldint23; // old int 23 vector (control-break)
static Handler oldint24; // old int 24 vector (critical error)
static Handler oldint28; // old int 28 vector (DOS scheduler)
static Handler oldint2F; // old int 2F vector (multiplex)
static int unhook (); // unload function used by int 2F
//--------------------------------------------------------------------------
//
// Compare two far strings.
//
// This is the same as "strcmp" except that we need a function to
// compare far pointers in all memory models, and "strcmp" only
// handles near pointers in the small and medium models.
//
int compare (const char far* a, const char far* b)
{
while (*a == *b)
{ if (*a == '\0')
return 0;
a++, b++;
}
return *a - *b;
}
//--------------------------------------------------------------------------
//
// Check if a key has been pressed.
//
// This nasty little routine is necessary because bioskey(1) does
// not recognise extended keyboards and will swallow keystrokes
// such as "ctrl-del" not supported on 84-key keyboards. This
// makes it necessary to use bioskey(0x11) instead, but there
// is a problem in that this function does not leave AX clear
// if no key has been pressed (it just sets the zero flag) so
// some assembler is needed to deal with this. It also means
// this library will not work with old (pre-AT) BIOSes that
// don't support int 16h, function 11h.
//
int check_key ()
{
asm {
mov ah,0x11; // do function 11h,
int 0x16; // ... int 16h
jnz key_found; // skip if a key has been pressed
mov ax,0; // else set AX = 0
};
key_found:
return _AX;
}
//--------------------------------------------------------------------------
//
// Int 08 (timer) handler.
//
// This handler first calls the original timer handler and then
// checks if either timeslicing has been selected or the hotkey
// has been pressed (as shown by "hotkeyflag"). If so, the TSR
// request flag is set. If DOS is in a safe state, the TSR is
// then activated.
//
static void interrupt TSRint8 ()
{
// chain to old int 8
oldint8 ();
// exit if TSR is already running
if (TSRactive)
return;
// amend "nexttick" if midnight has passed
if (*midnight != 0 && nexttick >= DAY_LENGTH)
nexttick -= DAY_LENGTH;
// check if TSR should be activated
if (hotkeyflag == 0 && (interval == 0 || (*timer - nexttick) < 0))
return;
// set request flag if so
TSRrequest = 1;
// reset timer if past last activation period
if (interval != 0 && (*timer - nexttick) >= 0)
nexttick += interval;
// activate TSR if DOS is safe
if (*indos == 0 && *critical == 0 && diskflag == 0)
activate ();
}
//--------------------------------------------------------------------------
//
// Int 09 (keyboard) handler.
//
// This handler first calls the original keyboard handler (with
// interrupts enabled) and then looks to see if the keys pressed
// match the hotkey. If so, "hotkeyflag" is set. If a keycode
// is included in the hotkey code (i.e. it is not just a set of
// shift keys only) the key is removed from the keyboard buffer.
//
static void interrupt TSRint9 ()
{
// chain to old int 9
asm { sti; }
oldint9 ();
// check if TSR uses hotkey
if (hotshift == 0 && hotscan == 0)
return;
// check if hotkey modifier keys are pressed
if ((bioskey(2) & 0x0F) != hotshift)
return;
// check if hotkey (if any) has been pressed
if (hotscan == 0 || (check_key() >> 8) == hotscan)
{ hotkeyflag = 1;
if (hotscan != 0)
bioskey (0x10);
}
}
//--------------------------------------------------------------------------
//
// Int 13 (disk) handler.
//
// This handler sets a flag to show that a (time-critical) disk
// transfer is in progress and then calls the original disk handler.
// It is declared as a far non-interrupt function, although it will
// be called as an interrupt handler; the code here does not affect
// any registers, and we want the flag settings from the original
// disk handler to be returned to the caller.
//
// On entry, BP has been pushed on the stack. DS must be set up
// so that "diskflag" and "oldint13" can be accessed, so DS is
// pushed on the stack and the BP is used to reset it. On exit,
// DS and BP are popped from the stack and we return via a RETF 2
// which will throw away the flag register on the stack below the
// return address.
//
// The code here is highly dependent on the compiler-generated entry
// sequence, so CHECK WITH CARE if any compiler other than Borland
// is being used!
//
static void far TSRint13 ()
{
// set correct data segment
asm { push ds; mov bp,seg diskflag; mov ds,bp; }
// set flag while disk operation in progress
diskflag = 1;
// chain to old int 13
oldint13 ();
// clear disk flag
diskflag = 0;
// return using "retf 2" to leave flags intact
asm { pop ds; pop bp; retf 2; }
}
//--------------------------------------------------------------------------
//
// Int 21 (DOS service) handler.
//
// This handler is installed immediately prior to activating the
// TSR, and checks that the TSR does not call any unsafe services.
// The unsafe services are 00-0C (character I/O services), 3E (close
// file) for standard handles (0-4), 48 (allocate memory) and 4C
// (terminate process), or functions above 0C if a critical error
// is being handled. If any of these are called from the TSR, the
// virtual function "dos_error" will be called with the service
// number as a parameter. All other calls are passed to DOS in
// the normal way.
//
static void interrupt TSRint21 (unsigned, unsigned, unsigned,
unsigned, unsigned, unsigned,
unsigned, unsigned bx, unsigned ax,
unsigned ip, unsigned cs)
{
// static flag keeps track of whether called from "dos_error"
static int dosflag = 0;
// ignore DOS calls from within "dos_error"
if (dosflag != 0)
return;
// trap and ignore unsafe calls
const unsigned ah = ax >> 8;
if ((!*critical && (ah <= 0x0C || ah == 0x48 || ah == 0x4C
|| (ah == 0x3E && bx <= 4)))
|| (*critical && ah > 0x0C))
{ dosflag = 1;
instance->dos_error (ah, *critical, cs, ip);
dosflag = 0;
return;
}
// chain to old handler for safe calls
_chain_intr (oldint21);
}
//--------------------------------------------------------------------------
//
// Int 23 (control-break) handler.
//
// This handler is installed immediately prior to activating the
// TSR. It just sets a flag to record that control-break has been
// pressed. The main TSR function can poll this flag using the
// member function "userbreak", which returns the flag value and
// also resets the flag.
//
static void interrupt TSRint23 ()
{
breakflag = 1;
}
//--------------------------------------------------------------------------
//
// Int 24 (critical error) handler.
//
// This handler is installed immediately prior to activating the
// TSR. It just calls the virtual function "critical_error" to
// deal with the error.
//
static void interrupt TSRint24 (unsigned, unsigned di, unsigned,
unsigned, unsigned, unsigned,
unsigned, unsigned, unsigned ax)
{
ax = instance->critical_error (di & 0x00FF);
if (ax == 2 || ax > 3)
ax = 3;
}
//--------------------------------------------------------------------------
//
// Int 28 (DOS scheduler) handler.
//
// This handler is called by DOS whenever it is idle. It first
// checks to see if the TSR is not already running and that there
// is a pending activation request. If not, it calls the old
// handler and exits. Otherwise it checks if DOS is in a safe
// state and if it is activates the TSR. Note that the INDOS
// flag is safe if it is 1, as this means it is being called
// from inside DOS but that no nested calls are in progress.
//
static void interrupt TSRint28 ()
{
// chain to old handler if TSR is already running or activation not
// requested
if (TSRactive || (!hotkeyflag && !TSRrequest))
_chain_intr (oldint28);
// activate TSR if DOS is safe
if (*indos <= 1 && *critical == 0 && diskflag == 0)
activate ();
}
//--------------------------------------------------------------------------
//
// Int 2F (multiplex) handler.
//
// This function provides a means of communicating with the TSR
// for installation testing and unloading. AH must be the TSR
// function code (as located by the constructor), AL must be the
// subfunction code (00, 01, or 80 to FF), BX:DI must point to a
// copy of the class copyright notice and ES:SI must point to a
// copy of the TSR's name (or be a null pointer if AL = 00). If
// these conditions are not met, the call is passed to the original
// handler. Otherwise ES:SI is set to point to the copyright notice
// (which is guaranteed not to be the same as any identifying string
// due to its length) and a result code from the selected subfunction
// is stored in AX. The calls are handled as follows:
//
// Subfunction 00 (installation check): set AX to 0xFF if ES:SI point
// to a copy of this TSR's name (TSR is installed) or if ES:SI
// are both zero (check if function is available for use by a
// TSR built using this class).
// Subfunction 01 (unload TSR): the handler attempts to unhook the
// TSR ready to be unloaded from memory. If this is successful,
// AX is set to the TSR's PSP address; otherwise AX will be set
// to zero.
// Subfunctions 80 - FF (application-specific): CX and DX are the
// call parameters (which might be a far pointer to a longer
// parameter block). They are passed as references to the
// virtual function "respond". The function result is returned
// in AX; the values of CX and DX as set by "respond" will also
// be returned to the calling program.
//
static void interrupt TSRint2F (unsigned, unsigned di, unsigned si,
unsigned, unsigned es, int dx,
int cx, unsigned bx, unsigned ax)
{
if ((ax >> 8) != TSRfunction
|| compare ((char far*) MK_FP(bx,di), COPYRIGHT) != 0)
_chain_intr (oldint2F); // this call doesn't return
unsigned al = ax & 0xFF; // subfunction code
// installation check
if (al == 0 && ((es == 0 && si == 0)
|| compare ((char far*) MK_FP(es,si), TSRname) == 0))
ax = 0xFF;
// unload resident copy request
else if (al == 1 && compare ((char far*) MK_FP(es,si), TSRname) == 0)
ax = unhook ();
// application-specific request to resident copy
else if (al >= 0x80 && compare ((char far*) MK_FP(es,si), TSRname) == 0)
ax = instance->respond (al & 0x7F, cx, dx);
// interrupt not directed here, so chain to old handler
else
_chain_intr (oldint2F); // this call doesn't return
// set ES:SI to address of resident copy's copyright notice
es = FP_SEG (COPYRIGHT);
si = FP_OFF (COPYRIGHT);
}
//--------------------------------------------------------------------------
//
// Activate body of TSR.
//
// This function performs some essential housekeeping operations
// before and after calling the main TSR function. This involves
// hooking interrupts 21, 23 and 24, saving the current DTA and
// PSP addresses, switching to the user stack, setting the TSRactive
// flag to avoid re-entrant activations, and clearing TSRrequest.
// After TSR::main has returned, the hotkey flag is cleared and
// the original context (stack, DTA, PSP and interrupt vectors)
// is restored. Since this function is declared as a friend in
// the class declaration, it has to be public; to make sure it is
// not called directly, it checks that "TSRrequest" is non-zero
// before doing anything.
//
void activate ()
{
// declare non-stack variables
static unsigned oldpsp; // saved PSP segment
static unsigned olddtaseg; // saved DTA segment
static unsigned olddtaoff; // saved DTA offset
static int oldsp; // saved stack pointer
static int oldss; // saved stack segment
static union REGS r; // registers for DOS calls
static struct SREGS s; // segment registers for DOS calls
// test TSR activation has been requested
if (TSRrequest == 0)
return;
// save DTA and PSP addresses
r.h.ah = 0x2F; // get DTA
intdosx (&r, &r, &s);
olddtaseg = s.es, olddtaoff = r.x.bx;
r.h.ah = 0x51; // get PSP
intdos (&r, &r);
oldpsp = r.x.bx;
// set DTA and PSP to saved addresses
r.h.ah = 0x1A; // set DTA
s.ds = dtaseg, r.x.dx = dtaoff;
intdosx (&r, &r, &s);
r.h.ah = 0x50; // set PSP
r.x.bx = pspseg;
intdos (&r, &r);
// set interrupt vectors for TSR execution
oldint21 = getvect (0x21);
oldint23 = getvect (0x23);
oldint24 = getvect (0x24);
setvect (0x21, Handler (TSRint21));
setvect (0x23, Handler (TSRint23));
setvect (0x24, Handler (TSRint24));
// switch to TSR stack
asm { cli; }
oldsp = _SP;
oldss = _SS;
_SP = FP_OFF (stack);
_SS = FP_SEG (stack);
asm { sti; }
// execute body of TSR
TSRactive = 1;
instance->main (hotkeyflag);
TSRactive = 0;
// restore original stack
asm { cli; }
_SP = oldsp;
_SS = oldss;
asm { sti; }
// restore original interrupt vectors
setvect (0x21, oldint21);
setvect (0x23, oldint23);
setvect (0x24, oldint24);
// save DTA and PSP addresses
r.h.ah = 0x2F; // get DTA
intdosx (&r, &r, &s);
dtaseg = s.es, dtaoff = r.x.bx;
r.h.ah = 0x51; // get PSP
intdos (&r, &r);
pspseg = r.x.bx;
// reset original DTA and PSP
r.h.ah = 0x1A; // set DTA
s.ds = olddtaseg, r.x.dx = olddtaoff;
intdosx (&r, &r, &s);
r.h.ah = 0x50; // set PSP
r.x.bx = oldpsp;
intdos (&r, &r);
// clear hotkey and request flags
hotkeyflag = 0;
TSRrequest = 0;
}
//--------------------------------------------------------------------------
//
// TSR::TSR
//
// This is the class constructor. It checks for a number of
// possible errors, and stores the name and stack size of the
// TSR in global variables. A pointer to the current instance
// is stored in the global variable "instance" so that interrupt
// routines can invoke member functions. The multiplex (int 2F)
// functions from 0xC0 to 0xFF are scanned for an unused function
// or an existing TSR based on this class; this is stored in
// "TSRfunction" and will be the function code recognised by
// the int 2F handler.
//
TSR::TSR (const char* name, unsigned stacksize)
{
stat = SUCCESS;
// copy name
strncpy (TSRname, name, sizeof(TSRname));
TSRname [sizeof(TSRname) - 1] = '\0';
// check DOS version
if (_osmajor < 3)
{ stat = DOS_VERSION; // error: DOS version < 3
return;
}
// save instance pointer
if (instance != 0)
{ stat = MULTI_COPIES; // error: multiple instances
return;
}
instance = this;
// set stack length
stacklen = (stacksize < MIN_STACK ? MIN_STACK : stacksize);
// find spare multiplex function
union REGS r;
struct SREGS s;
for (int i = 0xC0; i <= 0xFF; i++)
{ r.h.ah = i, r.h.al = 0;
r.x.bx = FP_SEG (COPYRIGHT);
r.x.di = FP_OFF (COPYRIGHT);
s.es = 0, r.x.si = 0;
int86x (0x2F, &r, &r, &s);
if (r.h.al == 0)
break; // function not in use
if (r.h.al == 0xFF
&& compare ((char far*) MK_FP(s.es, r.x.si), COPYRIGHT) == 0)
break; // function in use by derivation of TSR
}
if (i > 0xFF)
{ stat = NO_MUX_FUNC; // error: can't find multiplex function
return;
}
TSRfunction = i;
}
//--------------------------------------------------------------------------
//
// Do TSR setup.
//
// This function creates the stack, saves the DTA and PSP addresses,
// locates the INDOS and critical error flags, and hooks the wake-up
// interrupts. Note that the stack is never deleted; it will remain
// in existence while the TSR is loaded, and will be deallocated along
// with everything else when it is unloaded. It returns zero if the
// stack could not be allocated.
//
static int setup ()
{
// create stack (note: this is never deleted!)
char* stk = new char [stacklen];
if (stk == 0)
return 0;
stack = (stk + stacklen);
// save DTA and PSP addresses
union REGS r;
struct SREGS s;
r.h.ah = 0x2F; // get DTA
intdosx (&r, &r, &s);
dtaseg = s.es, dtaoff = r.x.bx;
r.h.ah = 0x51; // get PSP
intdos (&r, &r);
pspseg = r.x.bx;
// locate INDOS flag
r.h.ah = 0x34;
intdosx (&r, &r, &s);
indos = (char far*) MK_FP(s.es, r.x.bx);
// locate critical error flag
r.x.ax = 0x5D06;
intdosx (&r, &r, &s);
critical = (char far*) MK_FP(s.ds, r.x.si);
// hook interrupts
oldint8 = getvect (0x08);
oldint9 = getvect (0x09);
oldint13 = getvect (0x13);
oldint28 = getvect (0x28);
setvect (0x08, Handler (TSRint8));
setvect (0x09, Handler (TSRint9));
setvect (0x13, Handler (TSRint13));
setvect (0x28, Handler (TSRint28));
return 1;
}
//--------------------------------------------------------------------------
//
// TSR::run
//
// This function makes the TSR resident, and will only return if
// a copy of the TSR is already loaded. The virtual function
// "startup" is called to perform any class-specific intialisation.
//
void TSR::run (int hotkey, unsigned timeslice)
{
if (stat != SUCCESS)
return;
// avoid reloading TSR if already loaded or failed
if (loaded ())
{ stat = RELOAD_FAIL;
return;
}
// set hotkey shift state and scancode
hotshift = (hotkey >> 8) & 0xFF;
hotscan = hotkey & 0xFF;
// check for F1 .. F10, which have different scan codes if ALT, CTRL
// or SHIFT is pressed (ALT takes precedence over CTRL which takes
// precedence over SHIFT)
if (hotscan >= F1 && hotscan <= F10)
{ if ((hotkey & ALT) != 0)
hotscan += ALT_FKEY;
else if ((hotkey & CTRL) != 0)
hotscan += CTRL_FKEY;
else if ((hotkey & (LSHIFT | RSHIFT)) != 0)
hotscan += SHIFT_FKEY;
}
// set length of timeslice
interval = timeslice;
nexttick = *timer + interval;
// perform user startup actions
if (instance->startup () != 0)
{ stat = STARTUP_ERR;
return;
}
// do TSR setup if hotkey or timeslice specified
if (hotkey != NONE || timeslice != 0)
{ if (setup() == 0)
{ stat = STACK_FAIL;
return;
}
}
// hook multiplex interrupt for self-identification
oldint2F = getvect (0x2F);
setvect (0x2F, Handler (TSRint2F));
// terminate and stay resident (exit status = 0), memory size taken
// from bytes 3 and 4 of arena header in paragraph preceding PSP.
keep (0, *(unsigned far*) MK_FP(_psp-1, 3));
// this line should never be reached
stat = LOAD_FAIL;
}
//--------------------------------------------------------------------------
//
// TSR::unload
//
// Unloads a previously loaded copy of the TSR from memory. The
// function returns 0 if the TSR was unloaded successfully, 1 if
// there is no previously loaded copy, and 2 if the previous copy
// cannot be unloaded. The TSR is invoked to unload itself using
// the multiplex interrupt (2F).
//
int TSR::unload ()
{
union REGS r;
struct SREGS s;
// call TSR to unload itself via multiplex interrupt
r.h.ah = TSRfunction, r.h.al = 1;
r.x.bx = FP_SEG (COPYRIGHT);
r.x.di = FP_OFF (COPYRIGHT);
s.es = FP_SEG (TSRname);
r.x.si = FP_OFF (TSRname);
int86x (0x2F, &r, &r, &s);
// exit if TSR is not loaded
if (compare ((char far*) MK_FP(s.es, r.x.si), COPYRIGHT) != 0)
return NOT_LOADED;
// exit if TSR cannot be unloaded
if (r.x.ax == 0)
return UNHOOK_FAIL;
// locate the TSR environment segment from the PSP
const unsigned env = *(unsigned far*) MK_FP(r.x.ax, 0x2C);
// free the TSR's memory
s.es = r.x.ax;
r.h.ah = 0x49;
intdosx (&r, &r, &s);
if (r.x.cflag != 0)
return MEM_FROZEN;
// free the TSR's environment
s.es = env;
r.h.ah = 0x49;
intdosx (&r, &r, &s);
if (r.x.cflag != 0)
return ENV_FROZEN;
// successfully unloaded
return SUCCESS;
}
//--------------------------------------------------------------------------
//
// TSR::request
//
// This function is used by foreground copies of the program to
// communicate with a resident copy. "Fn" is the function code
// and "p1" and "p2" are application-specific parameters to be
// passed to the resident copy. Their updated values are returned
// to the caller.
//
int TSR::request (int& fn, int& p1, int& p2)
{
union REGS r;
struct SREGS s;
r.h.ah = TSRfunction, r.h.al = fn | 0x80;
r.x.bx = FP_SEG (COPYRIGHT);
r.x.di = FP_OFF (COPYRIGHT);
s.es = FP_SEG (TSRname);
r.x.si = FP_OFF (TSRname);
r.x.cx = p1, r.x.dx = p2;
int86x (0x2F, &r, &r, &s);
if (compare ((char far*) MK_FP(s.es, r.x.si), COPYRIGHT) != 0)
return NOT_LOADED;
fn = r.x.ax, p1 = r.x.cx, p2 = r.x.dx;
return SUCCESS;
}
//--------------------------------------------------------------------------
//
// Unhook TSR.
//
// This function prepares the TSR for unloading from memory. The
// function returns the PSP address if the TSR can be unloaded
// successfully, and 0 otherwise (if any of the interrupt vectors
// have been re-hooked). The virtual function "shutdown" is also
// called to perform any class-specific finalisation. Interrupts
// 08, 09, 13 and 28 only need unhooking if there is a non-null
// hotkey or timeslice.
//
static int unhook ()
{
// exit if something has hooked the multiplex interrupt
if (getvect (0x2F) != Handler (TSRint2F))
return 0;
// try to unhook wake-up interrupts if necessary
if (hotshift != 0 || hotscan != 0 || interval != 0)
{ if (getvect (0x08) != Handler (TSRint8) ||
getvect (0x09) != Handler (TSRint9) ||
getvect (0x13) != Handler (TSRint13) ||
getvect (0x28) != Handler (TSRint28))
return 0;
setvect (0x08, oldint8);
setvect (0x09, oldint9);
setvect (0x13, oldint13);
setvect (0x28, oldint28);
}
// unhook multiplex interrupt
setvect (0x2F, oldint2F);
// perform user shutdown actions
if (instance->shutdown () != 0)
return 0;
// return PSP address
return _psp;
}
//--------------------------------------------------------------------------
//
// TSR::loaded
//
// Returns true if a copy of the TSR is already in memory.
//
int TSR::loaded ()
{
union REGS r;
struct SREGS s;
r.h.ah = TSRfunction, r.h.al = 0;
r.x.bx = FP_SEG (COPYRIGHT);
r.x.di = FP_OFF (COPYRIGHT);
s.es = FP_SEG (TSRname);
r.x.si = FP_OFF (TSRname);
int86x (0x2F, &r, &r, &s);
return r.h.al == 0xFF
&& compare ((char far*) MK_FP(s.es, r.x.si), COPYRIGHT) == 0;
}
//--------------------------------------------------------------------------
//
// Display a string using BIOS calls.
//
// This function is used by "dos_error" below to display error
// messages without using DOS calls.
//
static void display (int page, const char* s)
{
union REGS r;
while (*s != '\0')
{ if (*s >= ' ') // set colours for printing characters
{ r.x.ax = 0x0920; // display a space, using...
r.h.bh = page;
r.h.bl = 0x0E; // ...yellow on black
r.x.cx = 1;
int86 (0x10, &r, &r);
}
r.h.ah = 0x0E; // now display actual character
r.h.al = *s++;
r.h.bh = page;
int86 (0x10, &r, &r);
}
}
//--------------------------------------------------------------------------
//
// TSR::dos_error
//
// This function is called if an unsafe DOS call is made from the
// main TSR function. If it is called, it indicates a program bug.
// This is a virtual function so that a more appropriate function
// can be provided in classes derived from TSR. The parameter "fn"
// is the function code from register AH; "ce" is non-zero if a
// critical error is being handled; "cs:ip" is the return address
// (i.e. the address of the instruction after the illegal call).
//
void TSR::dos_error (int fn, int ce, int cs, int ip)
{
union REGS r;
char fmt [10];
// get video mode
r.h.ah = 0x0F;
int86 (0x10, &r, &r);
const int mode = r.h.al;
// select text mode if in graphics mode
const int graphics = (r.h.al > 3 && r.h.al != 7);
if (graphics)
{ r.x.ax = 0x0002;
int86 (0x10, &r, &r);
}
// get active page
r.h.ah = 0x0F;
int86 (0x10, &r, &r);
const int page = r.h.bh;
// display error message
display (page, "\a\r\n*** Illegal DOS call detected in TSR \"");
display (page, TSRname);
display (page, "\"\r\nFunction ");
sprintf (fmt, "%02X", fn); display (page, fmt);
display (page, " called from ");
sprintf (fmt, "%04X:%04X", cs, ip - 2); display (page, fmt);
if (ce != 0)
display (page, " during critical error handling");
display (page, ".\r\nThis call should not be used by a TSR and indicates"
" a bug in the program.\r\nPress any key to ignore the"
" call and continue... ");
// wait for a keypress
bioskey (0);
display (page, "\r\n");
// restore screen mode if it was a graphics mode
if (graphics)
{ r.x.ax = mode;
int86 (0x10, &r, &r);
}
}
//--------------------------------------------------------------------------
//
// TSR::pause
//
// This function generates an INT 28 (the DOS scheduler interrupt).
// This interrupt is used to wake up TSRs, so the main TSR function
// can call this during lengthy processing to ensure that other TSRs
// get a chance to wake up.
//
void TSR::pause ()
{
union REGS r;
int86 (0x28, &r, &r);
}
//--------------------------------------------------------------------------
//
// TSR::sync
//
// This function resynchronises the timeslice interval so that the
// next timed wakeup will happen after "interval" ticks from now.
//
void TSR::sync ()
{
nexttick = *timer + interval;
}
//--------------------------------------------------------------------------
//
// TSR::userbreak
//
// This function returns the value of the flag which indicates if
// control-break has been pressed. It also resets the flag.
//
int TSR::userbreak ()
{
int b = breakflag;
breakflag = 0;
return b;
}
//--------------------------------------------------------------------------
//
// TSR::name
//
// This function returns the name of the TSR instance.
//
const char* TSR::name ()
{
return TSRname;
}