Passing Parameters from an Assembly Language Routine
Published By | Date | Version | Knowledge Level | Keywords |
---|---|---|---|---|
Revelation Technologies | 14 FEB 1989 | 1.1X | EXPERT | ASM.DATE, ASM.TEST, INTERFACE |
Calling assembly language routines from the Revelation environment offers the developer several advantages over using R/BASIC alone. Assembly language subroutines provide the following:
- speed, especially for mathematical computations (but not necessarily for character (string) manipulation)
- direct access to the operating system (DOS), such as to execute DOS interrupts
- direct access to the hardware of the machine, such as communications devices, CD-ROM, etc.
However, using assembly language routines is slightly more complex than calling subroutines or functions written in R/BASIC. This bulletin addresses the issue of passing parameters (specific or varying in number) between R/BASIC programs and assembly language subroutines or functions.
Revelation Functions
Certain functions have been written into the AREV.EXE and REV.EXE that are callable from assembly language routines. These function calls provide the interface between the Revelation environment and assembly language routines.
Included in the Revelation assembly language interface are functions to input and output characters, set and query the Revelation environment (example: break key), allocate new string space, access operating system files, and as illustrated in this bulletin, pass parameters between the two environments.
Parameter Passing
When calling an assembly language subroutine from most other languages, including other assembly language programs, the parameters are usually passed by pushing and popping them onto and off the stack. To return a value from an assembly language function in non-Revelation environments, the return value is typically put into the DX:AX registers.
However, when calling an assembly language subroutine from R/BASIC, the parameters are passed instead through the use of one of the Revelation-specific function calls mentioned above. Specific Revelation functions exist that copy strings or numeric data between the assembly language routine and Revelation.
Like DOS interrupt calls, Revelation function calls are preceded by loading certain registers to determine which function is executed. The function call used to copy a value back to the Revelation environment differs (in other words, the AH register value differs) depending on the type of the data to be passed. One of the example programs in this bulletin, for instance, illustrates how the function 10H is used to copy a string from the assembly language routine to the Revelation environment. There are other functions for passing back integers, double integers, floating point numbers, etc.
Note: Some functions used in assembly language routines are only available in later versions of Advanced Revelation. See the documentation accompanying your assembly language interface for information on functions described here.
Calling an Assembly Language Function
Figure 1 illustrates an assembly language function called ASM.TEST that has no arguments (is passed no values), but returns a string of characters. Although ASM.TEST will not receive any parameters, it must return a value to the calling program.
The function ASM.TEST is called from an R/BASIC program using syntax such as this:
DECLARE FUNCTION ASM.TEST * call assembly function VAR = ASM.TEST()
The program ASM.TEST allocates space for a variable through the function call 0FH (0FH is loaded into register AH). In this example, it allocates 11 bytes for the string hello world by putting 11 into the CX register.
The function call is made and the memory address for this variable is returned in the DI register. The routine saves this address on the stack to pass the string back to the calling program in a later function call. Then the routine puts the string hello world into memory. Here one can see how cumbersome it would be to do string handling in assembly language.
The ASM.TEST routine executes another function call to copy the string into the Revelation environment. This is done by putting 10H in the AH register to pass a string and 0 in the AL register for a non-dimensioned local variable. The starting address of the variable is restored to the DI register as well.
Notice that DX has a value of zero when this function call is made. This is because a variable is being assigned that was not passed to the routine. It is the variable on the left side of the assignment in the R/BASIC program – in this case, VAR. This is similar to a RETURN VAR in an R/BASIC function.
Calling an Assembly Language Subroutine
Figure 2 is an example of an R/BASIC program that calls an assembly language subroutine, ASM.DATE, to return the date and day of the week. Figure 3 is the source code of an assembly language routine called by the program illustrated in figure 2.
Although four parameters are passed to the routine by the R/BASIC program, they are not accessed by the assembly language subroutine (they are unassigned at this point). In this example there is no need to allocate space for the parameters, as the Revelation environment has already done so.
The first task the routine accomplishes is a DOS interrupt (this cannot be done in R/BASIC ) to get the system date. The subsequent Revelation function calls are done to pass back the results of the DOS interrupt. 12H is loaded into the AH register in order to pass an integer from the assembly language environment into the Revelation environment.
The CX register contains the integer. The AL register contains information about the type of variable being passed – in this case, a local non-dimensioned variable. Note that the DX register contains the number of the parameter in the parameter list being passed. Thus, 1 is in DX when passing the day, 2 for the month and so forth. In the routine ASM.TEST (above), DX contained 0, since the routine was a function that had no arguments.
There are Revelation functions that can be used in assembly language routines as the inverse of the examples used here. The examples used here pass data back to the calling program. There are similar functions to access the values passed to the assembly language routine.
Returning to R/BASIC
Because Revelation cleans up the stack, there is no need for the ret 4 or ret 6 frequently used in other environments at the end of the assembly language routine's main procedure.
Passing Varying Number of Parameters
Calling an assembly language routine allows the possibility of passing varying number of parameters. To do this with an R/BASIC routine, one would have to pass a single variable and parse it using some character as a delimiter.
In an assembly language routine the number of parameters passed is in memory. The memory location is defined as a byte with the label no_args. By accessing this location, one can program the logic necessary to access the exact number of parameters passed.
For example, even though no_args is initialized to 0 in figure 3, it will acquire the value 4 during execution of the program in figure 2 because four parameters are passed. The same routine can be passed only three parameters, but the logic would have to be added so that the fourth parameter is not accessed in any way.
Note: The Revelation BBS ( 1-206-641-8110 up to 9600 baud, 8 bit, no parity, one stop ) has a variety of assembly language routines that are available to be downloaded. These routines are a mixture of object and source code so that assembly language programmers can look at the source code and non-assembly language programmers can run the object code.
Summary
The following tables list the functions used commonly to pass parameters between Revelation and Assembly Language subroutines.
Passing Parameters From Revelation To Assembly Language
Function Other Call Description RegValues Returned Values 0DH Fetch string pointer See note 2 DS:SI pointer to start of string CX length of string BP # of bytes that can be added without fetching more storage 11H Fetch integer See note 2 CX 16 bit integer 13H Fetch long integer See note 2 SI:CX 32 bit integer 1AH Fetch temporary float See note 2 DS:SI pointer to 10-byte floating point number
Passing Parameters From Assembly Language To Revelation
Function Other Call Description RegValues Returned Values 0FH Allocate space for CX # bytes desired ES:DI pointer to start string storage of string BP available bytes (<= to # in CX due to paragraph units) 10H Pass string pointer ES:DI pointer to start of string CX length of the string 12H Pass integer CX 16 bit integer (see note 2) 14H Pass long Integer SI:CX 32 bit integer (see note 2) 1BH Pass temporary Float DS:SI pointer to a 10-byte floating point number (see note 2)
Note 1: The double byte label no_args contains the number of parameters to be passed.
Note 2: DX register must be loaded with the ordinal number of the parameter passed (starting with one, not zero). AL register is loaded as follows:
AL High Nibble Meaning 00B non-dimensioned variable 01B single dimension array (BX must equal element number) 10B double dimension array (BX = row index, BP = column index) AL Low Nibble Meaning 00B local variable 01B COMMON variable
For example, a value of 1001B in AL indicates a 2 dimensional array in COMMON. (This value is listed incorrectly in the Assembly Language Interface Version 2.0 documentation.)
Note 3: Advanced Revelation 1.1 allows other function calls which are analogous to the ones above but use far pointers using the ES and DI registers.
Figure 1
;********************************************************************* ; Program ASM.TEST ; ; Written to be function called from a R/BASIC routine. ; Will return a string ('hello world') to the calling program. ; Will affect the ax,cx,es,di registers. ; ; Usage: ; ; variable = ASM.TEST() ; ; with ASM.TEST being this cataloged routine. ; ;********************************************************************* cseg segment para public 'CODE' assume cs:cseg main proc far org 0 ; start at address zero db 'ASSM' ; 4-byte flag db 2 ; identify as assembler routine no_args db 0 ; number of arguments passed no_coms dw 0 ; number of common variables dw 0 ; unused function dd 0 ; address for rev function calls ;********************************************************************* ; ; allocate space for return value ; mov cx,11 ;no. of bytes mov ah,0fh ;function to allocate space call function push di ;for later function call ; ; move string, 'hello world' into memory ; mov byte ptr es:[di],104 ;h inc di mov byte ptr es:[di],101 ;e inc di mov byte ptr es:[di],108 ;l inc di mov byte ptr es:[di],108 ;l inc di mov byte ptr es:[di],111 ;o inc di mov byte ptr es:[di],32 ;[space] inc di mov byte ptr es:[di],119 ;w inc di mov byte ptr es:[di],111 ;o inc di mov byte ptr es:[di],114 ;r inc di mov byte ptr es:[di],108 ;l inc di mov byte ptr es:[di],100 ;d inc di ; ; pass string back to revelation ; xor al,al ;non-dimensioned local variable xor dx,dx ;0 = number of the variable pop di ;starting address mov ah,10h ;function to copy string to rev call function ret main endp cseg ends end
Figure 2
* R/BASIC routine to illustrate a call to an assembly language routine * * this program calls an assembly language subroutine that * gets the date and day of the week (0 thru 6) *---------------------------------------------- DECLARE SUBROUTINE ASM.DATE,MSG *---------------------------------------------- * call the assembly language routine passing 4 variables ASM.DATE(DD,MM,YY,DAY) * determine the day of the week BEGIN CASE CASE DAY=0 ; DAY.OF.WEEK = 'Sunday' CASE DAY=1 ; DAY.OF.WEEK = 'Monday' CASE DAY=2 ; DAY.OF.WEEK = 'Tuesday' CASE DAY=3 ; DAY.OF.WEEK = 'Wednesday' CASE DAY=4 ; DAY.OF.WEEK = 'Thursday' CASE DAY=5 ; DAY.OF.WEEK = 'Friday' CASE DAY=6 ; DAY.OF.WEEK = 'Saturday' CASE 1 ; DAY.OF.WEEK = '' END CASE * display the results SENTENCE = 'Results from Assembly Language Program:|' MSG(SENTENCE:DAY.OF.WEEK:' ':MM:'/':DD:'/':YY,'','','') STOP
Figure 3
;*************************************************************************** * ; Program ASM.DATE ; ; Written to be called from an R/BASIC program ; Will return the system date ; Affects the ax,cx,dx registers ; ; Calling parameters are: ; ; CALL ASM.DATE(DD, MM, YY, WEEKDAY) ; ; with ASM.DATE being this cataloged routine. ; ;*************************************************************************** * cseg segment para public 'CODE' assume cs:cseg main proc far org 0 ; start at address zero db 'ASSM' ; 4-byte flag db 2 ; identify as assembler routine no_args db 0 ; number of arguments passed no_coms dw 0 ; number of common variables dw 0 ; unused function dd 0 ; address for rev function calls ;*************************************************************************** ; ; interrupt to get date ; returns: cx=year,dh=month,dl=day,al=day of week ; mov ah,2ah ;loaded for DOS interupt int 21h ;DOS interupt push dx ;save for later push ax ;save for later ; ; pass yy ; mov ah,12h ;to pass integer back mov al,00h ;local non-dimensioned variable mov dx,3 ;third parameter call function ;REV function ; ; pass weekday ; pop cx ;was in ax from DOS interupt xor ch,ch ;make sure ch is clear mov ah,12h mov al,00h mov dx,4 ;fourth parameter call function ; ; pass dd ; pop cx ;was in dx from DOS interupt push cx ;save for later xor ch,ch ;make sure ch is clear mov ah,12h mov al,00h mov dx,1 ;first parameter call function ; ; pass mm ; pop cx ;was in dx from DOS interupt mov cl,ch ;move ch to cl xor ch,ch ;clear out ch mov ah,12h mov al,00h mov dx,2 ;second parameter call function ret main endp cseg ends end