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.
------------------
Announcement
Collapse
No announcement yet.
ASM & Pointers
Collapse
X
-
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).]
Leave a comment:
-
-
Where can one get PBDK? I've looked in the products cataloge but have not seen it.
Leave a comment:
-
-
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).]Tags: None
-
Leave a comment: