[comp.sys.ibm.pc] Finding the address of functions in MSC, and the ^C interupt

pozar@hoptoad.uucp (Tim Pozar) (04/25/87)

   I was wondering how one finds the address of a function and would 
apply it to setting a vector interupt with it.  This is the code that
I came up with.  It compiles with one error (see below).  If anyone
has any insite on how this could be better done, I would like to 
hear it.
            Tim Pozar

---
/*
 * CTLC.C
 *
 *    A control-break or ^C handler demo. 
 *
 *    This programme will push the "DOS CONTROL-C Exit Address" vector
 * and set it to the "bye()" function.  bye() should pop the old 
 * address and then executes the users code.  The old vector should
 * be restored if the programme ends through the remaped vector or before
 * a standard exit().  bye() should also come before the retrival of the 
 * function's address with the way MSC is set up.
 *
 *    This is a way to make graceful exits from complex programmes that
 * may have several files open or be communication programmes that need
 * to handle the comm paths a paticular way to exit properly.  (Well, *I* 
 * thought it was an interesting hack in "C" !)
 *
 *    CTLC will count 20 seconds and exit, or if the user types a ^C or 
 * a CONTROL-BREAK the programme will exit with our routine.  If a char
 * is typed before the ^C is entered, the programme will exit normally 
 * because the keyboard buffer is not polled.  To correct this, keep 
 * checking on the kbhit() function supplied with MSC 4.0 and call on 
 * the console input, or keep watching the int 9 (KB_INT) for a keyboard
 * hit while you're in your routine.
 *
 *    I would guess you could make this function return back into the 
 * programme by having it call a assembly function that would pop an 
 * appropiate number off the stack and then execute an "iret". (messy)
 *
 * BUGS:
 *    Because we don't disable the interupts, if an interupt occurs while 
 * the programme is in the middle of stuffing the new or old address, your
 * up the creek.
 *    Also do not change the vector to the BIOS ^C vector (1Ah).  It seems 
 * that DOS has a go at the keyboard break first and so it exits leaving 
 * the modified vector unrestored.
 *
 *    This source is complier dependent.  It was written for the Microsoft
 * C Compiler 4.0.  And it goes without saying, it is VERY DOS dependent.
 *    
 *    At this point there is a complie waring at "funcp = bye;" in the 
 * main function.  But everything works perfectly.  Has anyone any ideas
 * on how to make this compile cleanly?
 *
 *    Tim Pozar -- 24.April.1987
 *    pozar@hoptoad.UUCP or Tim Pozar at Fido 125/406
 *    KLOK-FM
 *    77 Maiden Lane
 *    San Francisco CA 94108
 *    (415) 788-2022
 *
 *    Copyright 1987 Timothy M. Pozar -- All rights reserved.
 *
 */
 
#include <stdio.h>
#include <dos.h>
 
#define CNTLBK 0x23
#define SET_VECTOR 0x25
#define GET_TIME 0x2c
#define GET_VECTOR 0x35	
 
typedef struct timetype {
    unsigned hour;
    unsigned minute;
    unsigned sec;
    unsigned hsec;
} TIME, *TIME_PTR;
 
unsigned int pvoff;
unsigned int pvseg;
 
/*
 *  bye()
 * 
 *  If a keyboard break occurs, this routine is executed.
 *  This routine needs to be stuck into the vector table and taken out 
 *  properly when the programme exits.
 *
 */
 
bye()
{
  set_int(CNTLBK,pvseg,pvoff);	/* reset old vector */
 
/*
 *       Put code here.
 */
 
  printf("Restoreing old keyboard vector and exiting. \n");
  exit(0);
}
 
main()
{
char far *funcp;
unsigned int off;
unsigned int seg;
 
  funcp = bye;
  off = FP_OFF(funcp);
  seg = FP_SEG(funcp);
 
  set_int(CNTLBK,seg,off);
 
  printf("bye() is located at %04X:%04X.\n",seg,off);
  printf("The breakint was located at %04X:%04X.\n",pvseg,pvoff);
 
  sleep(20);
  printf("No keyboard break.  Exiting normally.\n");
  bye();
}
 
/*
 * set_int(n,segm,offs)
 *
 *   Set the interupt speced with the segment and offset passed to it,
 * and push its old address vector.
 *
 */
 
set_int(n,segm,offs)
int n;				/* interupt number */
unsigned int segm, offs;	/* segment and offset to point to */
{
  union REGS inregs;
  union REGS outregs;
  struct SREGS segregs;
 
  inregs.h.ah = GET_VECTOR;	/* get orginal vector */
  inregs.h.al = n;
  intdosx(&inregs, &outregs, &segregs);
  pvoff = outregs.x.bx;		/* push it old vector offset */
  pvseg = segregs.es;		/* and segment */
 
  inregs.h.ah = SET_VECTOR;	/* set new vector */
  inregs.h.al = n;
  inregs.x.dx = offs;
  segregs.ds = segm;
  intdosx(&inregs, &outregs, &segregs);
 
  return(0);
}
 
/*
 *
 * get_time(n)
 * TIME_PTR n;
 *
 *    Fills timetype structure n with current time.  This function could
 * be replace by calles to time() and then gmtime() to get a structor,
 * but since were fooling with vectors anyway...
 *
 */
 
get_time(n)
TIME_PTR n;
{
  union REGS inregs;
  union REGS outregs;
 
  inregs.h.ah = GET_TIME;
 
  intdos(&inregs, &outregs);
 
  n->hour = outregs.h.ch;
  n->minute  = outregs.h.cl;
  n->sec  = outregs.h.dh;
  n->hsec = outregs.h.dl;
 
  return(0);
}
 
sleep(x)
int x;
{
  int i;
  unsigned s;
  TIME n;               	/* current time record */
 
  i = 0;
  get_time(&n);
  s = n.sec;
 
  while (i < x){
  printf("secs = %d \r",i);	/* for debug only */
    while (s == n.sec)          /* while Mickey's hand hasn't moved... */
      get_time(&n);
    s = n.sec;			/* if Mickey's hand moved... */
    ++i;			/* increment the counter */
  }
}

-- 
        Tim Pozar
UUCP    pozar@hoptoad.UUCP
Fido    125/406
USNail  KLOK-FM
	77 Maiden Lane
	San Francisco CA 94108

cim2@pyuxv.UUCP (04/30/87)

[Long example of setting interrupts via set/getting interrupts using
MSC]

Several  things come to mind:

1. There is no need to do this at all in MSC since they provide the
   signal() handler function - to call bye() when the user types CTRL-C
   just do:

   #include <signal.h>

   main()
   {
   	int	bye();

	signal(SIGINT,bye);

   }

2.  If you need to set interrupt vectors for some other reason (e.g. an
    RTL) the FP_SEG and FP_OFF macros give weird results when fed a
    function pointer - ( I was working on MSC 4.0 in Large memory model)
    It seems these only really work with variables (Is this a bug?)

    For my application, I coerced the function pointer to a long,
    and then split the segment/offset off:

    unsigned	long	lvar;
    int		bye();
    int		seg,off;

    lvar=(unsigned long)bye;
    off= lvar&0xFF;
    seg= lvar>>16;

    I know this looks kludgy - but it works :)

Rob Fair
Bellcore/CHC

rosen@mtgzz.UUCP (04/30/87)

In article <2042@hoptoad.uucp>, pozar@hoptoad.uucp (Tim Pozar) writes:
> 
>    I was wondering how one finds the address of a function and would 
> apply it to setting a vector interupt with it.  This is the code that
> I came up with.  It compiles with one error (see below).  If anyone
> has any insite on how this could be better done, I would like to 
> hear it.
>             Tim Pozar
>  [. . .]

I didn't have a chance to look at your program in detail, but here are a few
pointers.

First of all instead of playing with interrupt vectors you should be using
the C library call signal(). I don't remember the exeact parameters but it is
in your manual and does axactly what you want--traps on ^C.

But assuming you want to do it your way...

You didn't say what your compiler error was, but it could be a pointer size
mismach. Are you using the large modal? You probably sould be if you want to
stuff a long function address into an interrup vector. It also might help to
put an '&' infront of 'bye', as in:
   funcp = &bye;

I once wrote a similar routine in MSC to cahnge interrupt vectors and restore
on exit. If you need more info contact me and I'll look it up.
-- 
Thomas Rosenfeld 	@ AT&T Information Systems Labs, Middletown, NJ
			(201) 957-5867 	
			UUCP:		{harpo,ihnp4,burl,akgua}!mtgzz!rosen
Disclaimer: I don't claim anything.

backman@interlan.UUCP (Larry Backman) (05/01/87)

