I got it into my head recently that I could have a good time trying to build my own computer out of SSI and MSI-scale logic chips, eschewing the last 40 years of progress in computer technology and returning to the pre-microprocessor era in order to learn a few things, just like learning to build a campfire in the Boy Scouts taught me (something, I'm told) about myself and the human condition.
As part of my research for this, I've been having fun looking at the architectures of several different generations of computers, all the way from the early post-war machines to some of the early microprocessors, for architectural inspiration. There is an amazing amount of variation in the shapes that even a conventional stored-program computer can take, and I'm going to take a look at a few of the most interesting.
The first I'll look at is the common ancestor of basically every computer I've ever used, since it was the first functioning stored program Von Neumann architecture computer. Being a British machine, however, the machine's name is a product of that nation's penchany for ironic understatement: The Small Scale Experimental Machine, or more infomally "Baby."
It was the first stored program computer, meaning that the program is stored in the same memory as everything else, and you can change any of it quite easily, rather than having to move a lot of cables.
This machine was intended only to be a way to test the use of a new kind of computer memory before going ahead with building a full, "real" computer. That memory was the Williams tube, one of the Rube Goldberg lashup types of computer memory that was used in computers before core memory, and later the more familiar semiconductor chip memory, were developed. CRT tubes leftover from wartime radar sets were turned into a way to store 32 bit numbers. Perverselely, this was a pretty big improvement over the mercury delay lines used previously (and for some time afterward) because it was a random access system, meaning that a machine could basically treat it like we would use RAM today, whereas with mercury lines the machine would basically have to wait around until the data was available. There was also the added advantage of not having to have columns of mercury sitting around your computer room.
As an amateur student of computer architecture, the Manchester Baby is ideal to look at because it's pure vanilla nature made it basically the platonic ideal of a Von Neuman architecture computer.
The only general purpose register was the single accumulator. To load the accumulator from memory, one was only given the option of a negated version of the value at any memory address, though you could store any value into memory without having it negated, meaning that if you had an address and wanted its contents in the accumulator, you would need to load the negative version, store it into memory again, and then load it back, negating it twice.
Branching and Program Control
There were two jump instructions, both uncoditional (absolute and relative, in modern terms). Branching could be accomplished by an instruction that incremented the program counter twice during instruction execution instead of once (skipping the next instruction) if the value in the accumulator was negative. This is certainly an interesting approach, since it effectively allows the unconditional jumps to be turned into branching instructions, which presumably is why they felt so generous with the two different addressing modes for the jump instructions, knowing that they would also be reused as branching instructions.
If you wanted to actually perform any operations on what was in the accumulator, you were limited to subtracting things from it. Addition could be performed by negating a number and using the subtractor to add by subtracting the negative. This instruction took one value from the accumulator and the other from the point in memory pointed to by the address part of the instruction word, storing the result back in the accumulator. This storing back to the accumulator was accomplished rather easily without any intermediate buffer, since the memory needed to be refreshed every cycle (they termed it a "beat") anyway, so changing the value there could be accomplished by simply substituting the new value for the old one in the refresh circuitry.
The best part was that, even though you were limited to just the bare minimum of instructions to allow computation, you still only had 32 words (of 32 bits each) of memory to work with, meaning you really had to make each one count. That was alright in Baby's case, since the whole point of it was just to prove that the whole general architecture was possible with the technology they were using. That makes it just about equivalent to the machine I'm building, whose main purpose is for me to have fun building and using it. That said, I'll probably use something closer to 8-16 bits for my word size, 32 bit-wide memory chips being rather hard to come by, and I think I'll allow myself the luxury of more than 32 words as well.
In terms of practical details that are of help to me in my quest, the use of a single-register architecture is interesting, but I don't believe I'll be using it, the reason being that I don't want to use a memory technology that needs to be constantly refreshed, as in the Williams Tube. Today, we'd call it DRAM, and it is significantly cheaper than the alternative SRAM. The difference is significant in multigigabyte installations, but for just a few K, it's worth it not to have to deal with the refresh circuitry. As a result, there would be no easy way for me to have my accumulator be both the source of one of the operands and the destination for the result without an intermediary buffer, and if I'm adding another register anyway, I may as well put it under the control of the sequencer so that it can be used in other ways that simply buffering the result of an ALU operation.
The skip instructions are of more interest. They would allow me, in the same way as in Baby, to expand the capabilities of the instruction set efficiently. With just a few kinds of jump (unconditional branch) instructions, conditional skipping allows one to create a whole vocabulary of different branching conditions. Since the skip instructions don't need to carry around an address in their instruction word, there can be effectively as many of them as I like while still only taking up one instruction "slot" (I'm currently planning on a 4-bit instruction format, meaning that all of the skip instructions can fit into one of the 16 available instructions). In that way, with just two different jump instructions in different addressing modes, I can effectively have N*2 conditional branch instructions, where N is the number of skip instructions that I have.
While I appreciate simplicity, especially since I am a klutz with a soldering iron, the architecture of this machine is a little too hardcore even for me. The takeaway from the Baby design, however, is that you can do a lot with a little, and that I shouldn't overcomplicate my design if I want to simply prove it's feasibility.