=====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:
CALL code_start
Instead use the following code:
B% = USR(code_start)
WHILE B%
PROC(B%)
B% = USR(P%)
ENDWHILE
If you prefer to call your assembler code with SYS then use code similar to the following:
SYS code_start, parameters TO B%
WHILE B%
PROC(B%)
B% = USR(P%)
ENDWHILE
It is also necessary to adapt the assembler code entry point itself, as follows:
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
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:\\ \\
mov eax,^PROCsomething
call CallBASIC
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:\\ \\
mov eax,0
xchg esp,[saved_esp]
ret
If you need to call one of the 'OS' routines, such as **oswrch**, you must do so as follows:\\ \\
xchg esp,[saved_esp]
call "oswrch"
xchg esp,[saved_esp]
Finally, here is the **CallBASIC** subroutine:\\ \\
.CallBASIC
xchg esp,[saved_esp]
mov dword [^P%],next
ret
.next
xchg esp,[saved_esp]
ret