|
|
| Author |
Message |
spamiam
Joined: 13 Nov 2005
Posts: 665
|
|
Posted: 06 November 2007, 3:30 AM Post subject: Passing a multidimensional array |
|
|
It seems that it is illegal to pass a multidimensional array.
I have an array of a few entries of 8 bytes each.
I want to pass one of the 8 byte entries to a subroutine.
I want that subroutine to be able to access that data as if it were an 8 byte long one dimensional array.
I suppose I could do it by passing the memory location of the start of the 8 byte sequence, but then how would I use that in the subroutine to use it as if it were an array?
In C, I would simply pass a pointer, but even then I stink at getting it to work right. I guess it would be *pointer[0], *pointer[1], etc. But in ZBasic, I do not know how to do an alias or a based array.
-Tony |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2499
Location: Portland, OR
|
|
Posted: 06 November 2007, 4:18 AM Post subject: Re: Passing a multidimensional array |
|
|
| spamiam wrote: | | It seems that it is illegal to pass a multidimensional array. | Yes. C'est dommage.
| spamiam wrote: | | I have an array of a few entries of 8 bytes each. I want to pass one of the 8 byte entries to a subroutine. I want that subroutine to be able to access that data as if it were an 8 byte long one dimensional array. |
For Byte arrays, you can use the CByteArray() conversion function. This is similar to a cast in C in that it says "take this address and treat it as if it were the address of an array of Byte". Here's an example
| Code: | Dim arr(1 to 8, 1 to 4) as Byte
Sub Main()
Dim i as Integer, j as Integer
' initialize the array to zero
Call MemSet(arr.DataAddress, SizeOf(arr), 0)
' call the sub to operate on the array
Call foo(CByteArray(arr(1, 3).DataAddress))
' display the array content
For i = 1 to 4
For j = 1 to 8
If (j > 1) Then
Debug.Print ", ";
End If
Debug.Print CStr(arr(j, i));
Next j
Debug.Print
Next i
End Sub
Sub foo(ByRef ba() as Byte)
ba(4) = 1
End Sub |
This produces the output:
| Code: | 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 1, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0 |
So you can see that the fourth byte in the third set of 8 was set to 1 as you would expect.
Another idea is to pass the integral address to the subroutine and then define an array that is Based at that address. Here's how to do that:
| Code: | Dim arr(1 to 8, 1 to 4) as Byte
Sub Main()
Dim i as Integer, j as Integer
' initialize the array
Call MemSet(arr.DataAddress, SizeOf(arr), 0)
' call the sub to operate on the array
Call foo(arr(1, 3).DataAddress)
' display the array content
For i = 1 to 4
For j = 1 to 8
If (j > 1) Then
Debug.Print ", ";
End If
Debug.Print CStr(arr(j, i));
Next j
Debug.Print
Next i
End Sub
Sub foo(ByVal addr as UnsignedInteger)
Dim ba() as Byte Based addr
ba(4) = 1
End Sub |
The output from this is the same as that above.
You can extend this idea to use the entire multi-dimension array in the subroutine.
| Code: | Dim arr(1 to 8, 1 to 4) as Byte
Sub Main()
Dim i as Integer, j as Integer
' initialize the array
Call MemSet(arr.DataAddress, SizeOf(arr), 0)
' call the sub to operate on the array Call foo(arr.DataAddress)
' display the array content
For i = 1 to 4
For j = 1 to 8
If (j > 1) Then
Debug.Print ", ";
End If
Debug.Print CStr(arr(j, i));
Next j
Debug.Print
Next i
End Sub
Sub foo(ByVal addr as UnsignedInteger)
Dim ba(1 to 8, 1 to 4) as Byte Based addr
ba(4, 3) = 1
End Sub |
Again, the output is the same as shown earlier. |
|
| Back to top |
|
 |
spamiam
Joined: 13 Nov 2005
Posts: 665
|
|
Posted: 06 November 2007, 18:31 PM Post subject: |
|
|
Don, thank you for this thorouogh tutorial!
Version #1 is just the kind of solution I had in mind when I wrote my question.
Version #2 is the method I thought might work, but I was not sure how to do it.
Version #3 is a great general purpose method to pass an actual full multidimensional array. You just need to know the size of the indices at compile time.
One question about #3: Can the indices of the array be variables too? (Or at least the last index) So it would look like this....
Sub Main
Dim arr(1 to 8, 1 to 4) as Byte
Call foo(arr.DataAddress, 8, 2)
'only use a subset of the full array
End Sub
Sub foo(ByVal addr as UnsignedInteger, ByVal X as Byte, ByVal Y as byte)
Dim ba(1 to X, 1 to Y) as Byte Based addr
End Sub
I presume this is not possible. Instead, the program would have to calculate the offset of the data of interest manually.
-Tony |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2499
Location: Portland, OR
|
|
Posted: 06 November 2007, 19:38 PM Post subject: |
|
|
| spamiam wrote: | | One question about #3: Can the indices of the array be variables too? |
Currently, no. However, since a Based array is virtual, the dimensions could probably be computed at run-time. |
|
| Back to top |
|
 |
