You don't need an MMU nor tons of RAM to do multitasking, just a timer interrupt. It's a lot easier if you require position independent code rather than doing fixups. The 6809 is a beautiful instruction set that makes position independent re-entrant code easy.
Of course an MMU and/or lots of RAM does make multitasking a lot easier.
OS-9 is an awesome UNIX-like for the 6809 that can do it in 64K or less.
A MMU is not needed for multi-tasking, but it is needed for memory protection. I wonder how well a system could work if its binaries were written on a bytecode that could be compiled to safe native code, making sure that it would not access non-permitted memory. This bytecode would be compiled at program load time. This could guarantee memory safety and a very small performance penalty.
You may just want to run a little bytecode interpreter, period. This was the approach used to implement many languages on the 6502 and other 8-bitters. UCSD Pascal comes to mind, but I know it was used to implement some C versions as well.
Those architectures are just so painful for generating reasonable code, mostly with regards to size, that a clean 16 bit VM starts looking attractive. Memory use is often more critical than speed on an 8-bitter, and for large applications even with the interpreter the bytecode is almost always smaller.
And if you're doing a full VM, it's not too painful to add a memory protection check. One can sustain about 5 - 10K virtual instructions a second on a 6502 @ 1 MHz.
As insanely inefficient as this all sounds, it was actually done back in the day. One notable example would be the old Business Operating System which was basically a bytecode interpreter glued to a small multitasking operating system. All the compilers and applications and most of the OS itself ran in interpreted bytecode.
> As insanely inefficient as this all sounds, it was actually done back in the day.
Java, Python, Perl, Ruby, and a host of others say this isn't a dead technique.
Another reason for it back then was to only need to port the interpreter to each different architecture and platform, an important consideration in the 80s with the bloom of CPUs and machines.
EUMEL, Oberon, Smalltalk-80 (including Squeak), and Cedar worked in approximately this way, but they did not have "a very small performance penalty". Modern browsers mostly do, with JS and wasm, but backstop compiler security (historically pretty iffy) with the MMU. eBPF in Linux is another go at the mule.
Mesa/Cedar, early Smalltalk, and Oberon are/were all primarily interpreted languages. And this was before JIT was common. So, significant overhead. Some of them had special hardware assists, like Mesa and the Xerox Alto with a processor designed around the language. Still not a speed demon, despite that. I believe they were more concerned with code density than speed. Cramming a networked multitasking operating system into about 128 KB of RAM is not trivial.
Such languages would be pretty painful on another architecture implemented as interpreters. I don't have exact numbers. But roughly an order of magnitude slower. Think Perl and Python vs. Pascal and C.
Things have changed a bit since then. Oberon is usually compiled to native code now and Squeak uses a JIT compiler. And Mesa/Cedar are extinct.
Hmm, I think Cedar and Oberon compiled to native code from very early on, perhaps the very beginning (I don't know of an interpreted version of either), and the reason I mentioned Smalltalk-80 in particular was Deutsch & Schiffman 01983 where they popularized JIT compilation and in particular introduced the first version of the inline-cache technique that makes Java monomorphic (and non-megamorphic) method calls fast today.
To quibble about one more thing, multitasking doesn't increase memory requirements significantly on anything big enough to run an interpreter. On this amd64 machine under Linux, sizeof(jmp_buf) is 200 bytes; that's how much space each new task would needs, plus of course the size of its stack. If I build for i386, it's 156 bytes. But of course you can define the calling convention to reduce this: on 16-bit Forth systems a suspended (cooperative!) thread was typically a data stack pointer, a return stack pointer: 4 bytes. (But typically each thread had its own dictionary of at least 4K.) Preemptive multitasking generally requires at least the size of the register file, since you can't discard caller-save registers on every context switch.
What does increase memory requirements enormously is memory protection — not mere multitasking but allocating exclusive segments or pages to each new program. That's a big part of the appeal of systems like Oberon, Smalltalk-80, and of course early Oak/Java: by implementing memory protection at the language level rather than the hardware level, you could get very fine-grained memory sharing without endangering system stability.
IBM mainframes still do on their classical mode, the bytecode gets AOT compiled at installation time, or when the binaries need to be refreshed due to changes on the hardware.
Burroughs B5500, nowadays still sold by Unisys as ClearPath MCP, was already doing this in 1961.
> IBM mainframes still do on their classical mode, the bytecode gets AOT compiled at installation time, or when the binaries need to be refreshed due to changes on the hardware.
I suspect in this comment you are confusing IBM i and its predecessors (i5, AS/400, System/38) with IBM mainframes (z, 390, 370, 360). The former do what you are talking about, the later don't. In proper IBM terminology, the former are called midrange systems, not mainframes.
Nope, IBM z still has language environments, and I call all of them mainframes, instead of precise definitions, because everyone without experience has a vague idea what a mainframe is, whereas midrange systems, most likely requires a Wikipedia visit for most of the audience.
What are you talking about? When you say "IBM z", which OS are you talking about – z/OS, z/VM, z/VSE, z/TPF or z/Linux?
Which "language environments" are you referring to? Do you mean "z/OS Language Environment"? That doesn't involve "bytecode" in the sense that OPM/EPM/ILE on OS/400 do. LE (on z/OS, z/VM and z/VSE) and ILE (on OS/400) are descendants of the same code base (and I believe that code base was even ported to OS/2 at one point), but ILE on OS/400 does some very OS/400-specific things which never happened on MVS, VM/CMS, DOS/VSE or OS/2.
You started out by saying:
> IBM mainframes still do on their classical mode, the bytecode gets AOT compiled at installation time, or when the binaries need to be refreshed due to changes on the hardware.
That is kind of true for IBM i, but just false for IBM z.
> I call all of them mainframes, instead of precise definitions, because everyone without experience has a vague idea what a mainframe is, whereas midrange systems, most likely requires a Wikipedia visit for most of the audience.
While "midrange" is a bit of an obscure IBMism, most people have heard of "minicomputer", and have the historic understanding of a "minicomputer" as something in between a microcomputer and a mainframe. I think "minicomputer" would be more accurate than "mainframe" when referring to the S/38 and its successors. Calling the later "mainframes" is just going to confuse anyone familiar with the proper IBM terminology.
I get the impression you like to make comments like "IBM did X years before this did" but they make you come across as someone opining on something they don't know a lot about, and you end up saying things which make people who know more about the topic than you do cringe.
In a language without pointers, you don't need to do anything fancy. Your program just doesn't get any references to objects from other processes, and it can't touch what it can't see. In a language like C, you would need some kind of bounds-checking to make sure addresses live within the correct range, which, if you're going to implement without hardware support, is going to be dog slow, unless you can optimize some of them away with the compiler.
However, either way, hardware memory protection adds another layer of safety/security, just like the no-execute (NX) bit. It makes it harder for attackers to hijack your VM, and also less likely that a program, because of a bug in your VM, could crash other processes or even the kernel.
The Inferno operating system released in 1996 does this.
It was designed to run on anything from light weight mmu-less bare metal embedded with no graphics to full fledged PC class hardware. It included a hosted mode where the kernel is built as a program that runs on plan 9, unix, unix-likes, and windows. The kernel is a vm with no way to implement native code unless you add it to the kernel.
Since everything runs in the dis vm you can have a truly portable os where the same program that runs on a headless embedded mips box will run unmodified in hosted inferno on an x86 Linux machine. Unfortunately it does not yet run on x86-64 due to dis having hard coded 32bit constraints. However, someone is actively working on that little issue.
Indeed, hooking the timer (or keyboard) interrupt was also the way to do "multitasking" (TSRs) in x86 real mode under MS-DOS.
The much-maligned x86 segment registers do come in handy here: they provide hardware-accelerated position independent memory addressing! Most DOS utility programs were small enough to fit in a single 64K segment, and provided they never touched the segment registers, these programs were trivially relocatable to any memory address.
You don't even need a timer interrupt, if userspace is well-behaved. Especially if user input is interrupt-based (think ^C to cancel; triggers context switch -> signal, can also have a kernel fallback for buggy userspace that tries to ignore the signal and refuses to yield).
What you need timer interrupt for is “true” preemptive multitasking/threading. For many applications that makes quite large difference in programmer comfort (ie. you can just run CPU-bound computation without any regard to yielding to other tasks). On the other hand you can successfully create create multitasking-like user experience without any OS level support for that simply by making everything event-driven.
Imagine you are interviewing a developer and find this line in his/her CV:
Personal achivement:
- I built my own OS and C compiler
Looks really impressive today, but perhaps it was the norm 3 or 4 decads ago. Anyway, I envy such people who can do that. Let me try that once again.... :)
Doing this today is actually way, way easier than it would have been back then. Today we have a wealth of information and much better tooling (QEMU alone makes the process of writing an OS so much easier). Writing a simple OS and compiler are both common school projects, so many programmers today have done it.
It's easier in some ways but harder in a lot of others. For starters the definition of an "OS" has greatly expanded over time. In the CP/M and even DOS days it was a very primitive shell and some disk I/O routines. Secondly low level knowledge was a lot more common because you needed it to get anything done. You could do some interesting things in BASIC but "real" programs were written in assembly.
Compare cooking dinner while backpacking vs cooking dinner at home: both produce warm food, but we probably neither take an induction stove (or microwave!) backpacking, nor build campfires in a kitchen.
>...faded lineprinter paper show that the bulk of the work on OMU dates from March/April/May 1984... by mid 1984 I had a perfectly usable O/S
It's impressive that Hosgood made what he did when he did. It's equally impressive for an entirely different set of reasons that modern CS students can burn through that in a semester. Or you could look it up on WikiHow.
As an electrical engineering student I designed and build my own processor (in VHDL), and wrote a simple assembler for it, it's not that difficult, just tedious.
Eventually I added a VGA frame buffer, PS/2 keyboard module and RS-232 support and made it work as a terminal. By connecting 2 of these 'computers' with a null-modem cable, we would have a little chat-box application to display during open days at my university.
Ironically, I was once rejected for a programming job because I studied electronics, not computers. So clearly I didn't know how computers worked.
It’s a feat - but do remember that a simple OS and a simple compiler are NOT the beasts we think of today. DOS for example is not much more than a standard library- and early UNIX wasn’t much more.
> Except a car doesn't need a combustion engine to fullfil its purpose, electric engines don't have combustion.
This is true, and is why using it as an analogy doesn’t work.
> There is nothing on the iOS experience that requires a file system with POSIX semantics.
Nobody mentioned posix before now. This is moving the goalposts.
> In fact that isn't what app developers using directly, rather Objective-C and Swift abstractions of a file system.
This is not true.
iOS provides all levels of Unix file system access from posix and libc upwards. Many apps and libraries make use of lower level access than Swift and Obj-C apis.
In addition, even those abstractions won’t work without a file system. They depend on concepts like files and directories.
This is why iOS had to have one when it shipped, and why a car power train is not a valid analogy.
> Last time I checked, UNIX and POSIX go hand in hand, no one is moving goalposts here.
Nobody said a POSIX file system was required.
The comment says that because iOS is a Unix, therefore it has a filesystem.
It doesn’t say anything about it needing to be POSIX.
The idea that anyone was saying it needed to be posix was introduced only by you and is moving the goalposts.
> The existence of a file system has nothing to do with UNIX,
The existence of the iOS filesystem has lot to do with Unix.
> unless you now mean only UNIX has filesystems.
I’m not sure why you’d think anyone would mean that.
Nothing in your comment addresses the problem with your analogy.
To recap:
iOS provides all levels of Unix file system access from posix and libc upwards. Many apps and libraries make use of lower level access than Swift and Obj-C apis.
In addition, even those abstractions won’t work without a file system. They depend on concepts like files and directories.
This is why iOS had to have one when it shipped, and why a car power train is not a valid analogy.
Files are quite central to Unix and are required by any relevant standards I'm aware of. iOS, deriving from Mac OS X, is a Unix and has always supported its fundamental filesystem abstractions, regardless of whatever the SDK exposes to third-party application programmers.
Coincidentally I just embarked on learning about the 6809 architecture. I became interested in the architecture after discovering a generation of synthesizers, such as Yamaha's DX line or Ensoniq's ESQ-1/SQ-80, in the mid 1980s were built around CPUs using it. I've been disassembling the freely available Operating System ROMs in an attempt to learn more about how these devices were programmed. The differences between the 6809 and modern architectures makes for some interesting learning!
> The differences between the 6809 and modern architectures makes for some interesting learning!
Funnily enough, the 6809 is by far the most "modern" 8-bit design. It was the last major one, released after the Intel 8086 and some other 16 bit designs.
Stack-relative addressing (with two stacks!), position-independent code, 16 bit arithmetic, hardware multiplication, and high code density. Very nice compared with the likes of the Z80 or 6502.
I am a couple of months into studying math for creating my own programming language. The larger goal is a complete system in an FPGA, having a few central ideas about how I want it to be, but the language seems like the hardest part of the "project", by far I would say.
Having finished intro books on logic, automata and category theory, I am now starting "Type Theory and Formal Proof" and writing a general purpose parser. Even if I end up, for whatever reason, abandoning it before doing anything, the journey is definitely worth it.
I hope you pull through and show us your work here. I love playing with low level stuff, but never manage to delve into an ambitious project. Bon voyage :)
Sounds like a good project. Version 7 was where the flood gates of UNIX finally opened. Even Microsoft had a version, 'Xenix', which eventually became SCO UNIX. I sometimes wonder what the world would be like if Microsoft had continued in the UNIX universe, instead of in their own isolated development.
Coherent was the v-7 clone by the Mark Williams Company that introduced me to the wonderful world of UNIX.
It's now open-sourced, and I have used it in a Linux x86 VM.
Of course an MMU and/or lots of RAM does make multitasking a lot easier.
OS-9 is an awesome UNIX-like for the 6809 that can do it in 64K or less.