29 ms·
I thought restartable signal handling pretty ingrained - eg Bourne shell apparently used to handle memory allocation by trapping SEGV and calling brk() from the
by fulafel 5y ago
I 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...
- fulafel 5y agoInteresting stuff, I never knew Lisa and the OS used used a MMU! Re Xenix 68k hardware - there's also Tandy. From the Xenix Wikipedia article: "Tandy more than doubled the XENIX installed base when it made TRS-XENIX the default operating system for its TRS-80 Model 16 68000-based computer in early 1983" Apparently the TRS also had some unique extra chips, who knows if they may have been used for propping up memory management. (Z80 that apparently also worked as a kind of IO coprocessor, and some PAL chips mentioned in connection with Xenix[1]) [1] https://groups.google.com/g/comp.sys.tandy/c/HSTKgp_TbFg/m/pxQxaQ04DxQJ https://groups.google.com/g/comp.sys.tandy/c/HSTKgp_TbFg/m/p...
- vidarh 5y ago> Z80 that apparently also worked as a kind of IO coprocessor, and some PAL chips mentioned in connection with Xenix[1] A total digression, but I love the proliferation of CPUs for IO etc. on older systems. My worst-ever Franken-machine like that was my Amiga 2000. Right out of the box the A2000 had a 6502 compatible SOC as a keyboard controller (I don't remember the model number - might be a 6507). Then I got a SCSI controller that had a Z80 on it. And a bridge-board with an 8086 on it I think. I plugged a 286 accelerator into the bridge board, and a 68020 accelerator into the Amiga side... Not all of them were used at once, but having a machine with a 6502 compatible, a 68000, a 68020, and 8086, a 80286 and a Z-80 in the same case was fun... Of course the Commodore 128 had both a 8502 (6502/6510 compatible) and a Z80 (for CP/M) at the same time, and in CP/M mode the Z80 actually called 8502/6502 code for IO etc.... And of course for the C64, C128 and many others the floppy drive had a CPU as powerful as the actual machine (the C64 1541 floppy drive had a 6502, only marginally different (GPIO and some very minor other differences) to the 6510)