This documentation is subject to change

Multitasking in KnightOS is done with hardware interrupts. When the system interrupt is fired, the following takes place:
All registers are saved to the current thread's register cache
The entire stack is poped into the current thread's stack cache
The thead counter moves the the next thread in the thread cache
The next thread's stack cache is pushed in
The next thread's registers are restored
The interrupt resumes execution

When a program is run, the following occurs:
Interrupts (threads) are disabled
Memory equal to the program size is allocated
The location of the allocated memory is loaded into the thread cache
The default stack (see below) and zeroed registered are loaded into the cache
Interrupts (threads) are enabled
The routine returns

The default stack is empty save the address of the ThreadKill routine, which will kill the calling thread when the thread performs a RET.

When a running thread needs to load a pointer, JP, or CALL, the following occurs:
The programmer has used KLD, KJP, or KCALL instead of ld, jp, or call, which are all macros defined in KnightOS.inc.
The macros consist of:

KLD Register, Address:
rst rKLD
dw Address
db RegisterID

KJP Address:
rst rKJP
dw Address

KJP Flag, Address:

rst rKJP
dw Address
db FlagID

KCALL Address:
rst rKCALL
dw Address

KCALL Flag, Address:
rst rKCALL
dw Address
db FlagID

The RSTs will pop the address they came from. The register and flag IDs are used to determine which register/flag is affected. Since all programs are located in RAM, it can use SMC to change the macro. In the case of KJP, for example, it will change the macro code from a macro to a jp call, add the RAM offset of the current thread to the address, and resume execution. The other macros are similar.
I'll read this more thoroughly when I have a chance, but did you intended for multitasking to be completely disabled while any program runs? Or was there supposed to be a "Interrupts (threads) are re-enabled" in there somewhere?

Edit: And I indeed see that you made this edit. Razz
Nice to know. Some of those macros take up more space than the non-relative version, though.
It seems to be a preemptive system...
Woops, took care of that KermM.
calcdude, it is preemptive, but not pre-interpreting like BAOS, and not fake like TSE. There is a bit more in terms of size, but it has fast start up time and will run faster in the long term, but it will be slow the first time each KJP, KLD, or KCALL is hit.
Might it be better to actually move the stack pointer itself instead of copying a bunch of data around?
One question, for true multitasking doesn't the processor itself have to be able to accept multiple commands? It sounds to me like all your doing is telling a program where to go to when its done, which couldn't that be made simpler by just pushing it on SP so it goes there after an RET instruction? Confused
Ooo, calc84maniac makes a very good point...
I should revise my plan.
Also, xenonparadox, that is exactly what I do. And the processor does not have to accept multiple commands, the method described here is very similar to what your computer is doing right now.
xenonparadox wrote:
One question, for true multitasking doesn't the processor itself have to be able to accept multiple commands? It sounds to me like all your doing is telling a program where to go to when its done, which couldn't that be made simpler by just pushing it on SP so it goes there after an RET instruction? Confused
No, a processor accepting multiple commands at a time is a multi-threaded system. A multi-tasking system splits the CPU time into coarse chunks, and gives each chunk to one of a set of running programs based on how much percentage of the CPU time they've used during their life, and a variety of other factors based on the scheduler algorithm in use.
xenonparadox wrote:
One question, for true multitasking doesn't the processor itself have to be able to accept multiple commands? It sounds to me like all your doing is telling a program where to go to when its done, which couldn't that be made simpler by just pushing it on SP so it goes there after an RET instruction? Confused


Nope. The way he is doing it is more or less the same way x86 does it. The CPU can only run one thing at a time (ignore multicore CPUs for now). The only way to run two things at the "same" time (like you are used to with all modern OSes) is to fake it by rapidly switching back and forth between two tasks. By switching quick enough (~1000 times a second on a modern OS, give or take), it appears that multiple things are running simultaneously.

Thus, there are two types of multitasking: Preemptive and cooperative.

In a preemptive system, the programs running have no idea that they are periodically being stopped and started. Every so often, a hardware interrupt is fired. That interrupt gets caught by the OS, which then does the task of suspending a thread and swapping it for another one. Windows, Linux, OS X - pretty much all multitasking OSes you've used - are all preemptive. The catch is that preemptive requires some hardware support. There must be a reliable interrupt that the OS can handle that is fired at constant intervals. This is pretty common, and Z80 can do this. The other ideal requirement is that the hardware at least supports what x86 calls rings. Basically, there are instructions that only the OS can use. This prevents programs from simply disabling the interrupt the OS uses, or replacing the OS. Z80 doesn't have this. As a result, all programs under KnightOS must play nice.

In a cooperative system, the OS doesn't handle the task of switching threads at all. Rather, it is up to the currently running thread to eventually say "I'm ready for a break, bring in the next thread". This can run on hardware that doesn't support interrupts, and, more crucially, systems without a preemptive OS. You could have a multithreaded program on an OS that doesn't support threads, for example. However, it is much harder to use from a development standpoint.

KermMartian wrote:
No, a processor accepting multiple commands at a time is a multi-threaded system. A multi-tasking system splits the CPU time into coarse chunks, and gives each chunk to one of a set of running programs based on how much percentage of the CPU time they've used during their life, and a variety of other factors based on the scheduler algorithm in use.


