$IF 0
ARRDESC.BAS ARRDESC.BAS
Passing Arrays Using BDECL
Written by Jamshid "The Kludge" Khoshrangi
NOTES:
PowerBASIC version 3.2 introduces a new concept for BASIC
programmers: code pointers. Without laboring the point, imagine
being able to call a SUB from a pointer, while still passing
it parameters, as in:
CALL DWORD ThePtr BDECL(Parm1, Parm2, ...)
This works fine, until you decide that you wish to pass an
array using the new BDECL format, as in:
CALL DWORD ThePtr BDECL(Parm1, TheArray(), Parm3)
Put simply, the version 3.2 compiler accepts the above
statement, but when the code is run, the code pointed to
by ThePtr is never called because the parameter list includes
a reference to an array. What to do? Is there a kludge to
work our way around this oversight of the overworked PB
development team?
Well, of course there's a kludge. I live for a good kludge.
Rather than passing the array itself using the BDECL format,
we must pass the array descriptor by value. Say what? The
array what?
Each PowerBASIC array has a 64 byte descriptor that we don't
need to worry about most of the time. But, when we wish to
call a code pointer that points to code that expects a given
parameter to be an array -- well, what it actually expects is
a 32 bit pointer to the array descriptor. That's how
PowerBASIC passes arrays to SUBs by reference in such a timely
manner.
And, simple as it all sounds ... it creates a problem. Just
how do we go about ascertaining the address of a given
array's descriptor? Well, 3.1 introduced us to the ANY
parameter, and the rest is only logical. We write a FUNCTION
that simply passes back the Array Descriptor of the array
it was passed. Then, once we have that descriptor, we
pass it by value on the BDECL stack.... and BAM! It works.
Let's call this magical widget ARRAYDESC32().
The SUB that is then called by the CALL DWORD statement in
all respects treats the array parameter as we would expect.
I don't normally sit down and try to find the hidden
oversights in a compiler. Since the introduction of code
pointers, well, I've been using them in lots of ways, so
I needed this for my pattern matching language. It took
me about $500.00 of my time to figure out what needed to
be done. You're getting my sweat and blood for free.
All this to say -- if you're ever in Vancouver, Canada,
you owe me a rum and Coke.
Jamshid
$ENDIF
DEFINT A-Z
CLS
'We'll need this array later
DIM OurArray(1:10) AS STRING
OurArray(1) = "Pussy cat, pussy cat, where have you been?"
' First, we call up ARRAYDESC32() to get the pointer to the
' descriptor....
DIM TheArrayPtr AS DWORD
TheArrayPtr = ARRAYDESC32(OurArray())
'The empty parens are needed ^^
' We then point our code ptr to the Test SUB
DIM TheCodePtr AS DWORD
TheCodePtr = CODEPTR32(Test)
CALL DWORD TheCodePtr BDECL(BYVAL TheArrayPtr)
'This BYVAL is *required* ^^^^^
PRINT OurArray(1)
OurArray(1) = "Pussy cat, pussy cat, what did you do there?"
' The following line, although we would expect such, will not
' be called because of VVVVVVVVVV <- this array reference
CALL DWORD TheCodePtr BDECL(OurArray())
' But this one will...
CALL DWORD TheCodePtr BDECL(BYVAL TheArrayPtr)
PRINT OurArray(1)
END
' This sub just prints the first element of the array it's passed
' and then changes it to the cat's meow....
SUB Test (TheArray() AS STRING)
PRINT TheArray(1)
' Let's change the value just to prove that the array was
' passed by reference and is therefore modifiable....
SELECT CASE INSTR(TheArray(1), "been")
CASE 0
TheArray(1) = "I chased a mouse under a chair there...."
CASE ELSE
TheArray(1) = "I've been to London to visit the Queen...."
END SELECT
END SUB
' This is the crucial FUNCTION that you might want to clip out and
' use for your own purposes....
FUNCTION ARRAYDESC32 (ANY) AS DWORD
DIM Desc AS DWORD
! mov ax, [bp+6]
! mov bx, [bp+8]
! mov Desc[0], ax
! mov Desc[2], bx
! mov ax, Desc
! mov FUNCTION, ax
END FUNCTION
--- Maximus/2 2.01wb
---------------
* Origin: Sound Stage BBS - Live Via Satellite - (604)944-6476 (1:153/7070)
|