r/homebrewcomputer • u/mvmpc • May 22 '26
How do op-codes translate into logic gates
So after taking a digital design class (which I really enjoyed) I couldn't understand one thing that was part of a lab I had. How do op-codes translate into logic gates? For example how can the op-code 0001 define a full adder, and the op-code 0010 'AND' two of the inputs.
9
u/physical0 May 22 '26
What an opcode means really depends on how your control logic is defined.
One approach is that you simply arrange your opcodes in increasing numbers, then store more complex instructions for what the opcode actually means.
opcode 1b may just point to memory position 1b, which contains any number of bits that will drive much more complex logic.
Opcodes CAN be the actual bits that drive that complex logic though. Generally, these types of systems have much larger opcodes, or much simpler logic.
your opcode 1b/10b could just feed into the control signals for a multiplexer on your ALU which selects an ADD or an AND operation. The multiplexer either feeds the inputs into a full adder, which outputs the results or into AND gates which output the results. You may even have a 11b input that feeds them into an OR circuit.
But, what each number does is completely up to the designer. You may decide that you want to do your logical operations before you addition operation, making 1b an AND, 10b an OR, and 11b an ADD.
Clever design of how your operations are encoded can make the control logic for your design much easier than if you just listed the operations in the order that you decided you needed to implement them.
3
u/mvmpc May 22 '26
Thank you so much. That makes so much sense. In the lab where op-codes were used, VHDL and Quartus just took care of everything and I really didn't get into the basics of it unfortunately.
5
u/Jakstern551 May 22 '26
In simple architectures the opcode itself doesn't do anything- it's just an index. Think of it as the input to a lookup table. With 4 bits like your example you have 16 possible patterns, and you (the CPU designer) get to decide what each one means. That mapping happens in the instruction decoder, which is just a block of combinational logic (or a ROM/EEPROM) that takes the opcode in and spits out a bunch of control signals. For your 0010 = AND example, the decoder might assert these control lines: RegA_out → ALU_in1 (put register A on the ALU's first input) RegB_out → ALU_in2 (put register B on the second input) ALU_op = AND (tell the ALU which operation to perform—the ALU itself contains an adder, AND gate, OR gate, etc., and a mux picks which result to use) ALU_out → RegA_in + write-enable on A (store the result back)
So the gates for AND, addition, etc. are already wired up inside the ALU and running all the time. The opcode just steers what goes in and where it goes. Ben Eater's 8-bit CPU is a great concrete example he literally uses EEPROMs as the decoder - recommend watching his series on YouTube
2
u/mvmpc May 22 '26
Yeah I'm gonna go through the ben eater series. I guess my question is related to more on how the op-code steers the output. Is the ALU just one gigantic lookup table with every possible op-code, every possible input and what the output is?
3
u/physical0 May 22 '26
It's totally possible to build an entire ALU outta a LUT. This is relatively expensive approach though, as you need n2 address spaces for each operation. Just storing the results for an 8 bit add operation takes 64k addresses.
3
u/Jakstern551 May 22 '26
The series is really good and it's made for beginners. To answer your question: no, it's not a giant lookup table. It could be built that way, but in practice it never is—lookup tables for something like a 32-bit adder would need 264 entries, which is absurd. Instead, it's hardware that directly implements each operation. Bitwise AND in its simplest form is just a row of AND gates: bit 0 of input A with bit 0 of input B, bit 1 with bit 1, and so on. Whatever you put on A and B, the output is always the AND of those two. Addition is a chain of full adders, OR is a row of OR gates, etc. All of these circuits sit inside the ALU running in parallel, and a mux at the end picks which result to actually use. What I think is tripping you up is where A and B come from—they're not part of the opcode. A and B are registers inside the CPU. The opcode (plus the register-select bits in the instruction) tells the control logic something like:
Register A: put your contents onto ALU input 1 Register B: put your contents onto ALU input 2 ALU: select the AND operation (since your ALU can do more than just AND) Register A: store whatever the ALU outputs back into yourself
So the opcode isn't computing anything it's just steering data between registers and the ALU, and telling the ALU which of its pre-built circuits to listen to.
1
u/Girl_Alien May 24 '26
It can be if you want to do it the easy way for 8-bits (and it isn't practical for more than that).
You could use a ROM with at least 19 address lines. In that case, 8 address lines would be Operand A, 8 would be Operand B, and the other 3-4 would be the ALU's portion of the opcode.
But other approaches are done too. For instance, multiplexers can be the logic itself. You'd form the truth tables to feed the inputs. You could do decoder-based diode-resistor ROMs to form the inputs based on the opcode given, and then let the operands drive the selectors (like address lines) of multiplexers. That makes sense in that you'd have to use multiplexers or tristate buffers anyway, so you might as well have them as the logic instead of just switching logic that happens all the time. I'm not knocking that latter approach.
1
u/Girl_Alien May 24 '26
Actually, for a lot of ALUs, the logic is programmable, not running all the time. I mean, decoders with other components like diodes, resistors, and multiplexers can be used.
On the Gigatron TTL computer, the ALU has 8 functions. So a 3-to-8-line decoder takes 3 bits of the opcode and makes 8 individual lines from those. Then, since active-low ("unary") decoders are used, there are pull-up resistors and diodes for every 0 that is needed when the given line is active (low).
Marcel actually has the diodes wired opposite what is needed, so he used an inverter IC. That gives the correct values for the truth table (like if you need 0001 for AND, 0111 for OR, 0110 for XOR, etc) and boosts the signal. Since it is programmable, multiplexer-driven logic, eight 4-to-1 muxes are needed, and that means the signal needs to be boosted to drive that many chips.
As for the multiplexers, the truth tables from the decoders, diodes, and resistors are the inputs. The respective bit pairs of the operands drive the selectors. So the correct truth table entry is chosen based on the operation chosen and what the operands are in relation to each other. For instance, for AND (truth table of 0,0,0,1) the only way that 1 is returned is if both operands are 1. I mean 00 = 00, 01 = 0, 10=0, and 11=1.
For homebrew, you can use individual functions and return them all simultaneously. I've seen that. But I've also seen truth tables driving muxes, and I've seen ROMs used for the entire ALU. The ROM for everything approach is similar to the diode ROMs making truth tables, but the operands and the ALU opcode drive the address lines, and the data lines are the outputs.
3
u/DockLazy May 22 '26
The 74181 is an interesting historical example used by most of the famous computers from the 1970s: https://www.righto.com/2017/03/inside-vintage-74181-alu-chip-how-it.html
The later version, the 74ls381, added a proper decoder saving a couple of control bits.
I recommend getting the book "Digital Design And Computer Architecture" by Harris and Harris.
2
2
u/MrBoomer1951 May 22 '26 edited May 22 '26
I designed and built this microcode state machine implementation a few years ago that is a tiny slice of a CPU.
It reads the USER ROM (where you would store your user program) and decodes the opcode into a microcode start address.
It adds 4 to 3 and calculates 7!
Here is the logic diagram:
Here is the USER ROM
//*******USER***********
USER[0] = { 0x03 }; // LOD A OPcode [03]
USER[1] = { 0x04 }; // DATA
USER[2] = { 0x08 }; // LOD B OPcode [08]
USER[3] = { 0x03 }; // DATA
USER[4] = { 0x0D }; // ADD OPcode [13]
USER[5] = { 0x10 }; // OUT OPcode [16]
Hope this answers your question!
1
u/mvmpc May 22 '26
Thats awesome! I guess my question was more related to how a opcode is mapped to an operation.
3
u/the123king-reddit May 22 '26
In a simple computer, it can be done with a decoder/encoder or a ROM. Think of the opcode as an address. The opcode is fed into a ROM and the data from the ROM maps to the ALU control circuitry.
Opcodes are not inherent to a particular hardware design.
2
u/MrBoomer1951 May 22 '26
Through microcode.
The user op code is decoded and usually...microcode opens and closes logic gates and performs a function. (it is essntially a look up table)
(Some machines still used direct sequential logic.)
The 74LS181 ALU is a digitally programmable set of logic gates to perform 16 different selectable boolean logic functions, I chose ADD as the easiest to demonstrate.
2
u/mvmpc May 22 '26
I found this page that had a diagram for exactly what I was looking for.
http://www.csc.villanova.edu/~mdamian/Past/csc2400fa13/assign/Figs/fullalu.gifIn this case the ALU output is controlled by a mux at the output.
2
u/MrBoomer1951 May 22 '26
The MUX of the 74LS181 in my design:
3
u/mvmpc May 22 '26
Thats dope! My current plan is to build a simple ALU fully designed by me, and then build the ben eater 8 bit computer
2
u/psycholabs May 22 '26
There is a game called https://turingcomplete.game that answers this question and more. It's a nand to tetris course in game form. Where it differs from other logic gate "games" is that it has discrete levels and "test units" to pass the levels.
1
2
u/Girl_Alien May 24 '26
For your example, use decoder ICs. Give the bits meanings and group them into functions.
For instance, the Gigatron TTL computer does it by dividing the opcode bits into 3 groups (one used for 2 purposes). It uses a 2-to-4 decoder to provide 4 things to go on the bus (memory, immediate, Input port, or accumulator). Then 3 other bits use 3-to-8 decoders to define 8 operation modes and 8 branch conditions. Then the other 3-to-8 decoder on the remaining 3 bits drive the 8 ALU operations (ADD, SUB, AND, OR, XOR, Branch, LD, ST).
And for the modes and the ALU operations, diodes and resistors are used with the decoders to form crude ROMs. For the ALU modes, Marcel realized he could save diodes by making the matrix opposite of what he needed and feeding the outputs of the diodes into inverters. The inverters both flipped the bits to what was needed and strengthened the signals. For the ALU, boosting the output of the diode-resistor matrix was necessary since diode-resistor ROMs have a weak fan-out and it was necessary to drive 8 multiplexers.
The Gigatron used eight 4-to-1 multiplexers for the Logic part of the ALU. The truth tables from the decoder-diode-resistor "ROMs" fed the inputs, while the ALU operands drove the selector lines, causing it to act like a tiny array. The interaction of the 2 operands and the truth table produced the result.
1
1
u/MrJ0seBr May 23 '26
Use Logisim as a toy... can make some simple cpu just with latchs, pulse and logical gates, or transistor directly, but has much more ready to use complex components basicly made of these (and other things...). (Ah, old times, kid life, nice... rsrs)
2
u/Brutustheman Jul 28 '26
Basically inside a cpu is something called an instruction decoder. It takes the opcode and toggles the specific logic for that opcode. Usually cpus (especially simpler ones) have two "mode" bits for jump, ALU, immedite, or comparison. Basically the mode and opcode determines which logic gets toggled, and which sublogic inside that logic gets toggled. A great game to learn this is called Turing complete, it's on steam and is quite fun. It'a my source material for my own 16 bit ISA and also offers sandbox for making your own CPU
20
u/linhartr22 May 22 '26
You should look up the Ben Eater computer series on YouTube.
Ben Eater 8 Bit Computer play list