home *** CD-ROM | disk | FTP | other *** search
- This is a thread from the Borland SIG, in which Rob Steele and Scott Bussinger
- discuss the structure of Turbo Pascal stack frames at run-time. It also
- describes how one can pass procedures as parameters, and then call those passed
- procedures with arguments.
-
- *******************************************************************************
-
-
- #: 25347 S1/Turbo Pascal
- 09-Nov-85 18:46:11
- Sb: #25271-#Turbo stack frames
- Fm: Scott Bussinger 72247,2671
- To: Rob Steele 72267,557 (X)
-
-
- The problem that has jumped up and bit me deals with trying to fake a
- PROCEDURE variable. What I want to do is save the address of a procedure in a
- pointer variable and then call this routine with a special procedure to
- execute the routine. All of that works fine. The problem is that any
- variables outside of that routine which are not globals cannot be accessed
- because the stack frames are not setup in the expected manner. Any help you
- could provide here would be much appreciated.
- Be seeing you.
-
- #: 25380 S1/Turbo Pascal
- 09-Nov-85 22:26:15
- Sb: #25347-Turbo stack frames
- Fm: Rob Steele 72267,557
- To: Scott Bussinger 72247,2671 (X)
-
- Okay, the best way to deal with variables outside the scope of the
- procedure you're working on is to pass them in as parameters. And
- when you're writing in assembly language it's easier to deal with
- parameters passed by reference (var parameters) than with value
- parameters. That is my opinion anyway; it is a matter of style.
-
- If you want to be sneaky though you can unwind the stack at run time
- and help yourself to all the variables and parameters in all the blocks
- that are open when you get control.
-
- It makes a big difference whether you do it with Turbo's INLINE
- command or Turbo's provision for EXTERNAL routines. With inline you
- get control after Turbo has already constructed a stack frame for you.
- External routines must do it all themselves. For reference, a Turbo
- stack frame looks like:
-
- [{local variables,} frame pointer, saved bp, saved ip, {local parameters,} ...]
- | ^
- |______________|
-
-
- Each entry here is a sixteen bit word, the leftmost entry being at the
- top of the stack. Items in curley braces are optional. The frame pointer
- points to the saved bp. Now, that stack frame is for a procedure or function
- declared at the outermost scope of your program. One that was nested within
- another procedure or function would have a stack frame like:
-
- -------------------------------------->
- |
- [{local vars,} local fp, fp2, saved bp, saved ip, {local params,} ...]
- | ^
- |______________|
-
- And a routine nested two levels down would have one like:
-
- -------------------------------------->
- | ------------------------------------>
- | |
- [{local vars,} local fp, fp2, fp3, saved bp, saved ip, {local params,} ...]
- | ^
- |___________________|
-
- The local frame pointer points to the saved bp, the outer frame pointer,
- fp2, points the the bp saved by the outer routine and the outermost frame
- pointer, fp3, points to the stack frame of the outermost routine.
-
- To build one of these yourself you just do like Turbo:
-
- push bp ; save bp
- mov bp,sp ; point bp to local stack frame
- push bp ; establish pointer to local stack frame
-
-
- For a nested procedure:
-
- push bp ; save bp
- mov ax,sp ; temporarily store address of local stack frame
- push [bp-2] ; establish pointer to outer routine's stack frame
- mov bp,ax ; point bp to local stack frame
- push bp ; establish pointer to local stack frame
-
- Or for a doubly nested procedure:
-
- push bp ; save bp
- mov ax,sp ; temporarily store address of local stack frame
- push [bp-2] ; establish pointer to outermost routine's stack frame
- push [bp-4] ; establish pointer to outer routine's stack frame
- mov bp,ax ; point bp to local stack frame
- push bp ; establish pointer to local stack frame
-
- And so on (I think Turbo will only go six deep).
-
- You would need to know the offset within its stack frame of every variable
- or parameter you intend to look at or mess with. You could then follow the
- appropriate frame pointer, add in the offset and there you are.
-
- Thanks to Bela who first let on that this might be the case.
-
- Rob
-
- #: 25447 S1/Turbo Pascal
- 10-Nov-85 23:39:56
- Sb: #25382-Turbo stack frames
- Fm: Scott Bussinger 72247,2671
- To: Rob Steele 72267,557
-
-
- What I did is define
- type MemAddr = ^byte;
- and then created a procedure
- procedure Call(RoutineAddress: MemAddr);
- Along with a few extra utility functions, I now have the basics for
- PROCEDURE variables. But with some important restrictions -- the routines
- must be procedures and not functions and they can have no parameters (since
- there is no easy way to set up the stack). This restrictions would be
- perfectly tolerable in my case. Unfortunately, there is also the restriction
- of the messed up stack frames when the routine is called. Since I cannot know
- when the routine will be called, I can't fake the stack framing and without
- it, the routine can only access purely local variables and globals. This
- limitation is too severe for my needs. Since you wouldn't know the nesting
- level of the routine, how can you know which of the intermediate stack frames
- is the one you need to get the scoping correct again?
- If I can't come up with a solution to this problem, I will probably have to
- re-write the entire application "inside-out" from the way it is currently
- written. Yuck!!!!
- Be seeing you (and thanks for the advice).
-
- #: 25495 S1/Turbo Pascal
- 11-Nov-85 13:54:45
- Sb: #25447-Turbo stack frames
- Fm: Rob Steele 72267,557
- To: Scott Bussinger 72247,2671 (X)
-
- Scott,
- This program shows a way to pass parameters into a parameterized procedure.
- Let me know what you think.
-
- program call (output);
-
- var
- s : string[12];
- n, m : integer;
-
-
- procedure a (var p1, p2, p3);
- var
- s : string[12] absolute p1;
- n : integer absolute p2;
- m : integer absolute p3;
- begin
- s := 'Okay';
- n := 123;
- m := 456;
- end;
-
-
- procedure b (var p1, p2, p3);
- var
- s : string[12] absolute p1;
- n : integer absolute p2;
- m : integer absolute p3;
- begin
- writeln (s, ' ', n, ' ', m);
- end;
-
-
- procedure call (routine : integer; var p1, p2, p3);
- begin
- inline (
- $FF/$76/$0E/ { push [bp+14] }
- $FF/$76/$0C/ { push [bp+12] }
- $FF/$76/$0A/ { push [bp+10] }
- $FF/$76/$08/ { push [bp+8] }
- $FF/$76/$06/ { push [bp+6] }
- $FF/$76/$04/ { push [bp+4] }
- $FF/$56/$10 { call [bp+16] }
- );
- end;
-
-
- begin
- call (ofs(a), s, n, m);
- call (ofs(b), s, n, m);
- end.
-
-
- -- Rob