Perhaps the most interesting question with any processor is this: how do instruction codes (i.e., code words in memory) get turned into electrical signals that in turn trigger various events in the processor, altering its internal state?

There are many solutions to this problem, one of which is the use of microcode. While machine instructions are designed with the user in mind (well, inasmuch as machine language can be code user-friendly, that is!), microcode instructions represent internal state changes of the machine itself.

In the case of my 4-bit contraption, each machine code instruction is translated into several microcode instructions. Each microcode "instruction" is really just an electrical signal that, along with the clock signals activates various components of the processor. One of the tricks is the use of a two-phase clock, whose signals look like this:

As you can see, the first clock signal transitions from a high to a low state before the second clock signal. When the first clock signal is in a low state, subsystems in the processor respond by presenting information on their outputs, but without altering their internal states. When the second clock signal transitions from a high-state to a low state, subsystems respond by latching whatever data is present on their inputs and alter their internal state.

Designing the microcode set was only possible after I have developed the functional block diagram for the entire processor, and had more than a clue about the way its main components will work. The finalized microcode instruction set consists of 22 microcode instructions:

*Use of square brackets implies indirection (e.g., store at [DA] means store in memory at the address in DA.)

The two rightmost columns tell us what happens in the various subsystems during the two clock phases. I called CLK1 events states (because they alter the output state of subsystems) and CLK2 events triggers (because they trigger an internal change upon the high-to-low transition of the clock signal.)

These states and triggers are essentially internally decoded electrical signals inside each subcomponent. They are described in detail along with the appropriate subcomponent's detailed description.

So now we can translate the 16 machine language instructions into microcode instructions:

 Opcode Microcode HLT HLT,LIR LDA LDL,LDH,LAL,LAH,LIR STA LDL,LDH,SAL,SAH,LIR JMP LDL,LDH,LPC,LIR SPC LDL,LDH,SPL,SPH,LIR AND LDL,LDH,AND,LIR OR LDL,LDH,OR,LIR ADD LDL,LDH,ADD,LIR SUB LDL,LDH,SUB,LIR CMP LDL,LDH,CMP,LIR JNZ LDL,LDH,PCZ,LIR JND LDL,LDH,PCD,LIR JNC LDL,LDH,PCC,LIR ROL ROL,LIR ROR ROR,LIR CLF CLF,LIR

So what does this all mean? Let's take, for instance, the LDA instruction. In plain English, this instruction accomplishes the following:

• Fetches the next two machine words into the internal Data Address Register, while incrementing the Program Counter

It is translated into five microcode instructions, which in turn cause the following to happen:

The states and triggers are decoded by logic circuits inside the individual subsystems. As you can see, there's a lot of repetitiveness here, but that's precisely the point: the goal here is to reduce relatively complex instructions into simple, repetitive operations that can then be implemented in hardware.

The table below cross-references all 22 microcode instructions with states and triggers in each subsystem.

 Microcode Instructions State/ Trigger HLT LIR LAL LAH LDL LDH SAL SAH SPL SPH LPC PCC PCD PCZ AND OR ADD SUB CMP ROL ROR CLF Accumulator LAC/LAC X X X X X X LAH/LAH X LAL/LAL X SAC/ X X X X X X X SAH/ X SAL/ X Program Counter APC/ X X X /LPC X X X X C D Z PCI/ X X X SPH/ X SPL/ X Data Address ADA/ X X X X X X DAI/ LDA/LDA X X X LDH/LDH X LDL/LDL X SDA/ X X X X X X X X X X X X ALU ADD/ X AND/ X /CLD X /CLF X CMP/ OR / X ROL/ X ROR/ X /STF X X X X X X X SUB/ X X TWO/ X X X X INC/ X X X X X X X Sequencer HLT/ X /LIR X Memory MRD/ X X X X X /MWR X X X X

An interesting side note about this design is that despite its simplicity, it actually does some parallel execution, a feature of modern, high-speed processors. Take a look at the LDL microcode instruction, for instance. Loading the DA register from memory doesn't involve using the ALU, so it is possible to exercise the ALU in parallel, using it to increment the program counter.

