Now for something different: one Bit processors.
The best example for a very simple architecture
is the MC14500, which was intended to be used
as "programmable logic controller" for industrial
applications.
We have data memory, a temporary register RR,
and a Logic Unit.
Note, that it isn't designed to do any arithmetic.
I'm leaving out quite a few details, like that it's
possible to write not only RR to memory, but also the
complement of RR.
Or that the data from memory and the "DIN" Flag go
through an AND gate, before the data enters the Logic Unit.
Program memory also isn't shown, or the circuitry related
to fetching an instruction and such.
Now an example about building a one Bit full adder:
The schematic looks a bit unusual, because it's
optimised for the MC14500 instruction set...
which contains XNOR, but no XOR.
(Carry has to be set to 0 before processing the first Bit).
ADD1: ;Q=A+B+C -> ;Q = A^B^C, C = A&C | B&C | A&B LD A ;three_input XOR: A, B, C. XNOR B XNOR C STO Q ;store the result into Q LD A ;if A = 1 or B = 1 OR B ;(we don't have an XOR instruction) AND C ;and a carry goes in STO C ;the carry is propagated to the next Bit. LD A ;if A = 1 and B = 1 AND B OR C ;generate a carry. STO C
We could still optimise:
ADD1: ;Q=A+B+C -> ;Q = A^B^C, C = A&C | B&C | A&B LDC A ;three_input XOR: A, B, C. XNOR B ;RR = A^B XNOR C STOC Q ;store the result into Q XNOR C ;set RR back to A^B. If A = 1 or B = 1... AND C ;and a carry goes in STO C ;the carry is propagated to the next Bit. LD A ;if A = 1 and B = 1 AND B OR C ;generate a carry. STO C
Now for a subtraction:
(Carry has to be set to 1 before processing the first Bit.)
SUB1: ;Q=A+/B+C -> ;Q = A^/B^C, C = A&C | /B&C | A&/B LD B XNOR A ;RR = /(A^B), what isn't different from A^/B XNOR C STOC Q ;store the result into Q XNOR C ;set RR back to A^/B. If A = 1 or B = 1... AND C ;and a carry goes in STO C ;the carry is propagated to the next Bit. LD A ;if A = 1 and B = 0 ANDC B OR C ;generate a carry. STO C
Note, that for these programs, Q shouldn't overwrite A or B.
For A-B, all we had to do was inverting B.
For decrementing A, we could assume that B is always 1.
For incrementing A, B is 0, except for the first Bit...
for which either B = 1 or C = 1.
Adding Carry to a bit in memory:
(Carry has to be set to 1 before incrementing a variable in memory)
INC1: ;A=A+C -> ;A = A^C, C = A&C LD A XNOR C ;if A = C, RR = 1. STOC A AND C ;if A = 1 and C = 1, generate a carry. STO C
Decrementing:
(carry has to be set to 0 before processing the first Bit)
DEC1: ;Q=A+1+C -> ;A = /(A^C), C = A|C LD A XNOR C STO A XNOR C ;get old A back into RR OR C STO C
You sure can imagine, how to optimize the code when
processing the first Bit for the increment/decrement example.
Now for a dirty trick:
ADTO1:;A=A+B+C ;don't know exactly, why this works... LD A XNOR B XNOR C STO A ;A = A^B^C XNOR B AND C STO C LDC A AND B OR C STO C
There are different approaches for writing a program
which works as a full adder, but they usually always seem
to take more than 10 instructions.
Means, when a 2 MHz MC14500 performs Q=A+B on two Bytes
in memory, it's at least four times slower than a 1 MHz 6502.
Writing your own "arithmetic software package" might be
technically possible, if you happen to have a good macro assembler...
and enough program memory.
However, not everything that is technically possible makes sense.
(And not everything that makes sense is technically possible.)
[HOME] [UP]/ [BACK] [1] [2] [3] [4] [5] [6] [7] [8] [NEXT]
(c) Dieter Mueller 2008