/// EE 3755 -- Fall 2013 -- Computer Organization
//
 ///  MIPS Functional Simulator

 /// Version of code at end of class 20 November 2013

//`define MIPS_PROG "tfirst-live.v"
`define MIPS_PROG "uc.v"
`include "mipsmemmacros.v"

module proc(exc,clk);
   input clk;
   output exc;

   // 0x40000000 add $2, $3, $4

   reg [7:0]  mem ['h400000:'h400200];

   parameter   O_type_r = 6'h0;
   parameter  F_add = 6'h20;
   parameter  F_sll = 6'h0;
   parameter  F_srl = 6'h2;
   parameter  F_sub = 6'h22;
   parameter  F_or  = 6'h25;
   parameter  F_jr  = 6'h8;

   parameter  O_addi = 6'h8;
   parameter  O_j    = 6'h2;
   parameter  O_jal  = 6'h3;
   parameter  O_beq  = 6'h4;
   parameter  O_bne  = 6'h5;
   parameter  O_slti = 6'ha;
   parameter  O_andi = 6'hc;
   parameter  O_ori  = 6'hd;
   parameter  O_lui  = 6'hf;
   parameter  O_lw   = 6'h23;
   parameter  O_lhu  = 6'h25;
   parameter  O_lh   = 6'h21;
   parameter  O_lbu  = 6'h24;
   parameter  O_lb   = 6'h20;
   parameter  O_sw   = 6'h2b;
   parameter  O_sb   = 6'h28;

   reg [31:0]  pc, npc, ir;

   initial begin pc = 'h400000; npc = pc + 4; end

   reg [4:0] rs, rt, rd, sa;
   reg [5:0] opcode, func;

   reg [31:0] rs_val, rt_val, result, simmed, uimmed;

   reg [31:0] gpr [0:31];
   reg [7:0]  md;

   reg [31:0] addr, nnpc;

   //  always @ ( gpr[0] ) gpr[0] = 0;

   always @ ( posedge clk ) begin

      ir = {mem[pc],mem[pc+1],mem[pc+2],mem[pc+3]};
      {opcode,rs,rt,rd,sa,func} = ir;
      //  {opcode,rs,rt,immed} = ir;
      //  {opcode,ii} = ir;

      //  rd = rs + rt;   How silly!

      rs_val = gpr[rs];
      rt_val = gpr[rt];
      uimmed = { 16'h0, ir[15:0] };
      simmed = {  ir[15] ? 16'hffff : 16'h0,  ir[15:0] };

      addr = `A(rs_val + simmed);

      nnpc = npc + 4;  // Provisional

      case ( opcode )

        O_type_r: case ( func )
             F_add: gpr[rd] = rs_val + rt_val;
             F_sub: gpr[rd] = rs_val - rt_val;
             default: $stop;
        endcase

        O_addi: gpr[rt] = rs_val + simmed; 
        O_ori: gpr[rt] = rs_val | uimmed;

        O_slti: gpr[rt] = rs_val < simmed;
        
        O_lui: gpr[rt] = { ir[15:0], 16'h0 };

        O_lw: gpr[rt] = { mem[addr], mem[addr+1], mem[addr+2], mem[addr+3] };
        
        O_lhu: gpr[rt] = { 16'h0, mem[addr], mem[addr+1] };
        
        O_lbu: gpr[rt] = { 24'h0, mem[ `A(rs_val + simmed) ] };
        O_lb:
          begin
             md = mem[ `A(rs_val + simmed) ];
             gpr[rt] = { md[7] ? 24'hffffff : 24'h0, md };
          end

        O_sb:
          mem[ `A(rs_val + simmed) ] = rt_val[7:0];

        O_bne:
          if ( rs_val != rt_val ) 
            nnpc = npc + { ir[15] ? 14'h3fff : 14'h0, ir[15:0], 2'b0 };

        O_beq:
          if ( rs_val == rt_val ) 
            nnpc = npc + { ir[15] ? 14'h3fff : 14'h0, ir[15:0], 2'b0 };

        O_j: nnpc = { npc[31:28], ir[25:0], 2'b0 };
          
        default:
          begin
             $display("Unexpected opcode: 0x%x\n",opcode);
             $stop;
          end

      endcase

      gpr[0] = 0;

      pc = npc;
      npc = nnpc;
      
   end

endmodule

`include "mips_fs_tb.v"