home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / FRAMES.ZIP / FRAMES.THD
Encoding:
Text File  |  1988-07-31  |  7.0 KB  |  198 lines

  1. This is a thread from the Borland SIG, in which Rob Steele and Scott Bussinger
  2. discuss the structure of Turbo Pascal stack frames at run-time.  It also
  3. describes how one can pass procedures as parameters, and then call those passed
  4. procedures with arguments.
  5.  
  6. *******************************************************************************
  7.  
  8.  
  9. #: 25347 S1/Turbo Pascal
  10.     09-Nov-85  18:46:11
  11. Sb: #25271-#Turbo stack frames
  12. Fm: Scott Bussinger 72247,2671
  13. To: Rob Steele 72267,557 (X)
  14.  
  15.  
  16.   The problem that has jumped up and bit me deals with trying to fake a
  17. PROCEDURE variable.  What I want to do is save the address of a procedure in a
  18. pointer variable and then call this routine with a special procedure to
  19. execute the routine.  All of that works fine.  The problem is that any
  20. variables outside of that routine which are not globals cannot be accessed
  21. because the stack frames are not setup in the expected manner.  Any help you
  22. could provide here would be much appreciated.
  23.   Be seeing you.
  24.  
  25. #: 25380 S1/Turbo Pascal
  26.     09-Nov-85  22:26:15
  27. Sb: #25347-Turbo stack frames
  28. Fm: Rob Steele 72267,557
  29. To: Scott Bussinger 72247,2671 (X)
  30.  
  31. Okay, the best way to deal with variables outside the scope of the
  32. procedure you're working on is to pass them in as parameters.  And
  33. when you're writing in assembly language it's easier to deal with
  34. parameters passed by reference (var parameters) than with value 
  35. parameters.  That is my opinion anyway; it is a matter of style.
  36.  
  37. If you want to be sneaky though you can unwind the stack at run time
  38. and help yourself to all the variables and parameters in all the blocks
  39. that are open when you get control.
  40.  
  41. It makes a big difference whether you do it with Turbo's INLINE
  42. command or Turbo's provision for EXTERNAL routines.  With inline you
  43. get control after Turbo has already constructed a stack frame for you.
  44. External routines must do it all themselves.  For reference, a Turbo
  45. stack frame looks like:
  46.  
  47. [{local variables,} frame pointer, saved bp, saved ip, {local parameters,} ...]
  48.                           |              ^
  49.                           |______________|
  50.  
  51.  
  52. Each entry here is a sixteen bit word, the leftmost entry being at the 
  53. top of the stack.  Items in curley braces are optional. The frame pointer
  54. points to the saved bp.  Now, that stack frame is for a procedure or function
  55. declared at the outermost scope of your program.  One that was nested within
  56. another procedure or function would have a stack frame like: 
  57.  
  58.                            -------------------------------------->
  59.                            |
  60. [{local vars,} local fp, fp2, saved bp, saved ip, {local params,} ...]
  61.                      |              ^
  62.                      |______________|
  63.  
  64. And a routine nested two levels down would have one like:
  65.  
  66.                            -------------------------------------->
  67.                            |    ------------------------------------>
  68.                            |    |
  69. [{local vars,} local fp, fp2, fp3, saved bp, saved ip, {local params,} ...]
  70.                      |                   ^
  71.                      |___________________|
  72.  
  73. The local frame pointer points to the saved bp, the outer frame pointer,
  74. fp2, points the the bp saved by the outer routine and the outermost frame
  75. pointer, fp3, points to the stack frame of the outermost routine.
  76.  
  77. To build one of these yourself you just do like Turbo:
  78.  
  79.     push bp          ; save bp
  80.     mov  bp,sp       ; point bp to local stack frame
  81.     push bp          ; establish pointer to local stack frame
  82.  
  83.  
  84. For a nested procedure:
  85.  
  86.     push bp          ; save bp
  87.     mov  ax,sp       ; temporarily store address of local stack frame
  88.     push [bp-2]      ; establish pointer to outer routine's stack frame
  89.     mov  bp,ax       ; point bp to local stack frame
  90.     push bp          ; establish pointer to local stack frame
  91.  
  92. Or for a doubly nested procedure:
  93.  
  94.     push bp          ; save bp
  95.     mov  ax,sp       ; temporarily store address of local stack frame
  96.     push [bp-2]      ; establish pointer to outermost routine's stack frame
  97.     push [bp-4]      ; establish pointer to outer routine's stack frame
  98.     mov  bp,ax       ; point bp to local stack frame
  99.     push bp          ; establish pointer to local stack frame
  100.  
  101. And so on (I think Turbo will only go six deep).
  102.  
  103. You would need to know the offset within its stack frame of every variable
  104. or parameter you intend to look at or mess with.  You could then follow the
  105. appropriate frame pointer, add in the offset and there you are.
  106.  
  107. Thanks to Bela who first let on that this might be the case.
  108.  
  109. Rob
  110.  
  111. #: 25447 S1/Turbo Pascal
  112.     10-Nov-85  23:39:56
  113. Sb: #25382-Turbo stack frames
  114. Fm: Scott Bussinger 72247,2671
  115. To: Rob Steele 72267,557
  116.  
  117.  
  118.   What I did is define
  119.      type MemAddr = ^byte;
  120.   and then created a procedure
  121.      procedure Call(RoutineAddress: MemAddr);
  122.   Along with a few extra utility functions, I now have the basics for
  123. PROCEDURE variables.  But with some important restrictions -- the routines
  124. must be procedures and not functions and they can have no parameters (since
  125. there is no easy way to set up the stack).  This restrictions would be
  126. perfectly tolerable in my case.  Unfortunately, there is also the restriction
  127. of the messed up stack frames when the routine is called.  Since I cannot know
  128. when the routine will be called, I can't fake the stack framing and without
  129. it, the routine can only access purely local variables and globals.  This
  130. limitation is too severe for my needs.  Since you wouldn't know the nesting
  131. level of the routine, how can you know which of the intermediate stack frames
  132. is the one you need to get the scoping correct again?
  133.   If I can't come up with a solution to this problem, I will probably have to
  134. re-write the entire application "inside-out" from the way it is currently
  135. written.  Yuck!!!!
  136.   Be seeing you (and thanks for the advice).
  137.  
  138. #: 25495 S1/Turbo Pascal
  139.     11-Nov-85  13:54:45
  140. Sb: #25447-Turbo stack frames
  141. Fm: Rob Steele 72267,557
  142. To: Scott Bussinger 72247,2671 (X)
  143.  
  144. Scott,
  145.     This program shows a way to pass parameters into a parameterized procedure.
  146. Let me know what you think.
  147.  
  148. program call (output);
  149.  
  150. var
  151.     s : string[12];
  152.     n, m : integer;
  153.  
  154.  
  155. procedure a (var p1, p2, p3);
  156. var
  157.     s : string[12] absolute p1;
  158.     n : integer absolute p2;
  159.     m : integer absolute p3;
  160. begin
  161.     s := 'Okay';
  162.     n := 123;
  163.     m := 456;
  164. end;
  165.  
  166.  
  167. procedure b (var p1, p2, p3);
  168. var
  169.     s : string[12] absolute p1;
  170.     n : integer absolute p2;
  171.     m : integer absolute p3;
  172. begin
  173.     writeln (s, ' ', n, ' ', m);
  174. end;
  175.  
  176.  
  177. procedure call (routine : integer; var p1, p2, p3);
  178. begin
  179.     inline (
  180.         $FF/$76/$0E/      { push [bp+14] }
  181.         $FF/$76/$0C/      { push [bp+12] }
  182.         $FF/$76/$0A/      { push [bp+10] }
  183.         $FF/$76/$08/      { push [bp+8]  }
  184.         $FF/$76/$06/      { push [bp+6]  }
  185.         $FF/$76/$04/      { push [bp+4]  }
  186.         $FF/$56/$10       { call [bp+16] }
  187.     );
  188. end;
  189.  
  190.  
  191. begin
  192.     call (ofs(a), s, n, m);
  193.     call (ofs(b), s, n, m);
  194. end.
  195.  
  196.  
  197. -- Rob
  198.