PDA

View Full Version : Simple spin questions



turbosupra
10-28-2011, 05:14 PM
If I want to say variable1 = variable2, how do I do that? Every time I do this, I get inconsistent results, and so I thought I'd just ask.

The purpose is to have them both be the same value, so that if variable2 changes, variable1 still has its old value. I do not want it to be a pointer that says to check the value of variable2, when you query variable1.

Also, I'm having a very hard time with knowing what data type I want with pst.Str or pst.Char. How do I know when to put the @ symbol in front of a variable name when using pst.Str(@variable) or pst.Str(variable) ?

Duane Degn
10-28-2011, 05:40 PM
If I want to say variable1 = variable2, how do I do that??

variable1 := variable2

should be enough.


Also, I'm having a very hard time with knowing what data type I want with pst.Str or pst.Char. How do I know when to put the @ symbol in front of a variable name when using pst.Str(@variable) or pst.Str(variable) ?

pst.Char needs a single byte as an argument.

pst.Str needs a pointer to an array of bytes with a terminating zero. This is why you would usually use the @ symbol. If you had a variable that you were using as a pointer so it already held the address of an array of bytes you wouldn't use the @ symbol.

For example:
VAR
word myPointer

PUB Main | localIndex
myPointer := @myArray
pst.Str(@myArray)
pst.Str(myPointer)
repeat localIndex from 0 to 10
pst.char(myArray[localIndex])
DAT
myArray byte "Hello World", 0

The output (untested) should be:

Hello WorldHello WorldHello World

Duane

sylvie369
10-28-2011, 05:45 PM
Re. your first question, you should be able to assign one variable the value of another using the variable assignment operator, := (colon equal sign). If you are using just the equal sign, weird things will happen. Apologies if you already knew that.


if pattern == 0
pattern := %11000000

The first line returns a Boolean value (TRUE or FALSE) indicating whether or not the variable pattern has a value of 0. If it does, then the second line assigns the value %11000000 to the variable pattern.

Re. the "@", a variable preceded by that symbol gives the memory address of the variable, not the contents at that address. A lot of Spin methods require that for their arguments.


PUB FromStr(StrAddr, Format): Num | Idx, N, Val, Char, Base, GChar, IChar, Field
''Convert z-string (at StrAddr) to long Num using Format.
''PARAMETERS: StrAddr = Address of string buffer containing the numeric string to convert.
'' Format = Indicates input format: base, size, etc. See "FORMAT SYNTAX" for more information. Note: three Format elements are ignored by
'' FromStr(): Zero/Space Padding, Hide/Show Plus Sign, and Digit Group Size. All other elements are actively used during translation.

A well-documented method should indicate as clearly as this one does that the argument you need to send is the address and not the variable's contents.

turbosupra
10-28-2011, 06:44 PM
Thanks Duane, this is very helpful and I can probably get my personal example working based on this example :)

The @/non @ explanation is good and hopefully a cornerstone for me to start building on. I was able to get the second code block to work, thank you!

I tried to insert the line below instead, but it did not work, any idea as to why?


repeat localIndex from 0 to strsize(myPointer)





CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
byteLimit = 100

VAR
byte Ptr2b_RxString[byteLimit]
byte b_RxString[byteLimit]

byte Pos[byteLimit]
byte PosOfDelimiter[byteLimit]
word myPointer


OBJ

pst : "Parallax Serial Terminal"



PUB Main | localIndex

pst.Start(115_200)
pst.Clear

