Array Descriptor
Array Descriptor
Using ONLY the QB PEEK function, how would I be able to extract the information of the array descriptor and print each elements. This procedure should be able to print the elements of the array regardless if its 1, 2, or more dimensions.
I'm following the information by Ethan Winer's book 'PC Magazine Basic Technicques and Utilities' it has the description of an array descriptor but no examples.
Example:
DIM Array1$r(1 to 5)
Array1$r(1) = "orange"
Array1$r(2) = "apples"
Array1$r(3) = "pear"
Array1$r(4) = "pineapple"
Array1$r(5) = "orange"
Call PrintArray ( Array1$() )
DIM Array2$r(1 to 5, 1 to 25)
Array2$r(1, 1) = "orange"
.
.
.
Array2$r(5, 25) = "orange"
Call PrintArray ( Array2$() )
Sub PrintArray (Array$())
'-- print the elements using only PEEK regardless dimension size
End Sub
I'm following the information by Ethan Winer's book 'PC Magazine Basic Technicques and Utilities' it has the description of an array descriptor but no examples.
Example:
DIM Array1$r(1 to 5)
Array1$r(1) = "orange"
Array1$r(2) = "apples"
Array1$r(3) = "pear"
Array1$r(4) = "pineapple"
Array1$r(5) = "orange"
Call PrintArray ( Array1$() )
DIM Array2$r(1 to 5, 1 to 25)
Array2$r(1, 1) = "orange"
.
.
.
Array2$r(5, 25) = "orange"
Call PrintArray ( Array2$() )
Sub PrintArray (Array$())
'-- print the elements using only PEEK regardless dimension size
End Sub
While generally not recommended to hax the internal structures, it can be done.
Your first stop is in the fbc code tree in the svn on sourceforge (still with me? :P).
This is specifically what you want: http://fbc.svn.sourceforge.net/viewvc/f ... iew=markup
Your first stop is in the fbc code tree in the svn on sourceforge (still with me? :P).
This is specifically what you want: http://fbc.svn.sourceforge.net/viewvc/f ... iew=markup
Allen, if you mean QuickBASIC, then here's what you have to do:
1. In one module, DECLARE a procedure that takes an array like normal. Write some code that calls the procedure.
2. In another module, define a TYPE that matches the structure of the array descriptor.
3. In the same module, define the procedure to take a parameter of this TYPE (instead of an array).
4. Work with the TYPE variable in the procedure to manipulate the array.
5. Compile and link the modules from the command-line (the interpreter will complain).
What's happening is the first module (the one that calls the procedure) will pass the address of the array descriptor, while the procedure in the second module will interpret the stack as having a TYPE variable's address; it's just a big, ugly kind of casting operation (from pointer to array descriptor to pointer to TYPE variable). The QuickBASIC interpreter won't allow you to do this, however.. and I don't believe QBASIC supports multiple modules.
Anyway, I've read Winer's book too (really good), and IIRC he does do some stuff with array descriptors, I think it was some directory searching snippet, or some such. I haven't played around much with the array descriptor, but here's a simple all QB example of how to do the same with string descriptors:
clean.bas: compile with `BC.EXE clean.bas, clean.obj;`
dirty.bas: compile with `BC.EXE dirty.bas, dirty.obj;`
Link modules together with `LINK.EXE clean.obj dirty.obj, program.exe;`
Hope this helps, sorry if I've misunderstood your question.
1. In one module, DECLARE a procedure that takes an array like normal. Write some code that calls the procedure.
2. In another module, define a TYPE that matches the structure of the array descriptor.
3. In the same module, define the procedure to take a parameter of this TYPE (instead of an array).
4. Work with the TYPE variable in the procedure to manipulate the array.
5. Compile and link the modules from the command-line (the interpreter will complain).
What's happening is the first module (the one that calls the procedure) will pass the address of the array descriptor, while the procedure in the second module will interpret the stack as having a TYPE variable's address; it's just a big, ugly kind of casting operation (from pointer to array descriptor to pointer to TYPE variable). The QuickBASIC interpreter won't allow you to do this, however.. and I don't believe QBASIC supports multiple modules.
Anyway, I've read Winer's book too (really good), and IIRC he does do some stuff with array descriptors, I think it was some directory searching snippet, or some such. I haven't played around much with the array descriptor, but here's a simple all QB example of how to do the same with string descriptors:
clean.bas: compile with `BC.EXE clean.bas, clean.obj;`
Code: Select all
declare sub ReverseString ( s as string )
dim a as string
a = "abcd"
ReverseString a
print "[" ; a ; "]"
Code: Select all
type QBStringDescriptor
size as integer
chardata as integer
end type
sub ReverseString ( desc as QBStringDescriptor )
print "number of chars in the string: "; desc.size
print "near offset to character data: "; desc.chardata
dim a as integer, b as integer
' var a = strptr( the_string )
' var b = a + len( the_string ) - 1
a = desc.chardata
b = desc.chardata + desc.size - 1
do while a < b
' var tmp = *a
' *a = *b
' *b = tmp
dim tmp as integer
tmp = peek( a )
poke a, peek( b )
poke b, tmp
' a += 1 : b += 1
a = a + 1
b = b - 1
loop
end sub
Hope this helps, sorry if I've misunderstood your question.
Placing the procedure that accesses the descriptor in a separate QB module to avoid the parameter type checking did not occur to me. The code below is based on the descriptor information here. The main module declares the array and then passes it to the Display procedure in the second module. When an entire array is passed QB passes the array descriptor by reference. From the POV of Display procedure the value passed is a pointer to a variable of type ArrayDescriptorType. The code simply displays the contents of the descriptor.
module1.bas:
module2.bas:
makeit.bat (note that this will work only for QuickBASIC 4.5, and that the bin and lib paths will need to be modified to something suitable):
module1.bas:
Code: Select all
'
' Note the AS ANY, used here to disable type checking.
'
DECLARE SUB Display (arg AS ANY)
DIM a(1, 2) AS STRING
Display a()
DO
LOOP UNTIL INKEY$ <> ""
Code: Select all
'====================================================================
'---------------------------------------------------------
'' This layout assumes that the array has two dimensions.
'---------------------------------------------------------
TYPE ArrayDescriptorType
dataOffset AS INTEGER
dataSegment AS INTEGER
farHeapDescriptorPointer AS INTEGER
farHeapDescriptorBlockSize AS INTEGER
numberOfDimensions AS STRING * 1
featureByte AS STRING * 1
adjustedOffset AS INTEGER
elementLength AS INTEGER
numberOfElements2 AS INTEGER
firstElement2 AS INTEGER
numberOfElements1 AS INTEGER
firstElement1 AS INTEGER
END TYPE
'------------------------------------------
'' Constants for interpreting featureByte.
'------------------------------------------
CONST cFeatureFar = 1
CONST cFeatureHuge = 2
CONST cFeatureStatic = 64
CONST cFeatureString = 128
SUB Display (ad AS ArrayDescriptorType)
PRINT "dataOffset "; HEX$(ad.dataOffset); "h"
PRINT "dataSegment "; HEX$(ad.dataSegment); "h"
PRINT "farHeapDescriptorPointer ";
PRINT HEX$(ad.farHeapDescriptorPointer); "h"
PRINT "farHeapDescriptorBlockSize";
PRINT ad.farHeapDescriptorBlockSize
PRINT "numberOfDimensions"; ASC(ad.numberOfDimensions)
IF ASC(ad.featureByte) AND cFeatureFar THEN PRINT "Far",
IF ASC(ad.featureByte) AND cFeatureHuge THEN PRINT "Huge",
IF ASC(ad.featureByte) AND cFeatureStatic THEN PRINT "Static",
IF ASC(ad.featureByte) AND cFeatureString THEN PRINT "String",
PRINT
PRINT "adjustedOffset"; ad.adjustedOffset
PRINT "elementLength"; ad.elementLength
PRINT "numberOfElements2"; ad.numberOfElements2
PRINT "firstElement2"; ad.firstElement2
PRINT "numberOfElements1"; ad.numberOfElements1
PRINT "firstElement1"; ad.firstElement1
END SUB
Code: Select all
set binpath=d:\qb45\bin\
set lib=d:\qb45\lib\
:: BC sourcefile[,[objectfile][,listingfile]]][options][;]
%binpath%bc module1.bas,,module1.lst /a /ah /c:512 /e /o /t /x;
pause
%binpath%bc module2.bas,,module2.lst /a /ah /c:512 /e /o /t /x;
pause
:: LINK [options] objfiles [,[exefile] [,[mapfile] [,[libraries] [,[deffile]]]]][;]
%binpath%link /ex /noe /nod:brun45.lib /inf module1.obj module2.obj,test,,bcom45.lib qb.lib;
pause
Code: Select all
dataOffset 36h
dataSegment 924h
farHeapDescriptorPointer 0h
farHeapDescriptorBlockSize 0
numberOfDimensions 2
Static String
adjustedOffset 54
elementLength 4
numberOfElements2 3
firstElement2 0
numberOfElements1 2
firstElement1 0
It would probably be best split ArrayDescriptorType into two TYPEs, the main descriptor structure and the variable-count dimension info structure. FreeBASIC's implementation does this as well.
Also, I cannot remember if array() as any is a valid parameter declaration -- I think it is, but it's been awhile -- but if so, it would be preferrable to arg as any.. it's nice to let the compiler do some work for you. :)
Also, I cannot remember if array() as any is a valid parameter declaration -- I think it is, but it's been awhile -- but if so, it would be preferrable to arg as any.. it's nice to let the compiler do some work for you. :)
I agree that it would be better to split ArrayDescriptorType into two structures, but I was coding in quick and dirty mode. The reason I used AS ANY is because I wanted the code to work with an array of any type, but I see now that the declaration should have been:
DECLARE SUB Display (array() AS ANY )
DECLARE SUB Display (array() AS ANY )
Your Display subprogram is exactly what I am trying to achieve. I had to make some modification to my original code. Instead of passing just the array itself, I created a type structure to hold, hopefully the correct address pointer.
I have posted this same question on other forums and even wrote to Ethan Winer asking him for some example of his array descriptor but I have yet to receive any response. I'm hoping you guys could find the answer before then.
Sorry for this unorthodox question.
I have posted this same question on other forums and even wrote to Ethan Winer asking him for some example of his array descriptor but I have yet to receive any response. I'm hoping you guys could find the answer before then.
Sorry for this unorthodox question.
Code: Select all
Type ArrayDescriptorType
dataOffset As Integer
dataSegment As Integer
farHeapDescriptorPointer As Integer
farHeapDescriptorBlockSize As Integer
numberOfDimensions As String * 1
featureByte As String * 1
adjustedOffset As Integer
elementLength As Integer
numberOfElements2 As Integer
firstElement2 As Integer
numberOfElements1 As Integer
firstElement1 As Integer
End Type
Type ArrayType
Address As Long
End Type
Dim Descr as ArrayType
Dim Array$(1 To 50)
Descr.Address = VARPTR(Array$(1)) <-- THIS IS JUST A GUESS
Display Descr
Sub Display(Descr As ArrayType)
Dim AD As ArrayDescriptorType
DescAdd& = Descr.Address
AD.dataOffset = PEEK(DescAdd&)
AD.dataSegment = PEEK(DescAdd& + 2)
.
.
'-- PRINT THE AD ELEMENT INFORMATIONS HERE
PRINT " DATA OFFSET: "; AD.dataOffset
PRINt "SEGMENT OFFSET: "; AD.dataSegment
.
.
PRINT "FIRST ELEMENT1: "; AD.firstElement1
'-- PRINT THE STRING ELEMENTS HERE REGARDLESS OF THE
' NUMBER OF DIMENSIONS
DEF SEG = AD.dataSegment
Offset& = AD.dataOffset
FOR I = 1 TO AD.numberOfElements1
FOR J = 1 TO StringLength <--- JUST GUESSING HERE
PRINT CHR$(PEEK(Offset&));
Offset& = Offset& + 1
NEXT
PRINT
NEXT
'-- I'M JUST GUESSING AT THIS POINT BUT THIS IS BASICALLY WHAT
' I AM AFTER THE ABILITY TO PRINT THE ELEMENTS OF THE ARRAY
' USING ONLY PEEK WITHOUT PASSING THE ARRAY ITSELF.
End Sub
Re: Array Descriptor
Okay folks, here's how I got to the array descriptor of a QBasic array. Unsurprisingly it uses the same format as QuickBasic.This will return a flat pointer to the array descriptor. It works by abusing the fact that the function return value and the function arguments must reside in the same stack frame, which will have the same layout every time. You can use the returned pointer like so:Have fun hacking everyone!
Code: Select all
FUNCTION ArrPtr& (X() AS STRING)
DEF SEG = VARSEG(ArrPtr&)
ArrPtr& = PEEK(VARPTR(ArrPtr&) + 15) * 256& + PEEK(VARPTR(ArrPtr&) + 14) + VARSEG(ArrPtr&) * 16&
END FUNCTION
Code: Select all
SUB DumpArr (A() AS STRING)
p& = ArrPtr(A())
DEF SEG = p& \ 16
p% = p& AND 15
PRINT HEX$(p&); " "; HEX$(PeekW(p% + 2)); ":"; HEX$(PeekW(p%)); " FHD[Ptr="; HEX$(PeekW(p% + 4)); " BS="; HEX$(PeekW(p% + 6)); "] flags="; HEX$(PEEK(p% + 9)); " (0)="; HEX$(PeekW(p% + 10)); " ES="; HEX$(PeekW(p% + 12));
i% = PEEK(p% + 8)
IF i% - 1 AND -16 THEN
PRINT " dims="; HEX$(i%)
ELSE
PRINT " (";
i% = i% * 4 + p% + 10
DO
PRINT LTRIM$(STR$(PeekW(i% + 2))); " TO"; STR$(PeekW(i%) - 1 + PeekW(i% + 2));
i% = i% - 4
IF i% < p% + 14 THEN EXIT DO
PRINT ", ";
LOOP
PRINT ")"
END IF
END SUB
FUNCTION PeekW% (o%)
PeekW% = ((PEEK(o% + 1) XOR 128) - 128) * 256 OR PEEK(o%)
END FUNCTION