/***************************************************************************** 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 /* getch(), cputs(), outportb() */ /* don't #include 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; }