Microsoft KB Archive/10293
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:
- 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.
- 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
parameter 0 is at BP parameter 1 is at BP-2 parameter n is at BP-2*n (number of parameters = n+1)
- 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.
- The BASIC language pushes all parameters onto the stack in the same order in which they are declared; then, it does a FAR call.
- 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.
- 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.
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.