📄 gb_gates.w
字号:
@ @<Create gates for the general logic operation@>=start_prefix("L");for (k=0;k<16;k++)@/ do4(log[k],OR,@| make3(AND,mod[0],comp(old_dest[k]),comp(source[k])),@| make3(AND,mod[1],comp(old_dest[k]),source[k]),@| make3(AND,mod[2],old_dest[k],comp(source[k])),@| make3(AND,mod[3],old_dest[k],source[k]));@ @<Create gates for the conditional load operations@>=start_prefix("C");do4(tmp[0],OR,@| make3(AND,mod[0],comp(sign),comp(nonzero)),@| make3(AND,mod[1],comp(sign),nonzero),@| make3(AND,mod[2],sign,comp(nonzero)),@| make3(AND,mod[3],sign,nonzero));do4(tmp[1],OR,@| make3(AND,mod[0],comp(carry),comp(overflow)),@| make3(AND,mod[1],comp(carry),overflow),@| make3(AND,mod[2],carry,comp(overflow)),@| make3(AND,mod[3],carry,overflow));do3(change,OR,comp(cond),make2(AND,tmp[0],comp(op)),make2(AND,tmp[1],op));@ @<Local variables for |risc|@>=Vertex *change; /* is the destination register supposed to change? */@ Hardware is like software except that it performs all the operationsall the time and then selects only the results it needs. (If you think aboutit, this is a profound observation about economics, society, and nature.Gosh.)@<Create gates that bring everything together properly@>=start_prefix("Z");@<Create gates for the |next_loc| and |next_next_loc| bits@>;@<Create gates for the |result| bits@>;@<Create gates for the new values of registers 1 to |regs|@>;@<Create gates for the new values of \.S, \.N, \.K, and \.V@>;@<Create gates for the new values of the program register and |extra|@>;@<Create gates for the new values of register 0 and the memory address register@>;@ @<Create gates for the |next_loc|...@>=next_loc[0]=comp(reg[0]);@+next_next_loc[0]=reg[0];next_loc[1]=make_xor(reg[0]+1,reg[0]);@+next_next_loc[1]=comp(reg[0]+1);for (t5=reg[0]+1,k=2;k<16;t5=make2(AND,t5,reg[0]+k++)) { next_loc[k]=make_xor(reg[0]+k,make2(AND,reg[0],t5)); next_next_loc[k]=make_xor(reg[0]+k,t5);}@ @<Create gates for the |result| bits@>=jump=make5(AND,op,mod[0],mod[1],mod[2],mod[3]); /* assume |cond=0| */for (k=0;k<16;k++) { do5(result[k],OR,@| make2(AND,comp(op),log[k]),@| make2(AND,jump,next_loc[k]),@| make3(AND,op,comp(mod[3]),shift[k]),@| make5(AND,op,mod[3],comp(mod[2]),comp(mod[1]),sum[k]),@| make5(AND,op,mod[3],comp(mod[2]),mod[1],diff[k])); do2(result[k],OR,@| make3(AND,cond,change,source[k]),@| make2(AND,comp(cond),result[k]));}for (k=16;k<18;k++) /* carry and overflow bits of the result */ do3(result[k],OR,@| make3(AND,op,comp(mod[3]),shift[k]),@| make5(AND,op,mod[3],comp(mod[2]),comp(mod[1]),sum[k]),@| make5(AND,op,mod[3],comp(mod[2]),mod[1],diff[k]));@ The program register |prog| and the |extra| bit are needed forthe case when we must spend an extra cycle to fetch a word from memory.On the first cycle, |ind| is true, so a ``result'' is calculated but notactually used. On the second cycle, |extra| is true.A slight optimization has been introduced in order to make the circuita bit more interesting: If a conditional load instruction occurs withindirect addressing and a false condition, the extra cycle is not taken.(The |next_next_loc| values were computed for this reason.)@d latchit(u,@!latch) (latch)->alt=make2(AND,u,run_bit) /* |u&run_bit| is new value for |latch| */@<Create gates for the new values of the program reg...@>=for (k=0;k<10;k++) latchit(mem[k+6],prog+k);do2(nextra,OR,make2(AND,ind,comp(cond)),make2(AND,ind,change));latchit(nextra,extra);nzs=make4(OR,mem[0],mem[1],mem[2],mem[3]);nzd=make4(OR,dest[0],dest[1],dest[2],dest[3]);@ @<Local variables for |risc|@>=Vertex *jump; /* is this command a \.{JUMP}, assuming |cond| is false? */Vertex *nextra; /* must we take an extra cycle? */Vertex *nzs; /* is the \.{SRC} field nonzero? */Vertex *nzd; /* is the \.{DST} field nonzero? */@ @<Create gates for the new values of registers 1 to |regs|@>=t5=make2(AND,change,comp(ind)); /* should destination register change? */for (r=1;r<regs;r++) { t4=make2(AND,t5,dest_match[r]); /* should register |r| change? */ for (k=0;k<16;k++) { do2(t3,OR,make2(AND,t4,result[k]),make2(AND,comp(t4),reg[r]+k)); latchit(t3,reg[r]+k); }}@ @<Create gates for the new values of \.S, \.N, \.K, and \.V@>=do4(t5,OR,@| make2(AND,sign,cond),@| make2(AND,sign,jump),@| make2(AND,sign,ind),@| make4(AND,result[15],comp(cond),comp(jump),comp(ind)));latchit(t5,sign);do4(t5,OR,@| make4(OR,result[0],result[1],result[2],result[3]),@| make4(OR,result[4],result[5],result[6],result[7]),@| make4(OR,result[8],result[9],result[10],result[11]),@| make4(OR,result[12],result[13],result[14],@|@t\hskip5em@>make5(AND,make2(OR,nonzero,sign),op,mod[0],comp(mod[2]),mod[3])));do4(t5,OR,@| make2(AND,nonzero,cond),@| make2(AND,nonzero,jump),@| make2(AND,nonzero,ind),@| make4(AND,t5,comp(cond),comp(jump),comp(ind)));latchit(t5,nonzero);do5(t5,OR,@| make2(AND,overflow,cond),@| make2(AND,overflow,jump),@| make2(AND,overflow,comp(op)),@| make2(AND,overflow,ind),@| make5(AND,result[17],comp(cond),comp(jump),comp(ind),op));latchit(t5,overflow);do5(t5,OR,@| make2(AND,carry,cond),@| make2(AND,carry,jump),@| make2(AND,carry,comp(op)),@| make2(AND,carry,ind),@| make5(AND,result[16],comp(cond),comp(jump),comp(ind),op));latchit(t5,carry);@ As usual, we have left the hardest case for last, hoping that we willhave learned enough tricks to handle it when the time of reckoningfinally arrives.The most subtle part of the logic here is perhaps the case of a\.{JUMP} command with $\.A=3$. We want to increase register~0 by~1during the first cycle ofsuch a command, if $\.{SRC}=0$, so that the |result| will becorrect on the next cycle.@<Create gates for the new values of register 0...@>=skip=make2(AND,cond,comp(change)); /* false conditional? */hop=make2(AND,comp(cond),jump); /* \.{JUMP} command? */do4(normal,OR,@| make2(AND,skip,comp(ind)),@| make2(AND,skip,nzs),@| make3(AND,comp(skip),ind,comp(nzs)),@| make3(AND,comp(skip),comp(hop),nzd));special=make3(AND,comp(skip),ind,nzs);for (k=0;k<16;k++) { do4(t5,OR,@| make2(AND,normal,next_loc[k]),@| make4(AND,skip,ind,comp(nzs),next_next_loc[k]),@| make3(AND,hop,comp(ind),source[k]),@| make5(AND,comp(skip),comp(hop),comp(ind),comp(nzd),result[k])); do2(t4,OR,@| make2(AND,special,reg[0]+k),@| make2(AND,comp(special),t5)); latchit(t4,reg[0]+k); do2(t4,OR,@| make2(AND,special,old_src[k]),@| make2(AND,comp(special),t5)); {@+register Arc *a=gb_virgin_arc(); a->tip=make2(AND,t4,run_bit); a->next=new_graph->outs; new_graph->outs=a; /* pointer to memory address bit */ }} /* arcs for output bits will appear in big-endian order */@ @<Local variables for |risc|@>=Vertex *skip; /* are we skipping a conditional load operation? */Vertex *hop; /* are we doing a \.{JUMP}? */Vertex *normal; /* is this a case where register 0 is simply incremented? */Vertex *special; /* is this a case where register 0 and the memory address register will not coincide? */@* Serial addition. We haven't yet specified the parts of |risc| thatdeal with addition and subtraction; somehow, those parts wanted tobe separate from the rest. To complete our mission, we will usesubroutine calls of the form `|make_adder(n,x,y,z,carry,add)|',where |x| and |y| are |n|-bit arrays of input gates and|z|~is an |(n+1)|-bit array of output gates. If |add!=0|, the subroutinecomputes |x+y|, otherwise it computes |x-y|. If |carry!=0|, the |carry| gateis effectively added to~|y| before the operation.A simple |n|-stage serial scheme, which reduces the problem of |n|-bitaddition to |(n-1)|-bit addition, is adequate for our purposes here.(A parallel adder, which gains efficiency by reducing the problem sizefrom |n| to~$n/\phi$, can be found in the |prod| routine below.)The handy identity $x-y=\overline{\overline x+y}$ is used to reducesubtraction to addition.@<Internal...@>=static void make_adder(n,x,y,z,carry,add) unsigned long n; /* number of bits */ Vertex *x[],*y[]; /* input gates */ Vertex *z[]; /* output gates */ Vertex *carry; /* add this to |y|, unless it's null */ char add; /* should we add or subtract? */{@+register long k; Vertex *t1,*t2,*t3,*t4; /* temporary storage used by |do4| */ if (!carry) { z[0]=make_xor(x[0],y[0]); carry=make2(AND,even_comp(add,x[0]),y[0]); k=1; }@+else k=0; for (;k<n;k++) { comp(x[k]);@+comp(y[k]);@+comp(carry); /* generate inverse gates */ do4(z[k],OR,@| make3(AND,x[k],comp(y[k]),comp(carry)),@| make3(AND,comp(x[k]),y[k],comp(carry)),@| make3(AND,comp(x[k]),comp(y[k]),carry),@| make3(AND,x[k],y[k],carry)); do3(carry,OR,@| make2(AND,even_comp(add,x[k]),y[k]),@| make2(AND,even_comp(add,x[k]),carry),@| make2(AND,y[k],carry)); } z[n]=carry;}@ OK, now we can add. What good does that do us?In the first place, we need a 4-bit adder to compute the leastsignificant bits of $|old_dest|+\.{SRC}$. The other 12 bits of thatsum are simpler.@<Set |inc_dest| to |old_dest| plus \.{SRC}@>=make_adder(4L,old_dest,mem,inc_dest,NULL,1);up=make2(AND,inc_dest[4],comp(mem[3])); /* remaining bits must increase */down=make2(AND,comp(inc_dest[4]),mem[3]); /* remaining bits must decrease */for (k=4;;k++) { comp(up);@+comp(down); do3(inc_dest[k],OR,@| make2(AND,comp(old_dest[k]),up),@| make2(AND,comp(old_dest[k]),down),@| make3(AND,old_dest[k],comp(up),comp(down))); if (k<15) { up=make2(AND,up,old_dest[k]); down=make2(AND,down,comp(old_dest[k])); }@+else break;}@ @<Local variables for |risc|@>=Vertex *up,*down; /* gates used when computing |inc_dest| */@ In the second place, we need a 16-bit adder and a 16-bit subtracterfor the four addition/subtraction commands.@<Create gates for the arithmetic operations@>=start_prefix("A");@<Create gates for the shift operations@>;make_adder(16L,old_dest,source,sum,make2(AND,carry,mod[0]),1); /* adder */make_adder(16L,old_dest,source,diff,make2(AND,carry,mod[0]),0); /* subtracter */do2(sum[17],OR,@| make3(AND,old_dest[15],source[15],comp(sum[15])),@| make3(AND,comp(old_dest[15]),comp(source[15]),sum[15])); /* overflow */do2(diff[17],OR,@| make3(AND,old_dest[15],comp(source[15]),comp(diff[15])),@| make3(AND,comp(old_dest[15]),source[15],diff[15])); /* overflow */@ @<Create gates for the shift operations@>=for (k=0;k<16;k++)@/ do4(shift[k],OR,@| (k==0? make4(AND,source[15],mod[0],comp(mod[1]),comp(mod[2])):@| @t\hskip5em@>make3(AND,source[k-1],comp(mod[1]),comp(mod[2]))),@| (k<4? make4(AND,source[k+12],mod[0],mod[1],comp(mod[2])):@| @t\hskip5em@>make3(AND,source[k-4],mod[1],comp(mod[2]))),@| (k==15? make4(AND,source[15],comp(mod[0]),comp(mod[1]),mod[2]):@| @t\hskip5em@>make3(AND,source[k+1],comp(mod[1]),mod[2])),@| (k>11? make4(AND,source[15],comp(mod[0]),mod[1],mod[2]):@| @t\hskip5em@>make3(AND,source[k+4],mod[1],mod[2])));do4(shift[16],OR,@| make2(AND,comp(mod[2]),source[15]),@| make3(AND,comp(mod[2]),mod[1], make3(OR,source[14],source[13],source[12])),@| make3(AND,mod[2],comp(mod[1]),source[0]),@| make3(AND,mod[2],mod[1],source[3])); /* ``carry'' */do3(shift[17],OR,@| make3(AND,comp(mod[2]),comp(mod[1]), make_xor(source[15],source[14])),@| make4(AND,comp(mod[2]),mod[1],@| @t\hskip5em@>make5(OR,source[15],source[14], source[13],source[12],source[11]),@| @t\hskip5em@>make5(OR,comp(source[15]),comp(source[14]), comp(source[13]),@| @t\hskip10em@>comp(source[12]),comp(source[11]))),@| make3(AND,mod[2],mod[1], make3(OR,source[0],source[1],source[2]))); /* ``overflow'' */@* RISC management. The |run_risc| procedure takes a gate graph output by|risc| and simulates its behavior, given the contents of its read-only memory.(See the demonstration program {\sc TAKE\_\,RISC}, which appears in a moduleby itself, for a typical illustration of how |run_risc| might be used.)This procedure clears the simulated machine and begins executing the programthat starts at address~0. It stops when it gets to an address greaterthan the size of read-only memory supplied. One way to stop itis therefore to execute a command such as |0x0f00|, which will transfercontrol to location |0xffff|; even better is |0x0f8f|, which transfersto location |0xffff| without changing the status of \.S and \.N.However, if the given read-only memorycontains a full set of $2^{16}$ words, |run_risc| will never stop.When |run_risc| does stop, it returns 0 and puts the final contents of thesimulated registers into the global array |risc_state|.Or, if |g| was not a decent graph, |run_risc| returns a negative value andleaves |risc_state| untouched.@<The |run_risc|...@>=long run_risc(g,rom,size,trace_regs) Graph *g; /* graph output by |risc| */ unsigned long rom[]; /* contents of read-only memory */ unsigned long size; /* length of |rom| vector */ unsigned long trace_regs; /* if nonzero, this many registers will be traced */{@+register unsigned long l; /* memory address */ register unsigned long m; /* memory or register contents */ register Vertex *v; /* the current gate of interest */ register Arc *a; /* the current output list element of interest */ register long k,r; /* general-purpose indices */ long x,s,n,c,o; /* status bits */ if (trace_regs) @<Print a headline@>; r=gate_eval(g,"0",NULL); /* reset the RISC by turning off the \.{RUN} bit */ if (r<0) return r; /* not a valid gate graph! */ g->vertices->val=1; /* turn the \.{RUN} bit on */ while (1) { for (a=g->outs,l=0;a;a=a->next) l=2*l+a->tip->val;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -