Dec 192017
 
Borland tells (on BIX) how to chain interrupts in Turbo/Borland C/C++.
File CHAININT.ZIP from The Programmer’s Corner in
Category C Source Code
Borland tells (on BIX) how to chain interrupts in Turbo/Borland C/C++.
File Name File Size Zip Size Zip Type
CHAININT.TXT 10640 3043 deflated

Download File CHAININT.ZIP Here

Contents of the CHAININT.TXT file


==========================
borland/long.messages #90, from pete.williams, 10207 chars, Tue Jul 30 16:48:18 1991
--------------------------
TITLE: Chaining interrupts in C/C++

ISRs: CHAINING TO AND RETURNING VALUES IN REGISTERS
---------------------------------------------------
[ Bruneau Babet - Borland Assembler/Debug Tech Support ]

An interrupt is a signal to the CPU from a program or hardware
device (eg. keyboard) directing the CPU to temporarily suspend
what it is doing, in order to perform specific processing. For
each interrupt, a section of code (known as Interrupt Service
Routine or ISR) is executed each time the interrupt occurs. TSRs
(a.k.a. Memory Resident Programs) rely on Interrupts/ISRs to perform
*background processing* or be activated/popped up. ISRs are
also used in non-TSR programs whenever the default ones prove
inadequate (eg. Serial Communications), there's a need to
communicate with a new device etc. etc.

A MINIMAL ISR in C/C++:
-----------------------

The following is more or less the minimum code for an ISR in C/C++:

void interrupt MyISR(void)
{
}

The following displays the code actually generated for the above
example:

// An minimal ISR in C/C++ 3 // Assembly Code generated
3
3
void interrupt MyISR(void) 3 _MyISR proc far
3 push ax
3 push bx
3 push cx
3 push dx
3 push es
3 push ds
3 push si
3 push di
3 push bp
3 mov bp,DGROUP
3 mov ds,bp
3 mov bp,sp
{ 3
// BODY OF ISR // 3
} 3
3 pop bp
3 pop di
3 pop si
3 pop ds
3 pop es
3 pop dx
3 pop cx
3 pop bx
3 pop ax
3 iret
3 _MyISR endp

A TYPICAL ISR:
--------------
A more typical ISR, however, chains to the Old ISR and looks like the
following:

void interrupt (*OldISR)();

void interrupt MyISR(void)
{
// DO SOMETHING
OldISR();
}

The following code is generated for the above:

void interrupt MyISR(void) _MyISR proc far
push ax
push bx
push cx
push dx
push es
push ds
push si
push di
push bp
mov bp,DGROUP
mov ds,bp
mov bp,sp
{
// DO SOMETHING
OldISR();
pushf
call dword ptr DGROUP:_OldISR
}
pop bp
pop di
pop si
pop ds
pop es
pop dx
pop cx
pop bx
pop ax
iret
_MyISR endp




The compiler pushes all registers (actually all except SS & SP)
on the stack upon entry into the ISR and pops them off upon
exit... This ensures that the foreground's process registers are
preserved. However, consider the following:

- Since registers are pushed upon entry and popped, how do I
make my ISR return results in registers if it needs to?

- Since the value of DS and BP are destroyed by the code
generated (NOTE: this is required in order to give the ISR
access to the program's Global Variables and create a stack
frame to allow for local variables), what would happen if
the interrupt I am trapping and chaining to expects
information in those registers ?

(i) RETURNING VALUES IN REGISTERS FROM AN ISR
-----------------------------------------

To make your ISR return values in registers one can take advantage of one
fact:
since the stack frame for C/C++ ISRs is only created after the registers have
been pushed on the stack, one can almost consider the registers as variables
passed to the ISR.. Declaring your ISR as follows allows you to modify the
actual words on the stack which will be popped into the various registers
upon exiting the ISR...

void interrupt NewISR(unsigned bp, unsigned di, unsigned si, unsigned ds,
unsigned es, unsigned dx, unsigned cx, unsigned bx,
unsigned ax, unsigned ip, unsigned cs, unsigned flags)