Er, no. Are you sure you graduated? Threads, tasks, processes are all the same to the CPU. It can only ever run a single stream of instructions, one at a time. The differences are all OS level (well, they have "hardware" support for performance reasons, but they are still OS level ideas).
Ok, so I'm playing a bit fast and loose with terms, but I'm saying that multiple threads can run simultaneously on the multiple cores of a multi-core system, which is a separate phenomenon than breaking up CPU time on a single execution core to spread among multiple tasks. Of course, there's the combination of the two when a multitasking OS runs on a multicore system.
KermMartian wrote:
Ok, so I'm playing a bit fast and loose with terms, but I'm saying that multiple threads can run simultaneously on the multiple cores of a multi-core system, which is a separate phenomenon than breaking up CPU time on a single execution core to spread among multiple tasks. Of course, there's the combination of the two when a multitasking OS runs on a multicore system.


That still has nothing to do with threads or tasks. That is because there are two physical CPUs (well, two cores - but they are essentially independent CPUs with the exception of a shared L3 cache if present). No duh two CPUs can do two things at once. Razz

You're not playing fast and loose with the terms, you've got the terms all wrong Wink
Kllrnohj, you seem to have expertise, which is something I need. I'll keep in touch.

Also, KnightOS will use a preemptive system, and it will hope and pray that threads don't use DI unless they really need it. I mean really need it. If it gets too bad, I'll make some common system routines call EI just to be a jerk Just Joking

A cooperative system is too much to ask of developers.

For the reference of others, let me explain a couple of systems that were considered before the method above was accepted.
1) Interpreted assembly programs
a) It involved using some free RAM as "ExecutingOpCode." It was scrapped almost immediately. This is such a bad idea that I'm not even going to explain it and risk someone try using it.

2) Cooperative System
a) This has already been explained.

Interrupt-Based (The only ones that were seriously considered):

3) Page-Swapping
a) This would not require location independent code, but switching threads would take a long time and there would be a severe limit on the maximum number of threads. The method involves swapping pages to different executing threads so that their code was always at a specific location.

4) Preemptive pre-interpreting
a) This method involved using a preemptive system to switch threads, and requires location-independent code. It would interpret the code before running it, and fix all of the JPs, CALLs, and LDs.

5) Preemptive real-time interpreting
a) The preemptive system described in the first post. This is the accepted method.
Actually, lots of high-speed routines, including several of the Celtic III ones for instance, execute di so that they can take advantage of all the shadow registers. I don't see a problem with people doing this, as long as you make the convention for KnightOS be that you ei after relinquishing use of the shadow registers. In additional for the sake of sanity, you or the userspace programs should di/ei around things like LCD updates and link port activity to avoid leaving the system in an undefined state. I'm sure you already thought of those examples.
Despite popular belief from the community, I have put lots of serious thought and planning into this Rolling Eyes
Major OS routines that could leave the OS unstable if stopped, including fastCopy, are already enclosed with di/ei.
Also, the current version of the tasker does not require the shadow registers, and saves them along with the other registers, leaving them free for program use.
SirCmpwn wrote:
Despite popular belief from the community, I have put lots of serious thought and planning into this Rolling Eyes
Major OS routines that could leave the OS unstable if stopped, including fastCopy, are already enclosed with di/ei.
Also, the current version of the tasker does not require the shadow registers, and saves them along with the other registers, leaving them free for program use.
Ah good, I'm glad you've thought about these thoroughly. If you'd like, I'd be happy to share whatever knowledge I have from the scheduler part of my OS class, in case you're planning on trying to implement some kind of "fair" scheduling.
The current version uses equal scheduling, and just swaps everything evenly. I may change this as I see fit. Keep in mind that the OS is not even 10% done.
Also, I have a nice big stack of OS books from way back when I reference regularly. Unlike TI, I plan on following several standards already out there while writing my OS and making it a logical, functional OS. TIOS is nice to start out with, but it is limited and KnightOS will be much better, and much more OS-like. [rant]Such as using RAM as memory, not storage. What the hell is wrong with TI? No one uses RAM for storage, idiots[/rant]
Yeah, because real OSes have a backing store that has order of magnitudes lower latency and higher write limits than the calculator's Flash. Razz Not to mention that they're random-access read and write, unlike the calculator's Flash, which is only random-access read but block-access write.
Well, there are some standards that are simply not feasible on a calculator, but I'm writing an OS that I intend to be functional and logical, not weird and unlogical, like TIOS. TIOS doesn't follow any standards of OSes, as far as I'm aware.
SirCmpwn wrote:
Well, there are some standards that are simply not feasible on a calculator, but I'm writing an OS that I intend to be functional and logical, not weird and unlogical, like TIOS. TIOS doesn't follow any standards of OSes, as far as I'm aware.
Indeed, I think they mostly followed their own standards or specs. I think it's admirable of you to try to follow more widely-accepted standards, but not if you're going to take it to the extreme and follow standards at the cost of ignoring the vagaries of the platform you're writing for.
Nope. As you can see from my earlier post describing potential multitasking methods, a lot of thought goes into how to best apply these standards to a calculator, instead of just blatantly accepting each one as necessary.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 2
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement