Passing StringVectorData

Discussion about the ZBasic language including the System Library. If you're not sure where to post your message, do it here. However, do not make test posts here; that's the purpose of the Sandbox.
Post Reply
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Passing StringVectorData

Post by Don_Kirby »

For a serial port user interface, I've stored menu data in program memory as StringVectorData. Depending on which menu I am displaying, a sub is called which parses the data and sends it off to the port.

There's a bunch of them though, and the code to handle the VectorData is repeated for each sub because I can't get a working method to pass the information along to another function or sub that can be called from multiple locations.

Perhaps Don or Mike could clue me in as to how to effectively/efficiently pass the data, while maintaining it's current string formatting? I know it's possible using byte data, but the strings are so much easier to deal with.


-Don
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: Passing StringVectorData

Post by dkinzer »

Don_Kirby wrote:I can't get a working method to pass the information along to another function or sub that can be called from multiple locations.
There is no easy solution to this problem but it can be solved if you don't mind using some implementation-specific information. Firstly, consider this program fragment that has two string tables and a call to a single routine to display a particular string from each one of them. Here, the address of the string table is passed to the display routine.

Code: Select all

Dim ps1 as StringVectorData({
  "able",
  "baker",
  "charlie",
  "delta"
})

Dim ps2 as StringVectorData({
  "red",
  "orange",
  "yellow",
  "green"
})

Sub Main()
  Call dspStr(CUInt(ps1.DataAddress), 3)
  Call dspStr(CUInt(ps2.DataAddress), 2)
End Sub
There a several ways that the display routine could be implemented but they rely on information about how the StringVectorData structure is implemented. If you look at a listing file, you'll see that two pieces of information are generated for StringVectorData: an index table and the string data. The each entry of the index table contains the Program Memory address where the corresponding string begins. The actual string data consists of a byte giving the string length, a zero byte, then the characters of the string. This latter content happens to be the same format as used by the BoundedString type, which fact is exploited in the of second solution.

First, the brute force" solution.

Code: Select all

Sub dspStr(ByVal strTbl as UnsignedInteger, ByVal idx as Integer)
  Dim addr as UnsignedInteger
  Dim strData as ProgMem Byte Based addr
  Dim strLen as Byte

  ' read the string address from the index table
  Call GetProgMem(CLng(strTbl + CUInt(idx - 1) * 2), addr, 2)
  
  ' read the string's length and advance the address to the first character
  strLen = strData
  addr = addr + 2
  
  ' display the characters of the string
  Do While (strLen > 0)
    Debug.Print Chr(strData);
    addr = addr + 1
    strLen = strLen - 1
  Loop
  Debug.Print
End Sub
The second solution is perhaps a bit more elegant but uses more stack space.

Code: Select all

Sub dspStr2(ByVal strTbl as UnsignedInteger, ByVal idx as Integer)
  Const MaxStrLen as Byte = 30
  Dim addr as UnsignedInteger
  Dim strLen as Byte
  Dim str as BoundedString(MaxStrLen)

  ' read the string address from the string table
  Call GetProgMem(CLng(strTbl + CUInt(idx - 1) * 2), addr, 2)

  ' read the string length
  Call GetProgMem(CLng(addr), strLen, 1)

  ' limit the string length to fit in the string variable
  If (strLen > MaxStrLen) Then
    strLen = MaxStrLen
  End If

  ' populate the string variable
  Call GetProgMem(CLng(addr), str.DataAddress, strLen + 2)

  ' set the string length (possibly truncated)
  Call RamPoke(strLen, str.DataAddress)

  Debug.Print str
End Sub
The code for both implementations does not validate the index but it should. A third parameter could be supplied giving the highest valid index. The lowest valid index is 1.

It should be noted that SizeOf(ps1) returns the value 8, i.e. it returns the size of the index table, twice the number of entries in the table.
- Don Kinzer
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Re: Passing StringVectorData

Post by Don_Kirby »

dkinzer wrote: The code for both implementations does not validate the index but it should. A third parameter could be supplied giving the highest valid index.
Luckily, the upper bound is fixed at 15, so no bound checking is required.

After I posted, I implemented a quick-fix by simply copying the string data into an array, which gets passed to the sub for output (via a module level variable). It uses about 50 bytes more RAM, but frees up 2064 bytes of program space.

I'm currently working on your method to see what the results are. At the moment, the RAM usage is well below the device's limit, so I have some room to work with. The code size , however, is growing continuously as I add features, so this is where I am concentrating the effort to optimize.

Thanks Don, for once again, pointing the way.


-Don
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Post by Don_Kirby »

In order to pass the entire table, I added a simple loop around the code. Pass in the max number of entries, and it cycles through. I am unable to test it at the moment, however it compiles fine, with no impact on RAM usage, and still 1700 bytes less program memory than the original code.

Code: Select all

Private Sub ShowMenu(ByVal MenuAddress as UnsignedInteger, ByVal maxIdx as Integer)
	Const MaxStrLen as Byte = 30
	Dim strLen as Byte
	Dim addr as UnsignedInteger
	Dim str as BoundedString(MaxStrLen) 
	Dim I as Byte
	
	For I = 1 to CByte(maxIdx)
		' read the string address from the string table
		Call GetProgMem(CLng(MenuAddress + CUInt(I - 1) * 2), addr, 2)
		
		' read the string length
		Call GetProgMem(CLng(addr), strLen, 1) 
		
		' limit the string length to fit in the string variable
		If (strLen > MaxStrLen) Then
			strLen = MaxStrLen
		End If 
		
		' populate the string variable
		Call GetProgMem(CLng(addr), str.DataAddress, strLen + 2)
		
		' set the string length (possibly truncated)
		Call RamPoke(strLen, str.DataAddress)
	
		Console.WriteLine(str) 'show the menu item

	Next I
	
End Sub
It is called via:

Code: Select all

Call ShowMenu(CUInt(CAL_MENU_ITEMS.DataAddress), uBound(CAL_MENU_ITEMS))
-Don
Post Reply