TKGate User Documention (Index)

## 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-2015 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.,
    //    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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