The following example is an ISR for INT 13H (DISK BIOS SERVICES). It is
customary to trap Interrupt 13H so that one may monitor DISK I/O activity
and therefore determine whether it is safe to do asynchronous processing.
In this example, we merely set a flag whenever INT 13H is busy:

void interrupt (*Old13Handler)();
unsigned Int13BusyFlag = 0x00 ;

void interrupt New13Handler( unsigned bp, unsigned di, unsigned si,
unsigned ds, unsigned es, unsigned dx,
unsigned cx, unsigned bx, unsigned ax,
unsigned ip, unsigned cs, unsigned flags)
{
Int13BusyFlag++; // Increment the Int13BusyFlag
(*Old13Handler)(); // Call Old Handler
ax = _AX; // Return value of AX
bx = _BX; // Return value of BX
cx = _CX; // Return value of CX
dx = _DX; // Return value of DX
es = _ES; // Return value of ES
di = _DI; // Return value of DI
flags = _FLAGS; // Return the Flags
--Int13BusyFlag; // Restore the Int13BusyFlag
}

(ii) CHAINING TO AN INTERRUPT WITH ALL REGISTERS INTACT
--------------------------------------------------

Often one will trap an interrupt with expects some values in particular
registers. As shown above, the setup for a C/C++ ISR results in the
modification of the values of BP and DS... The BIOS Video Interrupt 10H
may have important data in BP when called (see Function 13H) while
several of the INT 21H functions have various values passed in DS...
Trapping these Interrupts therefore requires that the value of BP and DS
(and other registers whenever appropriate) be restored to their original
values prior to chaining to the Old ISR. The following Code gives an
example where all registers are restored prior to chaining:

#include

#define INT_NUMBER 0x21 // Trap Interrupt 21H (!)

void interrupt (*OldISR)(); // For Storing address of Old Vector

void interrupt NewISR(unsigned bp, unsigned di, unsigned si, unsigned ds,
unsigned es, unsigned dx, unsigned cx, unsigned bx,
unsigned ax, unsigned ip, unsigned cs, unsigned flags)
{
// Insert your ISR Code Here...
// .
// if (I'mNotChaining)
// {
// DoWhatever();
// }

// The following Code indirectly chains to the Old Vector by:
// 1. Putting the Address of the Old ISR on the Stack
// 2. Restoring the Value of Registers Pushed on the Stack
// 3. Executing a Far Return which translates into a jump to
// the address on the Stack (i.e. the Old Vector's Address)

_BX = bx; // Restore value of BX
register
_CX = ax; // Save value of AX in CX
ax = FP_SEG((void far *)OldISR); // Place Address of OldISR
bx = FP_OFF((void far *)OldISR); // on the stack...
_AX = _CX ; // Restore value of AX
register
__emit__(0x5D); // asm POP BP -> Restore BP
__emit__(0x5F); // asm POP DI -> Restore DI
__emit__(0x5E); // asm POP SI -> Restore SI
__emit__(0x1F); // asm POP DS -> Restore DS
__emit__(0x07); // asm POP ES -> Restore ES
__emit__(0x5A); // asm POP DX -> Restore DX
__emit__(0x59); // asm POP CX -> Restore CX
__emit__(0xCB); // asm RETF -> Indirect Far Jmp to OldISR

//NOTE: Any Code of the ISR beyond this point will not be executed.
// The above does not *CALL* the Old ISR but rather *JUMPS* to it.
// When the OldISR executes its IRET, control will resume at the
// original location prior to the INTERRUPTion !!
} // B.B.

int main(void)
{
unsigned CountDown = 999;

OldISR = getvect(INT_NUMBER);
setvect(INT_NUMBER, ( void interrupt (*)() )NewISR);

while(CountDown--)
printf("This is the countdown: [%03d]\r", CountDown);
printf("\nThis is the end of the countdown !\n");

setvect(INT_NUMBER, OldISR);
return 0;
}
-------------------- CUT HERE --------------------------
Pete Williams @ Borland Tech Support


 December 19, 2017  Add comments

Leave a Reply