Newsgroups: comp.os.msdos.programmer
Subject: Trapping INT 21 is easy.
From: dueck@brandonu.ca
Date: 31 Aug 92 23:01:36 CST
Organization: Brandon University, Brandon, Manitoba, Canada


Its strange how things come together.

There has been a thread in this newsgroup about trapping interrupt 21.
Coincidentally, I have been writing a system in which trapping int 21
is essential. I need to both front-end and back-end the interrupt.

In essence, here is what happens:

newint21:
   save regs
   do some stuff

   restore regs
   call old int 21
   save result flags
   save result regs

   do some more stuff

   restore result regs
   move result flags into stack frame
   iret

Prior to calling old int 21, all regs must be the same as they
were supplied from the application. The call itself is simulated
by a pushf and call far, since the old handler will iret.

One exception: function 4B (exec) blows away the SS:SP, so continuing
upon return becomes a little dicey. So for 4B (exec), the filter jumps to
the old interrrupt handler rather than calling it (see below). This can be
fixed, but the application doesn't require it, so I let it ride.

This solution works perfectly. It ensures that the regs
of the caller are passed on to the interrupt routine and that the
result regs and flags are passed back to the caller.

Then one day it failed. Specifically, the MEM command broke.
It said (approximately):

  Runtime error r6002
  Floating point library not loaded

I logged all interrupt 21 calls along with regs ax, bx, cx, and dx
(out the network, onto the vax, and into a log file, but that's another
story). The upshot of this was that just prior to the interrupt 21
calls that produced the error message came a series of function 35/25 calls
(getvect and setvect).

The assumption was that somehow my filter was interfering with these calls.
So the filter was modified:

newint21:
   cmp	ah, 25h
   je	jmpoldint21
   cmp	ah, 35h
   je	jmpoldint21

   other filter code as above
   iret

jmpoldint21:
   db	02eh,0ffh,02eh			; cs: jmp far
   dw	offset sd.oldint21

Klugey, but it works. This solves the problem with MEM, but doesn't
explain why it failed in the first place. By the way, I found this
problem by coding a range check and jumping to everything inside
the range. The range could be modified from debug, so I didn't have
to reboot each time. By changing the range, I could determine a range
of jumps for which the MEM command would succeed or fail.

Using Bolzano bisection on a range of 0 through 7F, and modifying just the
upper bound zeroed in on int 35 as the upper bound. Modifying just
the lower bound gave int 25.

But other situations that arose during debugging gave rise to the following
bizarre suggestion: Is it possible that the contents of the flags on
INPUT to int 21 are important? Easy enough to check, and it turns out that,
yes, they are and the problem is solved.

So here is what works:

_int21hook:
	push	ax
	pushf
	pop	ax
	mov	cs:_sd.flags, ax
	pop	ax

	The call to the old handler is

	push	cs:_sd.flags
	call	cs:[_sd.oldint21]	; far call

Beaming with delight, I continued with other work. Who would have
believed that the flags were that important? I can see, just barely,
passing information back in the flags, but passing information
via flags into an interrupt handler??? It makes no sense. But it works.

Then it broke.

It happened this way. I have a command to log out from the network
and disable the computer by entering a hard loop:

here:	jmp	here

This makes it impossible to continue after logging out without rebooting.
For a variety of reasons, this is what I want to happen.
Usually I CTRL-ALT-DELETE to reboot. Some times I press the reset button.
Sometimes I turn off the computer and go have a beer.

With the interrupt 21 filter in place, CTRL-ALT-DELETE has NO EFFECT.
It is ignored! What gives? Back to the drawing board!

Here is what works: instead of restoring the old flags before the
far call, set the z flag:

	push	ax                      ; save
	mov	ax, 0
	cmp	ax, 0			; obviously equal
	pop	ax			; restore w/o mod'ing flags
	pushf				; save flags: garbage + Z=1
	call	cs:[_sd.oldint21]	; far call

This allows CTRL-ALT-DELETE while the cpu is in a hard loop.
And MEM still works. No way you say? Way!, I say.

Thus ends the saga. Until the next time it breaks. In the meantime,
can anyone out there in net land give me some insight? It started
off as such a simple problem and ended up so really wierd.

Gery Dueck
dueck@brandonu.ca

Newsgroups: comp.os.msdos.programmer,news.answers