Page 1 of 1
coercing/casting
Posted: 11 March 2012, 9:51 AM
by stevech
In ZBasic, is there a way to derive a byte array reference variable (pointer?) given a string's data address (mystring.dataAddress)? Then use that reference variable as a parameter passed to a function?
Kind of like a (cast *) in C.
Re: coercing/casting
Posted: 11 March 2012, 10:17 AM
by dkinzer
stevech wrote:In ZBasic, is there a way to derive a byte array reference variable (pointer?) given a string's data address (mystring.dataAddress)?
For the specific case of a reference to a Byte array, you can use CByteArray() which essentially "casts" an integral value to be a reference to a Byte array. For the general case, you can use an intermediate ByRef variable thus:
Code: Select all
Sub Main()
' using a ByRef variable
Dim s as String
Dim a() as Byte ByRef
a.DataAddress = s.DataAddress
Call foo(a)
' using CByteArray()
Call foo(CByteArray(s.DataAddress))
End Sub
Sub foo(ByRef myArray() as Byte)
End Sub
Examining the code produced (below) you can see that the two methods are equivalent with regard to the value that gets passed to foo().
Code: Select all
void
zf_Main(void)
{
zbString_t zv_s;
uint8_t *zv_a;
zbStrInit(&zv_s, ZB_AN_STR);
zv_a = (uint8_t *)(uint16_t)&zv_s;
zf_foo(zv_a); <-- using the ByRef variable
zf_foo((uint8_t *)(uint16_t)&zv_s); <-- using CByteArray
zbStrFree(&zv_s);
}
It is important to note that s.DataAddress does
not yield the address of the first character of a string. Rather, it yields the address of the
internal representation of a ZBasic string. Included in that representation (either implicitly or explicitly, depending on the String type) is a pointer to the first character of the string which may be in RAM, in Persistent Memory or in Program Memory. The
StrAddress() and
StrType() functions can be useful when working with String types.
If you're wanting to pass a string to a C function, you'll need to convert the ZBasic string to null-terminated C string and pass the address of the latter.
Re: coercing/casting
Posted: 11 March 2012, 13:47 PM
by dkinzer
dkinzer wrote:If you're wanting to pass a string to a C function, you'll need to convert the ZBasic string to null-terminated C string and pass the address of the latter.
The function NullTermString() below may be useful for this purpose. It guarantees that the returned string is null terminated
and that it resides in RAM. This allows the return value of StrAddress() to be passed to a C function that is expecting a RAM-based, null terminated string.
The Main() and dspStrData() subroutines are useful for demonstrating that the function works correctly.
Code: Select all
Dim ps as Persistent BoundedString(10)
Sub Main()
Dim s as String
s = NullTermString("abc")
Call dspStrData(s)
s = "def" & s
s = NullTermString(s)
Call dspStrData(s)
s = NullTermString("a")
Call dspStrData(s)
s = NullTermString("able" & Chr(0))
Call dspStrData(s)
ps = "baker" & Chr(0)
s = NullTermString(ps)
Call dspStrData(s)
s = NullTermString("")
Call dspStrData(s)
End Sub
'
'' dspStrData
'
' Display information about a string including its type, storage address,
' length and content.
'
Sub dspStrData(ByVal s as String)
Dim slen as Integer
slen = Len(s)
Debug.Print "type=&H"; CStrHex(StrType(s)); ", addr=&H"; CStrHex(StrAddress(s)); ", length="; slen; " [";
Dim i as Integer
Dim addr as UnsignedInteger
addr = StrAddress(s)
Dim c as Byte Based addr
For i = 1 to slen
If (i > 1) Then
Debug.Print ", ";
End If
Debug.Print "&H"; CStrHex(c);
addr = addr + 1
Next i
Debug.Print "]"
End Sub
'
'' NullTermString
'
' Given a string (of any length other than the maximum string length)
' and located in RAM, Program Memory or Persistent Memory, return a
' new string that is guaranteed to reside in RAM and is null terminated.
'
Function NullTermString(ByVal s as String) as String
Const MaxStrLen as Integer = 255
Dim slen as Integer
NullTermString = ""
slen = Len(s)
If ((slen = 0) Or (Asc(s, slen) <> 0)) Then
' zero-length string or string not already null terminated
If (slen < 2) Then
' resulting string will be 1 or 2 characters, construct in-situ
Dim strData() as Byte Based NullTermString.DataAddress
strData(1) = CByte(slen + 1)
strData(2) = &He5
strData(3) = IIF(slen = 0, 0, Asc(s, 1))
strData(4) = 0
ElseIf (slen < MaxStrLen) Then
' resulting string will be at least 3 characters in allocated RAM
NullTermString = s & Chr(0)
End If
Else
' string is already null-terminated, ensure it is in RAM
Select Case StrType(s)
Case &He2, &He3
' source string is in ProgMem or Persistent Memory
NullTermString = Left(s, slen)
Case Else
' source string is already in RAM
NullTermString = s
End Select
End If
End Function
Posted: 11 March 2012, 14:43 PM
by stevech
thanks much.
Not yet trying to pass data to C function.. all ZBasic.
From AN218.pdf at RS485Send(), for bytes, I see this code showing a method to access an array of bytes passed to a function. Isn't there a less obtuse way? More like a simple pointer/address to an array, passed as a param, and used directly as one would in C, like
uint8_t bytes[] or bytes* in C
then in ZB
byref bytes() as byte
with
b = bytes(n)
I'm stumped on how an ISR can use a global byte address pointer to a buffer to store received data at each interrupt, where the pointer is established by the non-ISR code prior to starting up the device that will interrupt with data to read. I can try to adapt that code in RS485Send() but the relationship, in that code, of buffer() and "data" and "address" is, esp. in an ISR, is befuddling to me being so C-entrenched.
Posted: 11 March 2012, 15:31 PM
by dkinzer
stevech wrote:From AN218.pdf at RS485Send(), for bytes, I see this code showing a method to access an array of bytes passed to a function. Isn't there a less obtuse way?
Yes. The code in RS485Send() could have simply indexed the array directly. I've rewritten the code that way (untested).
Code: Select all
Public Sub RS485Send(ByRef buffer() as Byte, ByVal length as UnsignedInteger)
' make sure channel exists
If channel = 0 Then
Exit Sub
End If
' calculate and add CRC as last two bytes of buffer (a 1-based array)
Dim CRC as UnsignedInteger
#if Option.CPUType = "mega32"
CRC = calcCRC16(buffer, length)
#else
CRC = CRC16(buffer, length, &H8005, &Hffff, &H03)
#endif
buffer(length + 1) = LoByte(CRC)
buffer(length + 2) = HiByte(CRC)
length = length + 2
' start sending of message by enabling RS485 driver
Call PutPin(sendReceivePin, zxOutputHigh)
' send half a transmit buffer's worth of data at a time until everything is sent
Dim idx as UnsignedInteger
idx = 1
Do While length <> 0
' calculate how much more data to send
Dim chunkSize as UnsignedInteger
If length > OUTPUT_BUFSIZE \ 2 Then
chunkSize = OUTPUT_BUFSIZE \ 2
Else
chunkSize = length
End If
' queue up next chunk of data
Call PutQueue(outQueue, buffer(idx), chunkSize)
' prepare for the next pass
length = length - chunkSize
idx = idx + chunkSize
' wait until the transmit buffer is less than half full
Call waitForSend(CINT(OUTPUT_BUFSIZE \ 2))
Loop
' wait for remaining bytes to finish sending i.e. transmit buffer is empty
Call waitForSend(0)
Call Sleep(messageFrameInterval)
' disable the RS485 driver and re-enable the receiver
Call PutPin(sendReceivePin, zxOutputLow)
End Sub
stevech wrote:More like a simple pointer/address to an array, passed as a param, and used directly as one would in C ...
In ZBasic, an array is always passed by reference so the receiving procedure gets a pointer to the data, exactly as you would in C. However, ZBasic doesn't have a
pointer to type type; the closest you can get is to define an integral variable to which you assign the address of interest and then define a variable of the desired type that is Based at that address. Referring to the based variable yields automatic dereferencing (much like a reference in C++) whereas in C you have to explicity dereference.
Code: Select all
C:
uint8_t buf[50];
uint8_t *ptr = &buf[10];
*ptr++ = val;
ZBasic:
Dim buf(1 to 50) as Byte
Dim addr as UnsignedInteger
Dim data as Byte Based addr
addr = buf.DataAddress + 10
data = val
addr = addr + 1
In the ZBasic code,
data is a byte whose address is the value of the
addr variable just as in the C code where
*p is a byte whose address is
p.
stevech wrote:I'm stumped on how an ISR can use a global byte address pointer to a buffer to store received data at each interrupt, where the pointer is established by the non-ISR code prior to starting up the device that will interrupt with data to read.
Depending on the requirements, you could just use a global Byte array for the buffer and and index value for where to write next. The same functionality can be achieved by having a global UnsignedInteger address of where to write next and have the ISR define a Byte variable Based at that address. Which you elect to use is more a matter of style than substance.
Posted: 11 March 2012, 15:57 PM
by stevech
thanks... I'll try the based data dereference method - because the byte array's base address varies. This is because the application passes the buffer to use in the ISR and that may vary.