But the microcode sequencer isn't responsible for executing microcode instructions (except, of course, for microcode instructions that affect the sequencer itself.) It is merely responsible for generating microcode instructions in the proper sequence from machine instructions. Microcode instructions are completely decoded; i.e., there are 22 individual control lines, one for each instruction.

Microcode is stored in a Flash ROM; an oversized Flash ROM in this case, but it so happens that that's what I had in my toolbox! It is a 28C16 Flash ROM from Microchip.

The sequencer circuit (shown below) also contains a series of DIP switches that are used to program the Flash ROM. These DIP switches are not strictly necessary; you can also use an external programmer. In my case, for one thing I didn't have an appropriate programmer unit handy when I was building this circuit, and I also found it convenient to program the chip in-circuit. Hence, the switches.

So, how does this thing work? Four-bit instruction codes are fetched from the data bus by a 74LS175 quad flip-flop. The instruction word is represents the upper 4 bits of the microcode address. The lower 4 bits are generated by a 74LS93 counter, leaving room for a maximum of 16 microcode words per instruction. The counter is controlled by the clock signals, the HLT and the LIR microcode instructions, and the START line (which is pulled low by a manually operated button).

The complete 8-bit microcode address is fed to the 28C16 via a 74LS244 octal buffer. The output of the 28C16 is then decoded into 22 individual lines using three 74LS138 3-to-8 demultiplexers. LEDs are also used to show the current state of the 22 control lines; I found this very helpful while testing/debugging the circuit.

Lastly, here's the actual microcode table, used to program the 28C16:

 Opcode Address Microcode words (binary) Hex Mnemonic 0 HLT (00) 00000000 00000 00001 00000 00000 00000 00 01 HLT,LIR 1 LDA (10) 00010000 00100 00101 00010 00011 00001 04 05 02 03 01 LDL,LDH,LAL,LAH,LIR 2 STA (20) 00100000 00100 00101 00110 00111 00001 04 05 06 07 01 LDL,LDH,SAL,SAH,LIR 3 JMP (30) 00110000 00100 00101 01010 00001 00000 04 05 0A 01 LDL,LDH,LPC,LIR 4 SPC (40) 01000000 00100 00101 01000 01001 00001 04 05 08 09 01 LDL,LDH,SPL,SPH,LIR 5 AND (50) 01010000 00100 00101 01110 00001 00000 04 05 0E 01 LDL,LDH,AND,LIR 6 OR (60) 01100000 00100 00101 01111 00001 00000 04 05 0F 01 LDL,LDH,OR ,LIR 7 ADD (70) 01110000 00100 00101 10000 00001 00000 04 05 10 01 LDL,LDH,ADD,LIR 8 SUB (80) 10000000 00100 00101 10000 00001 00000 04 05 11 01 LDL,LDH,SUB,LIR 9 JNZ (90) 10010000 00100 00101 01101 00001 00000 04 05 0D 01 LDL,LDH,PCZ,LIR A CMP (A0) 10100000 00100 00101 10010 00001 00000 04 05 12 01 LDL,LDH,CMD,LIR B JND (B0) 10110000 00100 00101 01100 00001 00000 04 05 0C 01 LDL,LDH,PCD,LIR C JNC (C0) 11000000 00100 00101 01011 00001 00000 04 05 0B 01 LDL,LDH,PCC,LIR D ROL (D0) 11010000 10011 00001 00000 00000 00000 13 01 ROL,LIR E ROR (E0) 11100000 10100 00001 00000 00000 00000 14 01 ROR,LIR F CLF (F0) 11110000 10101 00001 00000 00000 00000 15 01 CLF,LIR

Only the bits shown here need to be programmed. Other bits are ignored. These include the top 3 bits of each microcode word, as well as unused words, such as the words at address 00010005 to 0001000F (the LDA instruction only uses five microcode words, at address 00010000-00010004.) This microcode table could also have been implemented as a diode ROM instead of wasting so much useful space in my 28C16; an address decode logic could have been used in conjunction with approx. 90 diodes.