Calling+BASIC+from+assembler+code

//by Richard Russell, November 2008//

You can't actually **call** BASIC from assembler code, but you can achieve an equivalent capability quite easily. What it involves is returning from the assembler code to BASIC, executing a BASIC procedure, and then re-entering the assembler code at the right place.

The first step in achieving this is to modify the way your assembler code is executed. Instead of a simple CALL statement:

code format="bb4w" CALL code_start code Instead use the following code:

code format="bb4w" B% = USR(code_start) WHILE B%       PROC(B%) B% = USR(P%) ENDWHILE code If you prefer to call your assembler code with SYS then use code similar to the following:

code format="bb4w" SYS code_start, parameters TO B%     WHILE B%        PROC(B%) B% = USR(P%) ENDWHILE code It is also necessary to adapt the assembler code entry point itself, as follows:

code format="bb4w" DIM mystack% 255, myesp% 3, gap% 2047, mycode% 1000, L% -1 FOR pass% = 8 TO 10 STEP 2 P% = myesp% [OPT pass% .saved_esp dd mystack%+256 ]       P% = mycode% [OPT pass% .code_start xchg esp,[saved_esp] ; rest of assembler code continues here code Here a //dual stack// arrangement is established, where **mystack%** is a 256-byte memory area containing the second stack. Needless to say, you should increase (or decrease) the amount of memory allocated for the assembler code (here shown as 1000 bytes) as required.

When you want to 'call' a BASIC procedure from assembler code, do so as follows:

code format="bb4w" mov eax,^PROCsomething call CallBASIC code Note that only procedures **without parameters** can straightforwardly be called this way. If you need to transfer data from the assembler code into the procedure(s), the easiest way to do so is via global variables. You can call as many procedures as you like, and they can be called from anywhere within the assembler code (even within a subroutine, or if values have been **pushed** onto the stack).

Since procedure pointers are used, the usual restriction applies that at least one PROC must have been called conventionally before the code is assembled (if the assembly routine itself is in a procedure, this is sufficient).

When you finally want to exit from the assembler code, instead of a simple **ret** use this code:

code format="bb4w" mov eax,0 xchg esp,[saved_esp] ret code If you need to call one of the 'OS' routines, such as **oswrch**, you must do so as follows:

code format="bb4w" xchg esp,[saved_esp] call "oswrch" xchg esp,[saved_esp] code Finally, here is the **CallBASIC** subroutine:

code format="bb4w" .CallBASIC xchg esp,[saved_esp] mov dword [^P%],next ret .next xchg esp,[saved_esp] ret code