> 	                    Ha3sm  
> 	        sketch of a new operating system
> 
>              Hohensee's autonomous 3-stack machine
> 
> 			Rick Hohensee
> 			 Dec 23 2000
> 			Maryland, USA
> 
> Overview
> intro
> 

Dec 22 2001 
This is now an accretion. First, some recent thoughts...

There are several basic ideas to Ha3sm, and they are gradually becoming
clearer to me, although they have been at work in less clear form
previously. For my purposes, an OS is the thing that manages access to a
computer over a single time axis. Not a network. A bus. Ha3sm models a
time-interlaced CPU-heterogenous multi-processor system on a single main
memory bus. Let's call this HIMP, Heterogenous Interlaced Multi Processor.

"Rick, you gotta understand, A computer is a bus." 
			my late friend and Forth guru Wilfred Bordouin

Another basic idea of Ha3sm is "everything is a device". Another is that
the local console user is analagous to the kernel, and may as well live in
the same memory, "0wnerland". In conjunction with sequestered user
processes, Ha3sm is then a hybrid of Forth, AmigaDos and unix.

A typical personal computer can be viewed as a HIMP. Devices capable of
bus-mastering such as FPUs, DMA controllers, video controllers and so on
are co-CPUs. When they have the bus, they are the CPU. Except as pertains
to interrupts. THE CPU is THE CPU for two reasons; it is the most general,
and it handles interrupts. That is, THE CPU does the crucial
synchronizing. From what I can tell of the inscrutable discussions of SMP
in the linux-kernel mailing list, this doesn't really go away in SMP
systems either.

The synchronization role of the CPU or synchronizer is the crux of a
system, and the crux of an OS. Consider a system where the scheduling
function of the OS is all the synchronizer hardware does. There is no
great design leap to having numerous ancillary processors for processes.
In fact, the term "process" seems to strongly predict this line of
evolution. Processes are typically virtual machines. Even on a single main
bus, they need not be virtual. Sequestering process memory is about the
same with numerous task-processors as it is with single-device
state-changed processes. Thus Ha3sm is in some ways an implementation of
what computers seem to me to be strongly trying to become. I believe that
numerous stack machines can be a revolution analagous to, but not as
far-reaching as, the advent of the microprocessor.

Time maps one-to-one to main memory access in a Ha3sm system. There's one
processor on the bus at any one time, and all others are idle. This does
not have the allure of SMP, but it doesn't have the quagmire aspect
either. But consider the advantages of a HIMP with numerous
task-processors. A process switch is a DMA-switch. There is no wholesale
state save. That is, process-switch latency is near nil. This could also
benefit device drivers, which would I suppose actually be the first things
to get dedicated processors. The Von Neumann Bottleneck remains, but the
fuel passing through the bottleneck is enriched tremendously by extremely
crisp state-changes.

Ha3sm models a machine of this nature, a DMA-tasking HIMP, and such a
machine is quite achievable now in hardware at modest investment (compared
to a new x86 CPU design or similar). Ha3sm maximizes potential
heterogeneity by a syscall mechanism that assumes nothing of either the
synchronizer or the various other processors. The only thing that can be
assumed so far in this design is that there is a shared main memory, and
we will here mention that we further assume that the synchronizer can see
all of it, and thus any process's memory. This is sufficient means of
communication for a general parameter-passing scheme for syscalls that
allows extreme heterogeneity.

There are only a handful of distinct syscalls in Ha3sm, because one of
them, "please", is a general interpreter. (this is a way to pretend we got
rid of ioctl() :o). A typical syscall will require several arguments to be
passed between the process and the synchronizer. In Ha3sm these are placed
in a predefined order in a predefined integer format at the process's 0
address, and a trap to the synchronizer is invoked. Processes are passed
the requisite knowledge of how the synchronizer likes it's ints at process
invocation time, also at process address 0. No stacks, no registers. Total
heterogeneity. We do assume that a process knows the submission (syscall) 
numbers.

The "please" interpreter the Ha3sm synchronizer implements is Ha3sm's
namesake, Hohensee's autonomous 3-stack machine. This is a Forth-like
language with a Pyte Data Stack, which is ints of variable size. please
calls are interpreted though, and no processor in Ha3sm has to actually be
a hardware H3sm (Hohensee's 3-stack machine, without the "autonomous",
i.e. a programming system and virtual machine only, not an OS.). Thus it
can be implemented on commodity one-stack machines. Forth engines are
extremely compact in hardware. Current technology such as FPGAs should be
able to host numerous switchable stack machines on a single device. Thus
it is envisioned that early Ha3sm hardware would be a commodity chip
synchronizer and a one-die array of stack machines.

On 386, the Ha3sm synchronizer will be implemented in osimpa, my
meta-assembler in Bash. I have converted H3sm, the application version of
my 3-stack machine, originally in C, to a Gas/m4 style that was a
predecessor of osimpa. The please interpreter will be based on that, while
other handlers and services may be straight osimpa, or may also be written
in H3sm if that proves acceptable for performance.

Only guests have to say please. At the console, the user will be faced
with a H3sm prompt from 0wnerland, providing the please interpreter
without the please. Ha3sm is thus extensible interactively, and comes with
a "shell" in the kernel. I have implemented a predecessor of this by
inserting H3sm in the Linux kernel as a kernel thread or "daemon". You can
thereby read, or write I guess, Linux internals interactively.

Ha3sm can be approached from scratch, or wiggled into from existing
working systems. H3rL, Hohensee's 3-ring Linux, the aforementioned Linux
with a H3sm in it, could grow a please syscall, and then a few others,
consuming Linux slowly from inside. Linux also allows reuse of the 0
address of a process. I have clobbered the ELF header of various demo
utilities I've written in osimpa on Linux for use as locals, to no harm.
osimpa executables use 0 for the load address. Alternately, there are
several bootable Forths extant now for x86 pmode and similar, and they too
could grow toward Ha3sm piecemeal.





I've decided to post this without crediting the respondant so he won't be
harrassed about my project, since he has his own. If that's incorrect I'll
fix it. The respondant holds court on EFnet IRC.

> This is a design excercise. It is for my own mental organization, but the
> reader may see a variant of this she wishes to pursue, such as a 2-stack
> version. Comments on things that clearly won't work will be appreciated,
> if they are in terms of the parts of this design that apparently will
> work. H3sm, for example, the unix-hosted 3-stack programming environment,
> works. H3sm is without the "a" for autonomous. It's not an OS.  
> Additional detail is also invited, if it is based on what is established
> here. I don't know if any of Ha3sm is new. The "everything is a device"
> thing may be, and the device IO syscalls look a bit fresh. Some things in
> H3sm are separately novel, and the H3sm keyword abbreviations facility is
> even useful.
> 
> Syscalls
> 
> We start from the syscall interface, which I find has value for defining
> an OS. Firstly, yes, Ha3sm does have a syscall interface. There are
> processes, and a kernel, and processes can request services from the
> kernel via syscalls. A syscall is a syncronous exception, or trap. A trap
> is like a subroutine call, but more of the machine's state is changed than
> just the program counter and the return stack. In the case of Ha3sm
> syscalls the state change is fairly lightweight. There is a change to the
> kernel memory space, but there is no wholesale pushing of registers.
> Syscalls therefor are "caller-saves". The caller doesn't usually have to
> save anything though.

X>Consider that the difference between a syscall and an exception/trap is
X>in terms of security.

X>How does the kernel request services from user-space?

It doesn't, because it shouldn't.

OR, same as any other process, but with no perms restrictions. Via syscalls.

i.e. I'm not sure, and am not sure I see a need for that.

Dec. 2001    Hmmmm. Via published services. 


> Ha3sm can have a separate protected memory address space for each process.
> There are two types of process; owner and guest. Guest processes must
> login, and are in individual protected memory spaces. The kernel and owner
> processes are not so sequestered. The main purpose of a syscall is to give
> control of the machine to the kernel temporarily at the bidding of a
> process. The kernel can see all memory, and knows what process called it
> on this particular syscall, so the syscall handler code in the kernel can
> use guest process memory areas and/or kernel areas.

X>Ie to break out of an untrusted context into a trusted context.
X>We have no protection from the kernel.

Ha3sm does not protect processes from the kernel. I don't think it's
possible to do so, and I believe claims to the contrary by various systems
are illusory, so merely as a design constraint, I will not attempt to.

X>What is an owner process?
X>An owner process is a kernel thread.

Yes. Usually with some other goodies, but yes.

> So far we have a multi-tasking kernel, owner processes and 
> protected-memory processes. Ha3sm does not do memory virtualization to
> persistant store, AKA paged or segmented virtual memory. There is no
> swapping of memory to/from disk as a general kernel service. Ha3sm also
> provides no mechanism for a process to change it's initial memory
> allocation. The most a process will be allocated is half the physical RAM
> remaining unallocated.

X>Extremely conservative, can see no justification for this arbitrary
X>resource mangling.

I'd call this an extremely conservative and arbitrary avoidance of
resource mangling. This also is subject to change, but this does express
my design tendency vis-a-vis malloc()/brk(). Just say no. This is a
corollary to Rick's VM Polemic: "Virtual memory is obsolete, and always
was." (mass-storage VM, not protected memory spaces.)

> Processes are like virtual computers. Each one has an affiliated user, an
> address space, and normally, a core H3sm dictionary, including syscall
> words, which provide the virtual machine with I/O. H3sm (no a) is a
> 3-stack virtual machine, programming language, and interpreter. Each Ha3sm
> process then is like a shell, and also somewhat analagous to libc or other
> dynamic linking library. This is typical of a Forth-like language. The
> H3sm programming language defines a 3-stack (virtual) CPU. This is the the
> CPU Ha3sm is designed around. Ha3sm will allow non-H3sm-based processes,
> but H3sm-based is the norm. When a Ha3sm process calls a syscall, the
> kernel handler for that syscall is invoked. The kernel gets the CPU, as
> far as program flow is concerned, but because it's a lightweight
> task-switch, the CPU still has the register state the process left it with
> for most registers. This is how things are communicated between the
> process and the syscall. This means that the kernel observes the register
> usage of H3sm. In platform-independant terms, syscalls model the 3 stacks
> of H3sm. On H3sm-style hardware this would not be a matter of
> virtualization. In other words, Ha3sm uses H3sm calling conventions. This
> is relatively flexible as pertains multiple return values and so on.
>
> Devices
> 
> Syscalls are sync/h/ronous. The running program which currently is in control
> of the CPU causes them, perfectly in step with what it is otherwise doing.
> Syscalls then are like subroutine calls into the kernel. Asynchronous
> interrupts are another matter entirely. Hardware devices like keyboards,
> discs and so on may need to interrupt the processor at any time.  Such
> interrupts are called asynchonous exceptions. The system has to deal with
> them immediately, which means it has to drop (and save) what it's doing
> and handle the interrupt. Hardware interrupts are signaled directly to the
> CPU. The CPU then invokes a handler for the particular type of interrupt
> detected. This is a crucial facility of an operating system. The handling
> of various interrupts is usually considered in terms of discrete device
> drivers.  A complete driver includes the asyncronous handlers to handle
> the activities of the device itself, and the interface to the syscalls
> that let the rest of the system use what the device does. Asynchronous
> interrupts involve a heavier state change than syscalls. The current
> registers are all saved, and a full kernel state is entered, since the
> state of the process that was running at the time the interrupt occured is
> irrelevant, and must be preserved for when the device handler is finished.
> 
> Ha3sm is based on a simple division of hardware devices into two types.
> There are block devices and stream devices. Each type may have a send
> and/or a recieve channel, and the entire driver namespace has a message or
> command channel. A driver may therefor be one, two or three channels.
> Syscalls will exist for talking to the various channel types. The
> asynchronous interrupt handlers, the "hardware interrupts", run entirely
> within the kernel creating the channels for the syscalls to talk to.
> 
> Scheduler
> 
> For a system to have multiple processes each getting some portion of the
> CPU time, there has to be something causing them to take turns. This is
> called the scheduler. Ha3sm has a system timer and affiliated interrupt
> and handler that will assure that no process keeps the CPU for more than
> some measured timeslice at a time.  The scheduler then invokes the next
> process. Methods for determining who's turn it it with the CPU vary, but
> the main thing is that Ha3sm will preempt a process if necessary. This is
> called preemptive multi-tasking.
> 
> Realtime
> 
> Hardware interrupts are prioritized. In a system wth several hardware
> devices operating on thier own individual timescales, there will be timing
> conflicts to resolve. Various things will want attention at the same time,
> and there's only one CPU. A particular machine will have one interrupt
> signal that is not interruptable by others. In Ha3sm this interrupt is
> reserved for use for special tasks that must always be serviced as soon as
> the system is capable of doing so. This is called hard realtime. It is
> feasible to guarantee a short response time for one thing at a time, and
> that one thing may be periodic. Ha3sm will be implemented such that
> resources for one periodic hard realtime task are reserved, and so that
> that interrupt can be serviced as quickly as possible. Tricks are
> contemplated to allow servicing an asynchronous interrupt without a full
> task-switch, i.e. in callee-saves fashion. 
> 
> Users
> 
> When Ha3sm starts, it starts a H3sm running in the kernel memory space.  
> This is an owner process, and is distinct from other processes mentioned
> so far. Guest processes in the system are invoked via the login syscall.
> The login syscall checks for a user name and password, and only starts the
> new process if the user info given is valid. If it is, the resulting
> process can access only the devices that user is registered for. Access
> permissions as enforced by the Ha3sm kernel itself are per-device
> per-user.
> 
> Services
> 
> A process may offer some facility to the rest of the system by registering
> a service it provides as a device. This is called a process service. Upon
> successful registration of a service, the service may be invoked by the
> syscall interface. A process may register several services. This allows a
> process to implement things like filesystems, network interfaces and so
> on. Ha3sm is therefor what is called a microkernel, where "everything is a
> device". The "everything is a device" aspect provides interprocess
> communication needed for such things via the regular device syscalls and
> namespace.
> 
> Recapitulation of overview
> 
> Ha3sm, Hohensee's autonomous 3-stack machine, the hypothetical OS, is
> based on the Hohensee's 3-stack machine model of a CPU, the existing but
> very rustic programming environment. Ha3sm syscalls pass values between
> the kernel and user processes based on the H3sm model. Ha3sm is
> preemptive multitasking. It is true multi-user, in that non-owner users
> are in (hardware) sequestered memory-spaces. Ha3sm can provide hard
> realtime handling for only one periodic task. Ha3sm is a microkernel
> providing device abstraction and CPU abstraction, but not virtual memory,
> network interfaces or filesystem abstractions. Interprocess communication
> and other facilities may be implemented as services of processes, which
> are registered as psuedo-devices. In Ha3sm, "Everything Is A Device", but
> there are 2 types of devices with 5 types of "channels". There is also no
> provision planned for SMP, journalling or snapshotting the system. Ha3sm
> is planned to be implemented on the 386 first, but note that this is the
> first mention of a specific hardware platform. The hardware requirements
> of Ha3sm are similar to, but a bit less than, what unix requires. Ha3sm
> would probably take better advantage of multi-media hardware than unix
> does. 
> 
> 
> 			------------
> 
> Device types and thier syscalls
> 
> There are block devices and stream devices. This is in aknowledgement of
> the deep difference between things that have a finite size, and things
> that can best be expressed as having a rate over time, but no static size
> bounds. This directly gives rise to four types of data channels, and I add
> a control channel. Each of these channel types has a syscall. Block
> devices communicate via blocks in a manner similar to Forth. Stream
> devices communicate via ring buffers. Ring buffers keep a throughput
> count, which can be masked down to a current input cursor, and also can be
> used as a check value to see if a process has not kept up with device
> throughput, i.e. a process may check if it has been lapped by the device
> in the endless race around the ring. The size of a particular ring buffer
> is a literal in the code for the particular device, and should be a power
> of two for easy masking down the throughput value to the cursor value.
> 
> Forth standard practice involves stack-effect diagrams when presenting a
> Forth word. A Forth example...
> 
> SWAP 	( A B --- B A )
> 
> The above is semi-temporal, left to right, and --- signifies the action of
> the word. Thus the above starts with B on the top of the stack, does SWAP,
> and then the result is that A is on top.
> 
> H3sm stack-effect diagrams are the same, with the addition of (P: ) for
> pointer stack effects, and Size! for things that may change the Size
> register.  For some words that need stack items they don't modify, I use 
> (required ||| consumed --- produced )  e.g.  over ( a b ||| --- a). 
> Here are our first syscall words. This is the process side of these
> syscalls.
> 
> 
> read		( --- success ) (P: device_ID block ||| )  	
> 		read a block into process memory directly from a block
> 			device.
> 
> write		( timeout --- success ) (P: device_ID block ||| )
> 		lock the device, or wait/timeout. If obtained, write block
> 			in process memory directly to device. unlock.
> 
> recieve		( --- success ) (P: device_ID ring ||| )
> 		read a segment of a kernel ring buffer from the process's
> 			concept of current in the kernel ring buffer to 
> 			the kernel's concept of current. That is,
> 			sync/update the process buffer with the device 
> 			buffer. Check if the process has been lapped. This
> 			requires a buffer in the driver and the process.
> 
> send		( timeout --- success ) (P: device_ID ring ||| )
> 		lock the device, or wait/timeout. If obtained, write 
> 			the process's pending ring segment to the drivers 
> 			ring buffer from the driver's current. unlock.
> 
> message		( --- success ) (P: message/reply_buffer ||| )
> 		Send an ASCII string to a device. Wait/timeout for reply.
> 			The device may be addressed by the ASCII header
> 			syntax vis-a-vis the kernel device namespace.
> 			The reply, if any,  may go to to same buffer the
> 			message came from. This is a half-duplex channel.
> 			The process is the master. There is no buffer on
> 			the kernel side. There is the device namespace in
> 			the kernel, and other device-specific dynamic info
> 			and literal strings. ASCII itself contains a
> 			telegraphy addressing syntax, which is reusable
> 			as is. message may be hardwired to wait one
> 			Cycle. 

