An example of a simple ALU.
Two input_busses, 4 bit each.
4 Bit command.
Carry and Zero_Flag.
/* * * 4bitalu.c V1.0 (c) Dieter Mueller 08/2004 * * example: 4 Bit ALU inside the 27512 EPROM. Some Pins unused. * * WARNING: this code is intended for demonstration * only, and was never testet in a real/existing CPU. * * Don't really expect it to work at all. * */ #include <stdio.h> #include <stdlib.h> //definition of ALU_commands #define ALU_PASS_A 0x00 #define ALU_PASS_B 0x01 #define ALU_ROL_A 0x02 #define ALU_ROR_A 0x03 #define ALU_LSL_A 0x04 #define ALU_LSR_A 0x05 #define ALU_ADD 0x06 #define ALU_SUB 0x07 #define ALU_ADC 0x08 #define ALU_SBC 0x09 #define ALU_AND 0x0a #define ALU_OR 0x0b #define ALU_XOR 0x0c #define ALU_CONST_0 0x0d #define ALU_CONST_1 0x0e #define ALU_CONST_F 0x0f int main(void); char do_rom(long addr); int main(void) { FILE *outstream; long i; outstream=fopen("4bitalu.bin","wb"); if(outstream==NULL) { printf("\nFile open error.\n"); return(-1); } for(i=0; i<0x10000; i++) //try all possible input_patterns, 64 kBytes { fputc(do_rom(i),outstream); //write one EPROM Byte } fclose(outstream); return(0); } char do_rom(long addr) { char out; char cmd,a,b; char cin; //carry input char cout,zout; //flags //default: clear flags, clear result out=0; cin=0; cout=0; zout=0; //EPROM: //A0..3=A, A4..7=B, A8..11=ALU_command, A12=Carry_in //Q0..3=Q, Q4=Z_Flag, Q7 =C_Flag a = addr & 0x0f; b =(addr>>4) & 0x0f; cmd=(addr>>8) & 0x0f; if((long)addr & 0x1000) cin=-1; //carry input switch(cmd) { case ALU_PASS_A: out=a; break; case ALU_PASS_B: out=b; break; case ALU_ROL_A: if(cin) out=0x01; //the rest like LSL case ALU_LSL_A: out|=(a<<1)&0x0e; if((char)a&0x08){cout=-1;} break; case ALU_ROR_A: if(cin) out=0x08; //the rest like LSR case ALU_LSR_A: out|=(a>>1)&0x07; if((char)a&0x01){cout=-1;} break; case ALU_AND: out=a&b; break; case ALU_OR: out=a|b; break; case ALU_XOR: out=a^b; break; case ALU_ADC: if(cin) out=1; case ALU_ADD: out+=(a&0xf)+(b&0xf); if(out>0x0f){cout=-1;} break; case ALU_SBC: if(cin==0) out=-1; //subtract 1, borrow active. case ALU_SUB: out+=(a&0xf)-(b&0xf); if(out>=0){cout=-1;/*no borrow.*/} break; case ALU_CONST_0: out=0x00; break; case ALU_CONST_1: out=0x01; break; case ALU_CONST_F: out=0x0f; break; } out&=0x0f; //limit result to 4 Bit, to keep the flags clean. if(out==0) zout=-1; //check for Zero_Condition //now to pass the flags to the outputs. if(zout) out|=0x10; //Zero if(cout) out|=0x80; //Carry return(out); }
When wasting a few more thoughts on this, we could
increment, decrement, add, subtract, multiply and divide,
barrel shift, sign extend, in binary and BCD.
Convert from binary to BCD to ASCII and back.
Generate any constant we need to set up the
address bus for jumping Interrupt vectors.
V_Flag, parity checking, Bit merging from both input_busses...
no problem at all.
Speaking of Bit merging, another trick is to define
special ALU commands to update/modify the status register
after an arithmetic/logic operation.
Simply latch the (temporary) flag outputs from the ALU
into a 74374, and pass them together with the contents
of the status register through the ALU in the next
microcode cycle.
Wastes not much CPU time (could be done during
memory write back, or during command fetch,
when the ALU is not used)...
but this can save quite a few components
in a 74XX TTL_based design.
So far, we could cram every function into the EPROM
we can imagine, and a few we can not.
The only problem is: with one EPROM,
we may be limited to 4 Bit.
[HOME] [UP]/ [BACK] [1] [2] [3] [4] [5] [6] [7] [8] [NEXT]
(c) Dieter Mueller 2004