Now that we got NOP working, let's finally do something useful. Like recognizing an actual instruction.

For that, we'll need to come up with some kind of system for giving our instructions numeric codes. For simplicity, I'll just reuse roughly the same system Intel's 8080 processor used.

We'll have several major classes of instructions, coded by the first two bits of the command, plus two arguments, coded by the next three and three bits.

First instruction we'll implement is MVI (MoVe Immediate) -- it takes a byte of memory following the instruction itself and loads it in the specified register. Enough talking, let's look at the code. Just like last time, I removed old comments, and commented newly added lines.

module CPU(input iClk, input iRst, output [7:0] oAddressBus, input [7:0] iMemoryValue);  
    //Opcode constants, to improve code readability
    localparam  //Like in 8080, assume there are 4 major types of instructions:
        MVI=2'b00, //Immediate memory load operations,
        MOV=2'b01, //Data transfer operations,
        ALU=2'b10, //Arithmetic/logic operations, and
        ALI=2'b11; //Immediate arithmetic/logic operations.

    //src/dest specifier constants
    localparam B = 3'b000, C = 3'b001, D = 3'b010, E = 3'b011, H = 3'b100, L = 3'b101, M = 3'b110, A = 3'b111;

    reg [7:0] _pc;

    always @(posedge iClk, posedge iRst)
    begin
        if(iRst)
            begin
                $display("resetting...");
                _pc = 8'h0;
            end
        else begin
            $display("PC:%d", _pc);
            $display("mem:%d", iMemoryValue);

            //see if we can recognize the instruction we know how to execute
            if(iMemoryValue == 8'h0)
                $display("executing NOP!");
            else case(iMemoryValue[7:6]) //first two bits of the instruction is the opcode
                MVI:
                    //A true MVI instruction always has Memory as the source
                    //(illegal "MVI" instructions are reused for other operations)
                    if(iMemoryValue[2:0] == M) begin //last three bits is the source. must be M for MVI.
                        case(iMemoryValue[5:3]) //the destination is specified in the middle three bits:
                            //can't read the second operand, at the next memory address in the same clock cycle
                            B: $display("MVI B, ?");
                            C: $display("MVI C, ?");
                            D: $display("MVI D, ?");
                            E: $display("MVI E, ?");
                            H: $display("MVI H, ?");
                            L: $display("MVI L, ?");
                            M: $display("MVI M, ?");
                            A: $display("MVI A, ?");
                        endcase
                    end
            endcase
            _pc = _pc + 1'b1;
        end
    end

    assign oAddressBus = _pc;

endmodule

module ROM(input [7:0] iAddress, output [7:0] oReadPort);

    reg [7:0] _rom [0:255];

    initial
    begin
        //I went ahead and entered two "real" MVI instructions
        _rom[0] = 0; //NOP
        _rom[1] = 8'b00000110; //MVI B, 55
        _rom[2] = 55;
        _rom[3] = 8'b00001110; //MVI C, 255
        _rom[4] = 255;
        _rom[5] = 1; //unknown instr
        //etc.
    end

    assign oReadPort = _rom[iAddress];
endmodule  

The rest of the code stayed the same as last time. This produces the following printout:

--------------Tick           1---------------
PC:  x  
mem:  x  
--------------Tick           2---------------
PC:  x  
mem:  x  
--------------Tick           3---------------
PC:  x  
mem:  x  
resetting...  
--------------Tick           4---------------
PC:  0  
mem:  0  
executing NOP!  
--------------Tick           5---------------
PC:  1  
mem:  6  
MVI B, ?  
--------------Tick           6---------------
PC:  2  
mem: 55  
--------------Tick           7---------------
PC:  3  
mem: 14  
MVI C, ?  
--------------Tick           8---------------
PC:  4  
mem:255  
--------------Tick           9---------------
PC:  5  
mem:  1  
--------------Tick          10---------------
PC:  6  
mem:  x  
--------------Tick          11---------------
PC:  7  
mem:  x  
--------------Tick          12---------------
PC:  8  
mem:  x  
--------------Tick          13---------------
PC:  9  
mem:  x  

This gets us as far as being able to recognize an MVI, but how should we go about reading the immediate value from memory? That's a topic for the next post.