Shop OBEX P1 Docs P2 Docs Learn Events
Bugs in "Parallax Serial Terminal" object — Parallax Forums

Bugs in "Parallax Serial Terminal" object

Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
edited 2012-02-20 10:21 in Propeller 1
The hex method in "Parallax Serial Terminal" has a bug:
PUB Hex(value, digits)

  {{ Send value as hexadecimal characters up to digits in length.
  ''
  '' `Parameters:
  ''
  ''     `value: byte, word, or long value to send as hexadecimal characters.
  ''     `digits: number of hexadecimal digits to send.  Will be zero padded if necessary.
  ''
  '' `Return: none.
  ''
  '' `Example: pst.Hex(1234, 5)
  ''
  ''     Output decimal 1234 as five hex digits. Outputs `004D2.
  ''     Next lines, if needed...
  }}

  value <<= (8 - digits) << 2
  repeat digits
    Char(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))
When digits is greater than 8, too much gets shifted out in the value <<= (8 - digits) << 2 statement, and what remains cycles through the rotate in the repeat loop more than once.

The following correction fixes the problem:
PUB Hex(value, digits)

  {{ Send value as hexadecimal characters up to digits in length.
  ''
  '' `Parameters:
  ''
  ''     `value: byte, word, or long value to send as hexadecimal characters.
  ''     `digits: number of hexadecimal digits to send.  Will be zero padded if necessary.
  ''
  '' `Return: none.
  ''
  '' `Example: pst.Hex(1234, 5)
  ''
  ''     Output decimal 1234 as five hex digits. Outputs `004D2.
  ''     Next lines, if needed...
  }}

  if (digits > 8)
    repeat digits - 8
      Char("0")
    digits := 8
  else
    value <<= (8 - digits) << 2
  repeat digits
    Char(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))

The bin method is similarly afflicted. A corrected version of the object is attached.

IMO, methods like dec, hex, and bin do not even belong in I/O objects. Programmers should use conversion routines, such as those found in SimpleNumbers, in conjunction with the I/O objects' str methods.

-Phil
«1

