Here are two test pieces that show how registers are preserved in PowerBASIC. As a basic compiler in must conform to the specifications of basic and this means a stack frame and cleared locals. PowerBASIC also preserves EBX ESI and EDI as well as the normal stack frame registers EBP and ESP so when you create a sub or function in PowerBASIC you can freely use EAX EBX ECX EDX ESI + EDI. If you know what you are doing you can also push / pop EBP if you need a 7th register but it can only be done AFTER all of the memory operands are loaded into registers as the EBP address of a local is destroyed when you overwrite it.
This is the first that shows the registers preserved.
#IF 0 ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Something you regularly see in assembler code written in other languages or an assembler is
code like the following.
push ebx
push esi
push edi
; assembler code here
pop edi
pop esi
pop ebx
This is done to preserve the contents of 3 of the 5 registers that must remain unchanged
before and after function/sub calls.
The other two registers ESP & EBP are commonly use to construct what you call a stack frame
but you must know what you are doing here and the requirement varies from language to language.
When you code a function or sub in PowerBASIC in assembler you are working in a protected
environment that already preserves the 5 required registers ESP EBP ESI EDI EBX and of
those you can commonly use EBX ESI & EDI in your own code as the SUB/FUNCTION design
already preserves them for you.
The upside is its a safe and easy to use environment, the downside is you have a slightly
higher overhead in a function or sub call which effects very short assembler procedures
but there is a way around that problem, if the procedure is so short that it is effected
by stack overhead, you simply inline the code and it will be faster than a no-stack-frame
procedure call.
When using either of the current 32 bit PB compilers, if you want to write assembler
code, use the #REGISTER NONE directive to turn off the compiler's own internal
optimisation techniques otherwise you will get some of the registers remapped to LOCAL
variables and your code will not work properly.
Below is a simple proof that a PB sub or function already does the required preservations
of all of the 5 required registers to be fully complaint with the Microsoft / Intel
specification on register usage.
#ENDIF ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
' -----------------------------------------------
' make a 32 bit variable for each 32 bit register
' -----------------------------------------------
GLOBAL eax_ as DWORD
GLOBAL ecx_ as DWORD
GLOBAL edx_ as DWORD
GLOBAL ebx_ as DWORD
GLOBAL ebp_ as DWORD
GLOBAL esp_ as DWORD
GLOBAL esi_ as DWORD
GLOBAL edi_ as DWORD
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
FUNCTION PBmain as LONG
! mov esp_, esp
StdOut hex$(esp_,8)+" --- ESP register content"
! mov ebp_, ebp
StdOut hex$(ebp_,8)+" --- EBP register content"
! mov ebx_, ebx
StdOut hex$(ebx_,8)+" --- EBX register content"
! mov esi_, esi
StdOut hex$(esi_,8)+" --- ESI register content"
! mov edi_, edi
StdOut hex$(edi_,8)+" --- ESP register content"
' ÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·
teststack ' call the SUB that modifies registers
' ÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·
! mov esp_, esp
StdOut hex$(esp_,8)+" --- ESP register content"
! mov ebp_, ebp
StdOut hex$(ebp_,8)+" --- EBP register content"
! mov ebx_, ebx
StdOut hex$(ebx_,8)+" --- EBX register content"
! mov esi_, esi
StdOut hex$(esi_,8)+" --- ESI register content"
! mov edi_, edi
StdOut hex$(edi_,8)+" --- ESP register content"
StdOut chr$(13,10)+"Press any key to exit ...."+chr$(13,10)
Do
Sleep 1
Loop while Inkey$ = ""
FUNCTION = 0
End FUNCTION
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
SUB teststack()
#REGISTER NONE
StdOut chr$(13,10)+"Hi hi hi, Test Stack Empty Function Here !!!!"+chr$(13,10)
' ------------------------------------------
' leave ESP and EBP alone or it will go BANG
' ------------------------------------------
' ! mov esp, &H12345678
' ! mov ebp, &H12345678
' ------------------------------------------
! mov ebx, &H12345678 ' modify EBX, ESI & EDI
! mov esi, &H12345678
! mov edi, &H12345678
End SUB
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
The second example is the corollary to the first. You can use EAX ECX + EDX but if you call an external sub or function so can it so if you have values in any of EAX ECX + EDX you must protect them.
#IF 0 ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
This example is the corollary to the Microsoft / Intel register convention.
You can freely modify the 3 volatile or transient registers but so can any other
protocol compliant sub or function which means if you have values stored in any of
the EAX ECX or EDX registers they can be changed by and external sub or function.
This means that if you have values stored in any of these registers before you make
a call to an external procedure and you need them to be the same after the procedure call
then you must preserve them.
As always if you use the PUSH POP mnemonics you must push them then later pop them in
reverse order.
push eax
push ecx
push edx
call external function
pop edx
pop ecx
pop eax
In practice you tend to use the other 3 normally available registers where you can so
you don't have to do the extra preservations if you can keep the values you need to
preserve in EBX ESI & EDI.
Now there is another couple of tricks that you can do with the 3 volatile registers.
You can implement your own version of FASTCALL by passing up to 3 values in those 3
registers. You must exercise caution so you don't overwrite them before you store them
in the proc you have called but its faster than using the stack.
The other trick, even though its not politically correct and is illegal, immoral and
fattening, you can pass up to 3 values back from a sub/function using EAX ECX and EDX
and save doing it some slow messy way.
#ENDIF ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
%usemacros = 1
#include "\pbwin90\include\win32api.inc"
' -----------------------------------------------
' make a 32 bit variable for each 32 bit register
' -----------------------------------------------
GLOBAL eax_ as DWORD
GLOBAL ecx_ as DWORD
GLOBAL edx_ as DWORD
GLOBAL ebx_ as DWORD
GLOBAL ebp_ as DWORD
GLOBAL esp_ as DWORD
GLOBAL esi_ as DWORD
GLOBAL edi_ as DWORD
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
FUNCTION PBmain as LONG
#REGISTER NONE
! mov eax_, eax
! mov ecx_, ecx
! mov edx_, edx
StdOut hex$(eax_,8)+" --- EAX register content"
StdOut hex$(ecx_,8)+" --- ECX register content"
StdOut hex$(edx_,8)+" --- EDX register content"
' ÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·
change_regs
' ÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·÷·
! mov eax_, eax
! mov ecx_, ecx
! mov edx_, edx
StdOut hex$(eax_,8)+" --- EAX register content"
StdOut hex$(ecx_,8)+" --- ECX register content"
StdOut hex$(edx_,8)+" --- EDX register content"
StdOut chr$(13,10)+"Press any key to exit ...."+chr$(13,10)
Do
Sleep 1
Loop while Inkey$ = ""
FUNCTION = 0
End FUNCTION
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
SUB change_regs()
#REGISTER NONE
! mov eax, &H12345678
! mov ecx, &H12345678
! mov edx, &H12345678
END SUB
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