Announcement

Collapse

New Sub-Forum

In an effort to help make sure there are appropriate categories for topics of discussion that are happening, there is now a sub-forum for databases and database programming under Special Interest groups. Please direct questions, etc., about this topic to that sub-forum moving forward. Thank you.
See more
See less

ASM & Pointers

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • ASM & Pointers

    How does one go about using declared 32-bit pointers with inline ASM? For example:

    DEFINT A - Z

    DIM A AS WORD
    DIM B AS WORD
    DIM aPtr AS WORD PTR
    DIM bPtr AS BYTE PTR

    A = 15
    B = 20
    aPtr = VARPTR32(A)
    bPtr = VARPTR32(B)

    ! mov AX,@aPtr

    produces a pointer error (521 I think)

    PS: Where can one get PBDK? I've looked in the products cataloge but have not seen it.

    [This message has been edited by Walt Decker (edited February 28, 2000).]
    Walt Decker

  • #2
    Where can one get PBDK? I've looked in the products cataloge but have not seen it.
    Probably because it has been discontinued.
    If you try to make something idiot-proof, someone will invent a better idiot.

    Comment


    • #3
      I've got some time to kill, so bear with me.

      As far as the inline assembler is concerned, PowerBASIC's pointer
      variables are nothing more than DWORD variables.
      To point to (and retrieve) the contents of a variable with inline ASM,
      you must specify the 32 bit segmentffset address yourself. VARPTR32
      stores the 32 bit address of a variable in a DWORD variable (which is
      basically what a pointer variable is). So in this case, there's really
      no need to use a pointer variable, unless you use it elsewhere in
      your BASIC code.

      Once you've got the address of the variable, you have to load it
      into a segmentffset register pair. Actually, inline ASM does
      recognize predefined BASIC variables and thier values. So its ok
      to use a WORD or INTEGER variable as the offset portion of an
      address. I'm using registers only for simplicity. Note that you
      MUST use a segment register for the segment portion of an address.

      Segment registers are DS, ES, CS, and SS.
      CS points to the Code Segment, which is where the program code is stored.
      SS points to the Stack Segment, and should NOT be changed.

      DS is the default Data Segment register, and it points to the 64K
      segment that will be referenced if no segment is specified (usually).
      If you change the contents of DS, be sure to restore it before the
      routine ends, else you'll likely crash. Use PUSH DS to save it and
      POP DS to restore (following the Last In First Out rule of POPing
      from the stack, meaning POP registers in the reverse order in which
      they were PUSHed, if you push more than one register).
      After changing the contents of DS, you won't be able to access your
      global (main and PUBLIC) variables by name until you restore DS, since
      PowerBASIC looks for these variables in the data segment (DS).

      ES is the Extra Segment register. As the name implies, this is a
      general purpose segment register, and can be used anywhere that
      DS can, except when using ASM string commands (it has a specific
      use in that case). Unlike DS, you'll need to specify ES if you wish
      to use it as your segment register. Just add ES: in front of the
      source or destination operand, such as:
      ! MOV ES:[DI], AX
      or
      ! MOV AX, ES:[DI]
      Without the ES:, the data segment (DS) would be used by default.


      Offset registers are SI, DI, BX, BP, and SP.
      SP is the Stack Pointer and should NOT be changed directly (normally).

      To make one of these registers point to an address relative to its
      default segment, enclose it in brackets:
      ! MOV AX, [BX]

      The brackets tell the assembler to reference the address pointed to
      by BX (BX becomes a pointer), in the data segment (DS), since that
      is the default segment for BX.
      (Without the brackets, BX becomes a simple 16 bit data register, not
      a pointer).
      So the above is equivalent to:
      ! MOV AX, DS:[BX]

      The segment overide prefix (which is what specifying a segment is
      called) should not be used when it is already the default segment,
      because it imposes an unnecessary inefficiency.

      By default, SI, DI (usually), and BX, when used as offset registers,
      will point to an address relative to the data segment (DS).

      BP (Base Pointer), on the other hand, will, by default, point to
      an address relative to the stack segment (SS). To point to an address
      within the data segment with BP, you must specify DS as the segment
      to use.
      ! MOV AX, DS:[BP]
      BP is normally used to temporarily save and restore the SP register
      (to create what is called a stack frame of reference, for parameter
      passing and return addresses), which is why it is relative to the
      stack segment by default. But as long as you save BP before using
      it and restore it when you're done with it, it can be used like
      any other offset register (except that it's stack segment relative
      by default).
      After changing the contents of BP, you won't be able to access your
      local and paramater passed variables by name until you restore BP,
      since PowerBASIC looks for these variables using BP.


      Since I've gone this far, I'll mention the remaining registers.

      The general purpose registers are:
      AX (Accumulator. THE general purpose register)
      BX (Base register. Can be used as an offset register as shown above)
      CX (Counter register. Automatically decremented in looping or repeating instructions)
      DX (Data register. Used for I/O ports above 255 and MSW of multiplication and division)

      The general purpose registers can be referenced 8 bits at a time
      by their 8 bit counterparts:
      AX(16 bits). AL(lower 8 bits of AX). AH(upper 8 bits of AX)
      BX(16 bits). BL(lower 8 bits of BX). BH(upper 8 bits of BX)
      CX(16 bits). CL(lower 8 bits of CX). CH(upper 8 bits of CX)
      DX(16 bits). DL(lower 8 bits of DX). DH(upper 8 bits of DX)


      Now, with all that out of the way, there's an easy way to get the
      DWORD value of VARPTR32 into a 32 bit segmentffset register pair:

      LDS offset register, value
      or
      LES offset register, value

      LDS loads DS and an offset register with the 32 bit value.
      LES loads ES and an offset register with the 32 bit value.

      If you prefer, you can use VARSEG and VARPTR to get the segment
      and offset portions of the address seperately, and then load the
      appropriate segment and/or offset registers as needed. Just remember,
      if you load DS with a value first, then you won't be able to reference
      your BASIC offset variable by name if it is a global (main or PUBLIC)
      variable until DS is restored. So load the offset value first if you
      need to use DS as your segment register (and you're using global
      variables). Also, segment registers cannot be loaded from memory or
      variables. They must be loaded from other registers. So you must
      store the segment value in a non segment register first, then load
      it from there into the segment register.

      Destination operands are on the left.
      Source operands are on the right.
      Data cannot be moved from memory to memory directly. It must move
      to or from a register.
      Registers that must be preserved are:
      SI, DI, BP, SP, DS, SS.

      So finally, here is what you might use:

      DIM A AS WORD
      DIM B AS WORD
      DIM aPtr AS DWORD
      DIM bPtr AS DWORD

      A = 15
      B = 20
      aPtr = VARPTR32(A)
      bPtr = VARPTR32(B)

      ! PUSH DS
      ! PUSH SI
      ! PUSH DI
      ! LES DI, bPtr ;address of B in ESI
      ! LDS SI, aPtr ;address of A in DS:SI
      ! MOV AX, [SI] ;this loads 16 bits, since that's the size of the destination register (AX).
      ! MOV BL, ES:[DI] ;this loads 8 bits, since that's the size of the destination register (BL).
      ! POP DI
      ! POP SI
      ! POP DS

      This is just an equivalent of your sample program, and doesn't really
      do anything but load values into registers.


      I hope that helps a little (and that I haven't bored you to tears).


      [This message has been edited by G Grant (edited February 29, 2000).]

      Comment


      • #4
        Thank you for your time, Mr. Grant. I thought that may be the case although I was hoping that since the compiler can handle something like:

        @aPtr = @aPtr + @bPtr

        it could handle something like:

        ! mov AX,@aPtr
        ! mov Bh,@bPtr

        without choaking. Oh, well. Such is life.

        Thanks again for your time and effort.

        ------------------
        Walt Decker

        Comment

        Working...
        X