Shop OBEX P1 Docs P2 Docs Learn Events
PNut/Spin2 Latest Version (v48.1 - preprocessor and flash-image saving) - Page 68 — Parallax Forums

PNut/Spin2 Latest Version (v48.1 - preprocessor and flash-image saving)

16668707172

Comments

  • cgraceycgracey Posts: 14,232

    @bob_g4bby said:
    I turned Windows Security off, downloaded the zip file and unpacked it all. Windows Security turns itself back on after a minute or so. Attempting to run PNUT_v47.exe aborts and the file is removed from the PNUT directory. I'm not familiar with this situation, so I've left it for now - it's a little late here in the UK.

    That's so frustrating. The OS should let the computer owner make decisions about what software they want to run on their machine. Hopefully, PNut hasn't been blacklisted again.

  • RaymanRayman Posts: 14,789

    It’s been a while but wasn’t there a trick of not running it as a download but a file on usb drive or something?
    Think anything in the downloads folder gets extra scrutiny…

  • I disagree, if only because the C API is really poorly designed in some ways.
    The way fread and fwrite in particular work is somewhat obnoxious. The size*count logic is rarely useful, requires a size_t*size_t multiply (a slow QMUL in our case) and more often leads to confusion (I think FlexC's fread/fwrite implementation gets the return value wrong... Though to fix it a QDIV would need to be added, so I'm keeping quiet.) Also, for some reason fread/fwrite/fgets/fputs take the FILE* as their last argument, whereas fseek and fprintf take it as their first. That's super awful!

  • evanhevanh Posts: 16,075

    @Wuerfel_21 said:
    I disagree, if only because the C API is really poorly designed in some ways.
    The way fread and fwrite in particular work is somewhat obnoxious. The size*count logic is rarely useful, requires a size_t*size_t multiply (a slow QMUL in our case) and more often leads to confusion (I think FlexC's fread/fwrite implementation gets the return value wrong... Though to fix it a QDIV would need to be added, so I'm keeping quiet.) Also, for some reason fread/fwrite/fgets/fputs take the FILE* as their last argument, whereas fseek and fprintf take it as their first. That's super awful!

    True, that size and count in the arguments is redundant. The API doesn't have to be an exact match.

    I've been using the read/written amount in my tester programs as part of the verifying step. It always matched the expected bytes.

  • evanhevanh Posts: 16,075

    @cgracey said:

    @bob_g4bby said:
    I turned Windows Security off, downloaded the zip file and unpacked it all. Windows Security turns itself back on after a minute or so. Attempting to run PNUT_v47.exe aborts and the file is removed from the PNUT directory. I'm not familiar with this situation, so I've left it for now - it's a little late here in the UK.

    That's so frustrating. The OS should let the computer owner make decisions about what software they want to run on their machine. Hopefully, PNut hasn't been blacklisted again.

    Time to ditch Windoze, maybe.

    Actually, in my occasional encounter with installing on modern Windoze I've never actually encountered this reported issue. And I have done at least one Pnut install, albeit a couple of years back now.

  • True, that size and count in the arguments is redundant. The API doesn't have to be an exact match.

    I've been using the read/written amount in my tester programs as part of the verifying step. It always matched the expected bytes.

    That's the thing, the return value shouldn't be bytes, it should be in units of the size parameter. Of course any actual underlying implementation works in bytes, thus a divide is needed (though you can skip it in the common case). Note that this doesn't have any useful implication w.r.t to keeping the file pointer aligned, partial elements can be read/written and in that case the return value is rounded down.

  • @cgracey said:
    That's so frustrating. The OS should let the computer owner make decisions about what software they want to run on their machine. Hopefully, PNut hasn't been blacklisted again.

    @evanh said:

    Time to ditch Windoze, maybe.

    Real enshittification vibes. Microsoft makes money on signing certs so they have zero incentive to make life easy for the rest of us.
    At this point Linux is the way. It gets better over time, while Windows is getting worse with every version since 8.

  • @cgracey said:
    It's all working now, even with task halt/continue.

    Looking very useful.

    Can TASKCONT/TASKNEXT be called from an ISR? That could be useful for fast context switching from an ISR, although you have to be careful you don't starve a task this way.
    I wonder if it also makes sense to also allow task switching to a specific task rather than next only. Having that might allow some sort of priority management to be done by an external scheduler which tracks priority and decides what/when to switch.

  • evanhevanh Posts: 16,075

    An ISR has to actually return or it's IRQ will stay masked. Chip has suggested a mechanism that might do what you want - https://forums.parallax.com/discussion/comment/1563934/#Comment_1563934

  • RossHRossH Posts: 5,503

    @evanh said:

    Another area, like the preprocessor, where following C's lead is desirable. Please use the same function names and arguments like fopen(), fread() and the likes. Porting C code will be a breeze then.

    Hmmm ...

    I can see the point of a C-like preprocessor, because the C preprocessor is essentially independent of the C language, and many Spin compilers already have bits of one anyway - but they are often only partial and also incompatible (especially when it comes to #include). Standardizing on a preprocessor would be good.

    But the C stdio functions? Not so sure. Just using a few of the same names and parameter profiles is not really enough. They also have to work the same way. And these functions were designed to work well in C-like languages. Bolting them onto a non-C language would not be easy. Just as a trivial example, fread() is very often used in conjunction with the sizeof operator, since it reads a number of binary elements of a defined size. Without that operator fread() is not much use even if it accepts the same arguments, and nor would C code that uses it be much easier to port to Spin.

    C is C and Spin is Spin. If you want to program in C, use a C compiler.

  • evanhevanh Posts: 16,075
    edited 2024-12-08 07:08

    Ada already addressed that. The size * count is redundant in fread()/fwrite() and I doubt anyone actually uses it. I'd be more than happy to have something like that be merged in the Spin equivalent functions. They don't have to be exactly the same.

  • RossHRossH Posts: 5,503

    @evanh said:
    Ada already addressed that. The size * count is redundant in fread()/fwrite() and I doubt anyone actually uses it. I'd be more than happy to have something like that be merged in the Spin equivalent functions. They don't have to be exactly the same.

    Goodness. Of course C programmers use it. That's how fread() and fwrite() were designed to work. Look up any tutorial on them, and I'd lay odds you will see sizeof used in calls to these functions. If you don't, it is a very poor tutorial.

    If you want to implement fread() and fwrite(), then by all means - but implement them properly. If you don't want to do that, then also fine - but don't call them fread() and fwrite(). That would just be confusing.

  • evanhevanh Posts: 16,075

    That's not a real use. It's just redundant.

  • RossHRossH Posts: 5,503
    edited 2024-12-08 11:33

    @evanh said:
    That's not a real use. It's just redundant.

    Right. I just did a search of all my C code. In about 50% of calls to fread() and fwrite(), the sizeof operator is used, because types are being read/written. In the other 50%, the value 1 is used, indicating just bytes are being read/written.

    But by all means eliminate 50% of C code being able to be easily ported to Spin.

  • cgraceycgracey Posts: 14,232
    edited 2024-12-08 13:04

    @rogloh said:

    @cgracey said:
    It's all working now, even with task halt/continue.

    Looking very useful.

    Can TASKCONT/TASKNEXT be called from an ISR? That could be useful for fast context switching from an ISR, although you have to be careful you don't starve a task this way.
    I wonder if it also makes sense to also allow task switching to a specific task rather than next only. Having that might allow some sort of priority management to be done by an external scheduler which tracks priority and decides what/when to switch.

    An ISR does not know the state of the Spin2 interpreter and there may be state data spread across 40 registers when an interrupt occurs. Between Spin2 instructions, however, the state data is confined to only 8 contiguous registers. When a TASKNEXT() executes, the interpreter is in that inter-instruction state, so those 8 registers can be quickly saved and another set can be loaded to effect a task switch. Because ISRs can occur at any time, the ISR cannot do anything with Spin2. However, there is a register called TASKHLT that contains the halt bits for the 32 possible tasks. If you have the task number, you could set or clear bit (31 - task#) to halt or continue that task. This can be done asynchronously from a resident PASM ISR, because the interpreter will check that bit when doing an upcoming TASKNEXT() instruction. By this means, an ISR can affect Spin2 task execution. It can halt and un-halt a task, or the task can halt itself using TASKHALT(THISTASK), to be later turned on the by ISR.

    Here is an example program that starts a 250ms interrupt that periodically un-halts a task. In a REPEAT block, the task does a TASKHALT(THISTASK), which halts the task and switches to the next task, then the PASM ISR clears the task's halt bit, so that the task continues, flipping the state of an LED before looping back.

    ' This program demonstrates how an ISR can un-halt a task.
    ' It uses the LEDs on the P2 Edge board.
    
    {Spin2_v47}
    
    _clkfreq = 100_000_000
    
    VAR stack[30]
    
    PUB Start() | task, rate
    
      task := taskspin(NEWTASK, ToggleLED(56), @stack)
      rate := clkfreq / 4           'number of clocks in 250ms
    
            org     $100            'put routine high up in registers
    
            not     bit,task        'get task bit
            and     bit,#$1F        'internal task bits are reversed
            mov     adder,rate      'get isr rate
            mov     ijmp1,#isr1     'set int1 vector
            setint1 #1              'set int1 for ct-passed-ct1 event
            getct   ct1             'set initial ct1 target
            addct1  ct1,adder
            ret
                                    'int1 isr, runs once every 250ms
    isr1    bitnot  taskhlt,bit     'clear task's halt bit, same as TASKHALT(task)
            addct1  ct1,adder       'update ct1 target
            reti1                   'return from interrupt
    
    bit     res     1               'task bit
    adder   res     1               'adder for ct1
    ct1     res     1               'sum for ct1, sets next interrupt
    
            end
    
      pinl(57)                      'stop the other LED from floating on/off
    
      'taskstop(THISTASK)           'you could TASKSTOP/TASKHALT this task
    
      repeat                        'or you could do something in here
        tasknext()                  '..just keep the multitasker switching
    
    PRI ToggleLED(pin)
    
      repeat
        taskhalt(THISTASK)          'halt this task, the ISR will unhalt it
        pintoggle(pin)              'toggle the LED
    

    By the way, I uploaded a new v47 to the OBEX because I discovered a bug in the TASKHALT/TASKCONT instructions that was causing a stack overflow. I also improved some boundary behavior which I documented in the Google Doc.

    Hopefully, this new v47 doesn't trigger the virus berzerkers. I uploaded the file to totalvirus and it seemed to do nothing.

  • What a difference a day makes - my Windows Security has decided it likes v47 after all - quite calmly accepting the latest download. Sorry for the unnecessary excitement yesterday. I am a happy multitasker, I can foresee it being a favourite feature of the language - great work!

  • cgraceycgracey Posts: 14,232

    @bob_g4bby said:
    What a difference a day makes - my Windows Security has decided it likes v47 after all - quite calmly accepting the latest download. Sorry for the unnecessary excitement yesterday. I am a happy multitasker, I can foresee it being a favourite feature of the language - great work!

    Whew!!! I hope it all makes sense as you start using it. Ask any questions here and I'll answer them.

  • evanhevanh Posts: 16,075
    edited 2024-12-08 13:55

    @RossH said:
    But by all means eliminate 50% of C code being able to be easily ported to Spin.

    It's a piece of cake to literally have sizeof() * count There's entirely no reason to have two separate arguments to any of the those functions.

    EDIT: I can think of one reason why they were like that originally - Because of some horribly segmented CPU architecture had a memory address range larger than an int could address.

  • @cgracey said:
    An ISR does not know the state of the Spin2 interpreter and there may be state data spread across 40 registers when an interrupt occurs. Between Spin2 instructions, however, the state data is confined to only 8 contiguous registers. When a TASKNEXT() executes, the interpreter is in that inter-instruction state, so those 8 registers can be quickly saved and another set can be loaded to effect a task switch. Because ISRs can occur at any time, the ISR cannot do anything with Spin2. However, there is a register called TASKHLT that contains the halt bits for the 32 possible tasks. If you have the task number, you could set or clear bit (31 - task#) to halt or continue that task. This can be done asynchronously from a resident PASM ISR, because the interpreter will check that bit when doing an upcoming TASKNEXT() instruction. By this means, an ISR can affect Spin2 task execution. It can halt and un-halt a task, or the task can halt itself using TASKHALT(THISTASK), to be later turned on the by ISR.

    Is the address of the tasknum register fixed so PASM could read the current task number directly?

  • cgraceycgracey Posts: 14,232

    @TonyB_ said:

    @cgracey said:
    An ISR does not know the state of the Spin2 interpreter and there may be state data spread across 40 registers when an interrupt occurs. Between Spin2 instructions, however, the state data is confined to only 8 contiguous registers. When a TASKNEXT() executes, the interpreter is in that inter-instruction state, so those 8 registers can be quickly saved and another set can be loaded to effect a task switch. Because ISRs can occur at any time, the ISR cannot do anything with Spin2. However, there is a register called TASKHLT that contains the halt bits for the 32 possible tasks. If you have the task number, you could set or clear bit (31 - task#) to halt or continue that task. This can be done asynchronously from a resident PASM ISR, because the interpreter will check that bit when doing an upcoming TASKNEXT() instruction. By this means, an ISR can affect Spin2 task execution. It can halt and un-halt a task, or the task can halt itself using TASKHALT(THISTASK), to be later turned on the by ISR.

    Is the address of the tasknum register fixed so PASM could read the current task number directly?

    I will make symbols for TASKNUM and TASKENA, as well. I actually moved them from $120, because that boundary may change. I will make a symbol for TASKPTR, too, which is currently register $100, or the base of task pointers, which build downward from TASKPTR + $1F.

  • JonnyMacJonnyMac Posts: 9,182
    edited 2024-12-08 20:38

    I will make symbols for TASKNUM and TASKENA, as well.

    If you're going to do that can you and an internal constant for the address of PR0 in the cog? I find myself doing this for inline PASM.

    con
    
      GP_REGS = $1D8                                                ' cog address of pr0
    

    Then, later...

    pub show(swap)
    
    '' Refresh pixels
    '' -- swap.[0] is 1 for red/green swap (WS2812b)
    '' -- cog prX regs:
    ''    0:pin, 1:p_buf/color, 2:count, 3:reset, 4:zero, 5:one, 6:period, 7:timer
    
      setregs(@pixout, GP_REGS, 7)                                  ' copy hub vars to cog regs pr0..pr6
    
      org
                            drvl      pr0                           ' make output/low
                            waitx     pr3                           ' allow reset
                            mov       ptrb, pr1                     ' point to start of buffer
    
    .pixloop                rdlong    pr1, ptrb++                   ' get pixel data
                            testb     swap, #0              wc      ' swap red & green?
            if_c            movbyts   pr1, #%%2310                  '  yes
                            getct     pr7                           ' initialize bit timer
    
                            rep       #7, #24                       ' loop through all bits
                             shl      pr1, #1               wc      ' get MSB
                             drvh     pr0                           ' pin on
            if_nc            waitx    pr4                           ' hold for bit timing
            if_c             waitx    pr5
                             drvl     pr0                           ' pin off
                             addct1   pr7, pr6                      ' update bit timer
                             waitct1                                ' let bit timing finish
    
                            djnz      pr2, #.pixloop
      end
    

    Maybe a better symbol is PRX_REGS so that the code is more readable.

  • cgraceycgracey Posts: 14,232
    edited 2024-12-08 21:03

    @JonnyMac said:

    I will make symbols for TASKNUM and TASKENA, as well.

    If you're going to do that can you and an internal constant for the address of PR0 in the cog? I find myself doing this for inline PASM.

    con
    
      GP_REGS = $1D8                                                ' cog address of pr0
    

    Then, later...

    pub show(swap)
    
    '' Refresh pixels
    '' -- swap.[0] is 1 for red/green swap (WS2812b)
    '' -- cog prX regs:
    ''    0:pin, 1:p_buf/color, 2:count, 3:reset, 4:zero, 5:one, 6:period, 7:timer
    
      setregs(@pixout, GP_REGS, 7)                                  ' copy hub vars to cog regs pr0..pr6
    
      org
                            drvl      pr0                           ' make output/low
                            waitx     pr3                           ' allow reset
                            mov       ptrb, pr1                     ' point to start of buffer
    
    .pixloop                rdlong    pr1, ptrb++                   ' get pixel data
                            testb     swap, #0              wc      ' swap red & green?
            if_c            movbyts   pr1, #%%2310                  '  yes
                            getct     pr7                           ' initialize bit timer
    
                            rep       #7, #24                       ' loop through all bits
                             shl      pr1, #1               wc      ' get MSB
                             drvh     pr0                           ' pin on
            if_nc            waitx    pr4                           ' hold for bit timing
            if_c             waitx    pr5
                             drvl     pr0                           ' pin off
                             addct1   pr7, pr6                      ' update bit timer
                             waitct1                                ' let bit timing finish
    
                            djnz      pr2, #.pixloop
      end
    

    Maybe a better symbol is PRX_REGS so that the code is more readable.

    I think you can use #PR0 from Spin2. Otherwise, I will add something.

    Here:

  • roglohrogloh Posts: 5,852
    edited 2024-12-08 22:19

    @cgracey said:

    @rogloh said:
    Can TASKCONT/TASKNEXT be called from an ISR? That could be useful for fast context switching from an ISR, although you have to be careful you don't starve a task this way.
    I wonder if it also makes sense to also allow task switching to a specific task rather than next only. Having that might allow some sort of priority management to be done by an external scheduler which tracks priority and decides what/when to switch.

    An ISR does not know the state of the Spin2 interpreter and there may be state data spread across 40 registers when an interrupt occurs. Between Spin2 instructions, however, the state data is confined to only 8 contiguous registers. When a TASKNEXT() executes, the interpreter is in that inter-instruction state, so those 8 registers can be quickly saved and another set can be loaded to effect a task switch. Because ISRs can occur at any time, the ISR cannot do anything with Spin2. However, there is a register called TASKHLT that contains the halt bits for the 32 possible tasks. If you have the task number, you could set or clear bit (31 - task#) to halt or continue that task. This can be done asynchronously from a resident PASM ISR, because the interpreter will check that bit when doing an upcoming TASKNEXT() instruction. By this means, an ISR can affect Spin2 task execution. It can halt and un-halt a task, or the task can halt itself using TASKHALT(THISTASK), to be later turned on the by ISR.

    Ok thanks Chip, got it. There will be no way to force a task to give up its COG processing time until it calls TASKNEXT.

    I guess the closest we could get is to have a scheduler task which only enables one other task apart from itself at any time in the 32 bit mask and the TASKNEXT simply alternate execution between the scheduler and it's next selected task to run based on priority or other wakeup state it uses. This would let us control the task execution order, but the time given to each task will be fully set by where the TASKNEXTs are sprinkled in each task.

  • I think you can use #PR0 from Spin2. Otherwise, I will add something.

    It doesn't work.

  • @evanh said:
    Another area, like the preprocessor, where following C's lead is desirable. Please use the same function names and arguments like fopen(), fread() and the likes. Porting C code will be a breeze then.

    Rather than stdio (fopen, fread, etc.) I would suggest using the POSIX functions (open, read, write, etc.) as a guide. The Parallax flash file system does this, and I think it would make sense to have the SD card have a very similar interface to the flash file system, so programs can easily switch between them.

  • cgraceycgracey Posts: 14,232

    @JonnyMac said:

    I think you can use #PR0 from Spin2. Otherwise, I will add something.

    It doesn't work.

    Okay. I will make it work. I think I will need to do some extra handling in the compiler constant-check routine.

  • evanhevanh Posts: 16,075

    Huh, didn't know POSIX had a separate set. I had wondered why open(), for example, even existed. I guess I should change to those then.

  • RossHRossH Posts: 5,503

    @evanh said:

    @RossH said:
    But by all means eliminate 50% of C code being able to be easily ported to Spin.

    It's a piece of cake to literally have sizeof() * count There's entirely no reason to have two separate arguments to any of the those functions.

    It's not that simple.

    One of the key differences between reading sizeof()*count bytes and count instances of objects of sizeof() bytes is what the function returns when there are not sizeof()*count bytes available in the stream (or maybe just not available yet - remember a stream may not be a file - it may be a serial device associated with stdin).

    There are also differences to do with the efficiency of the two calls.

    For some more information, look here:

    https://stackoverflow.com/questions/295994/what-is-the-rationale-for-fread-fwrite-taking-size-and-count-as-arguments

    and here:

    https://retrocomputing.stackexchange.com/questions/16633/is-this-the-reason-why-fread-fwrite-has-2-size-t-arguments

  • @RossH said:
    One of the key differences between reading sizeof()*count bytes and count instances of objects of sizeof() bytes is what the function returns when there are not sizeof()*count bytes available in the stream (or maybe just not available yet - remember a stream may not be a file - it may be a serial device associated with stdin).

    We've had that like 15 posts up. In the few times you care about the return value beyond "did it read everything?" you could do the division by sizeof yourself. This will also be more efficient since sizeof values are constant and often powers of two. Passing a size > 1 doesn't actually prevent partial elements from being read/written, so there's no reason to have the library deal with it. It's just not a good design.

    @ersmith said:

    @evanh said:
    Another area, like the preprocessor, where following C's lead is desirable. Please use the same function names and arguments like fopen(), fread() and the likes. Porting C code will be a breeze then.

    Rather than stdio (fopen, fread, etc.) I would suggest using the POSIX functions (open, read, write, etc.) as a guide. The Parallax flash file system does this, and I think it would make sense to have the SD card have a very similar interface to the flash file system, so programs can easily switch between them.

    Eric coming in with the big brain take. I never even looked into the POSIX-style functions... They seem like they're less bloated than the libc equivalents (though afaict it still allocates that accursed putc/getc buffer)

  • RossHRossH Posts: 5,503

    @Wuerfel_21 said:

    @RossH said:
    One of the key differences between reading sizeof()*count bytes and count instances of objects of sizeof() bytes is what the function returns when there are not sizeof()*count bytes available in the stream (or maybe just not available yet - remember a stream may not be a file - it may be a serial device associated with stdin).

    We've had that like 15 posts up. In the few times you care about the return value beyond "did it read everything?" you could do the division by sizeof yourself. This will also be more efficient since sizeof values are constant and often powers of two. Passing a size > 1 doesn't actually prevent partial elements from being read/written, so there's no reason to have the library deal with it. It's just not a good design.

    Did you read the links I posted?

    The difference in fread(buf, 1000, 1, stream) and fread(buf, 1, 1000, stream) is, that in the first case you get only one chunk of 1000 bytes or nothing, if the file is smaller and in the second case you get everything in the file less than and up to 1000 bytes.

    Is this a good design? That's kind of irrelevant - it just is the design.

    If you don't want your functions to work that way, then that's fine. But they will not be stdio compatible.

Sign In or Register to comment.