Shop OBEX P1 Docs P2 Docs Learn Events
PNut/Spin2 Latest Version (v47 - Cooperative multitasking added to Spin2, up to 32 tasks) - Page 64 — Parallax Forums

PNut/Spin2 Latest Version (v47 - Cooperative multitasking added to Spin2, up to 32 tasks)

1616264666770

Comments

  • cgraceycgracey Posts: 14,206
    edited 2024-11-14 08:27

    This is actually what happens:

    [--]ptr.BYTE[1]

    ...gets compiled as...

    BYTE[--ptrvalue][1]

  • maccamacca Posts: 806

    @cgracey said:
    The new PNut_v45 is done and posted at the top of this thread.

    I have to say, I find the pointer syntax very confusing, and, most important, not consistent with the existing syntax.

    For example, if I understand correctly, the pointer variables are shortcuts for the BYTE/WORD/LONG[var] syntax (where var is an address set with var := @varblock or something), why don't use the brackets to indicate the value pointed by ?

    BYTE ^ptrvar
    
    [ptrvar] shortcut for BYTE[ptrvar]
    [ptrvar++] shortcut for BYTE[ptrvar++]
    [--ptrvar]++ shortcut for BYTE[--ptrvar]++
    

    Seems more clear to me (and I think this is that @rogloh was writing above).

    When I see something like ptrvar := 1 I read that I have assigned 1 at the variable ptrvar, not at the memory it points to, this is higly counter-intuitive.

    Also, now that I think about it: how to assign the pointer to the variable, if the assignments writes to the pointed-to value ? Ah, I see, with [ptrvar] !

    No, sorry, I definitely don't like this syntax at all!

  • evanhevanh Posts: 16,022

    ptrvar := 1 That's definitely a syntactical conflict. As Eric says, Spin has previously always been operator driven rather than data type driven. The inclusion of floats has been a good example, where the data is defined by the operator. The variable allocation and naming is the same 32-bits as it was when it was just an integer. The operators make it a float.

  • roglohrogloh Posts: 5,836
    edited 2024-11-14 09:59

    @macca said:
    I have to say, I find the pointer syntax very confusing, and, most important, not consistent with the existing syntax.

    Yeah it's sort of gone and wandered off the reservation a bit. Maybe if we think of these as references only not actual "pointers" it can help a little but there's something very unsettling about it IMO with the bracket use. Probably as I'm a C programmer of old and must be somewhat set in my ways so it doesn't come naturally.

    So you can currently do this using "ptr" as a local:
    byte[ptr] := x ' to assign x which is a byte array pointed to by ptr

    but doing this with the new pointer type
    [ptr] := x
    assigns the pointer's value to x and not the pointed to location which is different to the above.

    And if ptr is defined as the new pointer type, then attempting the old
    byte[ptr][0]
    sort of thing would dereference ptr and use it as a second pointer into a byte array. Seems a bit inconsistent. We'll now have to keep very careful track of what is actually defined as a "pointer" and what is defined without an explicit type (like all current locals).

    No, sorry, I definitely don't like this syntax at all!

    I know what you mean too, though I don't really have a dog in this fight as I'm mainly a PASM2 guy these days and don't do a lot of SPIN2 stuff (so I can probably be ignored), but it just seemed a bit strange to me when I saw it and wanted to point it out. Maybe it's too late to change it anyway.

  • cgraceycgracey Posts: 14,206
    edited 2024-11-14 20:08

    I can do all this differently, but I'd need a complete set of syntax rules to make it work. I've already been around and around with other ideas, but nothing seems better than this, to me. Syntax highlighting could go a long ways in making this more readily understandable.

  • Chip had a good argument on LF: The thing is, if you do [name], that's almost the same as the usual type[name] and so doesn't really make anything better. it also allows C-style array indexing name[i] in a way congruent with the current spin syntax. Maybe I'm heavily biased since on the german keyboard layout, typing too many brackets is a big pain.

  • roglohrogloh Posts: 5,836
    edited 2024-11-15 00:30

    @cgracey said:
    I can do all this differently, but I'd need a complete set of syntax rules to make it work. I've already been around and around with other ideas, but nothing seems better than this, to me. Syntax highlighting could go a long ways in making this more readily understandable.

    Maybe if you guys have the time for it, then you and Eric could have a video call between yourselves to kick around your ideas and try to pick the best of them for the two most popular toolchains in use for P2 that makes the most sense. It's not a problem if it's not identical to C but it definitely needs consistency and ease of use and ease of learning is important too ideally for both old and new programmers.

    @Wuerfel_21 said:
    Chip had a good argument on LF: The thing is, if you do [name], that's almost the same as the usual type[name] and so doesn't really make anything better. it also allows C-style array indexing name[i] in a way congruent with the current spin syntax. Maybe I'm heavily biased since on the german keyboard layout, typing too many brackets is a big pain.

    Something like *ptr is fairly easy to type :wink: Brackets are definitely harder to type, especially mixed with ++/-- etc. Plus you have to match brackets if these are nested inside something else which doesn't help much either.

    Try out *p++ vs p[++]
    *p++ is a very common operation too so it'll go into the code a lot - it's frequently used for parsing/scanning text data for example.

  • maccamacca Posts: 806

    @cgracey said:
    I can do all this differently, but I'd need a complete set of syntax rules to make it work. I've already been around and around with other ideas, but nothing seems better than this, to me. Syntax highlighting could go a long ways in making this more readily understandable.

    Well, the question is: why you need this syntax ? what kind of problem you want to solve ?

    If it is to make the source "shorter", this always comes with more confusion from the coder's perspective, and you are also changing the variable perspective, generating more confusion (the var := x vs. [var] := x change)

    The current syntax:

    BYTE[a][b]
    WORD[a][b]
    LONG[a][b]
    

    May be verbose but is clear, you act on an address [a] as a BYTE/WORD/LONG with an index [b] (which if I'm not wrong, already takes the type-size into account).

    IMHO, a specific pointer variable declaration isn't needed, but if we want a specific ^BYTE/WORD/LONG pointer type, an alternative may be to make the type specifier optional:

    [a][b]
    

    This has the same effect, but depends on the [a] variable pointer type size. Note that this adds some confusion because the coder doesn't immediately know the size.

    We may add some "extra" features, like the pre and post-increments such as:

    [a][++]
    [--][a]
    

    Which increments or decrements the index variable by the type size (I think it could be applied to the verbose syntax too). It resembles the indexed syntax but without a variable should be clear that it affects the address variable (or hope so).

    The important thing is to not change the perspective, if var := x assign a value to var this can't change depending on the var type, ever!
    Remember that such a change with a pointer, without paying attentions, may lead to unexpected bugs, crashes and weird behaviours very difficult to debug because it directly acts on a memory address. This is (one of) the reason C is such a criticized language (and everybody forget that it is at the base of everything, not learning C is such a bad thing).

    We may add an extra operator, something like the C star *a := x or maybe ^a := x is better to make it clear that the statement acts on the memory pointed by the variable (not sure if this syntax may lead to more confusion since is already used for field pointers, maybe no).

    Sorry for the german friends, in Italy we have a similar problem with the C { } (requires SHIFT+ALT.GR + key, and aren't even labeled on some (all?) keyboards). Back in the days I had to type the ASCII code with ALT+123 / ALT+125, if memory serves me right. Had to buy a US keyboard just for that.

  • RaymanRayman Posts: 14,738
    edited 2024-11-15 12:05

    This way seems better to me too.
    If declare the pointer the way Chip did but use it like the above, seems more consistent with existing framework.

    Let’s one not have to specify the type everywhere in the code. Guess that’s the benefit?

    But do see Chip’s way avoids the brackets…
    Is using a ^ instead of brackets easier? Maybe?

  • roglohrogloh Posts: 5,836
    edited 2024-11-15 12:26

    Given that this new pointer is intended as a totally different way to access structured data vs the existing and fairly limited BYTE[ptr][index] method, perhaps to avoid excess confusion and inconsistency with its use vs exisiting code, you could simply restrict its use during dereferencing to access defined named structure fields only.

    Like how C uses
    ptr->field
    when ptr points to some struct containing a named field.

    We could similarly have in SPIN2

    ptr::field or something like that.

    So you could assign with

    ptr::field:=value

    and read with

    variable:=ptr::field

    :: was shown because it wasn't in use but it's possible to use the C syntax.
    You could potentially include increment or decrement stuff like this:

    ptr::field++ ' increments the variable pointed to, not the pointer itself
    ptr++::field ' post increments the pointer, after using/assigning the field variable
    --ptr::field ' pre decrements the pointer, before using/assigning the field variable

    ptr++ ' increments the pointer by the size of the structure it points to

    The :: could be continued for nested structs..

    ptr::structname::field

    And array indexes could be included if needed - eg. array of pointers to structures containing multiple nested fields/structures.

    ptr[x]::structname[y]::field[z]

    EDIT: also technically you probably want to differentiate between a pointer dereference and field member access so both :: and . separators might be needed, one for any pointers (::) and one for nested fields (.) .
    e.g ptr[x]::structname[y].field[z]

  • RaymanRayman Posts: 14,738

    Or do it Chip’s way but use the ^ when changing the value of the pointer instead of brackets? That’s the confusing part…

  • ersmithersmith Posts: 6,068

    Indeed, this very different (almost opposite) use of brackets is confusing -- in BYTE[p] := 1 we're dereferencing p, whereas in [p] := 1 we'd be accessing p directly.

    I like @Rayman 's suggestion of using ^ to mark changing the pointer directly, maybe something like:

    p := x ' p is a pointer reference, so changes what p is pointing to
    p ^:= x ' changes actual value of p
    p ^++ := x ' assigns to what p points to, then increments p
    ^-- p := x ' predecrements pointer, then assigns what it points to
    

    Another possiblity for assignment might be something like

    @p := x
    

    since if p is a pointer reference then @p would logically mean the actual value of p.

  • maccamacca Posts: 806

    @rogloh said:
    Given that this new pointer is intended as a totally different way to access structured data vs the existing and fairly limited BYTE[ptr][index] method, perhaps to avoid excess confusion and inconsistency with its use vs exisiting code, you could simply restrict its use during dereferencing to access defined named structure fields only.

    I have limited knowledge of the structure internals because the interpreter and compiler code are not yet published, but if I remember correctly, they can already be used as types, like any BYTE/WORD/LONG type.

    struct.member
    struct[a].member    ' array of structures
    struct[a].member[b] ' array of structure with array member
    

    This could be expanded to pointers in much the same way as the other types:

    ' with a structure pointer variable ^struct_type struct_varptr
    [struct_varptr].member
    [struct_varptr][a].member
    [struct_varptr][a].member[b]
    
    ' with a long address a
    struct_type[a]
    struct_type[a].member
    struct_type[a].member[b]
    

    Type type can be inferred from the struct_varptr type (or struct_type), just like the case for BYTE/WORD/LONG.

  • ElectrodudeElectrodude Posts: 1,660
    edited 2024-11-15 16:48

    @cgracey said:
    I think it is true that C established in people's minds what was not optimal, a long time ago. The angst over non-convention is undue. It's just upsetting to what became traditional thinking.

    C's approach is no different from pre-struct Spin. C is that way because, like Spin, it originally had no types. It is therefore natural for Spin to do it similarly, because both languages developed types in similar ways. Making Spin struct pointers this much unlike C pointers is antithetical to and inconsistent with all the ideas about pointers already in pre-struct Spin.

    @macca said:

    @rogloh said:
    Given that this new pointer is intended as a totally different way to access structured data vs the existing and fairly limited BYTE[ptr][index] method, perhaps to avoid excess confusion and inconsistency with its use vs exisiting code, you could simply restrict its use during dereferencing to access defined named structure fields only.

    I have limited knowledge of the structure internals because the interpreter and compiler code are not yet published, but if I remember correctly, they can already be used as types, like any BYTE/WORD/LONG type.

    struct.member
    struct[a].member    ' array of structures
    struct[a].member[b] ' array of structure with array member
    

    This could be expanded to pointers in much the same way as the other types:

    ' with a structure pointer variable ^struct_type struct_varptr
    [struct_varptr].member
    [struct_varptr][a].member
    [struct_varptr][a].member[b]
    
    ' with a long address a
    struct_type[a]
    struct_type[a].member
    struct_type[a].member[b]
    

    Type type can be inferred from the struct_varptr type (or struct_type), just like the case for BYTE/WORD/LONG.

    This seems reasonable to me, and consistent with the rest of Spin. The [ptr] syntax then just becomes a shorthand for struct_type[ptr] that can be used when the type is known to the compiler.

  • cgraceycgracey Posts: 14,206

    @evanh said:
    That's a quarter of total cogRAM! If you use all that then I bet you'll be taking an axe to usable space for things like regload().

    This all happens on the stack, so no extra cog RAM is taken by lots of parameters.

  • evanhevanh Posts: 16,022

    Ah, right, that's for max args to a method. I had to go find the related comments. Makes sense. In hindsight I had a brain fade not knowing they would be switched to the stack.

  • evanhevanh Posts: 16,022
    edited 2024-11-16 03:25

    Chip,
    I'd like to specify using ORGH/END for "inline" Pasm. I suspect it'd be easy to add as a feature. The CALL()+DAT way is ugly.

    EDIT: Hmm, a DAT can live within a method's local space? ...

    EDIT2: Never mind, it barely makes any sense to split the Pasm into two groups when run from the VM environment. I've moved everything to ORG/END now.

  • evanhevanh Posts: 16,022
    edited 2024-11-16 07:17

    Chip,
    It looks like REGLOAD() can only be used with DAT content arranged exactly as specified in the examples. ie: It has to have the ORG after the two WORDs at the beginning of the "chunk". Correct?

    I'm keen to copy a VAR'd array or structure into cogRAM instead of using DAT. This is because there is two functions involved. One function fills out the structure as configuration for the second function to later use.

    In FlexC I had been using, for the second function, a fast inline copy (LOC+SETQ+RDLONG) that could reference the struct directly. eg:

        __asm const {    // "const" enforces XIP, "volatile" enforces Fcache
            loc pa, #rxblkset    // fast copy from struct to cogRAM
            setq    #sizeof(rxblkset) / 4 - 1
            rdlong  r_clk, pa
            ...
        }
    

    EDIT: Bah! I don't really want to use REGLOAD() at all. Flexspin doesn't support it. I need to work out how to replicate what I've already done in FlexC ...

  • evanhevanh Posts: 16,022
    edited 2024-11-16 21:24

    Right, I now a have a generic agnostic solution. Arguably it is even cleaner than I had:

    CON
        _clkfreq = 200_000_000
    
    
    PUB  main()
    
        debug("Clock Freq: ", udec_long_(clkfreq))
    
        set_rxblkset()
        call( @rx_block )
    
        debug("main() finished")
    
    
    PUB  set_rxblkset()
    
        p_clk := 25
        p_dat := 23
        m_leadin := X_IMM_32X1_1DAC1 | (8 + 3 + 3)
    
    
    DAT             ORGH
    rx_block
            loc pa, #rxblk_pasm_rel
            setq    #(@rxblk_pasm_end - @rxblk_pasm_rel) / 4 - 1
            rdlong  0-0, pa    ' fast copy from struct to cogRAM
            jmp #0    ' continue as cogexec
    
    rxblk_pasm_rel
                    ORG     0
            debug("Debug start pasm")
            debug(udec_long_(p_clk))
            debug(udec_long_(p_dat))
            debug(udec_long_(m_leadin))
            debug(udec_long_(m_nco))
            ret
    
        p_clk       long 0
        p_dat       long 0
        m_leadin    long 0
        m_nco       long 0
    rxblk_pasm_end
    

    EDIT: PR0 can be exchanged for PTRB. PTRB is probably more precious though.
    EDIT2: Eliminated need of PR0.

  • evanhevanh Posts: 16,022
    edited 2024-11-16 13:01

    Oh-no!!! Chip, you've got an example in the Spin2 manual that I think must be broken. Maybe it once worked but it doesn't now:

    PUB go() | x
        REPEAT
            CALL(@random)
            PINWRITE(56 ADDPINS 7, x)
            WAITMS(100)
    
    DAT         ORGH    'hub PASM program to rotate a random bit into x
    random
                GETRND WC
        _RET_   RCL x,#1
    

    The symbol x doesn't work in the pasm code. I was counting on that working for the new agnostic code. :( It's not so elegant any longer without locals.

  • ersmithersmith Posts: 6,068

    @Electrodude said:

    @macca said:

    @rogloh said:
    Given that this new pointer is intended as a totally different way to access structured data vs the existing and fairly limited BYTE[ptr][index] method, perhaps to avoid excess confusion and inconsistency with its use vs exisiting code, you could simply restrict its use during dereferencing to access defined named structure fields only.

    I have limited knowledge of the structure internals because the interpreter and compiler code are not yet published, but if I remember correctly, they can already be used as types, like any BYTE/WORD/LONG type.
    This could be expanded to pointers in much the same way as the other types:

    ' with a structure pointer variable ^struct_type struct_varptr
    [struct_varptr].member
    [struct_varptr][a].member
    [struct_varptr][a].member[b]
    
    ' with a long address a
    struct_type[a]
    struct_type[a].member
    struct_type[a].member[b]
    

    Type type can be inferred from the struct_varptr type (or struct_type), just like the case for BYTE/WORD/LONG.

    This seems reasonable to me, and consistent with the rest of Spin. The [ptr] syntax then just becomes a shorthand for struct_type[ptr] that can be used when the type is known to the compiler.

    Except that in Chip's version 45 document [ptr] has exactly the opposite meaning to struct_type[ptr]. That is, in Chip's document

    pub setit(^long ptr, a)
        ptr := a   '  assigns a value to the thing pointed to, like long[ptr] := a
        [ptr] := a  ' changes the value of the pointer itself
    

    @cgracey I think this confusion about the meaning of [] the source of many of the objections, rather than a desire to keep compatibility with C. After all, C++ already has references, which behave a lot like Spin2 typed pointers.

    How would you feel about changing the syntax for accessing the pointer itself; instead of [ptr] := a something like ptr ^:= a to indicate that it's the pointer being changed and not the thing pointed to?

  • ElectrodudeElectrodude Posts: 1,660
    edited 2024-11-16 19:43

    @ersmith said:

    @Electrodude said:

    @macca said:

    @rogloh said:
    Given that this new pointer is intended as a totally different way to access structured data vs the existing and fairly limited BYTE[ptr][index] method, perhaps to avoid excess confusion and inconsistency with its use vs exisiting code, you could simply restrict its use during dereferencing to access defined named structure fields only.

    I have limited knowledge of the structure internals because the interpreter and compiler code are not yet published, but if I remember correctly, they can already be used as types, like any BYTE/WORD/LONG type.
    This could be expanded to pointers in much the same way as the other types:

    ' with a structure pointer variable ^struct_type struct_varptr
    [struct_varptr].member
    [struct_varptr][a].member
    [struct_varptr][a].member[b]
    
    ' with a long address a
    struct_type[a]
    struct_type[a].member
    struct_type[a].member[b]
    

    Type type can be inferred from the struct_varptr type (or struct_type), just like the case for BYTE/WORD/LONG.

    This seems reasonable to me, and consistent with the rest of Spin. The [ptr] syntax then just becomes a shorthand for struct_type[ptr] that can be used when the type is known to the compiler.

    Except that in Chip's version 45 document [ptr] has exactly the opposite meaning to struct_type[ptr]. That is, in Chip's document

    pub setit(^long ptr, a)
        ptr := a   '  assigns a value to the thing pointed to, like long[ptr] := a
        [ptr] := a  ' changes the value of the pointer itself
    

    @cgracey I think this confusion about the meaning of [] the source of many of the objections, rather than a desire to keep compatibility with C. After all, C++ already has references, which behave a lot like Spin2 typed pointers.

    That's my point. The way in Chip's version 45 is completely inconsistent with everything else in Spin. Doing it as I described, which is pretty much the other way around, would make more sense.

    As far as references go, unless we're talking about complicated languages like Rust that justify it with other benefits, references just add needless complexity and don't seem like something that would be helpful in Spin.

    How would you feel about changing the syntax for accessing the pointer itself; instead of [ptr] := a something like ptr ^:= a to indicate that it's the pointer being changed and not the thing pointed to?

    Reseating references is a recipe for confusion - it's better to just not have references at all. Normal struct pointers would be much more useful.

  • evanhevanh Posts: 16,022

    Why are we wanting pointers in Spin2?

  • evanhevanh Posts: 16,022

    VARs don't use stack space do they? Is there a way to temporarily use some stack within a method?

  • cgraceycgracey Posts: 14,206

    Here is how I see this, and it's very simple:

    [ptr] := @variable
    ptr[++]

    When it comes to pointers, what is inside the brackets has to do with the pointer, itself.

    This all stems from making pointers first-class, just like regular variables are.

  • cgraceycgracey Posts: 14,206

    @evanh said:
    Oh-no!!! Chip, you've got an example in the Spin2 manual that I think must be broken. Maybe it once worked but it doesn't now:

    PUB go() | x
        REPEAT
            CALL(@random)
            PINWRITE(56 ADDPINS 7, x)
            WAITMS(100)
    
    DAT         ORGH    'hub PASM program to rotate a random bit into x
    random
                GETRND WC
        _RET_   RCL x,#1
    

    The symbol x doesn't work in the pasm code. I was counting on that working for the new agnostic code. :( It's not so elegant any longer without locals.

    Only inline PASM can use local variables, because PASM in a DAT section has no local context. It's global to the object/file.

  • I think the real problem here is the methodology, wherein Chip comes up with something, implements it, and then gets complained on by everyone. That's just no good and leads to everyone being unhappy and the feature being delayed.

    I personally like the current idea:

    • Syntax stays the same regardless of using a locally defined structure or a pointer - no need for extra syntax noise
    • Indexing into a pointed-to array is consistent with how indexing arrays usually works in Spin (ever realize that there isn't actually a distinction between arrays and single variables?)

      • If deref was a separate operator like [p] and we wanted to index like [p][i], then a problem arises - we can only index into symbols, not values. So either a strange constraint appears where we can index into what otherwise results in a value, but only directly next to it or a virtual expression-only reference type would need to exist that can be indexed but coerces into a value - both nasty!
      • So I think the idea that it doesn't fit with Spin conceptually is bogus.
    • Does not cause changes to existing mechanics (such as the existing operators - introducing a new type of operator for pointer increment means the existing increment operator still only does +1 - otherwise type information would need to carry through the expression parser!)

    • The existing type[address] way of doing things remains valid for when it is better and more useful.
    • The exact syntax is whatever. Using the brackets allows for the terse p[++], which is definitely better than the C-style *p++ (non-obvious operator precedence).

    My own issues:

    • Initializing structures in DAT is currently a cop-out.
    • It may be easy to create nullpointer-flavoured problems and not notice it immediately due to stack/VAR initializing to zero and the hardware not providing a way to trap invalid memory access.
    • Multi-level pointers, if introduced, would behave somewhat awkwardly. Maybe it's best not to enter the territory of the 3-star programmers.

    Perhaps biased summary of other's issues:

    • Confusion about the actual thing being proposed (bad docs/communication from Chip)
    • Concern about aforementioned confusion
    • Arguing about the exact syntax
    • Wanting something more akin to a C pointer (the address is the value and an explicit deref operator is needed to get at the pointee)
  • RaymanRayman Posts: 14,738

    The syntax looks similar to Pascal. Maybe just copy that?

  • evanhevanh Posts: 16,022

    @cgracey said:
    Only inline PASM can use local variables, because PASM in a DAT section has no local context. It's global to the object/file.

    Maybe time to fix your example code in the Spin2 Manual then.

  • evanhevanh Posts: 16,022

    @cgracey said:
    Here is how I see this, and it's very simple:

    [ptr] := @variable
    ptr[++]

    We can all see that. It's backwards. And I'm not sure why we need this approach. The existing byte[ptr]/word[ptr]/long[ptr] approach seemed fine to me.

Sign In or Register to comment.