X>How does a device volunteer information to a client?
X>This implies that the only way a user-space program can know about the
X>world is constant polling at some arbitrary frequency.
 
Yup. Once per jiffy in most cases, which, so far, looks like exactly once
per Cycle. This means that ring buffers have to be big enough so that the
process doesn't get lapped by the driver in the ring buffer in one Cycle.
Fewer processes makes this arithmetically less problematic.

20021210 
 This is still an issue. Something like the Amiga event-driven scheme is
needed, where there are registered listeners for particular events. Thus
the mouse driver e.g. can summon the appropriare app. Or apps. I guess.
The Amiga went well beyond signals, I know that.

> Note that write and send channels must be locked on each use. Locking is
> implicit to write and send, but it is the process's responsibility to
> partition sends/write sizes appropriately. Read/recieve channels don't
> care how many concurrent readers there are; they multiplex implicitly.
> Writes, on the other hand, may only time-interlace. The maximum individual
> send is the size of the device's buffer. These calls need a success/fail
> return value. The sense of this value will be success = true = <>0 . This
> means success return values can be more informative than failure return
> values, which are always 0 (false). Specific calls may have other specific
> stack effects if more failure info is desired than a 0.
> 
> The kernel support for the message syscall could be very fancy. It's
> vaguely analagous to a hybrid of ioctl(), ICMP, and the proc system in
> e.g. Linux. The kernel must implement a device namespace. It may be
> convenient to provide answers to a message call that don't map to a
> particular driver, such as messaging for a list of visible drivers. Some
> devices, such as a time-of-day service or device, may need only a message
> channel.
> 
> A process needs to open and close devices it uses, via the open and close
> syscalls. open sets up references to the device, gets it's buffer size if
> any, determines if it exists, and in some cases, tells the driver where
> the in-process buffer is. open will also synchronize the driver's current
> throughput value and the process's version thereof. In other words,
> communication requires some commonality of state between parties, and for
> the four device-specific channel types that is established by open, and
> de-allocated by close. De-allocation on the kernel side will be minimal.
> Locks for example are already self-de-allocating. The kernel device
> namespace as a whole is always "open" vis-a-vis the message syscall.
> 
> 
> open		( timeout --- Device_ID=success ) (P: device-specific)
> 
> close		( Device_ID --- )
> 
> Timing
> 
> A simple round-robin scheduler is contemplated. It is hoped that "once
> around the process list" can be used as a simple, nearly implicit means of
> timing wait/timeouts. This may also, it is suspected, have some benefits
> in preventing deadlocks, if once around the process list has some
> randomness to it. If not, other arrangements will be necessary. A maximal
> process time slice, a "jiffy", wll be minimal. There is an obvious
> tradeoff between jiffy duration and time spent doing task-switch
> bookkeeping. A substantial portion of total CPU time can be consumed by
> state-change activities if the jiffy duration is too small. Less
> obviously, a significant hit here, on the order of 30%, may be worthwhile
> for overall simplicity. "Once around the process list" is hereinafter
> called a Cycle. The implementation of a Cycle may change to something more
> controlled, but the idea of a several-jiffy wait period will probably
> remain. Relatively heavyweight processes also means relatively few
> processes which also means a Cycle should be relatively short.
> 
> The "wait" kernel subroutine will be used by IO syscalls and the "rest"
> syscall. wait will wait, and will involve a timeout. This is a
> down-counter for number of times some condition is to be tested. A timeout
> spec of 0 means "now or never". wait must perform a conditional branch on
> timeout, which is handled by the syscall code in the kernel. wait doesn't
> branch if the test condition is satisfied.
> 
> rest		( no stack effects)
> 		relinquish the CPU for one Cycle. This is wait with a 
> 			timeout of 1.
> 
> Owner and guests
> 
> There are two very different kinds of process; owner and guest.  All guest
> processes must login with user ID and password. There is no separate
> fork() or exec(). The usual utilities are expected to be threads, i.e. not
> full-blown processes, i.e. they will be H3sm words. Owner processes are
> also only spawned by login, but may or may not require
> owner authentication.
> 
> The initial process invoked by the kernel is an owner process. Owner
> processes have a dictionary and so on at an appropriate point in the
> physical address-space, and can see all RAM. IFF a login is attempted from
> the system keyboard, which may be defined at boot as some other device
> such as a serial port, then an owner login is allowed. owner logins are
> strictly dis-allowed via process services, which precludes network
> interfaces, which precludes logging in as owner over a packet-switched
> network. 
> 
> The system may be configured at boot to find guest authentication data on
> mass storage. Guest processes don't need access to this data. This data
> may also include per-guest device access permissions lists. It seems the
> usual password encryption routine will have to be in the kernel as a
> subroutine of login if guests are to be supported. Guest processes are
> controlled by strict-constructionist permission mechanisms, i.e. guests
> can only see things specifically allowed to them individually. Owner
> processes, on the other hand, can "fandango on core". In short, Ha3sm is
> intended to be difficult or impossible to administrate over a network.
> Local graphical display drivers will perform best as, or as threads of,
> owner processes. Each guest process is then somewhat analagous to an
> instantiation of Tripos, CintPOS or AmigaDos, and an owner process sees
> the whole system like AmigaDos. This is unlike the unix superuser, which
> is still a regular user as far as memory protection.
> 
> This means that Ha3sm is actually divided into owner-space and
> guest-spaces, rather than kernel/user. The kernel provides an interpreter.
> If an owner process crashes it may well take the system with it. A guest
> process crash is in effect a logout.
> 
> login		(P: name password --- ) parent ( --- 1 )
> 					child  ( --- 0 )
> 
> The parent/child terminology is only germane at the moment of birth. Once
> a guest process is spawned it is the same as any other guest process, and
> so is the case with owner processes. Anyone can call login. Permissions
> aren't inherited.
> 
> 
> Simplicity, Portability and Performance
> 
> Pick any two. I'll take 1 and 3. My suspicion is that overall system
> simplicity can allow the Ha3sm kernel to be written entirely in assembly
> for a particular platform with an effort an individual can achieve in some
> few months. This may be a payback for some of the things that could be
> more elegant. So it is with simple round-robin and Cycles. The message
> syscall is unbounded in terms of what it might be called upon to provide,
> but it is also non-essential. Most of the device code should be re-usable
> for all devices of a particular channel type. Ha3sm therefor exchanges
> total code non-portability for smallness, which itself helps porting. It's
> probably possible to in some sense write the kernel in Forth, but I'd just
> go for asm. Looking at other kernels, and my own code for H3sm, high-level
> languages are over-rated for low-level tasks. There's about 90 primitives
> in H3sm that I'm currently rewriting in 386 assembly. The "a" in Ha3sm is
> probably a similar amount of code. Although this is utterly
> platform-specific code, the H3sm primitives and the Ha3sm interrupt
> handlers provide a succinct point of portability. This provdes a clear
> target or plateau above which asm is not necessary.
> 
> Once a Ha3sm is built, it seems to be capable of providing complete host
> abstraction to the H3sm model. This is nice, given that H3sm hardware is
> possible. Also of note, the syscalls I foresee don't use H3sm sized data,
> or Pytes. With a little twiddling of the syscall stack effects
> implementation, Ha3sm revolves quite neatly to Forth, and/or the kernel
> could be in Forth while userland is in H3sm, if a Forth could be contrived
> to agree on register use with a H3sm.
> 
> Compared to other systems
> 
> I've been trying to combine Forth, AmigaDos and unix for a long time.
> ancillary to that process, Forth grew another stack and a Size register,
> unix and AmigaDos lost files, and AmigaDos became a process. About a week
> ago the Everything Is A Device scheme occured to me, and the thing started
> to look like it might coalesce. The third stack in H3sm reduces namespace
> explosion in Forth. Similarly, throwing out files reduces syscalls versus
> other systems, but they no doubt will have to be emulated somehow. 
> 
> The Amiga had some clever hardware for handling multi-media realtime
> tasks. What can be done in this regard is limited on an IBM PC, but the
> Ha3sm realtime facility does what can be done.
> 
> The predecessor of AmigaDos was Tripos from Martin Richards at Cambridge.
> The latest incarnation of Tripos from Richards is CintPOS, which runs as a
> Tripos emulator on Linux. This is closely analagous to
> AmigaDos-as-process. CintPOS and Tripos are written in BCPL, which C was
> based on. BCPL is to some extent a Forther's C, or the third generation of
> what Forth is the fourth generation of. This is probably due do BCPL's
> educational primary mission. BCPL does not have types or structs. It is
> based on cells, as are recent Forths, H3sm, and Ha3sm. BCPL even has
> MULDIV, an algebraic analogue of Forth's */ . The most promising sources
> of application code examples for Ha3sm are thus the Forth world and
> CintPOS.
> 
> Graphics
> 
> The X Window System and AmigaDos bear comparison here. They are opposite
> extremes as far as portability. X is a network protocol, a dessert
> topping, AND a floorwax. AmigaDos ran on Amiga hardware. Neither is the
> ideal graphic interface for Ha3sm. For one thing, the PC hardware support
> of character cell displays, BKA "VGA text", is still useful, and exists in
> neither X nor AmigaDos, nor in Plan9 for that matter. Ha3sm will not make
> this oversight. Some of the problems I have with X are perhaps descended
> from problems I have with unix. A terminal in unix is two channels to a
> text display and a channel from a text input. These are firmly clumped
> together throughout unix. This models a physical teletype. The generality
> this provides is excellent, but somewhat illusory. Yes, you can pipe
> anything to anything. Predicting what happens, or whether or not the
> result is useful, is another matter. This is where "everything is a file"
> ain't. AmigaDos is better here. Things that can be muxed easily are, and
> the correlation of inputs and outputs is more flexible. This is reflected
> in Ha3sm. As I suspect that accepting that there are two types of device
> in the world makes things simpler, so I suspect that visual display
> devices can be categorized beneficially into 3 types; glyph, pixel, and
> vector, or teletype, tube/screen and plotter/scope. Initial Ha3sm
> facilities will be the VGA text variant of the glyph display category.
> That's about all I can see implementing any time soon.

