Author Topic: Things you need to know about registers in PowerBASIC  (Read 2831 times)

0 Members and 1 Guest are viewing this topic.

Offline Steve Hutchesson

  • Moderator
  • Jr. Member
  • *****
  • Posts: 83
    • The MASM Forum
Things you need to know about registers in PowerBASIC
« on: December 21, 2009, 11:30:14 AM »
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.

Code: [Select]
#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.

Code: [Select]
#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

' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
« Last Edit: December 24, 2009, 06:48:17 AM by Theo Gottwald »