There are special conditions with register preservation that apply when writing mixed assembler and BASIC code. PowerBASIC is a highly optimized compiler and among its optimizations are reductions in the stack overhead between BASIC code statements. Therefore, compiled PowerBASIC code is designed to expect that the EBX, ESI, and EDI registers will remain unchanged between lines of BASIC code.
This means that if your assembler algorithm uses any of the EBX, ESI, or EDI registers, you must preserve their original state from the last line of BASIC code that precedes the Inline Assembler code. This is, you must PUSH them before your ASM code, and POP them again right before the BASIC code commences.
This may appear to be more code than is necessary but it must be remembered that the internal structure of PowerBASIC does not duplicate the stack preservation that the application programmer must apply, so in terms of the stack overhead, the code is actually very efficient.
It should be noted that if your ASM code uses the EAX, ECX, or EDX registers, you should also preserve these as the internal execution of BASIC statements can also freely modify any of these three registers too.
The overall approach to preserving the registers around intermixed ASM and BASIC code is demonstrated in the following listing:
SUB TestProc(var1&, var2&)
#REGISTER NONE ' Ensure there is no conflict with
' PowerBASIC Register variables
' Code that uses EAX ECX and EDX goes here
! PUSH EAX ' Save the scratch registers
! PUSH ECX
! PUSH EDX
' Call an API function here
! POP EDX ' Restore the scratch registers
! POP ECX
! POP EAX
' Other ASM code that uses EAX ECX and EDX goes here
! PUSH EBX ' Save the volatile registers
! PUSH ESI
! PUSH EDI
' Other BASIC statements here, for example:
var1 = var2 + 2^8 - COS(var2)
! POP EDI ' Restore from the stack
! POP ESI
! POP EBX
' More ASM code that relies on EBX, etc
Using this format ensures that you are writing "safe" code and that all of the utilized registers are preserved, because:
The EAX, ECX, and EDX registers are preserved by the application programmer around the API function call and the BASIC statements. This strategy ensures that EAX, ECX, and EDX registers are not overwritten (destroyed) by the function that is called.
With those points in mind, if there are no BASIC statements or API calls after the assembler code, preserving these registers is of no consequence. In this case, the automatic preservation code will take care of EBX, ESI, and EDI registers before the Sub/Function terminates, and we can be sure that the calling code will also preserve the EAX, ECX, and EDX registers using the same conventions.
PROGRAMMING TIP: As described above, the EBX, ESI, and EDI registers are automatically preserved at the start and exit of a Sub/Function. Therefore, if you need to use registers for counters or to store other values in your Inline Assembler code, you may use any of the EBX, ESI, or EDI registers for this purpose as they are restored when the Sub/Function terminates. This helps ensure efficiency and can result in even slightly faster code since we do not have to preserve extra registers each time the Sub/Function is executed.
The Inline Assembler
Data types in registers
Using ESP and EBP
Saving Registers at the Sub/Function level
Saving the FPU registers
Tricks in preserving registers