X>This is incoherent, I'm not sure what you're actually trying to say at
X>all.

A: Yes, I'm rambling here. Let me attempt some salvage...
B: I'm not ready to deal with GUI's etc, but want to establish a 
	categorization.
C: There are 3 kinds of displays. I'll work from that when I am ready,
	and will model that for forward-compatibility to the extent that I
	do displays.
D: keyboards and pointers have nothing to do with displays.

> Microkernel
> 
> The microkernelness of Ha3sm is a recent accident of it's goals of
> Forthishness and implementability. Note that Ha3sm provides a threader and
> interpreter in kernel-space. "Modules" may be a triviality, given Forth's
> CATCH. CATCH and THROW don't quite address synchronous/asynchronous
> though, if I recall correctly. That is, does CATCH allow specifying the
> weight of the resultant state-change? There was a thread on this a few
> months back in comp.lang.forth, but it went over my head at the time.  
> They did seem to conclude that there's a bit of something left to be
> desired here. If the fix for this makes it's way into one of the recent
> pmode Forths, straight Forth is much closer to having the sorts of things
> envisioned here. Anyway, you could probably un-micro a Ha3sm kernel
> interactively if you are so inclined.
> 
> Toward Ha3sm Hardware
> 
> A while back in comp.lang.forth I posted a sketch of a machine that could
> switch various forth engines on one main bus. This makes for pretty snappy
> task-switches. Interprocess communication is complicated by this, however.
> The Ha3sm message syscall may provide the escape clause that allows Ha3sm
> to model a physical "DMA-tasking" system. message is semantically a single
> serial line to the kernel. My hardware sketch had a single serial line to
> the scheduler CPU. The sketch in question had a separate scheduler CPU and
> memory. This puts the interrupt handlers on a different CPU than the
> things making syscalls. Interesting, but I suspect soluble, problem.
> 
> Here's a curious thing about that architecture. Existing Forth engines
> don't have FPU's. A scheduler processor for an array of Forth engines
> might have an FPU. This gives rise to the concept of /dev/float, which
> might speak Forth.
> 
> Recent influences
> 
> I solicited comment on interprocess communication from Frank da Cruz,
> prime mover of C-Kermit. He did take enough time out to say "Allow
> timeouts!". Point taken. I've been browsing the sources or docs to Plan9,
> CintPOS, Minix, BCPL, ASCII, Linux and EROS lately.