spamiam
Joined: 13 Nov 2005
Posts: 665
|
|
Posted: 07 November 2007, 0:59 AM Post subject: |
|
|
I just tried this as below, and it does not work:
| Code: | Dim Rom_Codes (1 to 8, 1 to 3) as Byte 'room for 3 devices
.
.
J= 1
Call Put1WireData(OWPIN, CByteArray(Rom_Codes(1,J)),8) |
And when I moved the data from the multidimensional array to a linear buffer array , it does work.
| Code: | Dim Rom_Codes (1 to 8, 1 to 3) as Byte 'room for 3 devices
Dim Data_Buffer (1 to 8) as Byte
For J=1 to 8
Data_Buffer(J) = Rom_Codes(j,1)
Next 'J
Call Put1WireData(OWPIN, Data_Buffer,8) |
|
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2499
Location: Portland, OR
|
|
Posted: 07 November 2007, 1:55 AM Post subject: |
|
|
| spamiam wrote: | | I just tried this as below, and it does not work |
The particular use-case that you have is different from the example. In this use-case, the 1-wire routine expects a reference to a data item. Just use this:
| Code: | | Call Put1WireData(OWPIN, Rom_Codes(1, J), 8) |
The code generated for this computes the address of the byte at Rom_Codes(1, J) and passes it to Put1WireData() as the second parameter. That routine then accesses the number of bytes that you specify, in this case 8.
This technique should be applicable to any System Library routine parameter that is described as ByRef in the documentation. That is the indication that the address of the data will be passed. |
|
| Back to top |
|
 |
spamiam
Joined: 13 Nov 2005
Posts: 665
|
|
Posted: 07 November 2007, 20:54 PM Post subject: |
|
|
Don, I seem to be doing something wrong because it is not working for me. Here is a code snippet of the pertinent details.
The commented-out Call to Put1WireData with Rom_Codes as the data source is the one that does not work. The other call using Data_buffer as the data source works fine as far as I can tell.
WHat am I doing wrong?
| Code: | Dim Rom_Codes (1 to 8, 1 to 3) as Byte 'room for 3 devices
Dim Data_Buffer (1 to 9) as Byte
K = 2
For J = 1 to 8
Data_Buffer(J) = Rom_Codes(J,K)
Next 'J
Result = Reset1Wire(OWPin)
Call Put1WireByte(OWPin,Match_Rom) 'ready to send the serial number
'Call Put1WireData(OWPIN, Rom_Codes(1,K),8) 'send the serial # of the first
Call Put1WireData(OWPIN, Data_Buffer,8) 'send the serial # of the first |
-Tony |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2499
Location: Portland, OR
|
|
Posted: 07 November 2007, 22:02 PM Post subject: |
|
|
| spamiam wrote: | | WHat am I doing wrong? |
The code that you've shown looks correct. The proof, of course, is in the code that gets generated. Here is how the code looks for a test compilation:
| Code: | Dim Rom_Codes (1 to 8, 1 to 3) as Byte 'room for 3 devices
00a0 160 var:Rom_Codes (24 bytes)
Dim Data_Buffer (1 to 9) as Byte
var:Data_Buffer (9 bytes) [not used]
Dim K as Integer
00b8 184 var:K (2 bytes)
0000 10ba00 LODSP 0x00ba (186)
0003 eb1a00 INIT_VAR 0x001a (26)
0006 1b1400 PSHI_W 0x0014 (20)
0009 1bba00 PSHI_W 0x00ba (186)
000c 1b0000 PSHI_W 0x0000 (0)
000f fe4a SCALL TASK_START
0011 01fdff BRA 0011
Sub Main()
Call Put1WireData(5, Rom_Codes(1,K),8) 'send the serial # of the first
0014 1a05 PSHI_B 0x05 (5)
0016 1eb800 PSHA_W 0x00b8 (184)
0019 ce DEC_W
001a 1a03 PSHI_B 0x03 (3)
001c 9c SHL_W
001d 1ba000 PSHI_W 0x00a0 (160)
0020 36 ADD_W
0021 1a08 PSHI_B 0x08 (8)
0023 fe6d SCALL 1W_PUTD
End Sub
0025 06 RET |
The first line following the Put1WireData() call pushes the pin number on the stack. The line immediately preceding the one containing the SCALL pushes the number of data bytes on the stack. The code in between those two computes the address of the data to be sent. I've annotated the address computation sequence below so you can see how it works.
| Code: | This pushes the value of K whose address is 0xb8.
PSHA_W 0x00b8
This decrements the TOS to adjust for 1-based indexing.
DEC_W
These instructions multiply the adjusted K value by 8 which is
the length of each of the three sub-arrays of ROM_Codes().
PSHI_B 0x03
SHL_W
This pushes the address of the ROM_Codes array which is 0xa0.
PSHI_W 0x00a0
This adds the top two stack words to yield the address
of ROM_Codes(1, K).
ADD_W |
Once you've confirmed that you get similar code generated, you might want to verify that the sub-array has the correct data. You should confirm this without using ZBasic array indexing just to be certain that the code generated for that isn't causing a problem. A suggested way to do this confirmation is to use a routine similar to that below. Change the code to display in decimal if that is preferable.
| Code: | Sub Main
... other initialization code
Call dspRomCodes(Rom_Codes(1,K).DataAddress)
End Sub
' display sub-array contents beginning from a specified address
Sub dspRomCodes(ByVal addr as UnsignedInteger)
Dim i as Integer
For i = 1 to UBound(Rom_Codes, 1)
Debug.Print CStr(i); ": 0x"; CStrHex(RamPeek(addr))
addr = addr + 1
Next i
End Sub |
|
|
| Back to top |
|
 |
spamiam
Joined: 13 Nov 2005
Posts: 665
|
|
Posted: 08 November 2007, 13:38 PM Post subject: |
|
|
Don,
The third time is the charm! I took the correctly functioning code and re-enabled the call with the reference to the multidimensional array, and it worked. I wonder why it did not work last time? We will never know!
-Tony |
|
| Back to top |
|
 |
|