Basic Parameter Passing to MASM

ID Number: Q10293

3.x 5.x

MS-DOS

Summary:

The following information concerns how parameters are passed

between compiled BASIC and Assembler.

More Information:

With compiled BASIC, you have no choice as to how variables are

passed; they are all passed by address. The following rules need to be

in place to make this work properly:

1. The called routine must preserve segment registers DS, ES, SS,

and the base pointer (BP). If interrupts are disabled in the routine,

they must be enabled before exiting. The stack must be cleaned up on

exit.

2. The called program must know the number and length of parameters

passed. The following routine shows an easy way to reference

parameters:

PUSH BP

MOV BP,SP

ADD BP, (2*number of parameters)+4

so that

parameter 0 is at BP

parameter 1 is at BP-2

parameter n is at BP-2*n

(number of parameters = n+1)

3. Variables may be allocated either in the code segment or on the

stack. Be careful not to modify the return segment and offset stored

on the stack.

4. The BASIC language pushes all parameters onto the stack in the

same order in which they are declared; then, it does a FAR call.

5. The called routine must clean up the stack and do a FAR return.

A preferred way to do this is to perform a RETURN <n> statement where

<n> is 2*number of parameters passed. This procedure is done to adjust

the stack to the start of the calling sequence.

6. Data areas needed by the routine must be allocated either in the

CODE segment of the user routine or on the stack. It is not possible

to declare a separate data area in the assembler routine.

There are two ways to pass control to an assembler program from

BASIC. One way is to use the USR statement, the other is to use the

CALL statement. Each has advantages and disadvantages but, basically,

the USR statement can only pass one parameter (to a special area in

BASIC called the Floating Point Accumulator area); it returns just one

argument. The USR statement is used mainly to be compatible with many

different versions of BASIC and the interpretive BASIC.

However, the CALL statement can pass and return many parameters

quite easily because it places the address of the parameters on the

stack. The CALL statement is the preferred method of parameter passing

between program and routines.

Thus, the BASIC program (passing two integers) and subroutine EXP

works as follows:

100 DEF SEG=&H8000 110 FOO=&H7FA 120 CALL FOO (A,B)

Line 100 sets the segment to 8000H. The call to FOO will execute a

subroutine at location 8000:7FAh.

PUBLIC EXP

CSEG SEGMENT

ASSUME CS:CSEG

EXP PROC FAR

PUSH BP ;SET UP POINTER TO ARGUMENTS

MOV BP,SP ;MAKE BP ADDRESSABLE TO STACK

ADD BP,(2*3+4);REFERENCE ARGUMENTS TO BP

MOV BX,[BP-2] ;GET ADDRESS OF A FROM STACK

MOV AX,[BX] ;GET 'A' PARAMETER

MOV BP,[BP-4] ;GET ADDRESS OF B FROM STACK

MOV DX,[BX] ;GET 'B' PARAMETER

POP BP ;RESTORE POINTER

RET 4 ;RESTORE STACK

EXP ENDP

CSEG ENDS

END

Memory looks like the following:

BP -------> BP (2 bytes) ;original BP value placed on Stack BP + 2

---> Offset(2 bytes);offset of return address BP + 4 ---> Segment (2

bytes) ;segment of return address BP + 6 ---> Par 2 (2 bytes);address

of second parameter BP + 8 ---> Par 1 (2 bytes);address of first

parameter

There is no need to declare EXP as an external routine because

compiled BASIC assumes that all CALLs of this form are to external

routines. The assembly routine assumes that the parameters are

two-byte integers, whereas BASIC assumes the parameters to be 4-byte

real numbers. This is why you need to specifically declare variables

as integers.

For passing information as strings, compiled BASIC and interpretive

BASIC differ.

With compiled BASIC, the first two bytes are the string length and

the last two bytes are the string's starting address. When a string

parameter is passed on the stack to an assembly routine, it is the

address of the string descriptor that actually is passed, not the

address of the string.

To pass a string variable to an assembly routine from compiled

BASIC, pass the name of the string variable. Thus, to call a routine

that passes text, use CALL NN(A%,B%,TEXT%), where X% and Y% are

integer variables and TEXT% is a string variable.

To return a value to a compiled BASIC program, just use the CALL

statement in which the parameter whose value you need to change is

passed to the assembly routine. You then can change this value within

your assembler routine, since you know where the parameter's address

is stored in memory on the stack.