X>Ok, looking at this as an overview, I see a re-invention of UNIX, only
X>worse.

Thanks!!!! :o)

X>There is no provision for devices or even the system to communicate with
X>user-level code.

X>The kernel has no mechanism with which to call user-land (ie signals in
X> unix) devices have no mechanism with which to inform user-land that
X> something has happened.

X> This means that user-land has to poll both the kernel and devices on a
X> more or less constant basis.

X> This is very inefficient to say the least.

X> I'm not sure what the rationale or purpose of this design is.

What this points out to me is the difference between block and stream
devices. Blocks do have a concept of "transfer complete". Streams don't.
That's more fundamental than blocksize and whatnot.  This may or may not
call for some added info or pro-activeness from block devices. Not sure
yet. My Forth (block handling) is rusty.  I have to get down-and-dirty
with this to know the proper solution. Perhaps we can just always get the
block without rest'ing, if DMA is subject to jiffy slicing.

Whether or not this is inefficient remains to be seen, and is clearly very
dependant on what aspect of performance you are measuring. A process only
rest's at most once per jiffy. We always get around the Cycle in a very
reasonable time. Having most processes just re-reading and rest'ing on
most of thier jiffies may be bad, but how bad is not at all obvious. There
are a lot of trade-offs here that will be more like AmigaDos than unix.
Letting the kernel reschedule things is a huge simplicity loss versus a
relatively static list of processes. I don't know what the trade-offs are
there, but I know I like simplicity.

