Forum Index
HomeZBasic Home   Forum RulesForum Rules   Forum FAQForum FAQ   MemberlistMemberlist   UsergroupsUsergroups   RSS FeedRSS Feed
Site SearchSite Search   LinksLinks   DownloadDownload   Digests and SubscriptionsDigests and Subscriptions
ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in   RegisterRegister
Passing a multidimensional array

 
Post new topic   Reply to topic    Forum Index -> ZBasic Language
Author Message
spamiam



Joined: 13 Nov 2005
Posts: 665

Posted: 06 November 2007, 3:30 AM    Post subject: Passing a multidimensional array Reply with quote

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 Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    Forum Index -> ZBasic Language Time synchro. with the server - Timezone/DST with your computer
Page 1 of 1

 


All content Copyright © 2005-2012 Elba Corp. All Rights Reserved.
Opinions expressed in posts are those of the author and not necessarily those of Elba Corp.
Powered by phpBB © 2001, 2005 phpBB Group