Shop OBEX P1 Docs P2 Docs Learn Events
Simple spin questions — Parallax Forums

Simple spin questions

turbosupraturbosupra Posts: 1,088
edited 2011-10-29 15:10 in Propeller 1
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) ?

Comments

  • Duane DegnDuane Degn Posts: 10,588
    edited 2011-10-28 09:40
    turbosupra wrote: »
    If I want to say variable1 = variable2, how do I do that??

    variable1 := variable2

    should be enough.
    turbosupra wrote: »
    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
  • sylvie369sylvie369 Posts: 1,622
    edited 2011-10-28 09:45
    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.
  • turbosupraturbosupra Posts: 1,088
    edited 2011-10-28 10:44
    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
    
    



    Duane Degn wrote: »
    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
  • turbosupraturbosupra Posts: 1,088
    edited 2011-10-28 10:46
    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.

    sylvie369 wrote: »
    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 DegnDuane Degn Posts: 10,588
    edited 2011-10-28 11:07
    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
  • turbosupraturbosupra Posts: 1,088
    edited 2011-10-28 11:22
    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 DegnDuane Degn Posts: 10,588
    edited 2011-10-28 12:22
    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
  • MagIO2MagIO2 Posts: 2,243
    edited 2011-10-28 12:43
    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 DegnDuane Degn Posts: 10,588
    edited 2011-10-28 12:59
    MagIO2 wrote: »
    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
  • turbosupraturbosupra Posts: 1,088
    edited 2011-10-28 15:59
    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 :)

    MagIO2 wrote: »
    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 wrote: »
    Ah, yes. I didn't catch that.

    Duane
  • turbosupraturbosupra Posts: 1,088
    edited 2011-10-28 16:01
    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.

    Duane Degn wrote: »
    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 DegnDuane Degn Posts: 10,588
    edited 2011-10-28 16:12
    turbosupra wrote: »
    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 DegnDuane Degn Posts: 10,588
    edited 2011-10-28 19:58
    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
  • turbosupraturbosupra Posts: 1,088
    edited 2011-10-28 21:23
    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?

    Duane Degn wrote: »
    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
  • turbosupraturbosupra Posts: 1,088
    edited 2011-10-28 23:19
    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 DegnDuane Degn Posts: 10,588
    edited 2011-10-29 06:51
    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
  • turbosupraturbosupra Posts: 1,088
    edited 2011-10-29 06:55
    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 DegnDuane Degn Posts: 10,588
    edited 2011-10-29 07:58
    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
  • turbosupraturbosupra Posts: 1,088
    edited 2011-10-29 12:28
    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?

    Duane Degn wrote: »
    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 DegnDuane Degn Posts: 10,588
    edited 2011-10-29 15:10
    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
          [B]result := localPtr - 1[/B]
          '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
Sign In or Register to comment.