/*****************************************************************************
interrupt handler demo

CPU mode:	real mode (16-bit)
using DOS?	NO
language:	Borland C/C++, or Turbo C/C++ 3.x

Note: Turbo C 2.0 has no built-in assembler, so you need TASM
	if you use this compiler
Note: use of 'interrupt' keyword may cause TLINK error
	"Cannot generate COM file : segment-relocatable items present"
*****************************************************************************/
#include <conio.h> /* getch(), cputs(), outportb() */
/* don't #include <dos.h>
it's declarations of getvect() and setvect() conflict with our own */

#ifdef __cplusplus
extern "C"
{
#endif

unsigned char peekb(unsigned segment, unsigned offset);
void pokeb(unsigned segment, unsigned offset, char value);

#ifdef __cplusplus
}
#endif

#define	TIMER_VECT	8
#define	SYSCALL_VECT	50
#define	SYS_CPUTS	0

/* vector type and macros for portability  */

#ifdef __cplusplus
typedef void interrupt (*vector_t)(...);
#else
typedef void interrupt (*vector_t)();
#endif

#define	INTERRUPT			interrupt

#define	SAVE_VECTOR(vec, num)		vec = getvect(num)
#define	INSTALL_HANDLER(vec, num, fn)	setvect(num, (vector_t)fn)
#define	RESTORE_VECTOR(vec, num)	setvect(num, vec)
/*****************************************************************************
*****************************************************************************/
vector_t getvect(unsigned num)
{
	asm push es
	asm push bx
/* set ES to zero, so it points to the interrupt vector table at 0000:0000 */
	asm	xor bx,bx
	asm	mov es,bx
	asm	mov bx,[num]
/* x4 because each IVT entry (a far address) is 4 bytes long */
	asm	shl bx,1
	asm	shl bx,1
/* now ES:BX points to the vector we want; IVT[num].
Read the 32-bit vector (segment + offset) 'atomically',
without an interrupt happening in the middle of the read. */
	asm	les bx,es:[bx]
/* return 32-bit far address in DX:AX */
	asm	mov dx,es
	asm	mov ax,bx
	asm pop bx
	asm pop es
/* it DOES return a value... */
}
/*****************************************************************************
*****************************************************************************/
void setvect(unsigned num, vector_t h)
{
	asm push es
	asm push bx
/* set ES to zero, so it points to the interrupt vector table at 0000:0000 */
	asm	xor bx,bx
	asm	mov es,bx
	asm	mov bx,[num]
/* x4 because each IVT entry (a far address) is 4 bytes long */
	asm	shl bx,1
	asm	shl bx,1
/* I don't think 8088 or 80286 can do a 32-bit store,
so shut off interrupts and store 16 bits at a time. */
	asm	pushf
	asm		cli
	asm		mov ax,word ptr [h]
	asm		mov es:[bx],ax
	asm		mov ax,word ptr [h+2]
	asm		mov es:[bx+2],ax
	asm	popf
	asm pop bx
	asm pop es
}
/*****************************************************************************
increment char in upper left corner of screen on every timer tick
*****************************************************************************/
static void INTERRUPT timer_int(void)
{
	pokeb(0xB800, 0, peekb(0xB800, 0) + 1);
	outportb(0x20, 0x20);
}
/*****************************************************************************
*****************************************************************************/
#pragma argsused
static void INTERRUPT syscall_int(int junk, int di, int si, int ds,
		int es, int dx, int cx, int bx, int ax)
{
	unsigned char syscall_num;

	syscall_num = ax;
	syscall_num >>= 8;
	switch(syscall_num)
	{
		case SYS_CPUTS:
			cputs((char *)si);
			break;
	}
}
/*****************************************************************************
*****************************************************************************/
int main(void)
{
	static const char *msg = "press a key to quit\n\r";
/**/
	vector_t old_timer_vector, old_syscall_vector;

/* save old vectors */
	SAVE_VECTOR(old_timer_vector, TIMER_VECT);
	SAVE_VECTOR(old_syscall_vector, SYSCALL_VECT);
/* install new handlers */
	INSTALL_HANDLER(old_timer_vector, TIMER_VECT, timer_int);
	INSTALL_HANDLER(old_syscall_vector, SYSCALL_VECT, syscall_int);
/* make a system call */
	_AH = SYS_CPUTS;
	_SI = (unsigned)msg;
	asm int SYSCALL_VECT;
/* await key pressed */
	getch();
/* restore old vectors */
	RESTORE_VECTOR(old_timer_vector, TIMER_VECT);
	RESTORE_VECTOR(old_syscall_vector, SYSCALL_VECT);
	return 0;
}