myPointer := @myArray
pst.Str(@myArray)
pst.Str(String(pst#NL))
pst.Str(myPointer)
pst.Str(String(pst#NL))
repeat localIndex from 0 to 10
pst.char(myArray[localIndex])

pst.Str(String(pst#NL))

DAT
myArray byte "Hello World", 0







variable1 := variable2

should be enough.



pst.Char needs a single byte as an argument.

pst.Str needs a pointer to an array of bytes with a terminating zero. This is why you would usually use the @ symbol. If you had a variable that you were using as a pointer so it already held the address of an array of bytes you wouldn't use the @ symbol.

For example:
VAR
word myPointer

PUB Main | localIndex
myPointer := @myArray
pst.Str(@myArray)
pst.Str(myPointer)
repeat localIndex from 0 to 10
pst.char(myArray[localIndex])
DAT
myArray byte "Hello World", 0

The output (untested) should be:

Hello WorldHello WorldHello World

Duane

turbosupra
10-28-2011, 06:46 PM
Thank you, that does make sense. I've had some inconsistencies (which are due to me one way or the other) and this will hopefully help me catch them so I can figure out what I am doing wrong.



Re. your first question, you should be able to assign one variable the value of another using the variable assignment operator, := (colon equal sign). If you are using just the equal sign, weird things will happen. Apologies if you already knew that.


if pattern == 0
pattern := %11000000

The first line returns a Boolean value (TRUE or FALSE) indicating whether or not the variable pattern has a value of 0. If it does, then the second line assigns the value %11000000 to the variable pattern.

Re. the "@", a variable preceded by that symbol gives the memory address of the variable, not the contents at that address. A lot of Spin methods require that for their arguments.


PUB FromStr(StrAddr, Format): Num | Idx, N, Val, Char, Base, GChar, IChar, Field
''Convert z-string (at StrAddr) to long Num using Format.
''PARAMETERS: StrAddr = Address of string buffer containing the numeric string to convert.
'' Format = Indicates input format: base, size, etc. See "FORMAT SYNTAX" for more information. Note: three Format elements are ignored by
'' FromStr(): Zero/Space Padding, Hide/Show Plus Sign, and Digit Group Size. All other elements are actively used during translation.

A well-documented method should indicate as clearly as this one does that the argument you need to send is the address and not the variable's contents.

Duane Degn
10-28-2011, 07:07 PM
Try

repeat localIndex from 0 to strsize(myPointer) - 1

That counting starting with zero gets me a lot too.

strsize is evaluated every loop. You could speed up the code by using a temporary variable to hold the size and use that as a parameter.


temp := strsize(myPointer) - 1
repeat localIndex from 0 to temp

The above code should execute faster than the code in the first code block.

Duane

turbosupra
10-28-2011, 07:22 PM
Wow, that made it work! Thank you very much Duane.

The code example I was trying to write, was the following, and I would send "test=4" to it. I believe my code is failing because I'm not sure how to send b_RxString to DelimiterFinder. Whenever I send it, it seems to come out as gibberish. Since I pass it originally as a reference with DelimiterFinder(@b_RxString) on line 40, I should not have to use the @ symbol anymore, based on variable assignments, correct? On line 46, the value does not print out correctly inside of the PST either, what am I doing wrong there? And finally, why do I have to put the long "l_localindex" in the line of code repeat l_localindex from 0 to strsize(b_Ptr2b_RxString) -1 in line 51?

Thanks again.





{
To send a string, test a function and return a position




}
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
byteLimit = 100

VAR
byte b_Ptr2b_RxString[byteLimit]
byte b_RxString[byteLimit]

byte b_Pos[byteLimit]
byte b_PosOfDelimiter[byteLimit]

OBJ

pst : "Parallax Serial Terminal"


PUB Main

pst.Start(115_200)
pst.Clear




repeat
pst.Str(String(pst#NL, "Enter a name, then an equals sign and then their age: "))
pst.StrIn(@b_RxString)
pst.Str(String(pst#NL, "You typed "))
pst.Str(@b_RxString)
pst.Str(String(pst#NL, pst#NL))

DelimiterFinder(@b_RxString)


PUB DelimiterFinder(RxStringAddr) | l_localindex, variableLength

b_Ptr2b_RxString := RxStringAddr
pst.Str(b_Ptr2b_RxString)
pst.Str(String(pst#NL, pst#NL))

b_Pos := 0
variableLength := strsize(b_Ptr2b_RxString) - 1

repeat l_localindex from 0 to variableLength
if byte[b_Ptr2b_RxString + b_Pos] == "="
b_PosOfDelimiter := b_Pos
b_pos := b_pos + 1

pst.Str(String("postion of delimiter is "))
pst.Dec(b_PosOfDelimiter)
pst.Str(String(pst#NL, pst#NL))

return b_PosOfDelimiter

Duane Degn
10-28-2011, 08:22 PM
I can see several "style" problems in your program but I don't see why is doesn't work.

You declare b_PosOfDelimiter as an array but never use an index with it.

I'm pretty sure Spin treats b_PosOfDelimiter as b_PosOfDelimiter[0] but I don't think it's good programming style to do it that way.

The method DelimiterFinder returns b_PosOfDelimiter which is also a bit strange to use a global variable as a return value since the calling method will know its value without the need for it to be returned.

repeat l_localindex from 0 to strsize(b_Ptr2b_RxString) -1

could be written as

repeat strsize(b_Ptr2b_RxString)

since your code doesn't use l_localIndex

Duane

MagIO2
10-28-2011, 08:43 PM
byte b_Ptr2b_RxString[byteLimit]

does not fit with the way you use it:

b_Ptr2b_RxString := RxStringAddr
pst.Str(b_Ptr2b_RxString)

First of all I don't see the benefit of storing the address in just another variable. And if you want to store an address you at least need a word (which is the main problem):
word b_Ptr2b_RxString

Because b_Ptr2b_RxString was defined as byte only the byte will be passed to pst.Str, which cuts away parts of the real address.
But pst.str( RxStringAddr ) will work just fine!



repeat l_localindex from 0 to variableLength
if byte[b_Ptr2b_RxString + b_Pos] == "="
b_PosOfDelimiter := b_Pos
b_pos := b_pos + 1

there is also no need to have b_pos because l_localindex is aleady counting from 0 upwards.

Duane Degn
10-28-2011, 08:59 PM
And if you want to store an address you at least need a word (which is the main problem):
word b_Ptr2b_RxString

Ah, yes. I didn't catch that.

Duane

turbosupra
10-28-2011, 11:59 PM
Thank you for the reply and correction. It may not be very beneficial in this case, but is more supposed to be a learning exercise for me I guess.

So if all strings are byte arrays, why would I want to store and address to a word or long. I think me not knowing this is the crux, or part of the crux of why I'm having such a hard time with this. I thought it would store it as an array, and then pst.Str would receive the array and display it from the memory address of the first byte b_Ptr2b_RxString[0] to the 0 terminator at the end. Is that logic wrong? Your explanation makes sense with the results I was experiencing, I would just like to understand why so I can cement it into my brain :)



byte b_Ptr2b_RxString[byteLimit]

does not fit with the way you use it:

b_Ptr2b_RxString := RxStringAddr
pst.Str(b_Ptr2b_RxString)

First of all I don't see the benefit of storing the address in just another variable. And if you want to store an address you at least need a word (which is the main problem):
word b_Ptr2b_RxString

Because b_Ptr2b_RxString was defined as byte only the byte will be passed to pst.Str, which cuts away parts of the real address.
But pst.str( RxStringAddr ) will work just fine!



repeat l_localindex from 0 to variableLength
if byte[b_Ptr2b_RxString + b_Pos] == "="
b_PosOfDelimiter := b_Pos
b_pos := b_pos + 1

there is also no need to have b_pos because l_localindex is aleady counting from 0 upwards.


Ah, yes. I didn't catch that.

Duane

turbosupra
10-29-2011, 12:01 AM
Duane,

Thank you for the reply. This might be asking a lot, but would you mind showing me how you would write DelimiterFinder so that I can compare style and learn to write in a more "industry standard" way? If not, I understand, but if so I would really appreciate it and anticipate that I would be able to learn a lot from it.



I can see several "style" problems in your program but I don't see why is doesn't work.

You declare b_PosOfDelimiter as an array but never use an index with it.

I'm pretty sure Spin treats b_PosOfDelimiter as b_PosOfDelimiter[0] but I don't think it's good programming style to do it that way.

The method DelimiterFinder returns b_PosOfDelimiter which is also a bit strange to use a global variable as a return value since the calling method will know its value without the need for it to be returned.

repeat l_localindex from 0 to strsize(b_Ptr2b_RxString) -1

could be written as

repeat strsize(b_Ptr2b_RxString)

since your code doesn't use l_localIndex

Duane

Duane Degn
10-29-2011, 12:12 AM
Duane,

Thank you for the reply. This might be asking a lot, but would you mind showing me how you would write DelimiterFinder so that I can compare style and learn to write in a more "industry standard" way? If not, I understand, but if so I would really appreciate it and anticipate that I would be able to learn a lot from it.

Come on, you already know the answer will be yes. I'm a sucker for writing other people's code. Their problems are always easier then the ones I'm having.

I am headed of to the store with my wife right now. I'll likely write it later tonight (perhaps tomorrow). It shouldn't take long.

Duane

Duane Degn
10-29-2011, 03:58 AM
Here's the code. Of course there are as many ways to do this as there are programmers.


CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000

_ByteLimit = 100
_NoDelimiter = $FF

VAR

byte rxString[_ByteLimit]
byte PosOfDelimiter
OBJ
pst : "Parallax Serial Terminal"
PUB Main
pst.Start(115_200)
pst.Clear
repeat
pst.Str(String(pst#NL, "Enter a name, then an equals sign and then their age: "))
pst.StrIn(@rxString)
pst.Str(String(pst#NL, pst#NL, "You typed ", 34))
pst.Str(@rxString)
pst.Char(34)
pst.Char(pst#NL)

PosOfDelimiter := DelimiterFinder(@rxString)
if PosOfDelimiter == _NoDelimiter
pst.Str(String(pst#NL, "No delimiter found."))
else
pst.Str(String(pst#NL, "Postion of delimiter is "))
pst.Dec(PosOfDelimiter)
pst.Char(pst#NL)
pst.Char(pst#NL)

PUB DelimiterFinder(localPtr) | variableLength, foundFlag
pst.Str(String(pst#NL, "Looking for delimiter ", 34))
pst.Char(delimiter)
pst.Str(String(34, " in string ", 34))
pst.Str(localPtr)
pst.Char(34)
pst.Char(pst#NL)
variableLength := strsize(localPtr)
foundFlag := 0
repeat variableLength
if byte[localPtr++] == delimiter
foundFlag := 1
quit
result++
if foundFlag == 0
result := _NoDelimiter

DAT
delimiter byte "="



Here's an example of the output.


Enter a name, then an equals sign and then their age:
You typed "Fred=10"
Looking for delimiter "=" in string "Fred=10"
Postion of delimiter is 4

Enter a name, then an equals sign and then their age:
You typed "Susan=12"
Looking for delimiter "=" in string "Susan=12"
Postion of delimiter is 5

Enter a name, then an equals sign and then their age:
You typed "Duane=48"
Looking for delimiter "=" in string "Duane=48"
Postion of delimiter is 5

Enter a name, then an equals sign and then their age:
You typed "Joe18"
Looking for delimiter "=" in string "Joe18"
No delimiter found.

Enter a name, then an equals sign and then their age:


I started counting the positions with zero. The code could easily be modified to use one as the first position.

In case you don't know, 34 is the ASCII number for double quotes. You could asign a constant name to 34, but I personally think it's just as easy to use the ASCII numbers for many of the none printed and not easily printed characters.

Duane

turbosupra
10-29-2011, 05:23 AM
Thank you very very much. I will be analyzing this and learning from it!

Would you mind explaining this please?

So if all strings are byte arrays, why would I want to store and address to a word or long. I think me not knowing this is the crux, or part of the crux of why I'm having such a hard time with this. I thought it would store it as an array, and then pst.Str would receive the array and display it from the memory address of the first byte b_Ptr2b_RxString[0] to the 0 terminator at the end. Is that logic wrong?



Here's the code. Of course there are as many ways to do this as there are programmers.


CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000

_ByteLimit = 100
_NoDelimiter = $FF

VAR

byte rxString[_ByteLimit]
byte PosOfDelimiter
OBJ
pst : "Parallax Serial Terminal"
PUB Main
pst.Start(115_200)
pst.Clear
repeat
pst.Str(String(pst#NL, "Enter a name, then an equals sign and then their age: "))
pst.StrIn(@rxString)
pst.Str(String(pst#NL, pst#NL, "You typed ", 34))
pst.Str(@rxString)
pst.Char(34)
pst.Char(pst#NL)

PosOfDelimiter := DelimiterFinder(@rxString)
if PosOfDelimiter == _NoDelimiter
pst.Str(String(pst#NL, "No delimiter found."))
else
pst.Str(String(pst#NL, "Postion of delimiter is "))
pst.Dec(PosOfDelimiter)
pst.Char(pst#NL)
pst.Char(pst#NL)

PUB DelimiterFinder(localPtr) | variableLength, foundFlag
pst.Str(String(pst#NL, "Looking for delimiter ", 34))
pst.Char(delimiter)
pst.Str(String(34, " in string ", 34))
pst.Str(localPtr)
pst.Char(34)
pst.Char(pst#NL)
variableLength := strsize(localPtr)
foundFlag := 0
repeat variableLength
if byte[localPtr++] == delimiter
foundFlag := 1
quit
result++
if foundFlag == 0
result := _NoDelimiter

DAT
delimiter byte "="



Here's an example of the output.


Enter a name, then an equals sign and then their age:
You typed "Fred=10"
Looking for delimiter "=" in string "Fred=10"
Postion of delimiter is 4

Enter a name, then an equals sign and then their age:
You typed "Susan=12"
Looking for delimiter "=" in string "Susan=12"
Postion of delimiter is 5

Enter a name, then an equals sign and then their age:
You typed "Duane=48"
Looking for delimiter "=" in string "Duane=48"
Postion of delimiter is 5

Enter a name, then an equals sign and then their age:
You typed "Joe18"
Looking for delimiter "=" in string "Joe18"
No delimiter found.

Enter a name, then an equals sign and then their age:


I started counting the positions with zero. The code could easily be modified to use one as the first position.

In case you don't know, 34 is the ASCII number for double quotes. You could asign a constant name to 34, but I personally think it's just as easy to use the ASCII numbers for many of the none printed and not easily printed characters.

Duane

turbosupra
10-29-2011, 07:19 AM
Also,

I have gotten it to parse the data correctly now, thanks to your help.

The only thing that isn't making sense is when I change the value of "flashes" to a different number, my code ignores it (or at the very least does not use it properly). Any idea why this is happening? I initially set the value of flashes to 1, so it will flash once and then pause for 2 seconds. When I try and reset the value to say "flashes=4" it will just flash for forever?

Duane Degn
10-29-2011, 02:51 PM
The address of the byte arrray will be somewhere within the 32K of hub RAM. Since the address is very likely to larger than 255 (the limit of a byte) you want to use a word or a long to store the address.

Change the method "DelimiterFinder" in the code I posted above to this version.


PUB DelimiterFinder(localPtr) | variableLength, foundFlag
pst.Str(String(pst#NL, "Looking delimiter ", 34))
pst.Char(delimiter)
pst.Str(String(34, " in string ", 34))
pst.Str(localPtr)
pst.Char(34)
pst.Char(pst#NL)
variableLength := strsize(localPtr)
foundFlag := 0
repeat variableLength
pst.Str(String(pst#NL, "The byte in memory location "))
pst.Dec(localPtr)
pst.Str(String(" has the value "))
pst.Dec(byte[localPtr])
pst.Str(String(pst#NL, "The ASCII character for this value is ", 34))
pst.Char(byte[localPtr])
pst.Char(34)
pst.Char(".")
pst.Char(pst#NL)
if byte[localPtr++] == delimiter
foundFlag := 1
'quit
if foundFlag == 0
result++
if foundFlag == 0
result := _NoDelimiter




Here's the output when I typed "Fred=10".


Enter a name, then an equals sign and then their age:
You typed "Fred=10"
Looking delimiter "=" in string "Fred=10"
The byte in memory location 1596 has the value 70
The ASCII character for this value is "F".
The byte in memory location 1597 has the value 114
The ASCII character for this value is "r".
The byte in memory location 1598 has the value 101
The ASCII character for this value is "e".
The byte in memory location 1599 has the value 100
The ASCII character for this value is "d".
The byte in memory location 1600 has the value 61
The ASCII character for this value is "=".
The byte in memory location 1601 has the value 49
The ASCII character for this value is "1".
The byte in memory location 1602 has the value 48
The ASCII character for this value is "0".
Postion of delimiter is 4

Enter a name, then an equals sign and then their age:


The first byte "F" was stored in the memory location 1596. 1596 would not fit in a byte so a word or a long must be used to store it.

Local variables are all longs so the variable "localPtr" is a long and can hold the value 1596 without a problem.

You might have noticed I commented out "quit". This keeps the loop going even after the delimiter was found. I wanted the loop to continue so all the addresses in the string would be displayed.

This concept of addresses is very important (as you can tell). The modified method I just posted is giving the location of each item in the array. Just the address of the first member of the array is enough for the method to know where to start reading the data.

The line

if byte[localPtr++] == delimiter

is both checking to see if an equals sign is in the memory location localPtr and incrementing the value of localPtr (see Post-Increment p.152).

Something that helped me get strings straight in my brain was something like this.


temp := string("hello world")
pst.dec(temp)

Make sure temp is at least a word in size and you'll see where the temporary string "hello world" is stored in memory.

One of the reasons I've changed


pst.Str(String(pst#NL, pst#NL))

to


pst.Char(pst#NL)
pst.Char(pst#NL)

is because of the overhead required by the Propeller to hold the new line characters as a string which PST then needs to break apart. If I'm only displaying two characters, I usually send the characters individually rather than combining them to a string before sending them to a serial object. This is also a matter of personal preference.

Hopefully some of this is making sense. Keep the questions coming.

Duane

turbosupra
10-29-2011, 02:55 PM
Turns out it is recognizing the change, kind of. If I put flashes=4, it sets it to 52 and flashes 52 times before resetting, if I put flashes=5, it sets it to 53 and flashes 53 times before resetting.

So it is setting it to a char value of 4 or 5 instead of a decimal value. Is there a native way to correct that?


[edit, I see you posted while I was writing (thank you), I will try and comprehend that now]

Duane Degn
10-29-2011, 03:58 PM
I looked at the code you uploaded.

I made some changes.


CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000

_ByteLimit = 100
_NoDelimiter = $FF

VAR

long ledFlasherStack[20]
long length

byte rxString[_ByteLimit]
byte PosOfDelimiter
byte delimiter
byte flashes, variableName[_ByteLimit], variableValue[_ByteLimit]

OBJ
pst : "Parallax Serial Terminal"
'strings : "Strings"
PUB Main
pst.Start(115_200)
pst.Clear

cognew(ledFlasher, @ledFlasherStack)

repeat
pst.Str(String(pst#NL, "Enter a name, then an equals sign and then their age: "))
pst.StrIn(@rxString)
pst.Str(String(pst#NL, pst#NL, "You typed ", 34))
pst.Str(@rxString)
pst.Char(34)
pst.Char(pst#NL)

PosOfDelimiter := DelimiterFinder(@rxString)
if PosOfDelimiter == _NoDelimiter
pst.Str(String(pst#NL, "No delimiter found."))
else
pst.Str(String(pst#NL, "Postion of delimiter is "))
pst.Dec(PosOfDelimiter)
pst.Str(String(pst#NL))
length := strsize(@rxString)
pst.Str(String(pst#NL, "length is "))
pst.Dec(length)
pst.Str(String(pst#NL))
pst.Str(String(pst#NL, "variable name is ", 34))
bytefill(@variableName, 0, _ByteLimit)
bytemove(@variableName, @rxString, PosOfDelimiter)
pst.Str(@variableName)
pst.Str(String(34, pst#NL))
pst.Str(String(pst#NL, "variable value is ", 34))
bytefill(@variableValue, 0, _ByteLimit)
bytemove(@variableValue, @rxString + PosOfDelimiter + 1, length - PosOfDelimiter - 1)
pst.Str(@variableValue)
pst.Str(String(34, pst#NL))
if strcomp(@variableName, string("flashes"))
pst.Str(String(pst#NL))
pst.Str(String("It does contain ", 34, "flashes", 34))
pst.Str(String(pst#NL, pst#NL))
flashes := StrToBase(@rxString + PosOfDelimiter + 1, 10)
pst.Str(String("The variable ", 34, "flashes", 34, " is now set to ", 34))
pst.Dec(flashes)
pst.Str(String(34, pst#NL))

PUB DelimiterFinder(localPtr) | variableLength, foundFlag
delimiter[0] := "="
'delimiter[1] := 0
pst.Str(String(pst#NL, "Looking for delimiter ", 34))
pst.Char(delimiter)
pst.Str(String(34, " in string ", 34))
pst.Str(localPtr)
pst.Char(34)
pst.Char(pst#NL)
variableLength := strsize(localPtr)
foundFlag := 0
repeat variableLength
if byte[localPtr++] == delimiter
foundFlag := 1
quit
result++
if foundFlag == 0
result := _NoDelimiter

PUB ledFlasher
flashes := 1
waitcnt((clkfreq * 2) + cnt)
dira[23] := 1
outa[23] := 0
repeat

repeat flashes

outa[23] := 1
waitcnt((clkfreq /2) + cnt)
outa[23] := 0
waitcnt((clkfreq /2) + cnt)

waitcnt((clkfreq * 2) + cnt)

PRI StrToBase(stringptr, base) : value | chr, index
{Converts a zero terminated string representation of a number to a value in the designated base.
Ignores all non-digit characters (except negative (-) when base is decimal (10)).}
value := index := 0
repeat until ((chr := byte[stringptr][index++]) == 0)
chr := -15 + --chr & %11011111 + 39*(chr > 56) 'Make "0"-"9","A"-"F","a"-"f" be 0 - 15, others out of range
if (chr > -1) and (chr < base) 'Accumulate valid values into result; ignore others
value := value * base + chr
if (base == 10) and (byte[stringptr] == "-") 'If decimal, address negative sign; ignore otherwise
value := - value

'DAT
'delimiter byte "="




A single byte will do as long as you don't want to flash the LED more than 255 times.

I'm surprised your original code worked as well as it did. I think since all the strings you entered were relatively small the memory wasn't corrupted to the point of not working.

Remember to store strings in byte arrays. All local variables are longs.

The value four is not the same as the character "4". You need to convert the string to a number. I used the method "StrToBase" from PST to convert the string to number value.

The program in my previous post shows the difference between a number value and a numeric character. For example the character "4" is held as 52 in a single byte in memory.

You had delimiter[1] := 0 in the DelimiterFinder method. Strings need a terminating zero not single characters. Since delimiter isn't an array the index [1] moved the zero into the next byte.


I'll look through the PEK material to see if there's a section that explains all this.

Duane

turbosupra
10-29-2011, 08:28 PM
It works great, thank you so much!! I have a bunch of questions if that is ok, you don't have to answer them all if there are too many.

Ok, so methods accept and return longs, which then I can store into a byte array if it is a string ... which is really a pointer to a memory address with a numerical value next to it that says how many consecutive memory addresses are part of that variables (strings) value? I can view this memory address through the command pst.Dec(name of long), which is different then pst.Dec(byte[name of long]) of which then gives you the byte binary mathematical value. Then there is the command pst.Char(byte[name of long]) which gives the ascii character value, converted from the decimal byte value at the decimal location? Pst.Dec will give a numerical value when used with a byte or byte array, but a memory address when used with a long? If I wanted to have 260 flashes, could I just change it to a byte array, or would there be many other code changes required as well? If setting delimiter[1] := 0, does not create a terminating zero, because it actually sets it to a value of 48? What is an index[1]?

Is it safe to copy back and forth between between bytes and longs? Since all methods return a long, I can bytemove that long into a bytearray? Will bytearray := long not work, is that why I had to use bytemove instead of an assignment?



I looked at the code you uploaded.

I made some changes.


CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000

_ByteLimit = 100
_NoDelimiter = $FF

VAR

long ledFlasherStack[20]
long length

byte rxString[_ByteLimit]
byte PosOfDelimiter
byte delimiter
byte flashes, variableName[_ByteLimit], variableValue[_ByteLimit]

OBJ
pst : "Parallax Serial Terminal"
'strings : "Strings"
PUB Main
pst.Start(115_200)
pst.Clear

cognew(ledFlasher, @ledFlasherStack)

repeat
pst.Str(String(pst#NL, "Enter a name, then an equals sign and then their age: "))
pst.StrIn(@rxString)
pst.Str(String(pst#NL, pst#NL, "You typed ", 34))
pst.Str(@rxString)
pst.Char(34)
pst.Char(pst#NL)

PosOfDelimiter := DelimiterFinder(@rxString)
if PosOfDelimiter == _NoDelimiter
pst.Str(String(pst#NL, "No delimiter found."))
else
pst.Str(String(pst#NL, "Postion of delimiter is "))
pst.Dec(PosOfDelimiter)
pst.Str(String(pst#NL))
length := strsize(@rxString)
pst.Str(String(pst#NL, "length is "))
pst.Dec(length)
pst.Str(String(pst#NL))
pst.Str(String(pst#NL, "variable name is ", 34))
bytefill(@variableName, 0, _ByteLimit)
bytemove(@variableName, @rxString, PosOfDelimiter)
pst.Str(@variableName)
pst.Str(String(34, pst#NL))
pst.Str(String(pst#NL, "variable value is ", 34))
bytefill(@variableValue, 0, _ByteLimit)
bytemove(@variableValue, @rxString + PosOfDelimiter + 1, length - PosOfDelimiter - 1)
pst.Str(@variableValue)
pst.Str(String(34, pst#NL))
if strcomp(@variableName, string("flashes"))
pst.Str(String(pst#NL))
pst.Str(String("It does contain ", 34, "flashes", 34))
pst.Str(String(pst#NL, pst#NL))
flashes := StrToBase(@rxString + PosOfDelimiter + 1, 10)
pst.Str(String("The variable ", 34, "flashes", 34, " is now set to ", 34))
pst.Dec(flashes)
pst.Str(String(34, pst#NL))

PUB DelimiterFinder(localPtr) | variableLength, foundFlag
delimiter[0] := "="
'delimiter[1] := 0
pst.Str(String(pst#NL, "Looking for delimiter ", 34))
pst.Char(delimiter)
pst.Str(String(34, " in string ", 34))
pst.Str(localPtr)
pst.Char(34)
pst.Char(pst#NL)
variableLength := strsize(localPtr)
foundFlag := 0
repeat variableLength
if byte[localPtr++] == delimiter
foundFlag := 1
quit
result++
if foundFlag == 0
result := _NoDelimiter

PUB ledFlasher
flashes := 1
waitcnt((clkfreq * 2) + cnt)
dira[23] := 1
outa[23] := 0
repeat

repeat flashes

outa[23] := 1
waitcnt((clkfreq /2) + cnt)
outa[23] := 0
waitcnt((clkfreq /2) + cnt)

waitcnt((clkfreq * 2) + cnt)

PRI StrToBase(stringptr, base) : value | chr, index
{Converts a zero terminated string representation of a number to a value in the designated base.
Ignores all non-digit characters (except negative (-) when base is decimal (10)).}
value := index := 0
repeat until ((chr := byte[stringptr][index++]) == 0)
chr := -15 + --chr & %11011111 + 39*(chr > 56) 'Make "0"-"9","A"-"F","a"-"f" be 0 - 15, others out of range
if (chr > -1) and (chr < base) 'Accumulate valid values into result; ignore others
value := value * base + chr
if (base == 10) and (byte[stringptr] == "-") 'If decimal, address negative sign; ignore otherwise
value := - value

'DAT
'delimiter byte "="




A single byte will do as long as you don't want to flash the LED more than 255 times.

I'm surprised your original code worked as well as it did. I think since all the strings you entered were relatively small the memory wasn't corrupted to the point of not working.

Remember to store strings in byte arrays. All local variables are longs.

The value four is not the same as the character "4". You need to convert the string to a number. I used the method "StrToBase" from PST to convert the string to number value.

The program in my previous post shows the difference between a number value and a numeric character. For example the character "4" is held as 52 in a single byte in memory.

You had delimiter[1] := 0 in the DelimiterFinder method. Strings need a terminating zero not single characters. Since delimiter isn't an array the index [1] moved the zero into the next byte.


I'll look through the PEK material to see if there's a section that explains all this.

Duane

Duane Degn
10-29-2011, 11:10 PM
The problem with delimiter[1] := 0 is when you defined delimiter as a byte variable you didn't make it an array.


byte delimiter

To use delimiter[1] without cause a problem with the next byte variable, you need


byte delimiter[2] ' this reserves two bytes in a row

But you don't need a terminating zero anyway since you where just comparing a single character.


if x == "="

Is the same as


if x == 61

Is the same as


if x == $3D


Ok, so methods accept and return longs, which then I can store into a byte array if it is a string

Spin uses longs as arguments and return values. The method determines what is returned.

Normally you would not store a long into a byte array. A byte array is multiple sequential bytes. A method may return a memory location of a byte. A string is a byte array holding ASCII values of characters you may wish to send to a terminal. Many string commands and methods assume there is a terminating zero indicating the end of the string.

Each character is a single byte. In the Help menu of the Propeller Tool their is a menu item "View Character Chart..." this tells you the ASCII values of each character.


I can bytemove that long into a bytearray?

This will get you into trouble fast.

If the method returns the address of a byte array, you can use bytemove to move the bytes from the location returned to another location.

The location can be a large value since there are 32K bytes of RAM. The location tells you where in memory those bytes are located. You use the location in many of the string methods since you can only pass and receive a single value.

In an earlier post I had the program list the memory locations of each character.

"F" in "Fred" was located in memory location 1596.

Since "Fred" is stored in rxString we know the location of the first byte in rxString is 1596.

In the main method you have the line

PosOfDelimiter := DelimiterFinder(@rxString)


We could have used

PosOfDelimiter := DelimiterFinder(1596)


since 1596 and @rxString are the same value.

Although just by changing the code from


PosOfDelimiter := DelimiterFinder(@rxString)


to


PosOfDelimiter := DelimiterFinder(@rxString)

may change the location in memory where rxString is located when the program is recompiled. This make using @rxString much more practical to using 1596 since we didn't know the value 1596 was the same as @rxString until after the program was ran.

The method PosOfDelimiter returned how far from the start of the string the delimiter was located (starting counting with zero).

If we changed DelimiterFinder to

PUB DelimiterFinder(localPtr) | variableLength, foundFlag
pst.Str(String(pst#NL, "Looking delimiter ", 34))
pst.Char(delimiter)
pst.Str(String(34, " in string ", 34))
pst.Str(localPtr)
pst.Char(34)
pst.Char(pst#NL)
variableLength := strsize(localPtr)
foundFlag := 0
repeat variableLength
pst.Str(String(pst#NL, "The byte in memory location "))
pst.Dec(localPtr)
pst.Str(String(" has the value "))
pst.Dec(byte[localPtr])
pst.Str(String(pst#NL, "The ASCII character for this value is ", 34))
pst.Char(byte[localPtr])
pst.Char(34)
pst.Char(".")
pst.Char(pst#NL)
if byte[localPtr++] == delimiter
foundFlag := 1
result := localPtr - 1
'quit
'if foundFlag == 0
'result++
if foundFlag == 0
result := _NoDelimiter


Then the output would be changed to

Enter a name, then an equals sign and then their age:
You typed "Fred=10"
Looking delimiter "=" in string "Fred=10"
The byte in memory location 1596 has the value 70
The ASCII character for this value is "F".
The byte in memory location 1597 has the value 114
The ASCII character for this value is "r".
The byte in memory location 1598 has the value 101
The ASCII character for this value is "e".
The byte in memory location 1599 has the value 100
The ASCII character for this value is "d".
The byte in memory location 1600 has the value 61
The ASCII character for this value is "=".
The byte in memory location 1601 has the value 49
The ASCII character for this value is "1".
The byte in memory location 1602 has the value 48
The ASCII character for this value is "0".
Postion of delimiter is 1600



Duane