Newsgroups: comp.lang.pascal
Subject: Re: TP 6.0 Strange rounding problem (Debugger is *lying* to me!)
From: cscmd@lux.latrobe.edu.au (Mitch Davis)
Date: Tue, 19 Jan 1993 08:40:06 GMT
Organization: La Trobe University

In article <86277@ut-emx.uucp> mmigdol@ccwf.cc.utexas.edu (michael a migdol) writes:
>Here's a fragment of a program that was causing me some major grief last night.
>
>PROGRAM FOO;

I didn't look too closely at the problem, but here is a situation where
the debugger IS lying, or something else is screwing up badly.  (Text of a 
letter I sent Borland, and never got a reply from)

To:   Xxxxxx Xxxxx, Borland Pacific
From: Mitch Davis (10/1072 Whitehorse Rd, Box Hill Vic. 3128 (03)-890-2062)
Re:   Apparent problems with stack pushing and popping under TD 2.51

Here's a cut-down version of my program which displays the symptoms:

 1:{$M 2000,0,0}
 2:program test_debugger_stack;
 3:
 4:uses dos;
 5:
 6:const ScanCode = 1900; {Scan code for alt-R}
 7:
 8:var OldInt16:procedure; {Put the old handler's address in here}
 9:    active:boolean; {Shows our handler needs bypassing}
10:    regs:registers;
11:
12:procedure ShowHairs;
13:
14:begin
15:  {This is a do-nothing procedure which will have the body of the program}
16:end;
17:
18:procedure Int16Handler (flags,cs,ip,ax,bx,cx,dx,si,di,ds,es,bp:word); interrupt;
19:
20:{The above is straight out of the book.  I should be able to read and modify}
21:{any of these variables, and have them popped into the registers on IRET.   }
22:
23:begin
24:  {Support for extended kbds - The (ax and $EFFF) only lets AH=$00 or AH=$10}
25:  if (not active) and ((ax and $EFFF) = 0) then begin
26:    {To get here, we must be asking for a key, and not already involved.}
27:    active := true;
28:    regs.ax := ax;
29:    intr ($16,regs); {Let's get the key they asked us for}
30:    if regs.ax = ScanCode then ShowHairs; {Is it ours?}
31:    ax := regs.ax; {Put it in the stack space for the caller to use}
32:    active := false;
33:  end else asm
34:    {The first IF statement damaged some registers.  Refresh them}
35:    mov  ax, &ax; mov  dx, &dx
36:    {Perform a pseudo-interrupt to the old int16 handler}
37:    pushf
38:    call dword ptr [OldInt16]
39:    {some int16 routines modify the return flags, as well as many registers}
40:    {hence we want to subvert Turbo Pascal's stack cleanup by cleaning it  }
41:    {up ourselves.  We pop off as many registers as we can, effectively    }
42:    {throwing them away.  Why?  The Int16 routine we called may have       }
43:    {modified registers, and now we want our caller to see those changes.  }
44:    mov  ax, &ax ; {Put AX in it's proper place}
45:    pushf; pop  ax {Get the flags in AX}
46:    mov  ax, flags {And put them in their place too.  This is how the int  }
47:                   {16 handler changes the caller's flags.                 }
48:    pop  bp ; {We want to restore this}
49:    pop  ax ; {throw away caller's ES, we never changed it}
50:    pop  ds ; {we changed DS, so restore it}
51:    pop  ax ; {throw away caller's DI}
52:    pop  ax ; {throw away caller's SI}
53:    pop  ax ; {throw away caller's DX}
54:    pop  ax ; {throw away caller's CX}
55:    pop  ax ; {throw away caller's BX}
56:    pop  ax ; {Now AX has it's proper value, because we've popped where AX is}
57:    iret {Note this is procedure now has TWO IRETs - the TP supplied one,    }
58:  end;   {and the one we just did ourselves.                                 }
59:end;
60:
61:begin
62:  active := false;
63:  GetIntVec($16,@OldInt16);
64:  SetIntVec($16,@Int16Handler);
65:  readln; {This is to do an in-program test of the int16 handler}
66:  SetIntVec($16,@OldInt16);
67:end.

What's happened up to this point:
 o I compiled the program.
 o I typed "td test".
 o I set a breakpoint on the "begin" of "Int16Handler".
 o I opened the Code window, and closed the Source and Watch windows.
 o I pressed Ctrl-F2, then F9.  Execution stops where you see below.  Good.
   [Note I have replaced control characters with Xs, otherwise my faxcard]
   [wouldn't like me!  Note that every time you press Ctrl-F2 F9, the    ]
   [outcome is different (see below).  The only thing that's predictable ]
   [is that it's not what is wanted!                                     ]
+-[.]-CPU 80286-----------------------------------------------+------3---[X]-+
|TEST_DEBUGGER_STACK.INT16HANDLER: begin                      x ax 0100  |c=0|
|  cs:000C>50             push   ax                           . bx 0324  |z=1|
|  cs:000D 53             push   bx                           % cx 0001  |s=0|
|  cs:000E 51             push   cx                           % dx 0000  |o=0|
|  cs:000F 52             push   dx                           % si 0041  |p=1|
|  cs:0010 56             push   si                           % di 035E  |a=0|
|  cs:0011 57             push   di                           % bp 07C4  |i=0|
|  cs:0012 1E             push   ds                           % sp 083A  |d=0|
|  cs:0013 06             push   es                           % ds 0070  |   |
|  cs:0014 55             push   bp                           % es 0279  |   |
|  cs:0015 89E5           mov    bp,sp                        % ss 0279  |   |
|  cs:0017 B8125F         mov    ax,5F12                      % cs 5E96  |   |
|  cs:001A 8ED8           mov    ds,ax                        % ip 000C  |   |
|TEST_DEBUGGER_STACK.25:  if (not active) and ((ax and $EFFF) %----------+---+
|  cs:001C 803E480000     cmp    byte ptr [0048],00           x ss:0846 0E10 |
+x.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%x+ ss:0844 0201 |
|  es:0838 2B 13 96 02 3A 0F 70 00 xxxxxxxx                   | ss:0842 0E87 |
|  es:0840 46 02 87 0E 01 02 10 0E xxxxxxxx                   | ss:0840 0246 |
|  es:0848 94 0D 9D 0C 06 03 79 02 xxxxxxxx                   | ss:083E 0070 |
|  es:0850 70 00 80 74 00 00 69 5F xxxxxxxx                   | ss:083C 0F3A |
|  es:0858 90 10 69 5F 00 00 42 5D xxxxxxxx                   | ss:083A>0296 |
+-------------------------------------------------------------+--------------+
At this point, pressing F7 should push AX onto the stack.  That is, the IP
arrow should move to CS:000D, the stack arrow move to SS:0838, and SS:0838
becomes 0100, which is the value in AX.  I press F7:

[comp.lang.pascal note, I'm deleting further line and box chars]

  [ ] CPU 80286
|TEST_DEBUGGER_STACK.INT16HANDLER: begin		      ax 0100  c=0|
|  cs:000C 50		  push	 ax			      bx 0324  z=1|
|  cs:000D>53		  push	 bx			      cx 0001  s=0|
|  cs:000E 51		  push	 cx			      dx 0000  o=0|
|  cs:000F 52		  push	 dx			      si 0041  p=1|
|  cs:0010 56		  push	 si			      di 035E  a=0|
|  cs:0011 57		  push	 di			      bp 07C4  i=0|
|  cs:0012 1E		  push	 ds			      sp 0838  d=0|
|  cs:0013 06		  push	 es			      ds 0070	  |
|  cs:0014 55		  push	 bp			      es 0279	  |
|  cs:0015 89E5 	  mov	 bp,sp			      ss 0279	  |
|  cs:0017 B8125F	  mov	 ax,5F12		      cs 5E96	  |
|  cs:001A 8ED8 	  mov	 ds,ax			      ip 000D	  |
|TEST_DEBUGGER_STACK.25:  if (not active) and ((ax and $EFFF)
|  cs:001C 803E480000     cmp    byte ptr [0048],00           ss:0844 0201
							      ss:0842 0E87
|  es:0838 2B 13 96 02 3A 0F 70 00 xxxxxxxx		      ss:0840 0246
|  es:0840 46 02 87 0E 01 02 10 0E xxxxxxxx                   ss:083E 0070
|  es:0848 94 0D 9D 0C 06 03 79 02 xxxxxxxx                   ss:083C 0F3A
|  es:0850 70 00 80 74 00 00 5A 76 xxxxxxxx                   ss:083A 0296
|  es:0858 90 10 5A 76 00 00 42 5D xxxxxxxx                   ss:0838>132B

The IP arrow has moved, and so has the Stack arrow.  But see that the value
pushed onto the stack was not 0100, but 132B, which was the value already
present in the stack (see picture 1 in the data pane).  I press Ctrl-F2 and
then F9, then F7, F7:


 TEST_DEBUGGER_STACK.INT16HANDLER: begin		      ax 0100  c=0
   cs:000C 50		  push	 ax			      bx 0324  z=1
   cs:000D 53		  push	 bx			      cx 0001  s=0
   cs:000E>51		  push	 cx			      dx 0000  o=0
   cs:000F 52		  push	 dx			      si 0041  p=1
   cs:0010 56		  push	 si			      di 035E  a=0
   cs:0011 57		  push	 di			      bp 07C4  i=0
   cs:0012 1E		  push	 ds			      sp 0836  d=0
   cs:0013 06		  push	 es			      ds 0070
   cs:0014 55		  push	 bp			      es 0279
   cs:0015 89E5 	  mov	 bp,sp			      ss 0279
   cs:0017 B8125F	  mov	 ax,5F12		      cs 5E96
   cs:001A 8ED8 	  mov	 ds,ax			      ip 000E
 TEST_DEBUGGER_STACK.25:  if (not active) and ((ax and $EFFF)
   cs:001C 803E480000	  cmp	 byte ptr [0048],00	      ss:0842 0E87
							      ss:0840 0246
   es:0838 00 01 96 02 3A 0F 70 00 xxxxxxxx		      ss:083E 0070
   es:0840 46 02 87 0E 01 02 10 0E xxxxxxxx		      ss:083C 0F3A
   es:0848 94 0D 9D 0C 06 03 79 02 xxxxxxxx		      ss:083A 0296
   es:0850 70 00 80 74 00 00 69 5F xxxxxxxx		      ss:0838 0100
   es:0858 D2 10 69 5F 00 00 42 5D xxxxxxxx		      ss:0836>0324


  This time, AX gets correctly pushed, and it CLAIMS that BX gets correctly
pushed, but looking at the stack area in the data pane shows that Turbo
Debugger is not being entirely accurate - SS:0836 in the stack pane is
0324, whereas SS:0836 (SS=ES) in the data pane shows 0296.  If I believed
the stack pane (as I used to), then whenever I tried to access the BX
variable later in the handler, I would get the value 0296.  This is how I
first suspected that the debugger had a problem.

So you see, I'm at a bit of a loose end.  I realised there was a problem, and
I've investigated it to try and find out what was happening.  I've since had
a look in the debugger manual, and I can't see any place where it expressly
states that TD is not for debugging interrupts; in fact the section on
debugging TSRs and going Resident explicitly states that that is what you
will be doing.	If this is the case, I think maybe TD has some limitations
when debugging TSRs.

I hope this information is of benefit to you in improving what is _the_
definitive software-based debugger!

Yours,

Mitch Davis.
----------------------------------------------------------
Anyone else care to comment?  Note that all that hairy Pascal stuff
never gets executed - you put a breakpoint in before that point.
 
Mitch.