module Am2901
(
   input          cp,         // clock positive
   input    [8:0] i,          // operation code
   input    [3:0] a,          // port A address (readonly)
   input    [3:0] b,          // port B address (read/write)
   input    [3:0] d,          // direct data input
   output   [3:0] y,          // data output (3-state)
   inout          q0,         // shift line Q-register LSB
   inout          q3,         // shift line Q-register MSB
   inout          ram0,       // shift line register stack LSB
   inout          ram3,       // shift line register stack MSB
                              //
   input          oe_n,       // Y output enable
   input          cin,        // carry input
   output         cout,       // carry output
   output         g_n,        // carry generate output
   output         p_n,        // carry propagate output
   output         ovr,        // arithmetic overflow
   output         f3,         // MSB of ALU output
   output         zf          // (F[3:0] == 0) flag output (OC)
);

reg [3:0]   q_ram[15:0];      // RAM array
reg [3:0]   a_reg;            // RAM latch A
reg [3:0]   b_reg;            // RAM latch B
wire [3:0]  i_ram;            // RAM input mux
                              //
reg [3:0]   q_reg;            // Q register
wire [3:0]  q_mux;            // Q register input mux
                              //
wire [3:0]  r_alu;            // ALU input mux R
wire [3:0]  r_ext;            //
wire [3:0]  s_alu;            // ALU input mux S
wire [3:0]  s_ext;            //
wire [3:0]  f_alu;            // ALU result output
                              //
wire        c3, c4;           // carries
wire [3:0]  g_alu;            // ALU carry generate
wire [3:0]  p_alu;            // ALU carry propagate

