`define MIPS_PROG "uc.v"

`define MEMBASE 'h400000
`define DATABASE 'h10010000
`define TEXTSIZE 'h100
`define MEMSIZE 'h200
`define MEMRANGE `MEMBASE:`MEMBASE+`MEMSIZE-1
`define A(addr) ((addr)-`DATABASE+`MEMBASE+`TEXTSIZE)
`define MEM(addr) mem[((addr)-`DATABASE+`MEMBASE+`TEXTSIZE)]

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

   reg    exc;

   reg [7:0] mem [`MEMRANGE];

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

   reg [5:0]  opcode, funct;
   reg [4:0]  rs, rt, rd, sa;
   reg [15:0] immed;
   reg [25:0] ii;
   reg [31:0] uimm16, simm16;
   reg [31:0] branch_target;

   // Values for funct field.
   parameter  f_sll = 6'h0;
   parameter  f_srl = 6'h2;
   parameter  f_add = 6'h20;
   parameter  f_sub = 6'h22;
   parameter  f_or  = 6'h25;

   // Values for opcode field.
   parameter  o_j    = 6'h2;
   parameter  o_beq  = 6'h4;
   parameter  o_bne  = 6'h5;
   parameter  o_addi = 6'h8;
   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_lbu  = 6'h24;
   parameter  o_sw   = 6'h2b;
   parameter  o_sb   = 6'h28;

   initial begin
      exc = 0;
      pc = 0;
      npc = 4;
   end

   always @( posedge clk ) begin

      ir = {mem[pc],mem[pc+1],mem[pc+2],mem[pc+3]};

      // R Format
      {opcode,rs,rt,rd,sa,funct} = ir;

      nnpc = npc + 4; // May be reassigned below.

      if( !opcode ) begin
         //
         // R-Format Instructions
         case( funct )
           f_sll   : gpr[rd] = gpr[rt] << sa;
           f_srl   : gpr[rd] = gpr[rt] >> sa;
           f_add   : gpr[rd] = gpr[rs] + gpr[rt];
           f_or    : gpr[rd] = gpr[rs] | gpr[rt];
           f_sub   : gpr[rd] = gpr[rs] - gpr[rt];
           default : exc = 1;
         endcase

      end else begin
         //
         // I- and J-Format Instructions

         // I Format  (Also uses opcode, rs, and rt.)
         immed = ir[15:0];
         // J Format  (Also uses opcode.)
         ii = ir[25:0];

         uimm16 = { 16'b0, immed };
         simm16 = immed[15] ? { 16'hffff, immed } : uimm16;

         branch_target = npc + ( simm16 << 2 );

         case( opcode )
           o_j     : nnpc = {npc[31:28],ii,2'b0};
           o_beq   : if( gpr[rs] == gpr[rt] ) nnpc = branch_target;
           o_bne   : if( gpr[rs] != gpr[rt] ) nnpc = branch_target;
           o_andi  : gpr[rt] = gpr[rs] & uimm16;
           o_slti  : gpr[rt] = gpr[rs] < simm16;
           o_addi  : gpr[rt] = gpr[rs] + simm16;
           o_ori   : gpr[rt] = gpr[rs] | uimm16;
           o_lui   : gpr[rt] = { immed, 16'b0 };
           o_lbu   : gpr[rt] = { 24'b0, `MEM( gpr[rs] + simm16 ) };
           o_sb    : `MEM( gpr[rs] + simm16 ) = gpr[rt];
           default : exc = 1;
         endcase

      end

      gpr[0] = 0;

      pc = npc;
      npc = nnpc;

   end


endmodule

`include "mipsi1tb.v"