// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/5/CPU.hdl
/**
 * The Hack Central Processing unit (CPU).
 * Parses the binary code in the instruction input and executes it according to the
 * Hack machine language specification. In the case of a C-instruction, computes the
 * function specified by the instruction. If the instruction specifies to read a memory
 * value, the inM input is expected to contain this value. If the instruction specifies
 * to write a value to the memory, sets the outM output to this value, sets the addressM
 * output to the target address, and asserts the writeM output (when writeM = 0, any
 * value may appear in outM).
 * If the reset input is 0, computes the address of the next instruction and sets the
 * pc output to that value. If the reset input is 1, sets pc to 0.
 * Note: The outM and writeM outputs are combinational: they are affected by the
 * instruction's execution during the current cycle. The addressM and pc outputs are
 * clocked: although they are affected by the instruction's execution, they commit to
 * their new values only in the next cycle.
 */
CHIP CPU {

    IN  inM[16],         // M value input  (M = contents of RAM[A])
        instruction[16], // Instruction for execution
        reset;           // Signals whether to re-start the current
                         // program (reset==1) or continue executing
                         // the current program (reset==0).

    OUT outM[16],        // M value output
        writeM,          // Write to M? 
        addressM[15],    // Address in data memory (of M)
        pc[15];          // address of next instruction

    PARTS:
    Not(in=instruction[15], out=ai);
    Not(in=ai, out=ci);
    And(a=ci, b=instruction[5], out=wa);
    Mux16(a=instruction, b=alu, sel=wa, out=a);
    Or(a=ai, b=wa, out=la);
    ARegister(in=a, load=la, out=ao);
    Mux16(a=ao, b=inM, sel=instruction[12], out=am);
    And(a=ci, b=instruction[4], out=ld);
    DRegister(in=alu, load=ld, out=do);
    ALU(x=do, y=am, zx=instruction[11], nx=instruction[10], 
        zy=instruction[9], ny=instruction[8], f=instruction[7],
        no=instruction[6], out=alu, zr=zr, ng=ng);
    Or16(a=false, b=ao, out[0..14]=addressM);
    Or16(a=false, b=alu, out=outM);
    And(a=ci, b=instruction[3], out=writeM);
    And(a=zr, b=instruction[1], out=jeq);
    And(a=ng, b=instruction[2], out=jlt);
    Or(a=zr, b=ng, out=zon);
    Not(in=zon, out=p);
    And(a=p, b=instruction[0], out=jgt);
    Or(a=jeq, b=jlt, out=jle);
    Or(a=jle, b=jgt, out=j);
    And(a=ci, b=j, out=l);
    Not(in=l, out=i);
    PC(in=ao, inc=i, load=l, reset=reset, out[0..14]=pc);
}