@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
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:
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.
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.
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
'
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?...)
@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.
Comments
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...
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.
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:
(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
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.
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!
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:
But now, with Pnut v51a, I need to add an extra set of brackets as per the following:
Otherwise I basically get a syntax error. Pnut v51a reports a missing comma or closing bracket without the edit.
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.
Blimey! That arrived almost instantly!
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).
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" ?
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.
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.
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:
.
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?...)
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.
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 ?
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.