Also, by this scenario, device output is implicitly multiplexed. This is a
valuable flexibility. Also, the cost of reading a device, and deciding to
read a device, is borne by the processes that want the data. The driver
cost is flat one-time. 2001 Keeping things accountable like this, or
bearing thier own costs, is a good thing in a complex system. I think VM
is a counterexample of that.

Letting the kernel signal processes based on IO events means IO events are
tied to particular processes. This is, like parameter passing in 1-stack
languages, highly annoying to me. However, blocks are more amenable to
this sort of bondage than streams. My block devices may need what my buddy
X> is talking about, in some rudimentry form. Maybe not. At this point,
Ha3sm devices are like a Forth parameter stack is to Forth words. To
userland, devices "just are". This, I think, also makes devices more
distinctly discrete entities than other schemes.

This is also very faintly like IBM mainframe stuff, MVS and so on, from
what little I hear about such things. I've been looking for an opportunity
to bring that up. We just offer a virtual machine. From there it's your
problem. Each process must do some things that are usually regarded as OS
jobs. This is a place where Forthishness helps. Without VM paging we have
to replicate each H3sm dictionary in each process. EROS does something
similar, but I think it's done with pages rather than actually cloning the
code. Mercifully, a core H3sm is small, and most sites/boxes/users won't
need many processes on a Ha3sm. 2001 see also Tripos/CintPos, and OS/2 did
this poorly I understand.

What also came up in discussions with X> is that kernel threads, owner
processes, that are complete Forths, are somewhat novel, based on thier
response to them. This is something I've wanted explicitly, that doesn't
(usually) exist in unix. The unix superuser is a plain user memory-space
wise. There are interactive kernel debuggers in unix actually, which is
similar. Ha3sm userland includes ownerland, which is kernelspace. In e.g.
Linux you have /dev/kmem and so on. The performance is terrible. This in
fact, is the intended difference between Tripos/AmigaDos and unix, and is
why they wrote Tripos. In Ha3sm, you have N real AmigaDos's, the owner
processes, and N virtual Tripos's, the guest processes. You also have
shared-memory schemes in unix. CintPOS needs that on Linux, for example.
It's a limited resource. 2001 As mentioned before, I have this year
inserted a H3sm into Linux kernelspace.

What this also confirms is that I have a clear enough sketch here to start
coding seriously. I invite the Forth community and others to defend
themselves from this 3-stack monstrosity by responding in kind.  I am very
grateful for and flattered by my anonymous correspondant's closely
considered remarks. They have been helpful, and reflect his vast knowledge
of OSes.

H3sm, H3rL, osimpa and related things are in 
ftp://linux01.gwdg.de/pub/cLIeNUX/interim
