@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.
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!
@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.
@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.
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.
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.
@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.
@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.
@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!
@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.
@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?
@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.
@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.
@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.
@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 notsizeof()*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.
@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 notsizeof()*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)
@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 notsizeof()*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.
Comments
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.
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
andfwrite
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.
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.
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.
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.
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 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
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.
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.
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.
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.
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!
Whew!!! I hope it all makes sense as you start using it. Ask any questions here and I'll answer them.
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.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.
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.
Then, later...
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:
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.
It doesn't work.
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.
Okay. I will make it work. I think I will need to do some extra handling in the compiler constant-check routine.
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.
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
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.
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)
Did you read the links I posted?
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.