Listing 1: Thunk code and test stub
// ISRTHUNK.C
// Thunking code for interrupts.
// D.G. Taylor 1996.
// Borland C++ V3.1
#include <dos.h>
#include <mem.h>
#include <stdlib.h>
#include <isrthunk.h>
#ifdef DEBUG
#include <stdio.h>
#endif
// Note : The address mode used by the MOV instruction is Direct
// which requires the address bytes follow the op code and address mode
// bytes in the order <low order><highorder>. These are not displacement
// bytes but addresses relative to the CS value. Which will be the
// start of the THUNK. The prefix byte of 0x2E is the segment prefix
// override byte since the code is addressing data relative to the
// code segment.
static unsigned char ISRThunkCode[0x40] = {
0x2E,0x8C,0x16,0x31,0x00, // mov [CS:THCurrentSS],ss
0x2E,0x89,0x26,0x2F,0x00, // mov [CS:THCurrentSP],sp
0x2E,0x8E,0x16,0x35,0x00, // mov ss,[CS:THisrSS]
0x2E,0x8B,0x26,0x33,0x00, // mov sp,[CS:THisrSP]
0x2E,0xFF,0x36,0x39,0x00, // push [DWORD PTR CS:ThisrObject]
0x2E,0xFF,0x36,0x37,0x00, // The second word
0x9C, // pushf
0x2E,0xFF,0x1E,0x3B,0x00, // call [DWORD PTR CS:THisrAddress]
0x2E,0x8E,0x16,0x31,0x00, // mov ss,[CS:THCurrentSS]
0x2E,0x8B,0x26,0x2F,0x00, // mov sp,[CS:THCurrentSP]
0xCF }; // iret
// Variables follow here. The are defined as offsets obtained
// from the assembler listing.
#define THCurrentSP 0x2F
#define THCurrentSS 0x31 // The current stack.
#define THisrSP 0x33
#define THisrSS 0x35 // The new stack
#define THisrObject 0x37 // The client's this pointer.
#define THisrObjectSeg 0x39 // Object segment.
#define THisrAddress 0x3B // Pointer to the client's ISR.
#define SEGMENTSIZE 16 // Bytes per segment.
// Local Prototypes.
static void huge *Normalise(void *p);
//==================================================================
#ifdef THUNKTEST
#include <stdio.h>
#include <conio.h>
typedef struct {
long TickCount;
int Quit;
int SecElapsed;
int Seconds;
} TICKINFO;
void interrupt Ticker(THUNKARGS);
void main()
{
ISRTHUNK *pthunk;
TICKINFO *pInfo;
if ( HeapAlloc((void **)&pInfo,sizeof(TICKINFO)) == FALSE )
return;
pInfo->TickCount = 0L;
pInfo->Quit = 0;
pInfo->SecElapsed = 0;
pInfo->Seconds = 0;
#ifdef DEBUG
printf("Object address is %lx\n",(void far *)pInfo);
printf("Handler address is %lx\n",Ticker);
#endif
if ( ISRThunkCreate(&pthunk,0x1C,Ticker,pInfo,2048) == FALSE )
{
printf("Error opening thunk.\n");
return;
}
ISRThunkActivate(pthunk);
while ( pInfo->Quit == 0 && kbhit() == 0 )
{
if (pInfo->SecElapsed == 1 )
{
pInfo->SecElapsed = 0;
printf(".");
}
}
ISRThunkSuspend(pthunk);
printf("\nSeconds Elapsed are %d\n",pInfo->Seconds);
ISRThunkDestroy(pthunk);
HeapFree(pInfo);
}
//------------------------------------------------------------------
#pragma argsused
void interrupt Ticker(THUNKARGS)
{
TICKINFO *ptick = (TICKINFO *)pobject; // conversion from far to near.
ASSERT(HeapValidPointer(ptick,sizeof(TICKINFO)));
ptick->TickCount++;
if ( ptick->TickCount%18 == 0 )
{
ptick->SecElapsed = 1;
ptick->Seconds++;
}
if ( ptick->Seconds == 10 )
ptick->Quit = 1; // quit.
}
#endif
//------------------------------------------------------------------
// INTERFACE
//==================================================================
// Name: ISRThunkCreate()
// Action: Create a thunk object, for a specified interrupt number.
// Parameters: ppThunk - pointer to a pointer to a ISRTHUNK.
// If successful *ppThunk now points to the new ISRTHUNK
// object.
// uIntno - The interrupt number.
// pfISRHandler - pointer to the client's interrupt function
// pISRObject - pointer passed to the client's interrupt
// function
// uStacksize - The size of stack required for the
// client's handler.
// Returns: TRUE if memory allocation successful, otherwise FALSE.
BOOL ISRThunkCreate(ISRTHUNK **ppThunk,
unsigned uIntno,void interrupt (*pfISRHandler)(__CPPARGS),
void *pISRObject,unsigned uStacksize)
{
void *pvTemp;
unsigned uThunkSegment; // for readability.
unsigned char huge *phThunkCode; // for readability.
ISRTHUNK *pThunk;
// Allocate memory.
if ( HeapAlloc((void **)&pThunk,sizeof(ISRTHUNK)) == FALSE )
return FALSE;
// Allocate memory for the Code+Data+Stack. Code must be aligned
// on a segment boundary, so allow 1 segment extra.
if ( HeapAlloc((void **)&(pThunk->pbCodeDataStack),
sizeof(ISRThunkCode)+uStacksize+SEGMENTSIZE) == FALSE )
{
HeapFree(pThunk);
return FALSE;
}
pThunk->uIntNumber = uIntno;
pThunk->bActivated = FALSE;
// Find the next segment boundary.
uThunkSegment = FP_SEG(Normalise(pThunk->pbCodeDataStack))+1;
phThunkCode = (unsigned char huge *)MK_FP(uThunkSegment,0);
pThunk->pfThunkHandler = (void interrupt (*)())phThunkCode;
// The previous or old interrupt handler is not known until the
// ISRThunkActivate() is called.
// Copy in the Thunk code from the array above.
_fmemcpy(phThunkCode,(void huge *)ISRThunkCode,sizeof(ISRThunkCode));
// Assign the thunk stack. Note that for the small models the stack
// is relative to the current SS, for the large models the SS is set
// to the start of the thunk code.
#if defined(__COMPACT__) || defined(__LARGE__) || defined(__HUGE__)
*(unsigned huge *)(phThunkCode+THisrSS) = uThunkSegment;
*(unsigned huge *)(phThunkCode+THisrSP) =
(unsigned)(sizeof(ISRThunkCode)+uStacksize-2);
*(unsigned long huge *)(phThunkCode+THisrObject) =
(unsigned long)pISRObject;
#else // small models
{
struct SREGS segs;
segread(&segs);
*(unsigned huge *)(phThunkCode+THisrSS)=segs.ss;
*(unsigned huge *)(phThunkCode+THisrSP)=
((uThunkSegment-segs.ss)<<4)
+sizeof(ISRThunkCode)+uStacksize-2;
*(unsigned huge *)(phThunkCode+THisrObjectSeg)=segs.ds;
*(unsigned huge *)(phThunkCode+THisrObject)=(unsigned)pISRObject;
}
#endif
#ifdef DEBUG
printf("Thunk Stack Segment : %x, Thunk Stack Pointer : %x",
*(unsigned huge *)(phThunkCode+THisrSS),
*(unsigned huge *)(phThunkCode+THisrSP));
#endif
*(unsigned long huge *)(phThunkCode+THisrAddress) =
(unsigned long)pfISRHandler;
*ppThunk = pThunk;
return TRUE;
}
//------------------------------------------------------------------
// Name: ISRThunkActivate()
// Action: Switches interrupt vectors, causing subsequent interrupts
// to be sent to the clients interrupt function. The current
// interrupt vector is saved in the thunk object.
// Parameters: The thunk pointer.
// Returns: Nothing.
void ISRThunkActivate(ISRTHUNK *pThunk)
{
ASSERT(HeapValidPointer(pThunk,sizeof(ISRTHUNK)));
ASSERT(pThunk->bActivated == FALSE);
pThunk->pfPrevHandler = getvect(pThunk->uIntNumber);
setvect(pThunk->uIntNumber,pThunk->pfThunkHandler);
pThunk->bActivated = TRUE;
}
//------------------------------------------------------------------
// Name: ISRThunkSuspend()
// Action: Switches interrupt vectors, causing the saved interrupt
// vector to be reinstated. The client's interrupt function
// will no longer be called.
// Parameters: The thunk pointer.
// Returns: Nothing.
void ISRThunkSuspend(ISRTHUNK *pThunk)
{
ASSERT(HeapValidPointer(pThunk,sizeof(ISRTHUNK)));
ASSERT(pThunk->bActivated == TRUE );
setvect(pThunk->uIntNumber,pThunk->pfPrevHandler);
pThunk->bActivated = FALSE;
}
//------------------------------------------------------------------
// Name: ISRCallOldISR()
// Action: Call the previous interrupt subroutine.
// This function can only be called after the thunk has been
// activated.
// Parameters: The thunk pointer.
// Returns: Nothing.
void ISRCallOldISR(ISRTHUNK *pThunk)
{
ASSERT(HeapValidPointer(pThunk,sizeof(ISRTHUNK)));
ASSERT(pThunk->bActivated == TRUE);
pThunk->pfPrevHandler();
}
//------------------------------------------------------------------
// Name: ISRChainOldISR()
// Action: This function can only be called from within
// the client's interrupt function passed to ISRThunkCreate()
// It does not return. After the old interrupt subroutine has
// returned, the THUNK will switch stacks and return to
// the current program.
// Parameters: The thunk pointer.
// Returns: Nothing.
void ISRChainOldISR(ISRTHUNK *pThunk)
{
ASSERT(HeapValidPointer(pThunk,sizeof(ISRTHUNK)));
ASSERT(pThunk->bActivated == TRUE);
_chain_intr(pThunk->pfPrevHandler);
}
//------------------------------------------------------------------
// Name: ISRThunkDestroy()
// Action: Checks for whether a thunk is currently active, and if so
// suspends it. The memory used by the THUNK is freed.
// Parameters: The thunk pointer.
// Returns: Nothing.
void ISRThunkDestroy(ISRTHUNK *pThunk)
{
ASSERT(HeapValidPointer(pThunk,sizeof(ISRTHUNK)));
if ( pThunk->bActivated == TRUE )
ISRThunkSuspend(pThunk);
HeapFree(pThunk->pbCodeDataStack);
HeapFree(pThunk);
}
//==================================================================
// IMPLEMENTATION
//------------------------------------------------------------------
// Name: Normalise()
// Action: Take a pointer of the current memory model and convert it
// to a nomalised huge pointer.
// Parameters: The void pointer to be converted to a huge pointer.
// Returns: The huge pointer equivalent of the void pointer passed
// as the argument.
static void huge *Normalise(void *pv)
{
// convert to a 20 bit address, and then take the most significant
// 16 bits as the segment.
void huge *ph;
unsigned long ulAddress;
ph = (void huge *)pv; // converts to huge but does not normalise
// This is one of the few occasions where mixing arithmetic
// and bit operations appears justified.
ulAddress = ((unsigned long)FP_SEG(ph)<<4) + FP_OFF(ph);
ph = MK_FP((unsigned)(ulAddress>>4),(unsigned)ulAddress&0xF);
return ph;
}
//------------------------------EOF isrthunk.c----------------------
// Hmemory.c
// Allocation of heap memory.
// Interface suggested in the book
// "Writing Solid Code" by Steve Macquire, Microsoft Press.
#include <hmemory.h>
//----------------------------------------------------------------
BOOL HeapAlloc(void **ppv,size_t size)
{
BYTE **ppb = (BYTE **)ppv;
ASSERT(size > 0);
ASSERT(ppb != (BYTE **)0);
*ppb = (BYTE *)malloc(size);
return (*ppb != (BYTE *)0);
}
//---------------------------------------------------------------
void HeapFree(void *pv)
{
ASSERT(pv != (void *)0);
free(pv);
}
//---------------------------------------------------------------
// HeapValidPointer() : Check the validity of a memory pointer.
// Put in your own code for this or see the code in Steve Macquire's book.
BOOL HeapValidPointer(void *pv,size_t size)
{
pv =pv;
size=size;
return TRUE;
}
//=========================EOF hmemory.c ==========================
//End of File