Comments

  • Ron CzapalaRon Czapala Posts: 2,418
    edited 2012-02-17 15:31
    Thanks! Is someone going to fix it in OBEX?

    http://obex.parallax.com/objects/457/
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-17 15:48
    I'll send a note to Jeff Martin.

    -Phil
  • idbruceidbruce Posts: 6,197
    edited 2012-02-17 15:52
    IMO, methods like dec, hex, and bin do not even belong in I/O objects. Programmers should use conversion routines, such as those found in SimpleNumbers, in conjunction with the I/O objects' str methods.

    Makes perfect sense to me.
  • Cluso99Cluso99 Posts: 18,069
    edited 2012-02-17 21:02
    IMO, methods like dec, hex, and bin do not even belong in I/O objects. Programmers should use conversion routines, such as those found in SimpleNumbers, in conjunction with the I/O objects' str methods.
    -Phil

    I could not agree more. I should get into the habit of using SimpleNumbers :)

    Then having PST, FDX, TV & VGA have common calling names such as xx.out(ch) and xx.str(xxxx) and ch:=xx.in makes a lot more sense.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-17 21:33
    Cluso99 wrote:
    Then having PST, FDX, TV & VGA have common calling names such as xx.out(ch) and xx.str(xxxx) and ch:=xx.in makes a lot more sense.
    I absolutely agree. Moreover, if the object has both input and output methods, the output methods should have names that are complementary to the input methods. IOW, if the character input routine is in of inp, the character output routine should be out, not char or tx.

    -Phil
  • average joeaverage joe Posts: 795
    edited 2012-02-17 23:46
    I absolutely agree.
    -Phil
    I agree too, but I think the nature of PST is enriched by having these available. I love PST because it requires no work to pass any type of data in and out of objects. This comes in real handy for me when debugging. Interesting to know that bug was in there all along.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2012-02-18 00:04
    Phil, isn't it more a trap than a bug, because it was evidently written to display the number of hex digits in a long and no more? The issue of the extra cycles could alternatively be handled with a limit maximum:

    value <<= (8 - (digits <# 8)) << 2

    If it is a bug, it extends back to Full Duplex Serial, where it is the same. SimpleNumbers deals with the issue by limiting the number of digits in the string that is returned,
    digits := 1 #> digits <# 8
    although I question why it needs to disallow a null string.

    I agree about giving the format methods their own object. It took a while for me to reach that conclusion for myself, as my beginning inclination was to turn every object into it's own swiss army knife. Better now I think, to look on the whole OBJ section as the swiss army knife and I get to select which blades to include .
  • average joeaverage joe Posts: 795
    edited 2012-02-18 00:09
    Better now I think, to look on the whole OBJ section as the swiss army knife and you get to select which blades to include .
    If we had more people that coded like this, I think our community would be light-years ahead of where we are now.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-18 01:26
    Phil, isn't it more a trap than a bug, because it was evidently written to display the number of hex digits in a long and no more?
    It's not unreasonable to expect such a method to pad with leading zeroes when extra digits are called for, especially when it can be accomplished so easily. So, yes, "bug" is the appropriate term when it not only does not do the padding but also messes up the non-zero digits. If nothing else, the principle of "least surprise" should rule in such cases.
    If it is a bug, it extends back to Full Duplex Serial, where it is the same.
    I suspected that the other objects from which PST was likely derived had the same bug and alerted Jeff to that possibility in my email. 'Probably ought to check "TV_Text", as well.
    SimpleNumbers deals with the issue by limiting the number of digits in the string that is returned ...
    "SimpleNumbers" needs a complete revamp. I've begun the process with my "SimpleNumbersPlus", but I have more work to do.

    -Phil
  • RaymanRayman Posts: 14,845
    edited 2012-02-18 08:36
    Is this really a bug? Why would anyone ask for >8 digits of a 32 bit number?
  • SRLMSRLM Posts: 5,045
    edited 2012-02-18 08:38
    Parallax needs a standard way of reporting bugs, like proper open source communities have. For example, the PropGCC issues page or the Ubuntu bug system (sample bug here). This ad-hoc method of posting on the forum and emailing people might work for the most dedicated debuggers (PhiPi), but the visiting expert will move on or just fix it himself. Plus, the bug tracking system allows effective management of defects.
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-02-18 08:53
    I would not consider this being a bug as well. We still talk about mikrocontroller programming and people should know about the "borders" of a long. I'd always prefere to have the code being coded as efficient as possible rather than as foolproove as possible ... Maybe both should be available?!

    If you want an extra leading zero, there is no problem calling something like PST.tx("0") before.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2012-02-18 09:18
    One could equally expect it to be padded with spaces, or maybe elipsis or what have you, instead of zeros, that being for display right justified in columns. I prefer that number methods allow the options, either exactly the number of digits required, or fixed width with selectable padding on the left, and of course for scientific work the inclusion of the radix point or floating point notation where called for. Jeff Martin's original Numbers v1.1 is an amazing piece of condensed code, but it takes a lot of concentration to figure out the proper format code to get what you want. I guess that is what led to SimpleNumbers.

    I can agree with the principle of "least surprise", but tempered with "why that?", "caveat emptor" and "KISS".
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-18 09:24
    Rayman wrote:
    Is this really a bug? Why would anyone ask for >8 digits of a 32 bit number?
    For alignment purposes, perhaps, or as filler to satisfy the needs of some communication protocol. More to the point, though, why second-guess the users' intents when it's so easy to give them what they want or expect?
    MagIO2 wrote:
    I'd always prefere to have the code being coded as efficient as possible...
    ... which is an argument for not including conversion methods in I/O objects in the first place. But, as long as they're there, 'might as well do it right.

    -Phil
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-02-18 11:15
    Ok .. what I talked about was the internals of functions ... these should not waste clock-cycles for checks that make the function error-tolerant for mistakes you as a programmer simply should not do. From my point of view in this case it is more desirable to point out those race-conditions in the description of the function.

    If we now talk about objects I totally agree - it's not good to have a hex-function in each and every object dealing with byte-stream-interfaces. You can easily end up with code using several objects where say 3 objects have their own copy of a hex-function. Maybe this criteria should also be pointed out in the gold-standard object collection - did not fully read the requirements, so possibly it's already mentioned there?!

    Brads compiler helps in this case - well ... at least if you do not use all 3 hex-functions.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-18 11:22
    MagIO2 wrote:
    these should not waste clock-cycles for checks that make the function error-tolerant for mistakes you as a programmer simply should not do.
    Why would you assume that specifying an extra-large field width is a mistake? It simply does not fall into the same category as accessing an array out of bounds. (I can't believe we're even having this discussion.)

    -Phil
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-02-18 14:22
    The hex function is meant to convert one long into a hex string. So, 8 characters is the max. for that! With the same reasoning you use above, extras like leading zeros should not be coded inside of this function. Simply wrap another function around it which adds the leading zeros.

    Maybe someone else needs leading spaces ... someone wants trailing spaces ... how complex should hex be to implement all special wishes?

    So, again ... for me the current implementatin is not a bug, it's doing what you can expect from it. Of course each individual can add an unlimited amount of expectations.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-18 14:31
    So would you then also say that it's okay for C's printf output to choke and print garbage when, say "%24.24X" is specified? If it did, I think everyone would agree that there's a bug in the printf code. Why should Spin objects get off any easier?

    -Phil
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-02-18 15:26
    The question here is even more basic! The question is: Is it OK to have functions which only operate properly when the input parameters are in a defined range even if this range is not checked inside of the function.

    My answer is YES.

    Is a serial driver checking the baud-rate? Is a LCD driver connected to a 16 character display checking that a string is not longer than 16 characters? Is the waitcnt() checking whether your delta t is to little? Is an EEPROM complaining if you write beyond the boundaries?

    NO

    It's the expectation that the programmer knows or finds out what boundaries exist. That's why I say that this fact should be documented properly. And if you want to use this kind of functions in a sensible environment you can easily wrap it in a checker function which first validates the input.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-18 15:45
    You didn't answer my question. But it's obvious that we're at an impasse anyway, so let's just agree to disagree.

    Hopefully my argument will prevail with the object's authors, and one of them will resubmit the object with my suggested fix or something equivalent. If your argument prevails, there should still be an explanation in the comments that the output is screwed up for field widths greater than 8 (32 for bin). (I would be embarrassed to have to write such a comment, though, instead of fixing the problem. :) )

    -Phil
  • AribaAriba Posts: 2,690
    edited 2012-02-18 18:22
    So would you then also say that it's okay for C's printf output to choke and print garbage when, say "%24.24X" is specified? If it did, I think everyone would agree that there's a bug in the printf code. Why should Spin objects get off any easier?

    -Phil

    Perhaps because printf fills the whole HubRAM in propGCC if you not use a downsized version, and I would not like to see something like that in Spin.

    For me programming on the Propeller is all about: Keep it short and simple, so you can fit as much as possible into the limited RAM. So I'm really with MagIO and Rayman on this. Why double the code size of the hex methode for something that 99.9995% of the users never expect or need.(The old hex methode needs 39 bytes, your new version takes 63 bytes).

    But anyway, for the 0.0005% that wants more than 8 hex digits to show a 32bit value, here is a version which is only 38 bytes:
    pub hex(value,digits) : c
      repeat
        if digits < 9
          if (c := (value <-= 4) & $F) > 9
            c += 7
        out(c + "0")
      until --digits =< 0
    


    And an improved version, which also lets you define the padding character and grouping of digits with an underscore as separater (needs 54 bytes):
    pub hex(value,digits,chr,group)
      chr -= "0"
      repeat
        if digits < 9
          if (chr := (value <-= 4) & $F) > 9
            chr += 7
        if digits//group == 0
          out("_")
        out(chr + "0")
      until --digits =< 0
    
    The chr parameter defines the padding character, the group parameter defines how many digits to group (normally 2 or 4)

    And to make the disgreement complete: I really like the dec, hex, bin methodes in display objects. I don't want to know how many code gets added if you have to include a numbers object (especially if unused methodes are not optimized out). And then you need to write every call more complicated, something like: ser.str(num.dec(value)) instead of ser.dec(value). Not to forget that these are always 2 calls, which needs more space and is slower.

    And to finish this post: Normally I agree with nearly every post of Phil :smile:

    Andy
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-18 18:37
    Andy, I approve of, and defer to, your first solution. It's an excellent contribution to this thread (better and smaller: who says we can't have everything?) that should silence any naysayers once and for all. :)

    Thank you!
    -Phil
  • Cluso99Cluso99 Posts: 18,069
    edited 2012-02-18 19:13
    Once we get conditional compilation, we can have it both ways. And I mean, the I/O objects can have it conditionally compiled and have the option of using the SimpleNumbers object instead.
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-02-19 05:45
    Sorry for that, but the naysayer still says NAY ;o)

    As Ariba says, the original version is good for 99.9995% of the users, so why add size and/or runtime for 0.0005%?

    For objects you say: Keep it simple, but for the code of a single function you don't accept the same rule. It's easier to add things in wrapper functions when needed, than search for the cause of performance or memory problems in a complex program using tons of objects and remove that bottlenecks afterwards.

    I see why it is good for you to change this function! But maybe this whole discussion shows that we need a function-exchange which allows to check in functions with different optimizations. This way everybody can pick the right version. This could be the next level for a Propeller Tool. You'd have code completion based on the checked in functions. When using the function hex for the first time the tool will ask which version to use a) the fast b) the foolproof c) the one with leading characters d) the one which allows to convert a long-array ....
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-02-19 06:01
    Oh ... and to answer your question about printf ...
    I think you compare apples with oranges! The printf is part of the standard library of C which has to run in all possible environments - 8bit, 16bit, 32 bit and nowadays 64 bit processors. It's simply a need of C to allow any number of bits, otherwise the standard library needs maintenance with each new CPU architecture.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-19 10:13
    MagIO2 wrote:
    As Ariba says, the original version is good for 99.9995% of the users, so why add size and/or runtime for 0.0005%?
    You may have missed the fact that Ariba's code, which fixes the bug, is smaller than the buggy code. You can keep firing it you like, but your gun is empty. :)

    -Phil
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2012-02-19 12:59
    Arriba, that's neat. Even shorter (34 bytes) and within the intent of the original is,
    PUB arriba1(value, digits) : c
      repeat digits <# 8
        if (c := (value <-= 4) & $F) > 9
          c += 7
        tx(c + "0")
    

    Phil, I'm not trying to joust with you on a high horse, much less with an empty gun! Yes, correct the funny repeat for someone who might fall into the trap of trying to send more than 8 digits, but really, should it be at the top of the list for Jeff, Andy and Chip?

    The intent of the original was clearly limited to 8 bytes, and I think limiting it to 8 would easily qualify for "gold standard". Can you give a >1% justification for extending it beyond 8, even if it takes only 4 more bytes of code?

    Full Duplex Serial is the one (and only) that is currently "gold", so it is the one that should be impeccable, but it is the progenitor of that particular trap/bug. And given that PST bears the Parallax name, it too should be on the list for gold standard commissions.
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-02-19 13:18
    pub hex(value,digits) : c
      repeat
        if (c := (value <-= 4) & $F) > 9
          c += 7
        out(c + "0")
      until --digits =< 0
    
    32 bytes

    ;o)
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-02-19 13:58
    34 bytes
    MagIO2 wrote:
    32 bytes
    I guess there were still a couple rounds left! :) What was intended to be a simple bug report has turned into quite the golf contest. I like it; better code is always a Good Thing.

    But I still contend that if, "I specified 9 digits, so I should get nine correct digits instead of garbage," can be addressed with a mere six extra bytes of code, it should be. Heck, why not? There's a difference between succinct and stingy, after all.

    -Phil
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2012-02-19 14:04
    MagicIO, The post #29 solution has the same issue as the original. When the number of digits is greater than 8 it recycles the original digits, rather than leading zeros.

    Ariba's method and my munz of that in post #28 do have an undesirable catch I think. They send the most sigificant digits, so, for example, if value=$12345678 and digits =5, you get 12345. The original method would send 45678, the least significant digits. If digits=12, you get 000012345678 from Ariba's, as expected. It is not a good idea to change the way the method returns values especially for digits=<8!
Sign In or Register to comment.