Shop OBEX P1 Docs P2 Docs Learn Events
Using UDEC_xxx/ UBIN_xxx / UHEX_xxx in Spin2 — Parallax Forums

Using UDEC_xxx/ UBIN_xxx / UHEX_xxx in Spin2

PropGuy2PropGuy2 Posts: 360
edited 2020-08-12 00:21 in Propeller 2
The new DEBUG in the SPIN2 Documentation lists several useful functions for Decimal to Binary to Hex conversions. Is there a link or routine or other??? to allow these functions can be directly used with SPIN2 via the VGA / SEND commands?
363 x 186 - 32K

Comments

  • cgraceycgracey Posts: 14,155
    These are just part of the DEBUG system and can't be used outside of it. I could add such functions to Spin2. They would always take space, in that case, though.

    For the DEBUG system, all related code and data get tucked into the too 16KB of RAM and then write-protected from non-debug access.
  • That would be great - Something like
    PUB UDEC_LONG( value )

    but only if the DEBUG code can be easily transposed into SPIN2 functions, and regardless of memory space.
    and bundling it like the original SimpleNumbers.spin of P1 fame
  • JonnyMacJonnyMac Posts: 9,104
    edited 2020-08-12 15:52
    The current interpreter completely fills the cog and LUT RAM, so it may be tricky to add features. With unsigned modulus and divide operators, its easy enough to write a method to convert any value to a string. My jm_nstr.spin2 object does everything accept unsigned decimal, so I'm going to add this to it.
    dat
    
      nbuf          byte      0[16]
    
    
    pub udec(value, digits) : p_str | len, d
    
      bytefill(@nbuf, 0, 16)                                        ' clear buffer
      p_str := @nbuf                                                ' point to buffer
    
      digits := 0 #> digits <# 10                                   ' fix digits
    
      len := 0
    
      repeat
        d := value +// 10                                           ' get a digit
        byte[p_str++] := d + "0"                                    ' convert to ASCII
        value +/= 10                                                ' remove digit from value
        if (digits)                                                 ' specified length?
          if (++len == digits)                                      '  done?
            quit
        else
          if (value == 0)                                           ' done?
            quit    
      
      byte[p_str++] := 0                                            ' terminate the string
    
      return revstr(@nbuf)
    
    
    pub revstr(p_str) : result | first, len, last
    
    '' Reverse the order of characters in a string.
    
      result := first := p_str                                      ' start
      len := strsize(p_str)                                         ' length
      last := first + len - 1                                       ' end
    
      repeat (len >> 1)                                             ' reverse them
        byte[first++], byte[last--] := byte[last], byte[first]
    

    If you have a .str() method, you can do this:
      serial.str(udec(somevalue, 0))
    
    The second parameter is the number of digits to print; 0 is auto width, 1..10 limits the width (and may truncate).
  • Thanks Jon - that's some strong work!
  • Here's a slightly different approach that uses SEND directly. The ers_fmt and SmartSerial objects are attached, a demo is:
    CON
      _clkfreq = 180_000_000
      
    OBJ
      f: "ers_fmt"
      ser: "SmartSerial"
    
    PUB demo(): i
      ser.start(115_200)  ' pick your baud rate
      send := @ser.tx     ' set function to send one character
      send("hello", f.nl())
      repeat i from -2 to 3
        send("i=", f.dec(i), " hex=", f.hexn(i, 2), " unsigned=", f.unsdec(i), f.nl())
    
  • roglohrogloh Posts: 5,787
    edited 2020-08-15 07:49
    That's a pretty clean way to do it Eric. Nice one.

    Another extension I just thought of when looking at this might be if the send function was itself also format aware as it outputs characters, we could possibly use that to support format control string portions like this for C programmer types... it's somewhat of a hybrid approach compared to normal printf, working by splitting into multiple format strings. e.g.
    send("i=%d", i, " hex=%2x", i, " unsigned=%u", i, "\n")
    

    The sending function would need to look out for the % escape or \ chars and process the next item accordingly, but you could only do one escape per string at the end of it and no other characters could follow that until the next one. In the past I've tried to get send to take a single format string at the start but you do run into issues capturing the full string before all the data and also differentiating the chars from integers etc. Downside with the above is that the sender would need its own formatter object/capabilities (or its own send redirect control to other senders) which could add code overhead. Your method is clean and easily extended to other formats (if somewhat more verbose), and I like it. Maybe unsdec could be renamed udec, which reads better (to me anyway). EDIT: oops that name would clash with Chip's use above.

    By the way I tested the maximum number of send arguments, it seems to be limited to 15 in the version of FastSpin I had.


  • I also wonder if send and a custom formatter can be used for to gain some type of rudimentary sprintf capability. We could have a simple string generation object based on Eric's formatter that takes a pointer to where it needs to write to next, an optional limit, and returns the address of its own sending function which then accumulates the bytes into the string, as called by send(). It would just need to maintain its own pointer/limit context per COGID and restart whenever character 0 is printed or a new string is setup with sprint, and optionally stop output once the limit is reached, until a new string is setup perhaps.

    This could be handy to construct dynamic strings without lots of extra baggage to code up on the calling side, though being invoked per character it could be slower to run vs directly using other fixed strings and mem copies etc.
    OBJ
       fmt: "stringfmt"
    
    PUB demo() : x
       x:=20
       send := fmt.sprint(@strbuf1, 100) ' returns address of string printing format routine for send
       send("hello, x= ", fmt.dec(x), 0)
    
       send := fmt.sprint(@strbuf2, 50)
       send(fmt.hex(20), 0)
    
    DAT 
    strbuf1 long 0[100]
    strbuf2 long 0[50]
    
    
  • My second idea above didn't work without extra changes because the format code returns 0 after printing which also gets written into the string. @ersmith you may find you are sending nulls out the serial port with your code as a side effect.

    I found I could eliminate it if I filtered the tx routine to ignore values outside ascii range from 0-255 and just return -1 from each format routine. Then when this return value is also passed through to send() it will filter the -1. It's working now, files attached.
  • rogloh wrote: »
    Another extension I just thought of when looking at this might be if the send function was itself also format aware as it outputs characters, we could possibly use that to support format control string portions like this for C programmer types.
    That's an interesting idea. It could be done with a kind of double indirection, something like:
      send := fmt.printfsend(@ser.tx)
    
    where the fmt.printfsend would itself have to call the original method pointer. Not sure if it's worth it though.
    By the way I tested the maximum number of send arguments, it seems to be limited to 15 in the version of FastSpin I had.

    I'm not seeing that in 4.3.0.
    rogloh wrote: »
    I also wonder if send and a custom formatter can be used for to gain some type of rudimentary sprintf capability. We could have a simple string generation object based on Eric's formatter that takes a pointer to where it needs to write to next, an optional limit, and returns the address of its own sending function which then accumulates the bytes into the string, as called by send().
    I'd do that by having a separate string buffer class that appends characters to the string, and then using the original formatting class with SEND set to the "addchar" method. Something like:
    OBJ
       fmt: "ers_fmt"
       str:  "stringbuf"
    
    PUB demo() : x
       ' initialize string buffer with 100 characters
       str.init(@strbuf1, 100)
       send := @str.addchar
       x:=20
       send("hello, x= ", fmt.dec(x), 0)
    
    DAT 
    strbuf1 long 0[100]
    
    rogloh wrote: »
    My second idea above didn't work without extra changes because the format code returns 0 after printing which also gets written into the string. @ersmith you may find you are sending nulls out the serial port with your code as a side effect.
    That's a fastspin bug, it doesn't happen in PNut :(. SEND() in fastspin isn't ignoring the dummy return value of 0 for void functions. That was working at one time, but it looks like I broke it. It's fixed again in github now.
  • SEND() in fastspin isn't ignoring the dummy return value of 0 for void functions. That was working at one time, but it looks like I broke it. It's fixed again in github now.
    Good, thanks Eric. Glad it is fixed so we don't have to hack the format stuff to use special return codes. Separating string printing stuff into another independent string related object should work cleanly with SEND fixed now so those extra NULLs don't make it into the string prematurely.

    Actually I think having a good string manipulation library or a nice suite of methods for P2 would be good, particularly once dead code removal is working (I know Chip wants to add that add some point to PNut SPIN2 so hopefully we can count on this). There really isn't much string processing inherently built into SPIN2 apart from strsize(), and strcomp() so it sort of needs to be added by each user right now.

    Using some small amounts of inline PASM2 could work out reasonably well for special character searching or other token parsing on the P2, especially in the interpreted version. The sort of things available in <string.h> for example. This could help things like file parsers and other text processing applications work at higher speed on the P2.
  • Cluso99Cluso99 Posts: 18,069
    I converted Kye’s P1 string handling in spin to spin2. I’ll post it in the next day or so.
  • This formatting feature with SEND is really great one Eric and was a missing link to help me port my driver code (which now works on PNut and Fastspin) along with your new SmartSerial code that also now compiles in PNut. I had previously written some test stuff using your older SmartSerial which had a printf capability with variable args but that was not available in PNut and until now it has held me back getting things tested out better with PNut.

    Using this approach my test programs and drivers can now work with both Fastspin and PNut tools and now I should be able to directly send formatted strings/numeric stuff into my video buffer region(s) or serial port using SEND and your format object. It's very cool now, thanks so much for this! :smiley:
  • I'm glad you found it useful, Roger! Thanks for the feedback.
Sign In or Register to comment.