User Tools

Site Tools


Command disabled: register
passing_20data_20to_20assembler_20code

Passing data to assembler code

by Richard Russell, November 2006

When you incorporate assembly language code in your BBC BASIC program (either for speed or to do things which aren't possible in BASIC) you are very likely to want to pass data into, and possibly out of, the assembler code. There are a number of different ways to do this, each with its own particular advantages and disadvantages. This article discusses four alternative methods and illustrates each with a common example for ease of comparison: the passing of four integer numeric parameters from BASIC code into the assembler code.

Passing values in registers


  • Advantages: Simple, low code overhead, easy return of result via USR.
  • Disadvantages: Limited to integer values, maximum of four parameters can be passed.


When an assembly language routine is activated using CALL or USR the current values of the A%, B%, C% and D% static variables are copied into the processor's eax, ebx, ecx and edx registers respectively and may be used directly in the assembler code.

So if you need to pass four integer parameters par1%, par2%, par3% and par4% into the assembler code you would call it as follows:

        A% = par1%
        B% = par2%
        C% = par3%
        D% = par4%
        CALL address

and the skeleton assembler code would need to be:

      .address
      ; here eax=par1%, ebx=par2%, ecx=par3%, edx=par4%
      ; do something useful with the data
      ret

If you additionally wish to return a 32-bit integer result from the assembler code back to BASIC you can do the following:

        A% = par1%
        B% = par2%
        C% = par3%
        D% = par4%
        result% = USR(address)

and the skeleton assembler code would be:

      .address
      ; here eax=par1%, ebx=par2%, ecx=par3%, edx=par4%
      ; do something useful with the data
      ; load eax register with result value
      ret

A somewhat more elegant method of transferring the parameters and result, which avoids modifying the global values of A% to D%, is to use a function call thus:

        result% = FNfunction(par1%, par2%, par3%, par4%)

where the function is defined as follows:

        DEF FNfunction(A%, B%, C%, D%) = USR(address)


Using global memory locations


  • Advantages: Simple, no limit to number of parameters and results.
  • Disadvantages: Inelegant, unclear what is happening, difficult to maintain.


Memory locations reserved using DIM can be accessed in assembler code, and memory locations reserved using (for example) dd in assembler code can be accessed by BASIC. Hence it is possible to pass data into and out of assembler routines by using shared memory locations.

Here is how that method could be used to pass our four parameters, firstly using DIM:

        DIM p1% 3, p2% 3, p3% 3, p4% 3
        !p1% = par1%
        !p2% = par2%
        !p3% = par3%
        !p4% = par4%
        CALL address

where the corresponding assembler code would be:

      .address
      mov eax,[p1%] ; now eax=par1%
      mov ebx,[p2%] ; now ebx=par2%
      mov ecx,[p3%] ; now ecx=par3%
      mov edx,[p4%] ; now edx=par4%
      ; do something useful
      ret

Here is the equivalent, but reserving the memory locations in the assembler code:

        !p1 = par1%
        !p2 = par2%
        !p3 = par3%
        !p4 = par4%
        CALL address

where the corresponding assembler code would be:

      .p1 dd 0
      .p2 dd 0
      .p3 dd 0
      .p4 dd 0
      .address
      mov eax,[p1] ; now eax=par1%
      mov ebx,[p2] ; now ebx=par2%
      mov ecx,[p3] ; now ecx=par3%
      mov edx,[p3] ; now edx=par4%
      ; do something useful
      ret

In fact, in the case of this particular example, the assembler code could access the original variables directly:

      .address
      mov eax,[^par1%] ; now eax=par1%
      mov ebx,[^par2%] ; now ebx=par2%
      mov ecx,[^par3%] ; now ecx=par3%
      mov edx,[^par4%] ; now edx=par4%
      ; do something useful
      ret

The same techniques can be used to return one or more results back to BASIC.

Using CALL's parameter block


  • Advantages: Very flexible, all variable types (including arrays) can be passed.
  • Disadvantages: Relatively complicated, constants cannot be passed.


The CALL statement accepts any number of parameters (following the routine's address) and information about these parameters is stored in a parameter block in memory. This contains the number of parameters plus the type (e.g. integer, float, string etc.) and address of each parameter. On entry to the assembler code the ebp register points to the start of this parameter block.

To use this method to pass our four integer parameters use code like the following:

        CALL address, par1%, par2%, par3%, par4%

where the corresponding assembler code would be:

      .address
      mov eax,[ebp+2] ; eax=^par1%
      mov eax,[eax] ; now eax=par1%
      mov ebx,[ebp+7] ; ebx=^par2%
      mov ebx,[ebx] ; now ebx=par2%
      mov ecx,[ebp+12] ; ecx=^par3%
      mov ecx,[ecx] ; now ecx=par3%
      mov edx,[ebp+17] ; edx=^par4%
      mov edx,[edx] ; now edx=par4%
      ; do something useful
      ret

This may seem more complicated than the previous methods, but the advantage is that you can pass not only integers but any kind of variable, including arrays and structures. Although the listed code doesn't bother to check the variable types (stored at “[ebp+1]”, “[ebp+6]”, “[ebp+11]” and “[ebp+16]”) - it assumes they are the expected integers - it could do so, either to accept different types or to check for errors.

You cannot include constants in CALL's list of parameters (a constant doesn't have a memory address!) so in the event that you need to do so you must copy the 'constant' into a variable first:

        const% = 12345
        CALL address, const%

You can just as easily pass values out of the assembler code as into it, because since the memory addresses of the parameters are stored in the parameter block the assembler code can modify the variables directly (with care!).

A good example of the flexibility of CALL is the SORTLIB library supplied with BBC BASIC for Windows. This will sort any number of arrays of any type: the assembler code examines the parameter types and chooses the appropriate sort routine.

Using SYS


  • Advantages: Can pass constant parameters
  • Disadvantages: Only integers and strings can be passed, slower than CALL


The SYS statement is primarily intended for calling Windows API functions or other functions in DLLs (Dynamic Linked Libraries), however it is possible to use it to call your own assembler routines. To use it for our example the BASIC code required is as follows:

        SYS address, par1%, par2%, par3%, par4%

and the corresponding assembler code is:

      .address
      mov ebp,esp
      mov eax,[ebp+4] ; now eax=par1%
      mov ebx,[ebp+8] ; now ebx=par2%
      mov ecx,[ebp+12] ; now ecx=par3%
      mov edx,[ebp+16] ; now edx=par4%
      ; do something useful
      ret 16 ; discard parameters

Note particularly the ret 16; the number following ret must be four times the
number of parameters supplied.

You can return a single integer value in the same way as USR: the assembler code must return with the eax register containing the value and the SYS call must use TO to assign it to a variable:

        SYS address, par1%, par2%, par3%, par4% TO result%

Alternatively you can pass as parameters the address(es) at which you want the result(s) to be stored.

This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information
passing_20data_20to_20assembler_20code.txt · Last modified: 2018/04/13 10:51 by richardrussell