#include <stdio.h>

#define NOP  0
#define A 1
#define M 2
#define L 4
#define S 8

typedef int u32;
typedef u32 rgid;
typedef u32 indx;
typedef u32 valu;
typedef u32 opcd;
typedef u32 bool;
typedef u32 addr;

typedef struct RegStationTAG {
   indx QJ, QK;
   valu VJ, VK;
   opcd OP;
   bool BUSY;
} RegStation;

typedef struct RegFileTAG {
   indx QI;
   valu VI;
} RegFile;

typedef struct StoreQTAG {
   addr AD;
   indx QI;
   valu VI;
} StoreQ;

typedef struct LoadQTAG {
   addr AD;
} LoadQ;

typedef struct CDBTAG {
   bool BUSY;
   indx QI;
   valu VI;
} CDB;

typedef struct DECODE_LATCH_TAG {
   opcd OP;
   indx RS;
   indx RT;
   indx RD;
} DECODE_LATCH;

typedef struct FuncUnitTAG {
   bool BUSY;
   opcd OP;
} FuncUnit;

#define FU_MAX 3
#define RS_MAX 8
#define RF_MAX 8
#define LQ_MAX 2
#define SQ_MAX 2

RegStation   RS[RS_MAX + 1]; /* 2 RS_M, 3 RS_LS, 3 RS_A, 0 is not used*/
RegFile      RF[RF_MAX]; 
LoadQ        LQ[LQ_MAX];
StoreQ       SQ[SQ_MAX];
CDB          CB[1];
DECODE_LATCH DECODE[1];
FuncUnit     FU[FU_MAX + 1];

int WriteCDB(indx r, valu v) {
   int ok = 0;

   if (CB[0].BUSY == 0) {
      CB[0].QI = r;
      CB[0].VI = v;
      CB[0].BUSY = 1;
      ok = 1;
   }

   return (ok);
}

ExecA(int fu, indx r) {

}

ExecM(int fu, indx r) {

}

ExecL(int fu, indx r) {

}

ExecS(int fu, indx r) {

}

PushS(indx rd) {
   indx s; 

   if (RF[rd].QI != 0) { SQ[s].QI = RF[rd].QI; } else {  SQ[s].VI = RF[rd].VI;  SQ[s].QI =0; }
}

PushSA(addr AD) {

}

PushL(addr AD) {

}

int Get_Available_RS(opcd op) {
   int rr;
   int found = 0;

   for (rr = 1; rr <= RS_MAX; ++rr) {
      if (RS[rr].BUSY == 0 && ((RS[rr].OP & op) == op)) { found = rr; break; }
   }

   return (found);
}

int Get_Available_FU(opcd op) {
   int ff;
   int found = 0;

   for (ff = 1; ff <= FU_MAX; ++ff) {
       if (FU[ff].BUSY == 0 && ((FU[ff].OP & op) == op)) { found = ff; break; }
   }

   return (found);
}

void Get_Decoded_Instr(opcd *op, indx *rs, indx *rt, indx *rd) {
   *op = DECODE[0].OP;
   *rs = DECODE[0].RS;
   *rt = DECODE[0].RT;
   *rd = DECODE[0].RD;
}

int Get_ResStation(opcd op) {
   int rr;
   int found = 0;

   for (rr = 1; rr <= RS_MAX; ++rr) {
      if (RS[rr].BUSY == 1 && ((RS[rr].OP & op) == op)) { found = rr; break; }
   }

   return (found);
}

void Initialize(void) {

   FU[1].OP = M;
   RS[1].OP = M;
   RS[2].OP = M;

   FU[2].OP = L|S;
   RS[3].OP = L|S;
   RS[4].OP = L|S;
   RS[5].OP = L|S;

   RS[3].OP = A;
   RS[6].OP = A;
   RS[7].OP = A;
   RS[8].OP = A;
}

int Dispatch_STAGE(void) {
   indx r; opcd op; rgid rs; rgid rt; rgid rd;

   Get_Decoded_Instr(&op, &rs, &rt, &rd);
   r = Get_Available_RS(op);
   if (r == 0) return -1;


   switch(op) {
      case L: case S: case M: case A:
         if (RF[rs].QI != 0) { RS[r].QJ = RF[rs].QI; } else {  RS[r].VJ = RF[rs].VI;  RS[r].QJ =0; }
         if (RF[rt].QI != 0) { RS[r].QK = RF[rt].QI; } else {  RS[r].VK = RF[rt].VI;  RS[r].QK =0; }
         break;
      case NOP: default:
         printf("NOP or undefined instructions\n");
   }
   if (op == S) { PushS(rd); }
   RS[r].BUSY = 1;
   RF[rd].QI = r;
}

int Issue_STAGE(void) {
   int fu;
   indx r;
   opcd op;

   for (fu = 1; fu < FU_MAX; ++fu) {
      op = FU[fu].OP;
      if (FU[fu].BUSY == 0 && ((FU[fu].OP & op) == op)) {

         r = Get_ResStation(op);      
         if (r == 0) continue;

         switch (op) {
            case M: if (RS[r].QJ == 0 && RS[r].QK == 0) { RS[r].BUSY = 0; ExecM(fu, r); } break;
            case L: if (RS[r].QJ == 0 && RS[r].QK == 0) { RS[r].BUSY = 0; ExecL(fu, r); } break;
            case A: if (RS[r].QJ == 0 && RS[r].QK == 0) { RS[r].BUSY = 0; ExecA(fu, r); } break;
            case S: if (RS[r].QJ == 0 && RS[r].QK == 0) { RS[r].BUSY = 0; ExecS(fu, r); } break;
         }
      }

   }
}

void Complete_STAGE(void) {
   int rr;

   if (CB[0].BUSY == 1) {
      for (rr = 1; rr <= RS_MAX; ++rr) {
         if (RS[rr].QJ == CB[0].QI) { RS[rr].VJ == CB[0].VI; RS[rr].QJ = 0; RS[rr].BUSY = 0; }
         if (RS[rr].QK == CB[0].QI) { RS[rr].VK == CB[0].VI; RS[rr].QK = 0; RS[rr].BUSY = 0; }
      }
      for (rr = 0; rr < RF_MAX; ++rr) {
         if (RF[rr].QI == CB[0].QI) { RF[rr].VI == CB[0].VI; RF[rr].QI = 0; }
      }
      for (rr = 0; rr < SQ_MAX; ++rr) {
         if (SQ[rr].QI == CB[0].QI) { SQ[rr].VI == CB[0].VI; SQ[rr].QI = 0; }
      }
   }
}

int main (int argc, char *argv[]) {
}
