• Welcome to Jose's Read Only Forum 2023.
 

Things you need to know about registers in PowerBASIC

Started by Steve Hutchesson, December 21, 2009, 11:30:14 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Steve Hutchesson

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

' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