E. Sample Gmac Input File
This appendix contains a complete listing of the Gmac input file for
the Menagerie CPU including as a sample circuit.
//
// Copyright (C) 1987-2000 by Jeffery P. Hansen
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// Last edit by hansen on Thu Jul 26 20:56:44 2007
//
// Microcode memory bank declarations
microcode bank[31:0] iunit.m1;
microcode bank[63:32] iunit.m2;
map bank[7:0] iunit.map;
macrocode bank[7:0] memory.m1;
// Microcode field declarations
//
// Microcode branching. mpcop specifies the basic operation.
//
field mpcop[1:0]={
next=0, // Increment mpc
reinit=1, // Restart CPU
jmap=2, // Jump from map value
jump=3 // Jump from condition
};
//
// Specifies the condition on which to jump if mpcop is "jump".
//
field mpccond[12:10]={
jne=0, // Jump if not equal
jcarry=1, // Jump on carry
jeq=2, // Jump if equal
jlt=3, // Jump if less than
jgt=4, // Jump if greater than
jle=5, // Jump if less than or equal
jge=6, // Jump if greater than or equal
jmp=7 // Jump always
};
//
// Address to jump to if mpcop is "jump" and condition specified
// by mpccond is true. This field can not be used at the same
// time as the idata field.
//
field mpcaddr[9:2];
//
// Specifies 8 bits of data to be used by the EUNIT. This field
// can not be used on jump microinstructions.
//
field idata[9:2];
//
// Specifies the A and B operands of the ALU.
//
// qreg Use Q register
// din Use data in
// idata Use idata field from microinstruction
// reg Use register file
//
field aop[15:14]={qreg=0, din=1, idata=2, reg=3};
field bop[17:16]={qreg=0, din=1, idata=2, reg=3};
field ~ldir[13]; // Load instruction register
field cin[18]; // Carry in
field ~clq[19]; // Clear Q register
field ~ldq[20]; // 16-bit load of Q register
field ~lddata[21]; // Load EUNIT data from external bus
field ~ldopr[22]; // Load operand register
field ~wa[23]; // Write register file on SA
field sa[27:24]; // Register address A
field sb[31:28]; // Register address B
//
// These fields specify the ALU function.
//
field ALU_FUNC[36:32];
field ALU_SHOP[33:32]={arshift=0, lshift=1, rshift=2, roll=3};
field ALU_BCOMP[32];
field ALU_AZERO[33];
field ALU_OP[36:34]={shift=0,xor=1,and=2,or=3,mul=4,add=5,mod=6,div=7};
field ~incpc[37]; // Increment PC
field ~ldmar[38]; // Load MAR
field ~ldmdr[39]; // Load MDR
field ~ldpc[40]; // Load PC
field ~rd[41]; // Read main memory
field ~rdmdr[42]; // Read MDR onto external data bus
field ~wrt[43]; // Write main memory
field spc[44]; // Address main memory from PC
field ~isa[45]; // Use sa address from macro instruction
field ~isb[46]; // Use sb address from macro instruction
field ~ifunc[47]; // Use function code from macro instruction
field ~icond[48]; // Use branch condition from macro instruction
field ~ldql[49]; // 8-bit load of lower half of Q register
field ~ldqh[50]; // 8-bit load of upper half of Q register
field ~dout[51]; // Output EUNIT data to external bus
field ~ldcc[52]; // Load condition code register
field ~ldhmdr[53]; // Load mdr from high byte of data bus
field ~rdpc[54]; // Read PC onto external bus
field ~incmar[55]; // Increment mar (can't use with incpc)
field extra[63:56]; // Extra bits
//////////////////////////////////////////////////////////////////////
//
// +-+-+-+-+-+-+-+-+
// |7|6|5|4|3|2|1|0|
// +-+-+-+-+-+-+-+-+
//
// Basic instruction types are encoded by the high two bits of the
// first byte of the instruction. For certain types (e.g., ALU
// types) some bits are masked when forming the map address. Bits
// contributing to the map vector are marked with a *.
//
// Move Instruction
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |1 1 1 s|a a|b b| | reg1 | reg2 |
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
// * * * * * * * *
//
// s = size (1 = byte, 0 = word)
// aa = operand mode 1
// bb = operand mode 2
//
// Single operand instruction (push, pop, call, etc.)
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |1 0|0| op |b b| | reg1 |0 0 0 0|
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
// * * * * * * * *
//
//
// Branch instruction (jmp, jne, etc.)
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |1 0|1|cond |b b| | reg1 |0 0 0 0|
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
// * * * * *
//
//
// ALU Instruction
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 1| func |b| | reg1 | reg2 |
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
// * * *
//
// func = ALU function
// a = operand mode
//
// Other instructions
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 0| op |b| | reg1 | reg2 |
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
// * * * * * * * *
//
//
registers R0=0, R1=1, R2=2, R3=3, R4=4, R5=5, R6=6, R7=7, R8=8, R9=9, R10=10, R11=11, R12=12, R13=13, FP=14, SP=15;
operands basic {
%1,%2 = { +0[0] = 0; +1[7:4]=%1; +1[3:0]=%2; };
%1,#2 = { +0[0] = 1; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
};
operands runiop {
%1 = { +0[1:0] = 0; +1[7:4]=%1; +1[3:0]=0; };
#1 = { +0[1:0] = 1; +1=0; +2=#1[7:0]; +3=#1[15:8]; };
(%1) = { +0[1:0] = 2; +1[7:4]=%1; +1[3:0]=0; };
#2(%1) = { +0[1:0] = 3; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
};
operands wuniop {
%1 = { +0[1:0] = 0; +1[7:4]=%1; +1[3:0]=0; };
(%1) = { +0[1:0] = 2; +1[7:4]=%1; +1[3:0]=0; };
#2(%1) = { +0[1:0] = 3; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
};
//
// Operands for move instructions
//
operands movoprs {
%1,%2 = { +0[3:2] = 0; +0[1:0] = 0; +1[7:4]=%1; +1[3:0]=%2; };
%1,#2 = { +0[3:2] = 0; +0[1:0] = 1; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
%1,(%2) = { +0[3:2] = 0; +0[1:0] = 2; +1[7:4]=%1; +1[3:0]=%2; };
%1,#3(%2) = { +0[3:2] = 0; +0[1:0] = 3; +1[7:4]=%1; +1[3:0]=%2; +2=#3[7:0]; +3=#3[15:8]; };
%1,(#2) = { +0[3:2] = 0; +0[1:0] = 3; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
(%1),%2 = { +0[3:2] = 2; +0[1:0] = 0; +1[7:4]=%1; +1[3:0]=%2; };
(%1),#2 = { +0[3:2] = 2; +0[1:0] = 1; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
(%1),(%2) = { +0[3:2] = 2; +0[1:0] = 2; +1[7:4]=%1; +1[3:0]=%2; };
(%1),#3(%2) = { +0[3:2] = 2; +0[1:0] = 3; +1[7:4]=%1; +1[3:0]=%2; +2=#3[7:0]; +3=#3[15:8]; };
(%1),(#2) = { +0[3:2] = 2; +0[1:0] = 3; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
#1(%2),%3 = { +0[3:2] = 3; +0[1:0] = 0; +1[7:4]=%2; +1[3:0]=%3; +2=#1[7:0]; +3=#1[15:8]; };
#1(%2),#3 = { +0[3:2] = 3; +0[1:0] = 1; +1[7:4]=%2; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; +4=#3[7:0]; +5=#3[15:8]; };
#1(%2),(%3) = { +0[3:2] = 3; +0[1:0] = 2; +1[7:4]=%2; +1[3:0]=%3; +2=#1[7:0]; +3=#1[15:8]; };
#1(%2),#4(%3) = { +0[3:2] = 3; +0[1:0] = 3; +1[7:4]=%2; +1[3:0]=%3; +2=#1[7:0]; +3=#1[15:8]; +4=#4[7:0]; +5=#4[15:8]; };
#1(%2),(#3) = { +0[3:2] = 3; +0[1:0] = 3; +1[7:4]=%2; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; +4=#3[7:0]; +5=#3[15:8]; };
(#1),%2 = { +0[3:2] = 3; +0[1:0] = 0; +1[7:4]=0; +1[3:0]=%2; +2=#1[7:0]; +3=#1[15:8]; };
(#1),#2 = { +0[3:2] = 3; +0[1:0] = 1; +1[7:4]=0; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; +4=#2[7:0]; +5=#2[15:8]; };
(#1),(%2) = { +0[3:2] = 3; +0[1:0] = 2; +1[7:4]=0; +1[3:0]=%2; +2=#1[7:0]; +3=#1[15:8]; };
(#1),#3(%2) = { +0[3:2] = 3; +0[1:0] = 3; +1[7:4]=0; +1[3:0]=%2; +2=#1[7:0]; +3=#1[15:8]; +4=#3[7:0]; +5=#3[15:8]; };
(#1),(#2) = { +0[3:2] = 3; +0[1:0] = 3; +1[7:4]=0; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; +4=#2[7:0]; +5=#2[15:8]; };
};
//
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 0|1 1 1 1 1|0| |0 0 0 0|0 0 0 0|
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
//
op nop {
map nop : 0x3e;
+0=0x3e;
operands {
- = { +1=0; };
};
};
//
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 0|0 0 0 0 0|0| |0 0 0 0|0 0 0 0|
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
//
op halt {
map halt : 0x0;
+0=0;
operands {
- = { +1=0; };
};
};
//
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 0|0 0 0 0 1|0| | reg1 | reg2 |
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
//
op cmp {
map cmp_rr : 0x2;
map cmp_ri : 0x3;
+0[7:1]=0x1;
operands basic;
};
// Generic branch operation
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |1 0|1|x x x|b b| |0 0 0 0| reg1 |
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
//
// Example:
// br #7, loop
//
// Jump always to loop. 7 is a code indicating the condition always.
//
op jp {
map br_r : 0xa0;
map br_i : 0xa1;
map br_d : 0xa2;
map br_x : 0xa3;
+0[7:5] = 0x5;
operands {
#1,%2 = { +0[1:0] = 0; +0[4:2] = #1; +1[7:4]=%2; +1[3:0]=0; };
#1,#2 = { +0[1:0] = 1; +0[4:2] = #1; +1=0; +2=#2[7:0]; +3=#2[15:8]; };
#1,(%2) = { +0[1:0] = 2; +0[4:2] = #1; +1[7:4]=%2; +1[3:0]=0; };
#1,#3(%2) = { +0[1:0] = 3; +0[4:2] = #1; +1[7:4]=%2; +1[3:0]=0; +2=#3[7:0]; +3=#3[15:8]; };
};
};
op jne {
+0[7:5] = 0x5;
+0[4:2] = 0x0;
operands runiop;
};
op jcarry {
+0[7:5] = 0x5;
+0[4:2] = 0x1;
operands runiop;
};
op jeq {
+0[7:5] = 0x5;
+0[4:2] = 0x2;
operands runiop;
};
op jlt {
+0[7:5] = 0x5;
+0[4:2] = 0x3;
operands runiop;
};
op jgt {
+0[7:5] = 0x5;
+0[4:2] = 0x4;
operands runiop;
};
op jle {
+0[7:5] = 0x5;
+0[4:2] = 0x5;
operands runiop;
};
op jge {
+0[7:5] = 0x5;
+0[4:2] = 0x6;
operands runiop;
};
op jmp {
+0[7:5] = 0x5;
+0[4:2] = 0x7;
operands runiop;
};
// Generic ALU operation
//
// Note this is not a real instruction but is here for illustrative
// purposes only. Map entries must be made for each function code
// to use this instruction for real.
//
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 1|x x x x x|b| | reg1 | reg2 |
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
//
// Example:
// alu #0x14, R1, R2
//
// Does ALU operation 0x14 (addition) on R1 and R2, storing result in R1.
//
//op alu {
// map alu_rr : 0x40;
// map alu_ri : 0x41;
// +0[7:6] = 0x1;
// operands {
// #1,%2,%3 = { +0[0] = 0; +0[5:1]=#1; +1[7:4]=%2; +1[3:0]=%3; };
// #1,%2,#3 = { +0[0] = 1; +0[5:1]=#1; +1[7:4]=0; +1[3:0]=%2; +2=#3[7:0]; +3=#3[15:8]; };
// };
//};
//
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 1|1 0 1 0 0|b| | reg1 | reg2 |
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
//
op add {
map alu_rr : 0x68;
map alu_ri : 0x69;
+0[7:0]=0x68;
operands basic;
};
//
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 1|1 0 1 0 1|b| | reg1 | reg2 |
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
//
op sub {
map alu_rr : 0x6a;
map alu_ri : 0x6b;
+0[7:0]=0x6a;
operands basic;
};
//
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 1|1 0 0 0 0|b| | reg1 | reg2 |
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
//
op mul {
map xalu_rr : 0x60;
map xalu_ri : 0x61;
+0[7:0]=0x60;
operands basic;
};
//
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 1|1 1 1 0 0|b| | reg1 | reg2 |
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
//
op div {
map xalu_rr : 0x78;
map xalu_ri : 0x79;
+0[7:0]=0x78;
operands basic;
};
//
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 1|1 1 0 0 0|b| | reg1 | reg2 |
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
//
op mod {
map xalu_rr : 0x70;
map xalu_ri : 0x71;
+0[7:0]=0x70;
operands basic;
};
//
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 1|0 1 0 0 0|b| | reg1 | reg2 |
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
//
op and {
map alu_rr : 0x50;
map alu_ri : 0x51;
+0[7:0]=0x50;
operands basic;
};
// Call subroutine
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |1 0|0|0 0 0|b b| | reg1 |0 0 0 0|
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
//
// Example:
//
// call foo, #8
//
// This will perform the following actions:
// sp = sp - 2
// [sp] = pc
// sp = sp - 2
// [sp] = fp
// fp = sp
// sp = sp+8
// pc = foo
//
op call {
map call_ri : 0x80;
map call_ii : 0x81;
map call_di : 0x82;
map call_xi : 0x83;
+0[7:4] = 0x8;
operands {
#1,%2 = { +0[1:0] = 0; +1[7:4]=%2; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; };
#1,#2 = { +0[1:0] = 1; +1=0; +2=#1[7:0]; +3=#1[15:8]; +4=#2[7:0]; +5=#2[15:8]; };
#1,(%2) = { +0[1:0] = 2; +1[7:4]=%2; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; };
#1,#2(%3) = { +0[1:0] = 3; +1[7:4]=%3; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; +4=#2[7:0]; +5=#2[15:8]; };
#1 = { +0[1:0] = 1; +1=0; +2=0; +3=0; +4=#1[7:0]; +5=#1[15:8]; };
};
};
// Return from subroutine
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |0 0|0 0 0 1 0|0| |0 0 0 0|0 0 0 0|
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
//
// Example:
//
// ret
//
// This will perform the following actions:
// sp = fp
// fp = [sp]
// sp = sp + 2
// pc = [sp]
// sp = sp + 2
//
//
op ret {
map ret : 0x4;
+0=4;
operands {
- = { +1=0; };
};
};
// Push a word on the stack
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
// |1 0|0|0 0 1|b b| | reg1 |0 0 0 0|
// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ ...
//
// Example:
//
// pushw R1
//
op pushw {
map pushw_r : 0x84;
map pushw_i : 0x85;
map pushw_d : 0x86;
map pushw_x : 0x87;
+0[7:2]=0x21;
operands runiop;
};
op movb {
map movb_rr : 0xf0;
map movb_ri : 0xf1;
map movb_rd : 0xf2;
map movb_rx : 0xf3;
map movb_dr : 0xf8;
map movb_di : 0xf9;
map movb_dd : 0xfa;
map movb_dx : 0xfb;
map movb_xr : 0xfc;
map movb_xi : 0xfd;
map movb_xd : 0xfe;
map movb_xx : 0xff;
+0[7:4]=0xf;
operands movoprs;
};
op movw {
map movw_rr : 0xe0;
map movw_ri : 0xe1;
map movw_rd : 0xe2;
map movw_rx : 0xe3;
map movw_dr : 0xe8;
map movw_di : 0xe9;
map movw_dd : 0xea;
map movw_dx : 0xeb;
map movw_xr : 0xec;
map movw_xi : 0xed;
map movw_xd : 0xee;
map movw_xx : 0xef;
+0[7:4]=0xe;
operands movoprs;
};
/////////////////////////////////////////////////////////////////////////////
//
// The microcode for the Menagerie CPU begins here.
//
// The CPU begins executing microinstuctions at address 0. The instructions
// in the start block are executed only once. The next block consists of
// the instuction fetch sequnce. The multiple labels account for partial
// fetches that are done by some of the macrocode routines.
//
// For macro instructions which have no operands or have only only one operand,
// there is usually a plain label for that instruction. For example the
// microinstruction labeled 'ret' implements the 'ret' macroinstruction. This
// mapping is defined by the 'map ret : 0x4;' line in the operand declaration
// for the ret macroinstruction.
//
// For macro instructions with multiple addressing modes, labels are generally
// of the form 'op_??' where each character after the '_' denotes an addressing
// mode for an operand. By convension, the characters:
//
// r register direct
// i immediate
// d register indirect
// x indexed
//
// are used.
//
begin microcode @ 0
start: clq; // Q <- 0
idata=0x1 ALU_AZERO ALU_OP=add bop=idata ldqh; // Q.H <- 1
ALU_AZERO ALU_OP=add bop=qreg dout ldpc; // PC <- Q
mpcop=jump mpccond=jmp mpcaddr=fetch; // jump to 'fetch'
mpcop=next;
fetch: incpc spc rd ldmdr; // mdr <- [PC]; PC++;
fetch1: incpc spc rd ldmdr rdmdr ldir; // mdr <- [PC]; PC++; ir <- mdr;
fetch2: mpcop=jmap rdmdr ldopr; // opr <- mdr; jump to opcode
mpcop=next;
halt: mpcop=jump mpccond=jmp mpcaddr=halt extra=0x1;
mpcop=jump mpccond=jmp mpcaddr=halt extra=0x1;
//
// Standard ALU operations (add, sub, and, or, etc.)
//
alu_rr: mpcop=jump mpccond=jmp mpcaddr=fetch2 ifunc
isa isb wa aop=reg bop=reg ldcc incpc spc rd ldmdr; // Ra = Ra (op) Rb; CC; jump to fetch
incpc spc rd ldmdr rdmdr ldir;
alu_ri: incpc spc rd ldmdr; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql
ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din; // Q.H = mdr;
mpcop=jump mpccond=jmp mpcaddr=fetch ifunc
aop=reg bop=qreg wa isa ldcc; // Ra = Ra (op) Q; CC; jump to fetch
mpcop=next;
//
// 2-cycle ALU operations (mul, div, mod). These are the same as the alu_??
// ops except the ALU inputs and function code are held for 2 clock cycles
// due to the longer delay in computing these functions.
//
xalu_rr: mpcop=next ifunc isa isb aop=reg bop=reg; // Ra (op) Rb;
mpcop=jump mpccond=jmp mpcaddr=fetch2 ifunc
isa isb wa aop=reg bop=reg ldcc incpc spc rd ldmdr; // Ra = Ra (op) Rb; CC; jump to fetch
incpc spc rd ldmdr rdmdr ldir;
xalu_ri: incpc spc rd ldmdr; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql
ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din; // Q.H = mdr;
mpcop=next ifunc isa isb aop=reg bop=qreg; // Ra (op) Q;
mpcop=jump mpccond=jmp mpcaddr=fetch ifunc
aop=reg bop=qreg wa isa ldcc; // Ra = Ra (op) Q; CC; jump to fetch
mpcop=next;
cmp_rr: mpcop=jump mpccond=jmp mpcaddr=fetch ALU_OP=add ALU_BCOMP extra=0x22
isa isb aop=reg bop=reg ldcc; // Ra - Rb; CC; jump to fetch
mpcop=next;
cmp_ri: incpc spc rd ldmdr; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql
ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din; // Q.H = mdr;
mpcop=jump mpccond=jmp mpcaddr=fetch ALU_OP=add ALU_BCOMP
aop=reg bop=qreg isa ldcc; // Ra - Q; CC; jump to fetch
mpcop=next;
br_i: incpc spc rd ldmdr; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql
ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din; // Q.H = mdr;
btest: mpcop=jump mpcaddr=bdojmp icond; // jump to bdojmp if cond
mpcop=next;
mpcop=jump mpccond=jmp mpcaddr=fetch; // jump to fetch
mpcop=next;
bdojmp: mpcop=jump mpccond=jmp mpcaddr=fetch ALU_AZERO ALU_OP=add
bop=qreg dout ldpc; // PC <- Q, jump to fetch
mpcop=next;
br_r: mpcop=jump mpccond=jmp mpcaddr=btest extra=0x10
isa aop=reg sb=0 bop=reg ALU_OP=add ldq; // Q <- Ra; jump to btest
mpcop=next;
br_d: mpcop=jump mpccond=jmp mpcaddr=btest;
mpcop=next;
br_x: mpcop=jump mpccond=jmp mpcaddr=btest;
mpcop=next;
call_ii:
// Push the PC
aop=reg sa=0xf bop=idata idata=2 ALU_OP=add ALU_BCOMP
dout ldmar wa extra=0x11; // SP-2 -> mar; SP = SP - 2;
rdpc lddata aop=din bop=idata idata=4 ALU_OP=add ldq; // Q = PC+4
ALU_AZERO ALU_OP=add bop=qreg dout ldmdr; // Q.L -> mdr
wrt; // [mar] <- mdr;
incmar ALU_AZERO ALU_OP=add bop=qreg dout ldhmdr; // Q.H -> mdr; mar++;
mpcop=next;
wrt; // [mar] <- mdr;
mpcop=next;
// Push the FP
aop=reg sa=0xf bop=idata idata=2 ALU_OP=add ALU_BCOMP
dout ldmar wa; // SP-2 -> mar; SP = SP - 2;
ALU_AZERO bop=reg sb=0xe ALU_OP=add dout ldmdr; // FP.L -> mdr
wrt; // [mar] <- mdr;
incmar ALU_AZERO bop=reg sb=0xe ALU_OP=add dout ldhmdr; // SP.H -> mdr; mar++;
mpcop=next;
wrt; // [mar] <- mdr;
aop=reg sa=0xe bop=reg sb=0xf
ALU_AZERO ALU_OP=add wa; // FP <- SP
// Dec SP
incpc spc rd ldmdr; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql
ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din; // Q.H = mdr;
aop=reg sa=0xf bop=qreg ALU_OP=add wa; // SP = SP + Q
// PC = branch address
incpc spc rd ldmdr; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql
ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din; // Q.H = mdr;
mpcop=jump mpccond=jmp mpcaddr=fetch ALU_AZERO bop=qreg
ALU_OP=add dout ldpc; // PC <- Q
mpcop=next;
call_ri: mpcop=jump mpccond=jmp mpcaddr=fetch;
mpcop=next;
call_di: mpcop=jump mpccond=jmp mpcaddr=fetch;
mpcop=next;
call_xi: mpcop=jump mpccond=jmp mpcaddr=fetch;
mpcop=next;
ret: ALU_AZERO bop=reg sb=0xe ALU_OP=add sa=0xf wa dout ldmar extra=0x12; // SP <- FP; mar <- FP;
aop=reg sa=0xf bop=idata idata=4 ALU_OP=add wa rd incmar ldmdr; // SP <- SP+4; mdr <- [mar++]
ALU_AZERO bop=din lddata ALU_OP=add ldql rd rdmdr incmar ldmdr; // Q.L <- mdr; mdr <- [mar++]
ALU_AZERO bop=din lddata ALU_OP=add ldqh rdmdr; // Q.H <- mdr;
ALU_AZERO bop=qreg ALU_OP=add sa=0xe wa rd incmar ldmdr; // FP <- Q; mdr <- [mar++]
ALU_AZERO bop=din lddata ALU_OP=add ldql rd incmar rdmdr ldmdr; // Q.L <- mdr; mdr <- [mar++]
ALU_AZERO bop=din lddata ALU_OP=add ldqh rdmdr; // Q.H <- mdr;
mpcop=jump mpccond=jmp mpcaddr=fetch1
ALU_AZERO bop=qreg ALU_OP=add dout ldpc; // PC <- Q; jump fetch1
incpc spc rd ldmdr; // mdr <- [PC]; PC++;
pushw_d: mpcop=next;
pushw_x: mpcop=next;
pushw_r: aop=reg sa=0xf bop=idata idata=2 ALU_OP=add ALU_BCOMP
extra=0x13 dout ldmar wa; // mar <- SP-2; SP = SP - 2;
aop=reg isa bop=idata idata=0 ALU_OP=add dout ldmdr; // mdr <- Rb.L;
wrt; // [mar] <- mdr;
aop=reg isa bop=idata idata=0 ALU_OP=add dout ldhmdr incmar; // mdr <- Rb.H; mar++;
mpcop=next;
mpcop=jump mpccond=jmp mpcaddr=fetch wrt; // [mar] <- mdr; jump to fetch
mpcop=next;
pushw_i: incpc spc rd ldmdr extra=2; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din; // Q.H = mdr;
aop=reg sa=0xf bop=idata idata=2 ALU_OP=add ALU_BCOMP
extra=0x13 dout ldmar wa; // mar <- SP-2; SP = SP - 2;
aop=qreg bop=idata idata=0 ALU_OP=add dout ldmdr; // mdr <- Q.L;
wrt; // [mar] <- mdr;
aop=qreg bop=idata idata=0 ALU_OP=add dout ldhmdr incmar; // mdr <- Q.H; mar++;
mpcop=next;
mpcop=jump mpccond=jmp mpcaddr=fetch wrt; // [mar] <- mdr; jump to fetch
mpcop=next;
movw_rr: mpcop=jump mpccond=jmp mpcaddr=fetch2 ALU_AZERO ALU_OP=add
isa isb wa aop=reg bop=reg ldcc extra=1
incpc spc rd ldmdr; // Ra <- Rb; jump to fetch2; mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldir; // mdr <- [PC]; PC++; ir <- mdr;
movb_rr: ALU_AZERO ALU_OP=add isb aop=reg bop=reg ldcc extra=1 ldql; // Q.L <- Rb;
mpcop=jump mpccond=jmp mpcaddr=fetch2 aop=idata bop=idata
ALU_OP=add ldqh incpc spc rd ldmdr; // Q.H <- 0; jump to fetch2;mdr <- [PC]; PC++;
ALU_AZERO ALU_OP=add bop=qreg isa wa
incpc spc rd ldmdr rdmdr ldir; // Ra <- Q; mdr <- [PC]; PC++; ir <- mdr;
movw_ri: incpc spc rd ldmdr extra=2; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din; // Q.H = mdr;
mpcop=jump mpccond=jmp mpcaddr=fetch ALU_OP=add ALU_AZERO
aop=reg bop=qreg wa isa ldcc; // Ra = Q; CC; jump to fetch
mpcop=next;
movb_ri:incpc spc rd ldmdr extra=2; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
rdmdr ldqh ALU_OP=add ALU_AZERO bop=reg sb=0; // Q.H = 0;
mpcop=jump mpccond=jmp mpcaddr=fetch ALU_OP=add ALU_AZERO
aop=reg bop=qreg wa isa ldcc; // Ra = Q; CC; jump to fetch
mpcop=next;
movb_rd:isb aop=idata bop=reg idata=0 ALU_OP=add ldmar dout extra=3; // mar <- Rb
_mbrd: rd ldmdr; // mdr <- [mar]
mpcop=jump mpccond=jmp mpcaddr=fetch lddata rdmdr isa wa; // Ra <- mdr
ALU_OP=add idata=0 bop=idata aop=reg isa ldcc; // CC
movw_rd: isb aop=idata bop=reg idata=0 ALU_OP=add ldmar dout extra=3; // mar <- Rb
_mwrd: rd ldmdr incmar; // mdr <- [mar++]
rd ldmdr lddata rdmdr bop=din ALU_AZERO ALU_OP=add ldql; // Q.L <- mdr; mdr <- [mar++]
lddata rdmdr bop=din ALU_AZERO ALU_OP=add ldqh; // Q.H <- mdr;
mpcop=jump mpccond=jmp mpcaddr=fetch ALU_AZERO bop=qreg
ALU_OP=add isa wa ldcc; // Ra <- Q; CC
mpcop=next;
movb_rx: incpc spc rd ldmdr extra=9; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql
ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
mpcop=jump mpccond=jmp mpcaddr=_mbrd rdmdr ldqh
ALU_OP=add ALU_AZERO lddata bop=din; // Q.H = mdr; goto _mbrd
isb aop=qreg bop=reg
ALU_OP=add ldmar dout; // mar <- Rb+Q;
movw_rx: incpc spc rd ldmdr extra=9; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql
ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
mpcop=jump mpccond=jmp mpcaddr=_mwrd rdmdr ldqh ALU_OP=add
ALU_AZERO lddata bop=din; // Q.H = mdr;
isb aop=qreg bop=reg ALU_OP=add
ldmar dout; // mar <- Rb+Q
movb_dr: isa aop=reg bop=idata idata=0 ALU_OP=add ldmar dout ldcc; // mar <- Ra
_mbdr: isb aop=idata bop=reg idata=0 ALU_OP=add ldmdr dout; // mdr <- Rb
mpcop=jump mpccond=jmp mpcaddr=fetch wrt; // [mar] <- mdr; jump to fetch
mpcop=next;
movw_dr: isa aop=reg bop=idata idata=0 ALU_OP=add ldmar dout ldcc; // mar <- Ra
_mwdr: isb aop=idata bop=reg idata=0 ALU_OP=add ldmdr dout; // mdr <- Rb.L
wrt; // [mar] <- mdr;
isb aop=idata bop=reg idata=0 ALU_OP=add ldhmdr dout incmar; // mdr <- Rb.H; mar++;
mpcop=next;
mpcop=jump mpccond=jmp mpcaddr=fetch wrt; // [mar] <- mdr; jump to fetch
mpcop=next;
movb_xr: incpc spc rd ldmdr extra=9; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
mpcop=jump mpccond=jmp mpcaddr=_mbdr rdmdr ldqh ALU_OP=add
ALU_AZERO lddata bop=din; // Q.H = mdr; jump to _mbdr
isa aop=reg bop=qreg ALU_OP=add ldmar dout ldcc; // mar <- Ra+Q
movw_xr: incpc spc rd ldmdr extra=9; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
mpcop=jump mpccond=jmp mpcaddr=_mwdr rdmdr ldqh ALU_OP=add
ALU_AZERO lddata bop=din; // Q.H = mdr; jump to _mwdr
isa aop=reg bop=qreg ALU_OP=add ldmar dout ldcc; // mar <- Ra+Q
movw_xx: mpcop=next extra=7;
movb_xx: mpcop=next extra=7;
movw_xd: mpcop=next extra=7;
movb_xd: mpcop=next extra=7;
movw_dd: mpcop=next extra=7;
movb_dd: mpcop=next extra=7;
movw_dx: mpcop=next extra=7;
movb_dx: mpcop=next extra=7;
mpcop=next extra=7;
movw_di: isa bop=idata idata=0 ALU_OP=add dout ldmar; // mar <- Ra
_mwdi: incpc spc rd ldmdr extra=2; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din; // Q.H = mdr;
aop=idata bop=qreg idata=0 ALU_OP=add ldmdr dout; // mdr <- Q.L
wrt; // [mar] <- mdr;
aop=idata bop=qreg idata=0 ALU_OP=add ldhmdr dout incmar; // mdr <- Q.H; mar++;
mpcop=next;
mpcop=jump mpccond=jmp mpcaddr=fetch wrt; // [mar] <- mdr; jump to fetch
mpcop=next;
movb_di: aop=reg isa bop=idata idata=0 ALU_OP=add dout ldmar extra=0x21;// mar <- Ra
_mbdi: incpc spc rd ldmdr; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
aop=idata bop=qreg idata=0 ALU_OP=add ldmdr dout; // mdr <- Q.L
mpcop=jump mpccond=jmp mpcaddr=fetch wrt; // [mar] <- mdr; jump to fetch
mpcop=next;
movw_xi: incpc spc rd ldmdr extra=2; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
mpcop=jump mpccond=jmp mpcaddr=_mwdi rdmdr ldqh
ALU_OP=add ALU_AZERO lddata bop=din; // Q.H = mdr; goto _mwdi
isa aop=reg bop=qreg ALU_OP=add dout ldmar; // mar <- Ra+Q
movb_xi: incpc spc rd ldmdr extra=2; // mdr <- [PC]; PC++;
incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
mpcop=jump mpccond=jmp mpcaddr=_mbdi rdmdr ldqh
ALU_OP=add ALU_AZERO lddata bop=din; // Q.H = mdr; goto _mwdi
isa aop=reg bop=qreg ALU_OP=add dout ldmar; // mar <- Ra+Q
nop: mpcop=jump mpccond=jmp mpcaddr=fetch extra=0x15;
mpcop=next;
end
/////////////////////////////////////////////////////////////////////////////
//
// General notes on assembler
// Registers are not saved accross function calls unless explicitly saved.
// Functions return values in R1.
// The register R0 is a special "always 0" register. Writing to R0 will have no
// effect other than to set condition codes.
//
//
begin macrocode @ 0x100
ttydata: .symbol 0x11 // tty data in/out register
ttystatus: .symbol 0x10 // tty status register
//
// Nodes have the format (note that pointers are two bytes):
//
// struct node {
// struct node *yes_b;
// struct node *no_b;
// char text[100];
// };
//
nsize: .symbol 104 // Size of a tree node. That is "sizeof(struct node)".
yes_b: .symbol 0 // Pointer to yes side
no_b: .symbol 2 // Pointer to no side
text: .symbol 4 // Text of node (animal name or question)
//
// Execution starts here.
//
start:
movw SP, 0 // Initialize stack pointer
movw FP, 0 // Initialize frame pointer
call main // Call main program
sdone: jmp sdone // Infinite loop when main ends
////////////////////
// main()
//
.proc main
movw R13, mend // Use R13 as a sort of heap pointer
pushw welcome // Push the "Welcome to" message
call printf // Print the message
add SP,2 // Restore stack pointer
movw (root),top_node // Initialize root node
movw (known),1 // Know one animal
loop:
pushw think // Push the "Think of an animal..." message
call printf // Print the message
add SP,2 // Restore stack pointer
movw R1, (known) // Put number of known animals in R1
pushw R1 // Push that number on the stack
pushw numk // Push the "I know %d animals" message
call printf // Call printf to print message
add SP,4 // Restore stack
movw R1,(root) // Put the root pointer in R1
pushw R1 // Push the pointer on the stack
call find_animal // Ask questions to find animal
add SP,2 // Restore the stack
movw (root),R1 // Save new root
jmp loop // Go back and guess another animal
ret // Return to call (actually we should never get here)
.end
////////////////////
// find_animal(p) Find the animal. p is the root node in question tree
//
.proc find_animal
tree: .symbol -2 // Local variable for tree pointer
sub SP,2 // Allocate space for local variables
movw R1, 4(FP) // Get the pointer to top node
movw tree(FP),R1 // Store it in "tree"
movw R2,yes_b(R1) // Get pointer in "yes" branch of tree
cmp R2,0 // Compare against 0
jne get_response // If not 0, then treat as question node
pushw R1 // Push pointer to top node
call get_final // As the final question, and insert new node if necessary
add SP,2 // Restore stack
movw tree(FP),R1 // Save new root node in "tree"
jmp done // All done, go to final cleanup
get_response:
movw R1,tree(FP) // Put current node pointer in R1
movw R4,R1 // R4 = R1
add R4,text // Add offset to make R4 pointer to question
pushw R4 // Push text of question
call print // Print question in tree node
add SP,2 // Restore stack
pushw qprompt // Push "?" prompt
call print // Print the "? "
add SP,2 // Restore stack
pushw buf // Push address of buffer in which to get response
call gets // Read response from user
add SP,2 // Restore stack
pushw buf // Push users response on stack
pushw yes // Push "yes" on stack
call strcmp // Compare the strings
add SP,4 // Restore stack
cmp R1,0 // Check if R1 was 0
jeq say_yes // If so, user answered "yes", goto say_yes
pushw buf // Push users response on stack
pushw no // Push "no" on stack
call strcmp // Compare the strings
add SP,4 // Restore stack
cmp R1,0 // Check if R1 was 0
jeq say_no // If so, user answered "no", goto say_no
pushw yesno // Push the "yes or no" error message
call print // Print error message
add SP,2 // Restore stack
jmp get_response // Go back and ask again
say_yes:
movw R1,tree(FP) // Put current tree pointer in R1
movw R2,yes_b(R1) // Put "yes" branch pointer in R2
pushw R2 // Push the yes branch on the stack
call find_animal // Recursively ask the next question
add SP,2 // Restore the stack
movw R2,tree(FP) // Get tree pointer again
movw yes_b(R2),R1 // Update the "yes" branch
jmp done // Finish up and prepare to return to caller
say_no:
movw R1,tree(FP) // Put current tree pointer in R1
movw R2,no_b(R1) // Put "no" branch pointer in R2
pushw R2 // Push the no branch on the stack
call find_animal // Recursively ask the next question
add SP,2 // Restore the stack
movw R2,tree(FP) // Get tree pointer again
movw no_b(R2),R1 // Update the "no" branch
jmp done // Finish up and prepare to return to caller
done:
movw R1,tree(FP) // Put new root node in R1
ret // Return to caller
.end
////////////////////
// get_final(p) Ask final question (at node p), and update tree if necessary.
//
.proc get_final
tree: .symbol -2 // Local variable for tree pointer
animal_node: .symbol -4 // Local variable for new animal node pointer
discrim_node: .symbol -6 // Local variable for new question node pointer
sub SP,6 // Allocate space for local variables
get_response:
movw R1, 4(FP) // Get pointer to current node
movw tree(FP),R1 // Save it in the 'tree' variable
movw R1, 4(FP) // Put pointer to tree in R1
add R1,text // Advance R1 to animal name text
pushw R1 // Push pointer to animal name on stack
pushw isita // Push the "is it a..." string pointer
call printf // Print the final question
add SP,4 // Restore stack
pushw buf // Push buffer for response
call gets // Get the response
add SP,2 // Restore the stack
pushw buf // Push response on stack
pushw yes // Push "yes" on stack
call strcmp // Compare strings
add SP,4 // Restore stack
cmp R1,0 // See if a 0 was returned
jeq win // If so, we guessed the animal
pushw buf // Push response on stack
pushw no // Push "no" on stack
call strcmp // Compare strings
add SP,4 // Restore stack
cmp R1,0 // See if a 0 was returned
jeq loose // If so, we did not guess the animal
pushw yesno // Push the "yes or no" error message
call print // Print it
add SP,2 // Restore stack
jmp get_response // Go back and ask again
win:
pushw winmsg // Push the (computer) win message
call printf // Print it
add SP,2 // Restore stack
jmp done // All done, go finish up and return
loose:
pushw loosemsg // Push the (computer) loose message
call printf // Print it
add SP,2 // Restore stack
pushw nsize // Push number of bytes in a node
call malloc // Allocate memory for new animal node
add SP,2 // Restore stack
movw animal_node(FP),R1 // Put address of new node in animal_node
movw yes_b(R1),0 // Intialize yes branches
movw no_b(R1),0 // Intialize no branches
add R1,text // Move R1 to point to text area
pushw R1 // Push text buffer
call gets // Get animal name
add SP,2 // Restore stack
movw R1,tree(FP) // Put pointer to exiting node in R1
add R1,text // Advance to the text field
pushw R1 // Push pointer to old animal name
movw R1,animal_node(FP) // Put pointer to new node in R1
add R1,text // Advance to the text field
pushw R1 // Push new animal name on stack
pushw dscrim // Push "what is difference" question
call printf // Print the question
add SP,6 // Restore pointer
pushw nsize // Allocate memory for discrimination node
call malloc // Allocate memory for new discrimination node
add SP,2 // Restore stack
movw discrim_node(FP),R1 // Put address of new node in discrim_node
add R1,text // Advance pointer to text field of discrimination node
pushw R1 // Push pointer to text buffer to input question
call gets // Input the question
add SP,2 // Restore pointer
movw R1, (known) // Put number of known animals in R1
add R1,1 // Increment R1
movw (known),R1 // Store number of known animals back in "known"
L1: movw R1,animal_node(FP) // Put pointer to new animal node in R1
add R1,text // Advance R1 to the text field
pushw R1 // Push new animal name on stack
pushw which // Push the "..correct answer for..." message.
call printf // Print the message
add SP,4 // Restore stack
pushw buf // Push text buffer on stack
call gets // Input a "yes" or "no"
add SP,2 // Restore stack
pushw buf // Push user response
pushw yes // Push string "yes"
call strcmp // Compare strings
add SP,4 // Restore stack
cmp R1,0 // Is the result 0?
jeq L2 // If so, goto L2. New animal is on "yes" branch
pushw buf // Push user response
pushw no // Push string "no"
call strcmp // Compare strings
add SP,4 // Restore stack
cmp R1,0 // Is the result 0?
jeq L3 // If so, goto L3. New animal is on "no" branch
pushw yesno // Push the "yes or no" error message
call printf // Print it
add SP,2 // Restore stack
jmp L1 // Go back and ask again
//
// Insert new animal on yes branch of new question
//
L2: movw R1,discrim_node(FP) // Put new question node in R1
movw R2,tree(FP) // Put old animal node in R2
movw R3,animal_node(FP) // Put new animal node in R3
movw yes_b(R1),R3 // Set yes branch to new node
movw no_b(R1),R2 // Set no branch to old node
movw tree(FP),R1 // Save R1 to tree pointer
jmp done // Finish up and return
//
// Insert new animal on no branch of new question
//
L3: movw R1,discrim_node(FP) // Put new question node in R1
movw R2,tree(FP) // Put old animal node in R2
movw R3,animal_node(FP) // Put new animal node in R3
movw yes_b(R1),R2 // Set yes branch to old node
movw no_b(R1),R3 // Set no branch to new node
movw tree(FP),R1 // Save R1 to tree pointer
jmp done // Finish up and return
done:
movw R1,tree(FP) // Put tree pointer into return register R1
ret // Return to caller
.end
////////////////////
// malloc(n) -> p Allocate n bytes and return address in R1. Allocated memory
// can not be freed.
//
.proc malloc
movw R2, 4(FP) // Get number of bytes
movw R1,R13 // Pointer to block of memory
add R13,R2 // Update heap pointer
ret
.end
////////////////////
// print(s) Print the string s
//
.proc print
movw R1, 4(FP) // Get parameter (string address)
loop: movb R2, (R1) // Put character R1 is pointing to in R2
jeq done // If it was a 0, this is the end of the string
movb (ttydata),R2 // Move char to the tty data register
movb (ttystatus),#1 // Signal tty controller to print character
add R1, #1 // Move pointer to next char
jmp loop // Go back and print more
done: ret
.end
////////////////////
// printf(s,...) Print the format string
//
.proc printf
ptr: .symbol -2 // Local var for string pointer
arg: .symbol -4 // Local var for argument pointer
sub SP,4 // Allocate 4 bytes for local variables
movw R3,FP // Assign R3 and add an offset so that it points
add R3,6 // to the argument after the control string
movw R1, 4(FP) // Put the control string pointer in R1
loop: movb R2, (R1) // Get the next char from control string
jeq done // If it was a 0, this is the end of the string
cmp R2, '%' // See if it is the '%' character
jne cout // If not goto cout, and simply print it
add R1,1 // Advance the string pointer
movb R2,(R1) // Get the next char
add R1,1 // Advance the string pointer again
//switch (R2)
L1: cmp R2,'s' // See if this is a "%s" conversion
jne L2 // If not, goto L2
// case 's' :
movw ptr(FP),R1 // Save the string pointer value
movw arg(FP),R3 // Save the argument pointer value
movw R2,(R3) // Get address of string (from arguments) to print
pushw R2 // Push it on the stack
call print // Print the string
add SP,2 // Restore stack pointer
movw R1,ptr(FP) // Restore string pointer value
movw R3,arg(FP) // Restore argument pointer value
add R3,2 // Advance argument pointer to next argument
jmp loop // Go back and print more
L2: cmp R2,'d' // See if this is a "%d" conversion
jne loop // If not, ignore unknown conversion
// case 'd' :
movw ptr(FP),R1 // Save the string pointer value
movw arg(FP),R3 // Save the argument pointer value
movw R2,(R3) // Get number (from arguments) to print
pushw R2 // Push it on the stack
call nprint // Go print the decimal number
add SP,2 // Restore stack pointer
movw R1,ptr(FP) // Restore string pointer value
movw R3,arg(FP) // Restore argument pointer value
add R3,2 // Advance argument pointer to next argument
jmp loop // Go back and print more
cout:
movb (ttydata),R2 // Put character in tty data register
movb (ttystatus),#1 // Signal tty controller to print character
add R1, #1 // Advance to next char in control string
jmp loop // Go back and print more
done: ret // All done, return to caller
.end
////////////////////
// nprint(d) Print the number d in decimal
//
.proc nprint
movw R1, 4(FP) // Move argument value to R1
jeq zprint // If it was zero, goto zprint
pushw R1 // Push value to print on stack
call nprint_aux // Call aux function to print number
add SP,2 // Restore stack pointer
ret // All done, return to caller
zprint: movb (ttydata), '0' // Put the ascii value of '0' in tty data register
movb (ttystatus), #1 // Signal tty controller to print character
ret // All done, return to caller
.end
////////////////////
// nprint_aux(d) Print the number d in decimal (but prints nothing if d is zero)
//
.proc nprint_aux
digit: .symbol -2 // Digit to print
sub SP, 2 // Allocate space for local variables
movw R1, 4(FP) // Get argument
jeq done // If zero, return
// To print the number n we compute
// R2 = n / 10, and
// R3 = n % 10
// We can then recursively call nprint_aux to print all
// but the lowest digit, then print the lowest digit ourselves
//
movw R2, R1 // R2 = R1
div R2, 10 // R2 = R2 / 10
movw R3, R1 // R3 = R1
mod R3, 10 // R3 = R3 % 10
movw digit(FP),R3 // save lowest digit value
pushw R2 // Push the higher digits
call nprint_aux // Recursively call ourselves to print them
add SP,2 // Restore stack pointer
movw R3,digit(FP) // Restore digit value
add R3, '0' // Add ascii for '0' to get ascii for digit
movb (ttydata),R3 // Put char in tty data register
movb (ttystatus),#1 // Signal tty controller to print character
done:
ret // All done, resturn to caller
.end
////////////////////
// strcmp(a,b) Compare strings a and b
//
.proc strcmp
movw R2,4(FP) // Get pointer to first string
movw R3,6(FP) // Get pointer to second string
loop: movb R1,(R2) // Get next char from R2
jeq eos // If end of string, goto eos
movb R4,(R3) // Get next char from R3
jeq eos // If end of string, goto eos
sub R1,R4 // Compute difference in R1
jne done // If difference was not 0, we are done
add R2,1 // Advance to next char in R2
add R3,1 // Advance to next char in R3
jmp loop // Go compare more
eos:
movb R4,(R3) // Get next char from R3 again (in case R1 was eos)
sub R1,R4 // Compute difference in R1
done:
ret // Return to caller, result is in R1
.end
////////////////////
// gets(b) Reads chars from tty into b.
//
.proc gets
movw R1, 4(FP) // Get pointer to buffer
movw R4,R1 // Save starting pointer in R4
//
// Poll for a characcter
//
cwait: movb R2,(ttystatus) // Get ready status
and R2, #2 // Test if a char is ready
jeq cwait // If not, go back and wait some more
movb R3,(ttydata) // Get char from tty data register
movb (ttystatus),#2 // Signal char received
cmp R3,'\r' // Compare against return char
jeq done // Exit if return received
cmp R3,'\b' // Compare against backspace char
jeq del_char // Delete char if backspace
cmp R3,0x7f // Compare against delete char
jeq del_char // Delete char if delete character
movb (ttydata),R3 // Put char to echo in tty data register
movb (ttystatus),#1 // Signal tty controller to print character
movb (R1),R3 // Save in buffer
add R1,1 // increment pointer
jmp cwait // Go back and get another char
del_char:
cmp R1,R4 // Compare current pointer against start of line
jle bell // If already at start of line, ring bell
movb R3, 0x7f // Make sure delete char is in R3
sub R1,1 // Decrement pointer
movb (ttydata),R3 // Echo backspace
movb (ttystatus),#1 // Signal tty controller to print character
jmp cwait // Go back and get another char
bell:
movb R3, 7 // Put bell char in R3
movb (ttydata),R3 // Echo bell
movb (ttystatus),#1 // Signal tty controller to print character
jmp cwait // Go back and get another char
done:
movb (ttydata),'\n' // Output newline
movb (ttystatus),#1 // Signal tty controller to print character
movb (R1),0 // Put eos in the string we just read
ret // Return to caller
.end
buf: .bss 128 // Buffer for input
known: .bss 2 // Number of animals known
root: .bss 2 // root of animals tree
top_node:
.short 0 // Pointer to "yes" node
.short 0 // Pointer to "no" node
.byte "aardvark",0 // Animal name or question text
welcome: .byte "\nWelcome to Animals.\n", 0
think: .byte "\nThink of an animal and I will try to guess what it is.\n", 0
numk: .byte "I currently know %d animals.\n\n", 0
isita: .byte "Is the animal you are thinking of a %s? ",0
yes: .byte "yes",0
no: .byte "no",0
yesno: .byte "Please type 'yes' or 'no'.\n",0
winmsg: .byte "I guessed your animal!!!\n\n",0
loosemsg: .byte "I could not guess your animal. What was your animal? ",0
dscrim: .byte "Enter a question that would distinguish a %s from a %s.\n> ",0
which: .byte "The correct answer for a %s is? ",0
nl: .byte "\n",0
qprompt: .byte "? ",0
mend:
end