Shop OBEX P1 Docs P2 Docs Learn Events
BUG with local variables inside PRI/PUB function? — Parallax Forums

BUG with local variables inside PRI/PUB function?

Is this a bug in my code or something else? I am not able to find out what would be wrong.

If I use a local variable inside for PUB getnumber function the index (i) variable jumps after the third character (from 0, 1, 2, to 50!).

If I use a global VAR instead, the behavior is as expected.

(I have tried so many things! changing to PRI instead of PUB, using different variable names, without ALINGNL, use four spaces as tabulator instead of two spaces, both Flexprop and PNUT works the same ... I am not able to find the issue on my code )

CON
  _clkfreq = 160_000_000
  baud = 115_200

  STRING_SIZE = 8

OBJ
  ser: "jm_fullduplexserial"
  fmt: "ers_fmt"

VAR
  BYTE number_str[STRING_SIZE]
  BYTE i                              ' UNCOMMENT THIS LINE !!!!!

PUB main()

  ser.start(63, 62, %0000, baud)
  send := @ser.tx
  waitms(2000)
  send("Please write a number (max 8 characters) : ")
  getnumber(number_str)
  ser.fstr1(string("Your number is : "),number_str)

'PUB getnumber(str) | ALIGNL c, i       '   COMMENT THIS LINE !!!!!
PUB getnumber(str) | ALIGNL c         ' UNCOMMENT THIS LINE !!!!!
  i := 0
  repeat
      c := ser.rx()
      if (c == 8) or (c==127)
          if i > 0
             --i
             ser.tx(8)
             'send(fmt.nl(),"i:",fmt.unsdec(i)," ") 'debug
      elseif (c == 13) or (c == 10)
          ser.tx(13)
          ser.tx(10)
          quit
      elseif (i == STRING_SIZE)
          'send(fmt.nl(),"i:",fmt.unsdec(i)," ") 'debug
          next
      else
          if (c > 47) and (c < 58)
             str[i++] := c
             send(fmt.nl(),"i:",fmt.unsdec(i)," ") 'debug     '  UNCOMMENT THIS to disable DEBUG !!!
             ser.tx(c)
  str[i] := 0

Comments

  • getnumber(number_str) should be getnumber(@number_str)

  • JonnyMacJonnyMac Posts: 8,924
    edited 2021-04-12 16:30

    I couldn't run your code because I don't have ers_fmt.spin2, but I've written similar code many times. This works:

    pub get_number(p_str, maxlen) : len | c
    
      bytefill(p_str, 0, maxlen+1)                                  ' clear buffer
    
      repeat
        c := term.rx()                                              ' wait for character
        case c
          "0".."9" :                                                ' digit?
            if (len < maxlen)                                       '  room?
              byte[p_str][len++] := c                               '  yes, add to string
    
          8, 127 :                                                  ' backspace
            if (len > 0)                                            '  and not at beginning
              byte[p_str][--len] := 0                               '  yes, delete last
    
          13 :
            return                                                  '  and exit
    

    Keep in mind that string routines need a terminating zero. In the code that I used to test the above method, I had this:

    con
    
      BUF_SIZE = 8
    
    var 
    
      byte  nbuf[BUF_SIZE+1]
    

    This allows your buffer to hold as many characters as you need, plus the terminating zero.

    Here's my test loop that prints the length of the string and its contents

      repeat
        term.str(string("Enter a number", 13))
        x := get_number(@nbuf, BUF_SIZE)
        term.fstr2(string("-- (%d) : %s\r\r"), x, @nbuf)
    

    Dave made a good catch on the missing @, but given the code, I wonder if even that would correct it. The offending loop uses str[] instead of byte[str][] which would probably stuff characters (as longs) into the address holding str, ultimately causing things to go horribly wrong. No?

  • JonnyMacJonnyMac Posts: 8,924
    edited 2021-04-12 16:40

    Looking at your version I see you're echoing input to the terminal. Here's a version that also does that.

    pub get_number(p_str, maxlen) : len | c
    
      bytefill(p_str, 0, maxlen+1)                                  ' clear buffer
    
      repeat
        c := term.rx()                                              ' wait for character
        case c
          "0".."9" :                                                ' digit?
            if (len < maxlen)                                       '  room?
              byte[p_str][len++] := c                               '  yes, add to string
              term.tx(c)
    
          8, 127 :                                                  ' backspace
            if (len > 0)                                            '  and not at beginning
              byte[p_str][--len] := 0                               '  yes, delete last
              term.tx(8)
    
          13 :
            term.tx(13)
            return                                                  '  and exit
    

    And just for giggles, this version will show you the width of the input field.

    pub get_number(p_str, maxlen) : len | c
    
      bytefill(p_str, 0, maxlen+1)                                  ' clear buffer
    
      term.txn("_", maxlen)                                         ' show width of field
      term.txn(term.CRSR_LF, maxlen)                                ' backup to beginning
    
      repeat
        c := term.rx()                                              ' wait for character
        case c
          "0".."9" :                                                ' digit?
            if (len < maxlen)                                       '  room?
              byte[p_str][len++] := c                               '  yes, add to string
              term.tx(c)
    
          8, 127 :                                                  ' backspace
            if (len > 0)                                            '  and not at beginning
              byte[p_str][--len] := 0                               '  yes, delete last
              term.tx(8)
              term.tx("_")
              term.tx(term.CRSR_LF)
    
          13 :
            term.tx(13)
            return                                                  '  and exit
    
  • Thank you! Yes, all those things mentioned solved the problems I had.

      - missing @    : I though that It was only used for DAT an for method pointers, learned that is also for VAR.
      - [BUF_SIZE+1] : Yes, I was writing out of the array
      - byte[str][]  : This was the worst, I totally corrupted the code, no wonder didn't worked.
    
  • JonnyMacJonnyMac Posts: 8,924
    edited 2021-04-13 17:20

    You're welcome.

    @ provides the run-time address of (a pointer to) any object. In my programs I always preface a variable that is intended to hold a pointer with p_, so the name p_str means "pass a pointer to a string." In many cases, I will prefix the name of a predefined string (in a dat section) with s_ -- this is just a reminder to me that the object is a string and needs @ to determine its run-time address. So, you might see something like this in my programs:

        if (condition)
          term.str(@s_Warning)
    
Sign In or Register to comment.