5 ms·
Microsoft sold Xenix for the 68000, I wonder how that worked given the often mentioned unix problem caused by inability of the processor to recover from bus fau
by fulafel 5y ago
Microsoft sold Xenix for the 68000, I wonder how that worked given the often mentioned unix problem caused by inability of the processor to recover from bus faults.
As for a MMU, I guess it's not a given that Unix requires paged virtual memory. Traditionally whole processes were swapped in and out.
- my123 5y agoXenix was also sold for the 8086.
- rbanffy 5y agoI learned C on a Unix-like OS, QNX, running on a 640K 8088 PC clone. It booted from a 360K floppy and had, in those 360K, a shell, vi and a C compiler.
- jacquesm 5y agoI still miss QNX. Rock solid, super responsive, very tight and a neat model to build scalable applications.
- rbanffy 5y agoI'm probably unhealthily attached to my Blackberry Q10 because of this.
- nullsmack 5y agoWow, I didn't know QNX was always so compact. I remember a demo disk released in '99 or '98 that would boot from one floppy into a graphical environment with a web browser and a few other things. It was really impressive on a single floppy.
- retrac 5y agoThe 8086 was the first microprocessor UNIX was ported to. Maybe unsurprising as the first 16 bit processor that could address more than 64 KB of RAM. 8086 segmentation was and is much-lamented, but it did enable a poor man's form of virtual memory. If the user process never touched the segment registers, the OS could move allocated code, stack and data blocks in RAM around transparently to the process. It also allows every process to assume it starts at address 0.
- throwawayboise 5y agoMinix also ran on the 8086/8088.
- ch_123 5y agoSome of the early 68k workstations, including the original Sun and Apollo hardware simulated an MMU by using an extra 68k. IIRC, in the event of a trap, the second CPU would kick in, set up some mappings, and then hand control back to the main CPU.
- retrac 5y agoThose workstations still had custom MMUs, and the 68K had most of the signals to work with an MMU. The issue was instruction recovery on exception, particularly page fault. With an instruction like move (a2)+, (a3) the processor first loads the value from memory at the address in a2, increments a2, then goes to store it at the address in a3. If a3 holds an invalid address, the processor faults halfway through the instruction. The problem is the 68000 doesn't store enough state anywhere to tell you how to rollback and attempt the whole instruction again. (Was a2 incremented already or not?) The dual 68K approach would keep the whole instruction pending on the other processor, in case it needed to be restarted, after resolving the page fault. And it was still possible to implement classic base-and-bounds swapping with a single 68K.
- rjsw 5y agoSome C compilers for the 68000 added an instruction to do a dummy read before doing any stack operations to trigger a fault in a way that it didn't matter to the instructions doing real work.
- zozbot234 5y agoThis is still done today to guard against stack overflows, in combination with unmapped "guard" pages.
- vidarh 5y agoIt was not entirely unable to recover from bus faults. It was unable to restart the instruction executing at the time correctly. So it was unusable for more complex setups without hacks like ch_123 mentions Apollo and others used where you'd have two CPU's and let one of them pause while the second handled paging. But if you were content with killing the offending process, you could do that with "just" a single 68000 CPU and a simple MMU. (on a side note, Apollo's OS was fascinating for the era [1]: "Certain efficiencies were gained by careful design; for example, the memory page size, network packet, and disk sector were all 1K byte in size. With this arrangement, a page fault could take place across the network as well as on the individual computer and Aegis file system was a single system of memory mapped files across the entire network. The namespace of the network was self discovering as new nodes (workstations) were added.") [1] https://en.wikipedia.org/wiki/Apollo_Computer https://en.wikipedia.org/wiki/Apollo_Computer
- rbanffy 5y agoApollos were amazing. Truly a lost treasure. It saddens me how some beautiful creations just died because of worse is better.
- fulafel 5y agoI thought restartable signal handling pretty ingrained - eg Bourne shell apparently used to handle memory allocation by trapping SEGV and calling brk() from the signal handler. I guess it's possible Xenix people just changed all the problematic bits in the port. A compiler assisted way also comes to mind, if the issue is just certain complex instructions - if you document that you can't use non restartable instructions (at least if you want to catch signals), maybe you could have the C compiler emit only a restartable subset of the ISA.
- vidarh 5y agoI can't tell whether or not Xenix was available for other M68k platforms than the Lisa. The Lisa had some sort of MMU. The SUN 1 also had a custom MMU for the 68k. As long as you're content with dealing with the restart problem either by disallowing it (crashing any process that you can't help by triggering a bus error for - optionally minimised by doing paging while halting the main CPU like the Apollo's did) or other awful hacks, you can still do some things with the MMU with the 68000 too. Here's details about the Lisa architecture that includes bits about the MMU and how it's handled in Lisa OS[1]. While it doesn't describe how XENIX did it, it gives some hints about which options might have been available to them: > Although instructions in the MC68000 processor are not generally restartable, we have empirically determined that the four instructions that access code segments, JMP, JSR, RTS and RTE are restartable. When one of these four instructions attempts to reference a code segment that is not currently present, it causes a bus error which traps to the OS. It would have been nice if this used compiler assistance. As only trapping those would mean ensuring that you insert one of them on every segment boundary if you want to be able to cross it, and also ensuring that you don't have any BRA/Bcc (relative branches/jumps or DBcc (relative branches/jumps with register decrement) instructions crossing segment boundaries. If XENIX did this too, then it'd allow relatively painless paging of code segments. It wouldn't be very hard do do, but give a strong incentive to try to pack functions into segments so that no basic blocks crosses segment boundaries. But it seems the Lisa's default OS required programmers to explicitly split a program into different code segments: > The division of a program into these named code segments is dictated by the programmer through commands to the Compiler and Linker. No idea if XENIX also required that, or if they tried to be smarter about it. It wouldn't seem that hard to at least have logic to allow the compiler to automate it, though I'd guess some might want to explicitly control to be able to trade off amount of memory pinned at a time vs. amount of jumps between code segments (potentially triggering paging) given that the maximum code segment size of 128KB was a rather sizeable chunk of a machine as constrained as that (maximum addressable memory of 2MB) > Because the instructions that reference data are not all restartable, the system does not do automatic swapping of data segments. The memory manager must swap in all data segments needed by a process before the process is allowed to execute. However, the OS gives programs the ability to unbind data segments that are not needed in the memory while a particular part of the program is executing. You could presumably still allow memory to grow with brk(), but trapping SEGV to decide when to not so much. Though maybe - a hack occurred to me just now (no idea if anyone did it) to allow some patterns like that work: Have the MMU trigger a "soft" error condition if you access within X bytes of an unmapped page, and trigger a normal interrupt in that case. A normal interrupt on the 68000 is triggered immediately after the instruction has executed, so as long as a single instruction doesn't touch memory both within and outside mapped data segments you can trigger the pager when it gets "close enough". That's enough to e.g. make the stack auto-extending, or to allow sequential writes that approach and cross a page boundary, as long as the applications are ok with SEGV's not being triggered precisely when a page boundary is crossed. You'd still not be free to page data segments out. But you could free stack segments pretty easily with some minor compiler assistance to communicate lower stack bound now and again. Your idea of only issuing restartable instructions might work, with the caveat that the M68k is exactly the wrong architecture to have to worry about that on given how heavily it relied on various complex addressing modes to make the code compact... A lot of common instruction patterns would become unviable. [1] https://www.researchgate.net/publication/2996607_The_Architecture_Of_The_Lisa-_Personal_Computer https://www.researchgate.net/publication/2996607_The_Archite...
- jacquesm 5y agoThe best thing about Xenix was the documentation, not the OS. I used that set for years after I stopped using Xenix.