Shop OBEX P1 Docs P2 Docs Learn Events
PNut/Spin2 Latest Version (v52 - New MOVBYTS(), ENDIANL(), ENDIANW(), NEXT/QUIT <level>, DEBUG end) - Page 76 — Parallax Forums

PNut/Spin2 Latest Version (v52 - New MOVBYTS(), ENDIANL(), ENDIANW(), NEXT/QUIT <level>, DEBUG end)

1707172737476»

Comments

  • RaymanRayman Posts: 15,748
    edited 2025-08-18 22:19

    Said flash loader could also attempt to load from uSD, if desired, right?
    Could be useful if uSD is on different pins and/or has a power switch.

    Also, could maybe do something fancy like look for a backup boot file if the first one is missing...

    Also, guess this could allow for long file names and directories...

    4-pin uSD booting should be faster. Wonder if that'd make a difference...

  • Wuerfel_21Wuerfel_21 Posts: 5,657
    edited 2025-08-18 22:38

    @Rayman said:
    Said flash loader could also attempt to load from uSD, if desired, right?
    Could be useful if uSD is on different pins and/or has a power switch.

    Only if you can figure out how to hot-patch the SD boot code to do that - a full SD driver is probably too heavy for the 1024 bytes of bootloader space - just use the stock flash bootloader and then make the SD loader be the payload of that.

    @Rayman said:
    4-pin uSD booting should be faster. Wonder if that'd make a difference...

    You can make the SD boot A LOT faster as-is by making _BOOT_P2.BIX a tiny stub that sets the clock, patches some stuff in the ROM code and then pivots into a different boot file. I had one like that:

    CON _CLKFREQ = 300_000_000
    DAT
                  org 0
                  asmclk
                  '' Patch pullup check
                  wrlong #0,##$fc5b4 ' not sure why it fails but ok
                  '' Move filename into place
                  mov $1DC,name+0
                  mov $1DD,name+1
                  mov $1DE,name+2
                  drvh #38 ' Set LED
                  call #$fc578
                  drvl #38 ' Clear LED if fail
                  jmp #$
    
    name          byte "LOADTEST","BIX",0
    

    (as seen, this tries to load LOADTEST.BIX - also note the Pin 38 LED, change or remove that if necessary)
    Booting a large ~400K executable is a lot faster like that.

    You can also compress the executable you're booting. Flexspin and SpinTools have this option built-in, I also made a standalone version

  • ke4pjwke4pjw Posts: 1,231

    I can confirm this is a "me" problem. I am unsure why it does not boot properly, but I will figure it out. A simple program booted correctly from SD.

  • ke4pjwke4pjw Posts: 1,231

    Well, it's working as it should now. Maybe the card was flakey, but all is well now.

    The good news: No code changes for my code to work with PNUT v51a!

  • evanhevanh Posts: 16,793

    Chip,
    I just bumped into something that has changed in recent releases of Pnut. I don't know if this is an explicit change you intended or not. The following compiled and ran when I was compiling with Pnut v46:

        send( "SPI cmode: CPOL=", CPOL + "0", " CPHA=", CPHA + "0", 13,10 )
    

    But now, with Pnut v51a, I need to add an extra set of brackets as per the following:

        send( "SPI cmode: CPOL=", (CPOL + "0"), " CPHA=", (CPHA + "0"), 13,10 )
    

    Otherwise I basically get a syntax error. Pnut v51a reports a missing comma or closing bracket without the edit.

  • cgraceycgracey Posts: 14,286
    edited 2025-10-09 03:54

    There is a new version (v52) of PNut.exe at the top of this thread.

    v52 - 2025.10.08

    • New MOVBYTS(), ENDIANL(), ENDIANW() methods.

    • NEXT/QUIT now allowed to select nesting level of operation.

    • DEBUG(DEBUG_END_SESSION) now ends DEBUG session and exits PNut if -rd was used on command line.

    • New DEBUG Display TERM color commands for added simplicity.

  • @cgracey said:
    There is a new version (v52) of PNut.exe at the top of this thread.

    v52 - 2025.10.08

    • NEXT/QUIT now allowed to select nesting level of operation.

    Blimey! That arrived almost instantly! :smiley:

  • maccamacca Posts: 938
    edited 2025-10-09 10:03

    @cgracey said:

    • NEXT/QUIT now allowed to select nesting level of operation.

    Seems something is broken, I got an error on a simple if/else block:

    Can be reproduced also with the parser.spin2 (and other sources in the PNut package) example.

    Am i missing something ?

    Edit: worst than expected, seems that nothing can be added after a quit statement:

  • @macca Is a number now compulsory ? Does it work with "quit 0" or "quit 1" ?

    (Sure, that would be silly, but just an interesting test / workaround. I'm not able to try it at the moment).

  • maccamacca Posts: 938

    @VonSzarvas said:
    @macca Is a number now compulsory ? Does it work with "quit 0" or "quit 1" ?

    (Sure, that would be silly, but just an interesting test / workaround. I'm not able to try it at the moment).

    Ah, yes, with quit 1 it works (quit 0 is not valid since must be 1 to 16).

  • Oh goody. Might just need the default case adding back into the compiler.

    @cgracey
    May we have an override for "quit" and "next" without a parameter to behave like they used to, equivalent to "quit 1" and "next 1" ?

  • cgraceycgracey Posts: 14,286
    edited 2025-10-09 14:08

    Sorry, Guys!

    I see the problem. It's in my parsing of what comes after NEXT or QUIT. I need to back up when there is a line end, indicating there will be no constant.

    I will have this straightened up in a few hours.

  • cgraceycgracey Posts: 14,286
    edited 2025-10-10 00:09

    The new v52 is now posted on the OBEX and on Github.

    https://obex.parallax.com/obex/pnut-spin2-latest-version/

    NEXT and QUIT do not require integers after them, but an integer is needed if you want to NEXT/QUIT from outside your current REPEAT block. That integer would need to be a value of 2 or more.

  • cgraceycgracey Posts: 14,286
    edited 2025-10-10 00:14

    I added a new file into the .zip called PointerFlexibility.spin2. It gives coding examples of how you can use byte/word/long pointers. The same basic rules apply to structure pointers, as well.

    Here is that file, in case anyone wants to get a quick tour of pointers:

    {Spin2_v52}
    
    pub go() | ^byte p, byte buff[20]       'declare a byte pointer and a 20-byte buffer
    
      [p] := @buff                          'point the pointer to the buffer
    
      p[++].byte := $50                     'write a byte into the buffer, step pointer
      p[++].long := $77777777               'write a long, step pointer
      p[++].word := endianw($BEEF)          'write a big-endian word, step pointer
      p[++].byte := $01                     'write a byte, step pointer (.byte is for clarity, not needed)
      p[++].byte := $02                     'write a byte, step pointer
      p[++].byte := $03                     'write a byte, step pointer
      p[++].long := $FFFFFFFF               'write a long, step pointer
    
      debug(uhex_byte_array_(@buff,20))     'show the buffer
    
      [--]p.word := $5555                   'back up and write a word
      [++]p.word := $AAAA                   'move ahead and write a word
    
      debug(uhex_byte_array_(@buff,20))     'show the buffer
    
      p := $22                              'write a byte
      p[-1] := $33                          'write a byte at [-1]
    
      debug(uhex_byte_array_(@buff,20))     'show the buffer
    
      p++                                   'inc byte
      p[1]--                                'dec byte at [1]
    
      debug(uhex_byte_array_(@buff,20))     'show the buffer
    
      p~~                                   'set byte
      [--]p.long[-1].[31..28]++             'back up long pointer, increment top nibble of long at [-1]
    
      debug(uhex_byte_array_(@buff,20))     'show the buffer
    
    
    '
    ' Pointers are longs and they can be read/modified/written by putting brackets around them:
    '
    ' [p]                   'the actual pointer value (not the data being pointed to)
    ' @[p]                  'the address of the actual pointer value
    '
    '
    ' Once assigned, pointers can be used to read/modify/write the data they point to in
    ' these root syntax forms:
    '
    ' p                     'r/m/w the data being pointed to
    ' [++]p                 'pre-inc pointer by size, then r/m/w the data being pointed to
    ' [--]p                 'pre-dec pointer by size, then r/m/w the data being pointed to
    ' p[++]                 'r/m/w the data being pointed to, then post-inc pointer by size 
    ' p[--]                 'r/m/w the data being pointed to, then post-dec pointer by size 
    '
    '
    ' Any of these root syntax forms can be appended with an optional size override,
    ' an optional index, and an optional bitfield, in that order.
    '
    ' An optional size override starts with a period followed by BYTE/WORD/LONG:
    '
    '       .BYTE           'sets the data size to byte, regardless of the pointer data size
    '       .WORD           'sets the data size to word, regardless of the pointer data size
    '       .LONG           'sets the data size to long, regardless of the pointer data size
    '
    ' ...followed by an optional index in brackets:
    '
    '       [index]         'index gets scaled for byte/word/long addressing
    ' 
    ' ...followed by an optional bitfield, which is a period followed by a bitfield in brackets:
    '
    '       .[bitfield]     'selects a portion of the byte/word/long to read/modify/write
    '
    
  • VonSzarvasVonSzarvas Posts: 3,615
    edited 2025-10-10 05:01

    @cgracey said:
    The new v52 is now posted on the OBEX and on Github.

    https://obex.parallax.com/obex/pnut-spin2-latest-version/

    NEXT and QUIT do not require integers after them, but an integer is needed if you want to NEXT/QUIT from outside your current REPEAT block. That integer would need to be a value of 2 or more.

    .

    NEXT and QUIT can each be followed by an integer value 1..16 to alter the nesting level at which they are to occur. If no integer value is expressed, the default value of 1 is used, which means the current nesting level, The value 2 would mean the outer nesting level, while three would mean the next outer nesting level, and so on.

    What is the intended behavior when the nesting level integer exceeded the number of levels?
    For example, would QUIT maxLevel+1 be the same as QUIT maxLevel ? (Or should that throw a compiler error- or warning at least?...)

  • cgraceycgracey Posts: 14,286
    edited 2025-10-10 06:29

    The compiler will error if you try to go too many levels out. It knows the REPEAT nesting depth.

    I have a question:

    Rather than using integers 1..16 to specify level, might 0..15 be better? It would go like this:

    0 = This REPEAT block, no change in level (the default for NEXT/QUIT)
    1 = The first-outer REPEAT block
    2 = The second-outer REPEAT block

    Would that be better? If I change it, I should do it soon, before any serious code gets written by anyone.

    That feels a lot better to me. The way it is has been bugging me.

  • The languages I've used this with before, PHP and maybe Bash, both use 1-indexed. Personally I wouldn't change it. It's done; move on and conquer!

    Really it's just a human language issue - how we explain the feature in docs could be worded to make either way seem like the most natural.

  • maccamacca Posts: 938

    @cgracey said:
    Rather than using integers 1..16 to specify level, might 0..15 be better? It would go like this:

    0 = This REPEAT block, no change in level (the default for NEXT/QUIT)
    1 = The first-outer REPEAT block
    2 = The second-outer REPEAT block

    Would that be better? If I change it, I should do it soon, before any serious code gets written by anyone.

    Yes, I think it would be better since it represents the number of levels to go back, also may I suggest to use (or allow) negative numbers ? I think that quit -n better represents quit back n level(s).

    Even better would be to have some kind of label to clearly indicate what repeat level is referred, not sure what a good syntax may be, something like repeat : label maybe.

    Also, can you check if the following snippet generates the correct bytecode ?

    {Spin2_v52}
    PUB main() | a, b, c
    
        repeat a from b to c
            quit
            next
            repeat a from b to c
                quit 2
                next 2
            a++
    

    I had just a quick look and probably I miss something, but seems to me that the inner next 2 statement doesn't pop the inner loop stack, quit 2 correctly pops both the inner and outer stacks. Today I'm a bit busy so I may not be able to look at this with more details.

    Thanks.

  • @macca said:
    Even better would be to have some kind of label to clearly indicate what repeat level is referred, not sure what a good syntax may be, something like repeat : label maybe.

    Yes, named breaks are good because you can't get the number wrong and can't misread it. Languages like Rust do it this way.

  • maccamacca Posts: 938
    edited 2025-10-11 08:11

    I looked into the next level issue and I think there is an issue.

    With this example:

    {Spin2_v52}
    CON
    
        _CLKFREQ = 160_000_000
    
    PUB main() | a, b, c, d, e, f
    
        b := 1
        c := 5
        debug("start")
    
        repeat a from b to c
            e := 20
            f := 25
            repeat d from e to f
                if d == 23
                    next 2
                debug("     ", udec(a, d))
    
        debug("end  ", udec(a, d))
    
        repeat
    

    PNut outputs this:

    My implementation outputs this:

    Cog0  INIT $0000_0000 $0000_0000 load
    Cog0  INIT $0000_0F64 $0000_1888 jump
    Cog0  start
    Cog0       a = 1, d = 20
    Cog0       a = 1, d = 21
    Cog0       a = 1, d = 22
    Cog0       a = 2, d = 20
    Cog0       a = 2, d = 21
    Cog0       a = 2, d = 22
    Cog0       a = 3, d = 20
    Cog0       a = 3, d = 21
    Cog0       a = 3, d = 22
    Cog0       a = 4, d = 20
    Cog0       a = 4, d = 21
    Cog0       a = 4, d = 22
    Cog0       a = 5, d = 20
    Cog0       a = 5, d = 21
    Cog0       a = 5, d = 22
    Cog0  end  a = 6, d = 23
    

    If I'm not missing something, the latter is correct, it should be equivalent to use quit in the inner loop and indeed PNut's output is correct when using quit.

    Looking at the listing, PNut doesn't pop the inner loop stack with the next statement (the comments are the bytecode from PNut):

    ' Object "NextQuit.spin2" header (var size 4)
    01844 00000       08 00 00 80    Method main @ $00008 (0 parameters, 0 returns)
    01848 00004       30 00 00 00    End
    ' PUB main() | a, b, c, d, e, f
    0184C 00008       06             (stack size)
    '     b := 1
    0184D 00009       A2             CONSTANT (1)                           ' A2
    0184E 0000A       F1             VAR_WRITE LONG DBASE+$00001 (short)    ' F1
    '     c := 5
    0184F 0000B       A6             CONSTANT (5)                           ' A6
    01850 0000C       F2             VAR_WRITE LONG DBASE+$00002 (short)    ' F2
    '     debug("start")
    '     repeat a from b to c
    01851 0000D       42 13          ADDRESS ($00013)                       ' 42 13
    01853 0000F       E2             VAR_READ LONG DBASE+$00002 (short)     ' E2
    01854 00010       E1             VAR_READ LONG DBASE+$00001 (short)     ' E1
    01855 00011       D0             VAR_SETUP LONG DBASE+$00000 (short)    ' D0
    01856 00012       7B             REPEAT                                 ' 7B
    '         e := 20
    01857 00013       42 14          CONSTANT (20)                          ' 42 14
    01859 00015       F4             VAR_WRITE LONG DBASE+$00004 (short)    ' F4
    '         f := 25
    0185A 00016       42 19          CONSTANT (25)                          ' 42 19
    0185C 00018       F5             VAR_WRITE LONG DBASE+$00005 (short)    ' F5
    '         repeat d from e to f
    0185D 00019       42 1F          ADDRESS ($0001F)                       ' 42 1F
    0185F 0001B       E5             VAR_READ LONG DBASE+$00005 (short)     ' E5
    01860 0001C       E4             VAR_READ LONG DBASE+$00004 (short)     ' E4
    01861 0001D       D3             VAR_SETUP LONG DBASE+$00003 (short)    ' D3
    01862 0001E       7B             REPEAT                                 ' 7B
    '             if d == 23
    01863 0001F       E3             VAR_READ LONG DBASE+$00003 (short)     ' E3
    01864 00020       42 17          CONSTANT (23)                          ' 42 17
    01866 00022       70             EQUAL                                  ' 70
    01867 00023       13 05          JZ $00029 (5)                          ' 13 03
    '                 next 2
    01869 00025       18 0C          POP 16                                 ' (missing)
    0186B 00027       12 03          JMP $0002B (3)                         ' 12 03
    '             debug("     ", udec(a, d))
    0186D 00029       D3             VAR_SETUP LONG DBASE+$00003 (short)    ' D3
    0186E 0002A       7D             REPEAT_LOOP                            ' 7D
    0186F 0002B       D0             VAR_SETUP LONG DBASE+$00000 (short)    ' D0
    01870 0002C       7D             REPEAT_LOOP                            ' 7D
    '     debug("end  ", udec(a, d))
    '     repeat
    01871 0002D       12 7F          JMP $0002D (-1)                        ' 12 7F
    01873 0002F       04             RETURN                                 ' 04
    
  • cgraceycgracey Posts: 14,286

    Macca, I am glad you found this! I am not popping the stack correctly. I will get this fixed tomorrow, hopefully in the morning. Thank you for doing all these tests.

Sign In or Register to comment.