//______________________________________________________________________________
//
// RAM registers and RAM input mux
//
assign i_ram = (i[8:7] == 2'b00) ? 4'b0000 :                      // QREG, NOP
               (i[8:7] == 2'b01) ? f_alu[3:0] :                   // RAMA, RAMF - load
               (i[8:7] == 2'b10) ? {ram3, f_alu[3:1]} :           // RAMQD, RAMD - shift down/left
               (i[8:7] == 2'b11) ? 4'b0000 : {f_alu[2:0], ram0};  // RAMQU, RAMU - shift up/right

always @(posedge cp) if (i[8:7] != 2'b00) q_ram[b] <= i_ram;

assign ram0 = (i[8:7] == 2'b10) ? f_alu[0] : 1'bz;
assign ram3 = (i[8:7] == 2'b11) ? f_alu[3] : 1'bz;

//______________________________________________________________________________
//
// RAM output lacthes
//
always @(posedge cp)
begin
   if (i[8:7] == 2'b00)
   begin
      //
      // No RAM load operation, RAM data fetch
      //
      a_reg <= q_ram[a];
      b_reg <= q_ram[b];
   end
   else
   begin
      //
      // RAM load operation, propagate input RAM data
      // Actually data will be written to RAM at clock posedge
      //
      a_reg <= (a == b) ? i_ram : q_ram[a];
      b_reg <= i_ram;
   end
end

//______________________________________________________________________________
//
// Q-register input mux and latches
//
assign q_mux = (i[8:6] == 3'b000) ? f_alu[3:0] :                     // QREG
               (i[8:6] == 3'b100) ? {q3, f_alu[3:1]} :               // RAMQD - shift down/left
               (i[8:6] == 3'b110) ? {f_alu[2:0], q0} : q_reg[3:0];   // RAMQU - shift up/right

assign q0 = (i[8:7] == 2'b10) ? q_reg[0] : 1'bz;
assign q3 = (i[8:7] == 2'b11) ? q_reg[3] : 1'bz;

always @(posedge cp) q_reg <= q_mux;

//______________________________________________________________________________
//
// Y-output mux and enable logic
//
assign y = (oe_n) ? 4'bzzzz : (i[8:6] == 3'b010) ? a_reg : f_alu[3:0];

//______________________________________________________________________________
//
// ALU input muxes
//
assign r_alu = (i[8:6] == 3'b000) ? a_reg :                 // AQ
               (i[8:6] == 3'b001) ? a_reg :                 // AB
               (i[8:6] == 3'b010) ? 4'b0000 :               // ZQ
               (i[8:6] == 3'b011) ? 4'b0000 :               // ZB
               (i[8:6] == 3'b100) ? 4'b0000 : d;            // ZA, DA, DQ, DZ

assign s_alu = (i[8:6] == 3'b000) ? q_reg :                 // AQ
               (i[8:6] == 3'b001) ? b_reg :                 // AB
               (i[8:6] == 3'b010) ? q_reg :                 // ZQ
               (i[8:6] == 3'b011) ? b_reg :                 // ZB
               (i[8:6] == 3'b100) ? a_reg :                 // ZA
               (i[8:6] == 3'b101) ? a_reg :                 // DA
               (i[8:6] == 3'b110) ? q_reg : 4'b0000;        // DQ, DZ

assign r_ext = ( (i[5:3] == 3'b001)
               | (i[5:3] == 3'b101)
               | (i[5:3] == 3'b111)) ? ~r_alu : r_alu;

assign s_ext = (i[5:3] == 3'b010) ? ~s_alu : s_alu;

assign p_alu = r_ext | s_ext;
assign g_alu = r_ext & s_ext;
//
// Carry from f_alu[2]->f_alu[3]
//
assign c3 = g_alu[2]
          | g_alu[1] & p_alu[2]
          | g_alu[0] & p_alu[2] & p_alu[1]
          | cin      & p_alu[2] & p_alu[1] & p_alu[0];
//
// Carry from f_alu[3]->f_alu[4]
//
assign c4 = g_alu[3]
          | g_alu[2] & p_alu[3]
          | g_alu[1] & p_alu[3] & p_alu[2]
          | g_alu[0] & p_alu[3] & p_alu[2] & p_alu[1]
          | cin      & p_alu[3] & p_alu[2] & p_alu[1] & p_alu[0];
//
// ALU result
//
assign f_alu = (i[5:3] == 3'b000) ? r_ext + s_ext + cin :      // ADD
               (i[5:3] == 3'b001) ? r_ext + s_ext + cin :      // SUBR
               (i[5:3] == 3'b010) ? r_ext + s_ext + cin :      // SUBS
               (i[5:3] == 3'b011) ? r_ext | s_ext :            // OR
               (i[5:3] == 3'b100) ? r_ext & s_ext :            // AND
               (i[5:3] == 3'b101) ? r_ext & s_ext :            // NOTRS
               (i[5:3] == 3'b110) ? r_ext ^ s_ext :            // EXOR
               (i[5:3] == 3'b111) ? r_ext ^ s_ext : 4'b0000;   // EXNOR

assign cout  = (i[5:3] == 3'b000) ? c4 :                       // ADD
               (i[5:3] == 3'b001) ? c4 :                       // SUBR
               (i[5:3] == 3'b010) ? c4 :                       // SUBS
               (i[5:3] == 3'b011) ? ~&p_alu | cin :            // OR
               (i[5:3] == 3'b100) ?  |g_alu | cin :            // AND
               (i[5:3] == 3'b101) ?  |g_alu | cin :            // NOTRS
                ~( g_alu[3]                                    // EXOR
                |  g_alu[2] & p_alu[3]
                |  g_alu[1] & p_alu[3] & p_alu[2]
                | (g_alu[0] | ~cin) & &p_alu);

assign ovr   = (i[5:3] == 3'b000) ? c3 ^ c4 :                  // ADD
               (i[5:3] == 3'b001) ? c3 ^ c4 :                  // SUBR
               (i[5:3] == 3'b010) ? c3 ^ c4 :                  // SUBS
               (i[5:3] == 3'b011) ? ~&p_alu | cin :            // OR
               (i[5:3] == 3'b100) ?  |g_alu | cin :            // AND
               (i[5:3] == 3'b101) ?  |g_alu | cin :            // NOTRS
               (i[5:3] == 3'b110) ? r_ext ^ s_ext :            // EXOR
               (i[5:3] == 3'b111) ? r_ext ^ s_ext : 4'b0000;   // EXNOR

//g_n
//p_n
//ovr

assign f3   = f_alu[3];
assign zf   = ~|f_alu[3:0];
//______________________________________________________________________________
//
endmodule
