The microcode isn't quite as simple as you make it sound. Trust me, I've needed to understand a lot of the Alto's microcode to debug disk and other issues, and it's brain-twisting.
There are the 16 basic ALU micro-instructions, but these are combined with 8 different bus sources and two sets of "special functions" to make fairly complex micro-instructions. The special functions are a bit crazy and also totally change meaning depending on which hardware task is running.
Control flow in microcode is pretty wacky. Each micro-instruction contains the address of the next micro-instruction, so it's like a goto in every instruction with instructions randomly scattered through memory. Additionally, branches are done by the hardware setting bits in the address based on conditions, so you end up with basically a computed goto. Finally, branches happen the instruction after the condition. So even just following the control flow of microcode is hard.
Another fun thing with microcode is that to read memory you write the address to the memory address register. Then you need to wait two microinstructions before you can read or write the memory value.
Yes, I saw a bit of that, and did realize what you meant by pretty crazy. Rereading my last edit, I also notice that the smiley I had there got lost in editing. Apologies for that.
If you do have the microcode for AND, and a RAM board to store custom microcode, creating an OR instruction shouldn't be insanely hard, though, even without an assembler (finding a spot to put it without losing another instruction may be impossible, though, and that could mean a fairly large rewrite of your OS and applications)
Well, the microcode for AND is simple; it's this line
G17: L_ ACDEST AND T, TASK, :SHIFT;
...the problem is that that's in the middle of the decode (there's a computed jump into this lot from the line labelled DIS1:). So you could change the AND insn to do OR instead, trivially (use "OR" here where the line says "AND"), but you probably wanted to keep AND. So you'd need to at least add enough of your own decode to be able to put the insn somewhere else. And as you say that looks like the real problem: http://users.rcn.com/crfriend/museum/doco/DG/Nova/base-instr... suggests that given 16 bit instructions and the format they've used there isn't much space left. The best you could do would be to steal some of the 'io instruction' space and have it operate on fixed accumulator registers the way MUL and DIV do.
(The decode is remarkably confusing even knowing what it's supposed to be doing; there seems to be a lot of implicitly jumping to entries in decode tables based on bits of the instruction...)
Looks like you've figured a lot of it out, but if you want more details... The way instruction decoding works is the hardware slams some of the instruction bits onto the microaddress bus, causing a 16-way dispatch based on the instruction, selecting which instruction to execute. In detail, looking at the microcode:
"IR" is the special instruction register. Loading it from memory (MD=Memory Data) causes a dispatch after the next microinstruction. ":GETAD" is a microcode branch, so the next microinstruction after DIS1 would normally be at label GETAD. However, IR causes bits 0,5,6,7 of the instruction to get OR'd into the microaddress, resulting in the dispatch to G1, G2 etc. But how does the microassembler know that G1, G2, etc need to be aligned properly for this to work? That's what the !17,20 directive does: 17 and 20 are the necessary alignment in octal, followed by the list of labels that need to be aligned.
Further instruction decoding is done with the IDISP and ACSOURCE special microcode functions, which do more complex decoding (based on multiple bit combinations) resulting in similar dispatches. So the branch to :DOINS or :SHIFT will do a second dispatch, based on the earlier ACSOURCE.(See page 30 of the hardware manual for details.)
Actually, the PARC guys stole all of the io instruction space for other things on the Alto. Only the math and load/store instructions are compatible with the Nova.
There are the 16 basic ALU micro-instructions, but these are combined with 8 different bus sources and two sets of "special functions" to make fairly complex micro-instructions. The special functions are a bit crazy and also totally change meaning depending on which hardware task is running.
Control flow in microcode is pretty wacky. Each micro-instruction contains the address of the next micro-instruction, so it's like a goto in every instruction with instructions randomly scattered through memory. Additionally, branches are done by the hardware setting bits in the address based on conditions, so you end up with basically a computed goto. Finally, branches happen the instruction after the condition. So even just following the control flow of microcode is hard.
Another fun thing with microcode is that to read memory you write the address to the memory address register. Then you need to wait two microinstructions before you can read or write the memory value.
If you want to take a look at the Alto's microcode: http://bitsavers.trailing-edge.com/pdf/xerox/alto/microcode/...