In article <2042@hoptoad.uucp> pozar@hoptoad.UUCP (Tim Pozar) writes:
>
>   I was wondering how one finds the address of a function and would 
>apply it to setting a vector interupt with it.  This is the code that
>I came up with.  It compiles with one error (see below).  If anyone
>has any insite on how this could be better done, I would like to 
>hear it.
>            Tim Pozar
>
> *
> *    This programme will push the "DOS CONTROL-C Exit Address" vector
> * and set it to the "bye()" function.  bye() should pop the old 
> * address and then executes the users code.  The old vector should
> * be restored if the programme ends through the remaped vector or before
> * a standard exit().  bye() should also come before the retrival of the 
> * function's address with the way MSC is set up.
> *...
> ...
> *    I would guess you could make this function return back into the 
> * programme by having it call a assembly function that would pop an 
> * appropiate number off the stack and then execute an "iret". (messy)
> *
> * BUGS:
> *    Because we don't disable the interupts, if an interupt occurs while 
> * the programme is in the middle of stuffing the new or old address, your
> * up the creek.
> *    Also do not change the vector to the BIOS ^C vector (1Ah).  It seems 
> * that DOS has a go at the keyboard break first and so it exits leaving 
> * the modified vector unrestored.
> *
> *    This source is complier dependent.  It was written for the Microsoft
> * C Compiler 4.0.  And it goes without saying, it is VERY DOS dependent.
> *    
> *    At this point there is a complie waring at "funcp = bye;" in the 
> * main function.  But everything works perfectly.  Has anyone any ideas
> * on how to make this compile cleanly?
> *
> 
>main()
>{
>char far *funcp;
>unsigned int off;
>unsigned int seg;
> 
>  funcp = bye;
>  off = FP_OFF(funcp);
>  seg = FP_SEG(funcp);
> 
>  set_int(CNTLBK,seg,off);
> 
>  printf("bye() is located at %04X:%04X.\n",seg,off);
>  printf("The breakint was located at %04X:%04X.\n",pvseg,pvoff);
> 
>  sleep(20);
>  printf("No keyboard break.  Exiting normally.\n");
>  bye();
>}


	First off, I suspect that our program would compile correctl if you
	declare your function as:
		char far *funcp();


	Secondly, while you really shouldn't have to disable interrupts while
	swapping interrupt vectors, I'm the nervous type that believes that
	its safer.  Typically, you sway vectors at program initialization or
	termination, at this time who cares if interrupts are off for a few
	hundred instructions.

	Third, if you are doing communications, NETBIOS as an example, ou
	may get a CTL-C or CTL-Break while in the middle of issueing a 
	request. NETBIOS commands that wait do not return control to you until
	the have completed, this may be a 1 second delay on a LAN.  In my 
	situations, using NETBIOS over a WAN TCP network, a NETBIOS command can
	block for up to one minute in some circumstances.  This is a long time
	to wait for Control C/Break.

	In this situation, or other lie it, you absolutely have to intercept
	INT 9 (KBD), test for various nasty keystrokes, (CTL-C/BREAK, or
	CTL-ALT-DEL), and if one of those keystrokes is seen, do some immediate
	housecleaning, BUT GET YOURSELF BACK TO YOUR CODE THAT WAS EXECUTING
	BEFORE THE INTERRUPT OCCURRED!!!!! The UNIX setjmp/longjmp game does
	not work properly in DOS communications programs.

	My code looks something like this:

	main()
	{
	open a NETBIOS connection;

	while (1)
		{
		if  (break_flag)
			break;

		send_data();
		receive_data();

		if (done)
			break;
		}

	close NETBIOS connection();
	}


	and my keyboard interrupt handler. (Written in assembly in reality)

	int_9_handler()
	{

	test_for_ctl_c()
	test_for_ctl_alt_del()

	if (ctl_c || ctl_break)
		{
		send_netbios_cancel();
		break_flag = TRUE;
		}

	else if( ctl_alt_del)
		{
		send_netbios_cnacel();
		}

	push flags
	call far old_int_9_handler();
	iret;
}

	The important thing in these lousy little fragments is the concept of
	cleaning up after yourself, not just in your machine, but in your
	LAN or WAN environment.  If you don't do your communications cleanup
	correctly, you leave hung sockets, ports, sessions, or whatever on the
	other end of your communication line.


					Larry Backman
					Micom - Interlan, Inc.