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.
If 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.