ds is
equal to ss. These sections will also pass all parameters on
the stack. You can easily modify the details to pass these parameters elsewhere.
push GlobalVar ;Assume "GlobalVar" is in DSEG.
call Procedure
To pass a local variable by value to another procedure, you could use the
following code[6]:
push [bp-2] ;Local variable in current activation
call Procedure ; record.
To pass an intermediate variable as a value parameter, you must first locate
that intermediate variable's activation record and then push its value onto
the stack. The exact mechanism you use depends on whether you are using
static links or a display to keep track of the intermediate variable's activation
records. If using static links, you might use code like the following to
pass a variable from two lex levels up from the current procedure:
mov bx, [bp+4] ;Assume S.L. is at offset 4.
mov bx, ss:[bx+4] ;Traverse two static links
push ss:[bx-2] ;Push variables value.
call Procedure
Passing an intermediate variable by value when you are using a display is
somewhat easier. You could use code like the following to pass an intermediate
variable from lex level one:
mov bx, Display[1*2] ;Get Display[1] entry.
push ss:[bx-2] ;Push the variable's value.
call Procedure
dsss, then you must pass
far pointers to access all possible variables[8].
ds register; for non-global values, ss contains
the segment value. To compute the offset portion of the address you would
normally use the lea instruction. The following code sequence
passes a global variable by reference:
push ds ;Push segment adrs first.
lea ax, GlobalVar ;Compute offset.
push ax ;Push offset of GlobalVar
call Procedure
Global variables are a special case because the assembler can compute their
run-time offsets at assembly time. Therefore, for scalar global variables
only, we can shorten the code sequence above to
push ds ;Push segment adrs.
push offset GlobalVar ;Push offset portion.
call Procedure
To pass a local variable by reference you code must first push ss's value
onto the stack and then push the local variable's offset. This offset is
the variable's offset within the stack segment, not the offset within the
activation record! The following code passes the address of a local variable
by reference:
push ss ;Push segment address.
lea ax, [bp-2] ;Compute offset of local
push ax ; variable and push it.
call Procedure
To pass an intermediate variable by reference you must first locate the
activation record containing the variable so you can compute the effective
address into the stack segment. When using static links, the code to pass
the parameter's address might look like the following:
push ss ;Push segment portion.
mov bx, [bp+4] ;Assume S.L. is at offset 4.
mov bx, ss:[bx+4] ;Traverse two static links
lea ax, [bx-2] ;Compute effective address
push ax ;Push offset portion.
call Procedure
When using a display, the calling sequence might look like the following:
push ss ;Push segment portion.
mov bx, Display[1*2] ;Get Display[1] entry.
lea ax, [bx-2] ;Get the variable's offset
push ax ; and push it.
call Procedure
As you may recall from the previous chapter, there is a second way to pass
a parameter by value-result. You can push the value onto the stack and then,
when the procedure returns, pop this value off the stack and store it back
into the variable from whence it came. This is just a special case of the
pass by value mechanism described in the previous section.
TestThunk:procedure(name item:integer; var j:integer);
begin TestThunk;
for j in 0..9 do item := 0;
end TestThunk;
CallThunk:procedure;
var
A: array[0..9] : integer;
I: integer;
endvar;
begin CallThunk;
TestThunk(A[I], I);
end CallThunk;
The assembly code for the above might look like the following:
; TestThunk AR:
;
; BP+10- Address of thunk
; BP+8- Ptr to AR for Item and J parameters (must be in the same AR).
; BP+4- Far ptr to J.
TestThunk proc near
push bp
mov bp, sp
push ax
push bx
push es
les bx, [bp+4] ;Get ptr to J.
mov word ptr es:[bx], 0 ;J := 0;
ForLoop: cmp word ptr es:[bx], 9 ;Is J > 9?
ja ForDone
push [bp+8] ;Push AR passed by caller.
call word ptr [bp+10] ;Call the thunk.
mov word ptr ss:[bx], 0 ;Thunk returns adrs in BX.
les bx, [bp+4] ;Get ptr to J.
inc word ptr es:[bx] ;Add one to it.
jmp ForLoop
ForDone: pop es
pop bx
pop ax
pop bp
ret 8
TestThunk endp
CallThunk proc near
push bp
mov bp, sp
sub sp, 12 ;Make room for locals.
jmp OverThunk
Thunk proc
push bp
mov bp, sp
mov bp, [bp+4] ;Get AR address.
mov ax, [bp-22] ;Get I's value.
add ax, ax ;Double, since A is a word array.
add bx, -20 ;Offset to start of A
add bx, ax ;Compute address of A[I] and
pop bp ; return it in BX.
ret 2 ;Remove parameter from stack.
Thunk endp
OverThunk: push offset Thunk ;Push (near) address of thunk
push bp ;Push ptr to A/I's AR for thunk
push ss ;Push address of I onto stack.
lea ax, [bp-22] ; Offset portion of I.
push ax
call TestThunk
mov sp, bp
ret
CallThunk endp
procedure HasRef(var refparm:integer);
procedure ToProc(???? parm:integer);
begin
.
.
.
end;
begin {HasRef}
.
.
.
ToProc(refParm);
.
.
.
end;
The "????" in the ToProc parameter list
indicates that we will fill in the appropriate parameter passing mechanism
as the discussion warrants.ToProc expects a pass by value parameter (i.e., ???? is
just an empty string), then HasRef needs to fetch the value
of the refparm parameter and pass this value to ToProc.
The following code accomplishes this[10]:
les bx, [bp+4] ;Fetch address of refparm
push es:[bx] ;Push integer pointed at by refparm
call ToProc
To pass a reference parameter by reference, value-result, or result parameter
is easy - just copy the caller's parameter as-is onto the stack. That is,
if the parm parameter in ToProc above is a reference parameter, a value-result
parameter, or a result parameter, you would use the following calling sequence:
push [bp+6] ;Push segment portion of ref parm.
push [bp+4] ;Push offset portion of ref parm.
call ToProc
To pass a reference parameter by name is fairly easy. Just write a thunk
that grabs the reference parameter's address and returns this value. In
the example above, the call to ToProc might look like the following:
jmp SkipThunk
Thunk0 proc near
les bx, [bp+4] ;Assume BP points at HasRef's AR.
ret
Thunk0 endp
SkipThunk: push offset Thunk0 ;Address of thunk.
push bp ;AR containing thunk's vars.
call ToProc
Inside ToProc, a reference to the parameter might look like the following:
push bp ;Save our AR ptr.
mov bp, [bp+4] ;Ptr to Parm's AR.
call near ptr [bp+6] ;Call the thunk.
pop bp ;Retrieve our AR ptr.
mov ax, es:[bx] ;Access variable.
.
.
.
To pass a reference parameter by lazy evaluation is very similar to passing
it by name. The only difference (in ToProc's calling sequence) is that the
thunk must return the value of the variable rather than its address. You
can easily accomplish this with the following thunk:
Thunk1 proc near
push es
push bx
les bx, [bp+4] ;Assume BP points at HasRef's AR.
mov ax, es:[bx] ;Return value of ref parm in ax.
pop bx
pop es
ret
Thunk1 endp
es:bx (assume pass by name
parameter's AR pointer is at address bp+4 and the pointer to
the thunk is at address bp+6):
push bp ;Save our AR ptr.
mov bp, [bp+4] ;Ptr to Parm's AR.
call near ptr [bp+6] ;Call the thunk.
push word ptr es:[bx];Push parameter's value.
pop bp ;Retrieve our AR ptr.
call ToProc ;Call the procedure.
.
.
.
Passing a name parameter to another procedure by reference is very easy.
All you have to do is push the address the thunk returns onto the stack.
The following code, that is very similar to the code above, accomplishes
this:
push bp ;Save our AR ptr.
mov bp, [bp+4] ;Ptr to Parm's AR.
call near ptr [bp+6] ;Call the thunk.
pop bp ;Retrieve our AR ptr.
push es ;Push seg portion of adrs.
push bx ;Push offset portion of adrs.
call ToProc ;Call the procedure.
.
.
.
Passing a name parameter to another procedure as a pass by name parameter
is very easy; all you need to do is pass the thunk (and associated pointers)
on to the new procedure. The following code accomplishes this:
push [bp+6] ;Pass Thunk's address.
push [bp+4] ;Pass adrs of Thunk's AR.
call ToProc
To pass a name parameter to another procedure by lazy evaluation, you need
to create a thunk for the lazy-evaluation parameter that calls the pass
by name parameter's thunk, dereferences the pointer, and then returns this
value. The implementation is left as a programming project.| Pass as Value | Pass as Reference | Pass as Value-Result | Pass as Result | Pass as Name | Pass as Lazy Evaluation | |
|---|---|---|---|---|---|---|
| Value | Pass the value | Pass address of the value parameter | Pass address of the value parameter | Pass address of the value parameter | Create a thunk that returns the address of the value parameter | Create a thunk that returns the value |
| Reference | Dereference parameter and pass the value it points at | Pass the address (value of the reference parameter) | Pass
the address (value of the reference parameter) | Pass the address (value of the reference parameter) | Create a thunk that passes the address (value of the reference parameter) | Create a thunk that deferences the reference parameter and returns its value |
| Value-Result | Pass the local value as the value parameter | Pass the address of the local value as the parameter | Pass
the address of the local value as the parameter | Pass the address of the local value as the parameter | Create a thunk that returns the address of the local value of the value-result parameter | Create a thunk that returns the value in the local value of the value-result parameter |
| Result | Pass the local value as the value parameter | Pass the address of the local value as the parameter | Pass the address of the local value as the parameter | Pass the address of the local value as the parameter | Create a thunk that returns the address of the local value of the result parameter | Create a thunk that returns the value in the local value of the result parameter |
| Name | Call the thunk, dereference the pointer, and pass the value at the address the thunk returns | Call the thunk and pass the address it returns as the parameter | Call the thunk and pass the address it returns as the parameter | Call the thunk and pass the address it returns as the parameter | Pass the address of the thunk and any other values associated with the name parameter | Write a thunk that calls the name parameter's thunk, dereferences the address it returns, and then returns the value at that address |
| Lazy Evaluation | If necessary, call the thunk to obtain the Lazy Eval parameter's
value. Pass the local value as the value parameter | If necessary, call the thunk
to obtain the Lazy Eval parameter's value. Pass the address of the local value as the parameter | If necessary, call
the thunk to obtain the Lazy Eval parameter's value. Pass the address of the local value as the parameter | If necessary, call
the thunk to obtain the Lazy Eval parameter's value. Pass the address of the local value as the parameter | If necessary, call
the thunk to obtain the Lazy Eval parameter's value. Create a thunk that returns the address of the Lazy Eval's value field | Create a thunk that checks the boolean field of the caller's Lazy Eval parameter. It should call the corresponding thunk if this variable is false. It should set the boolean field to true and then return the data in the value field |
ds=ss or if you keep global variables
in the main program's activation record in the stack segment.