System/1 Build Log

An ongoing chronology of System/1's construction, and any other related musings.

1st February, 2017 - A slight interruption

This entry was backdated.

One aspect of the system which I have yet to design in any detail is interrupt handling. The general approach was settled before I started building anything; both the program counter register and the status flags are implemented in pairs, with the active set being selected by a signal called INT_CTX. The intention is that the system can handle an interrupt by switching to the shadow PC/flags registers and jumping to an interrupt vector; the interrupt routine can then exit simply by clearing the INT_CTX flag to allow the mainline code to continue execution (assuming the ISR saved and restored any general-purpose registers it used in the meantime, naturally).

The logic required to implement the jump to the ISR was originally intended to form part of the CPU's control unit, but due to space constraints on that board I had to change my mind and planned instead to make it part of the front panel controller; at least that way some of the front-panel functionality could be implemented in such a way as to share sections of logic with the interrupt handling. Unfortunately the front panel controller in turn ended up filling the board I'd allocated to it (plus a second one to condition the signals from the control switchgear!), so in the end this proved to be a non-starter as well; the final front panel controller merely exposes a few signals that will allow the interrupt logic to request a supervisory cycle called ENTER_INT before the next instruction is executed. Since the front panel controller already includes a priority queue for such requests for its own purposes, this consists simply of a request line (INT_PENDING) connected to the priority encoder via some gating logic to ensure it doesn't get serviced when the CPU is halted.

However, being able to request such a cycle is only the first step; more logic is required to actually detect an interrupt and assert INT_PENDING, as well as to jump to the ISR during the ENTER_INT cycle and then allow the ISR to exit when it's done. Luckily, the redesigned clock/machine cycle generator only occupies about a third of a board at the moment, so I have plenty of space to implement the bulk of the interrupt handling there. This is somewhat of a relief, as I definitely want to support them but have run out of spare slots on the CPU's backplane board!

Along with the bare interrupt functionality I'd also like to include instructions to enable and disable interrupts, trigger a software interrupt (trap), and provide some sort of mechanism for the ISR to alter the otherwise-hidden 'user mode' program counter and flags registers. I left a group of instructions free for this purpose — opcode 0 is decoded by the control unit as a SYSOP instruction, which could be further decoded using the sub-opcode bits to give sixteen individual instructions — and one option would be to simply decode those instructions using a 74HC259 addressable latch. Using one of the sub-opcode bits as the data bit and the other three as address bits would provide for eight flag bits that can be toggled under software control, with a pair of instructions to set or clear each bit individually. However, this implementation would be somewhat inelegant for at least two of these functions — leaving interrupt context and triggering a trap — since they're ideally be provided as single instructions that signal an event rather than stateful toggles that ought to persist after the instruction has finished executing.

One approach to improving that particular inelegance that seemed to show some promise was to partition the eight flag bits into four that can be set or reset as before, and four that can be set but are then automatically reset before the instruction completes. In the case of those four bits, the four 'set' instructions then become 'pulse' instructions, whereas the four corresponding 'reset' instructions effectively become NOPs. This turned out to be a situation where being able to play with a section of the design on a breadboard hooked up to the rest of the machine proved invaluable; my first attempt at implementing this idea had the flag bits changing state at the end of an instruction cycle, which didn't leave enough time for the pulsed bits to return low! An added complication is that the '259 is an asynchronous part, so it took a few iterations of a design for the logic that controls when the new states were being latched to find out that reliably avoided problems around the end of instruction cycles.

Eventually I was satisfied that the SYSOP and interrupt logic I'd breadboarded up was working well enough to commit to paper, so I added it to the clock generator schematics and was surprised to find it actually condensed down to fewer chips than I thought (mainly due to the way I'd scattered gates around with wild abandon during prototyping!). In fact, even after adding some glue to hedge my bets regarding edge vs level triggering of interrupts (the manual interrupt button on the front panel generates a single pulse, so needs to be edge triggered, but level triggering seems more reliable if there might ever be multiple interrupt-generating peripherals on the memory bus), it looked like I had a bit of room left over on the board for additional functionality.

After some pondering, I've decided to throw in a few extra gates for a non-maskable interrupt that could be used to signal a page fault if I ever get around to building some sort of memory management unit. By handling this interrupt immediately when signalled, instead of waiting for the end of the currently-executing instruction, the faulting instruction will always be restartable (a fault during instruction fetch won't have executed anything, and the only instructions that could fault during execution are memory accesses which either don't alter register state at all or don't do until after the memory operation completes). This should, I suspect, be enough to cope with basic MMU duties in a sensible fashion.

A further extension that follows from this idea is to track the source of any given interrupt, since we now have four (front panel, bus, NMI, and the TRAP instruction); the simplest way to provide access to that information looks to be decoding one of the four NOPs associated with the '259 as a separate instruction that stores a machine status word in a general-purpose register. Using four bits for interrupt sources and four bits for the stateful bits in the '259 would also leave plenty of spare bits for machine state that would otherwise have to be obtained in a rather roundabout manner, such as the flag bits. That would make context switching considerably quicker should I ever throw caution to the wind and attempt to implement basic multitasking on this contraption...

... on second thoughts, perhaps I'd better quit whilst I'm ahead. Let's get this thing built!