C cross-compiler for 6800 (yes, you read correctly)

I explicitly said "hardware" stack pointer. One can make as many stacks as there is memory for on any usable processor :-).

The fundamental difference here is that on an exception or subroutine call there is no memory access imposed by the CPU hardware (thus no GP register needs to be any different from the rest).

Sometimes one does not resort to any stack usage indeed; e.g. on a missing PTE exception on a 603e derivative core one gets r0-r3 switched to an alternative bank (typically used for nothing else) and loads the page translation entry in a few cycles (data memory access needed only to the page translation table, program memory can be locked in the cache). Must have allowed the designers to simplify the core at the (negligible) cost of a few bytes of program memory.

On "normal" exceptions (i.e. pretty much the rest of them) one usually does use a stack, e.g. in DPS there are 3 of them maintained (using only one GPR). There is the IRQ stack pointer - which is used at the beginning of an exception when it will save registers; there is the supervisor SP (task unique) to which the system call exception switches after initially using the IRQ SP and there is the user SP - also task unique.

Dimiter

------------------------------------------------------ Dimiter Popoff, TGI

formatting link

------------------------------------------------------

formatting link

Reply to
dp
Loading thread data ...

It's been a long time, but there were some small forth packages that would run on a 1K ram systems like the Kim 1. Loaded from cassete, iirc. Correct me if wrong...

Regards,

Chris

--
** Remove the meaning of life to reply...
Reply to
tridac

May be a bit unfair - just at the time I was a died in the wool asm programmer (there wasn't anything else) and used to getting a lot done with very few instructions :-)

Looking at the asm source, it produced what looked to me like very laboured and inefficient code, but perhaps that was the state of the art at the time, even allowing for the 16 bit requirements. The 6502 in particular was very efficient for 16 bit work, having pre and post indexed indirect addressing modes. You could write a linked list tree traversal program with just a few lines of asm...

Regards,

Chris

Reply to
tridac

I use coroutines to multithread my device drivers and file system server in my half working microkernel os. I don't have kernel support for threads so use the Libtask library for coroutines.

Libtask provides tasksleep(rendez), taskwakeup(rendez), taskwakeupall(rendez) and taskyield() functions. These act on condition variables. With pthreads a mutex is needed in conjunction with a condition variable for mutual exclusion.

Similarly Unix kernels used to disable preemption upon entering the kernel and use the same sleep and wakeup on condition variable primitives. So really Unix kernels acted like a collection of coroutines

Both my file system and drivers use coroutines and follow a similar multiple Directors - single Secretary pattern where the main task is the Secretary that listens for incoming events using a select()-like system call. The secretary then wakes up whatever Director coroutine handles that connection or unit to do the actual work.

The director coroutines may wakeup or sleep on other coroutines or wait for futher events from the secretary. When all Director coroutines can no longer progress and are sleeping the Secretary goes back to waiting on external events using the select()-like system call.

If a director blocks on say an interrupt it will still allow the other unit's director to service incoming messages and interrupts.

In pseudocode it might look like...

Secretary() { while (1) { handle = WaitFor (ANY);

if (handle is message port) { u = DetermineUnit(handle); unit[u]->message_pending = 1; taskwakeup (unit[u]->message_rendez); }

if (handle is interrupt) { u = DetermineUnit(handle); unit[u]->interrupt_pending = 1; taskwakeup (unit[u]->interrupt_rendez); }

// taskyield() returns 0 when there are no // other coroutines to run. while (taskyield() != 0); } }

Director() { while (1) { while (this->message_pending == 0) tasksleep (this->message_rendez);

this->message_pending = 0; msg = GetMsg (this->msgport_handle);

// Process message, begin io, wait for interrupt

while (this->interrupt_pending == 0) tasksleep (this->interrupt_rendez);

this->interrupt_pending = 0; UnmaskInterrupt(this->irq);

PutMsg (this->msgport_handle, reply_msg); } }

The file system works in a similar way, with a director coroutine for each incoming connection. These directors can be blocked if vnodes or cache buffers are marked as busy so they have to sleep like the example above. When another director is finished with a vnode or buffer it marks them as not busy and akes up the other coroutines sleeping on them.

There are an additional set of coroutines that I call subsecretaries, These handle the strategy() function, taking a queue of buffers and sending them to the device drivers to be read or written. Once done they mark the buffers as not busy and notify any waiting directors.

There are two other coroutines, one to automount drivers and another to perform a periodic sync so that buffers marked as delayed write eventually get written. My code for acquiring and waiting for the vnodes and cache buffers followed the description in the book by Bach called, "The design of the Unix operating system."

Multithreading with coroutines in this way allows the file system to continue serving requests from other processes for data already in the cache whilst an IO operation to the device driver is underway. It could be considered a form of hit-under-miss.

The original Minix file system implementation had issues with the lack of threading. A read from the floppy would block requests from other processes being serviced until the slow floppy IO was complete. Someone did implement a multithreaded file system for Minix in the early 90s, but I couldn't find anything about it.

The current Minix added a multithreaded file system in the last few years using coroutines I believe. QNX used coroutines inside its file system FSys server before they implemented kernel threads, they may still use coroutines. The Amiga's file system, dos.library also used coroutines.

The HelenOS operating system is a microkernel and they have coroutine support and calls them 'fibrils'. I believe they have true kernel threads as well so I'm not sure if it uses coroutines inside its file system server.

I really thought I had discovered something new when I worked out how to do cooperative multitasking inside a file system a few years ago. I thought I was genius who deserved a Turing award for what I discovered.

That is until I learnt that what I had discovered were called coroutines and were a really old technique. I felt a bit depressed that I hadn't discovered something new :-(

--
Marv
Reply to
Marven Lee

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.