module M65C02_MPCv3 #(
    parameter pAddrWidth = 10,          // Original F9408 => 10-bit Address
    parameter pRst_Addrs = 0            // Reset Address
)(
    input   Rst,                        // Module Reset (Synchronous)
    input   Clk,                        // Module Clock

    input   [1:0] uLen,                 // Microcycle Length Select
    input   Wait,                       // Microcycle Wait State Request Input

    output  reg [1:0] MC,               // Microcycle State outputs

    input   [3:0] I,                    // Instruction (see description)
    input   [3:0] T,                    // Conditional Test Inputs
    input   [2:0] MW,                   // Multi-way Branch Address Select
    input   [(pAddrWidth-1):0] BA,      // Microprogram Branch Address Field
    output  [1:0] Via,                  // Unconditional Branch Address Select


    output  reg [(pAddrWidth-1):0] MA   // Microprogram Address
);

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
//  Local Parameters
//

localparam pRTS  =  0;  // Return from Subroutine
localparam pBSR  =  1;  // Branch to Subroutine
localparam pFTCH =  2;  // Fetch Next Instruction
localparam pBMW  =  3;  // Multi-way Branch
localparam pBRV0 =  4;  // Branch Via External Branch Address Source #0
localparam pBRV1 =  5;  // Branch Via External Branch Address Source #1
localparam pBRV2 =  6;  // Branch Via External Branch Address Source #2
localparam pBRV3 =  7;  // Branch Via External Branch Address Source #3
localparam pBTH0 =  8;  // Branch if T[0] is Logic 1, else fetch next instr.
localparam pBTH1 =  9;  // Branch if T[1] is Logic 1, else fetch next instr.
localparam pBTH2 = 10;  // Branch if T[2] is Logic 1, else fetch next instr.
localparam pBTH3 = 11;  // Branch if T[3] is Logic 1, else fetch next instr.
localparam pBTL0 = 12;  // Branch if T[0] is Logic 0, else fetch next instr.
localparam pBTL1 = 13;  // Branch if T[1] is Logic 0, else fetch next instr.
localparam pBTL2 = 14;  // Branch if T[2] is Logic 0, else fetch next instr.
localparam pBTL3 = 15;  // Branch if T[3] is Logic 0, else fetch next instr.

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
//  Declarations
//

wire    NxtLenZ;                          // Next microcycle length is Z
reg     MPC_En;                           // MPC register enable

wire    [(pAddrWidth - 1):0] Next;        // Output Program Counter Incrementer
reg     [(pAddrWidth - 1):0] PC_In;       // Input to Program Counter
reg     [(pAddrWidth - 1):0] PC;          // Program Counter

//reg     [(pAddrWidth - 1):0] A, B, C, D;  // LIFO Stack Registers
reg     [(pAddrWidth - 1):0] A;           // LIFO Stack Registers

reg     dRst;                             // Reset stretcher
wire    MPC_Rst;                          // Internal MPC Reset signal

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
//  Implementation
//

//  Implement module reset generator

always @(posedge Clk)
begin
    if(Rst)
        dRst <= #1 1;
    else
        dRst <= #1 0;
end

assign MPC_Rst = (Rst | dRst);

//
//  Embedded Microcycle Length Controller
//
//  Three microcycles are implemented: 1, 2, or 4 clock in length. If word 0 of
//  the microprogram, or external logic, sets a different length during reset,
//  the microcycle length controller will exit reset in either state 0 or
//  state 2. If it exits reset in state 0, a single clock microcycle will be
//  performed after reset. If it exits reset in state 2, either a 2 cycle or
//  a 4 cycle microcycle will be performed after reset. The microcycle length is
//  sampled in state 0. This allows either the microprogram or external logic to
//  control the length of each microcycle that the MPCv3 performs.

always @(posedge Clk)
begin
    if(Rst)
        MC <= #1 ((|uLen) ? 2 : 0);
    else
        case(MC)
            2 : MC <= #1 ((uLen[1]) ? 3 : 0);   // First cycle of microcycle
            3 : MC <= #1 ((Wait)    ? 3 : 1);   // 2nd of 4 cycle microcycle
            1 : MC <= #1 ((Wait)    ? 1 : 0);   // 3rd of 4 cycle microcycle
            0 : MC <= #1 ((|uLen)   ? 2 : 0);   // Last cycle of microcycle
        endcase
end

//  Assign next microcycle length

assign NxtLenZ = (uLen == 0);

//  Determine the MPC Enable signal

always @(posedge Clk)
begin
    if(Rst)
        MPC_En <= #1 ~|uLen;
    else
        case(MC)
            2 : MPC_En <= #1 ((uLen[1]) ? 0 : 1);
            3 : MPC_En <= #1 0;
            1 : MPC_En <= #1 ~Wait;
            0 : MPC_En <= #1 NxtLenZ;
        endcase
end

////  Implement 4-Level LIFO Stack
//
//always @(posedge Clk)
//begin
//    if(MPC_Rst)
//        {A, B, C, D} <= #1 0;
//    else if(MPC_En)
//        if(I == BSR)
//            {A, B, C, D} <= #1 {Next, A, B, C};
//        else if(I == RTS)
//            {A, B, C, D} <= #1 {B, C, D, {pAddrWidth{1'b0}}};
//end

//  Implement 1-Level LIFO Stack

always @(posedge Clk)
begin
    if(MPC_Rst)
        A <= #1 0;
    else if(MPC_En)
        if(I == pBSR)
            A <= #1 Next;
        else if(I == pRTS)
            A <= #1 {pAddrWidth{1'b0}};
end

//  Program Counter Incrementer

assign Next = PC + 1;

//  Generate Unconditional Branch Address Select

assign Via = {((I == pBRV2) | (I == pBRV3)), ((I == pBRV3) | (I == pBRV1))};       

//  Generate Program Counter Input Signal

always @(*)
begin
    case({MPC_Rst, I})
        pRTS    : PC_In <=  A;
        pBSR    : PC_In <=  BA;
        pFTCH   : PC_In <=  Next;
        pBMW    : PC_In <=  {BA[(pAddrWidth - 1):3], MW};
        //
        pBRV0   : PC_In <=  BA;
        pBRV1   : PC_In <=  BA;
        pBRV2   : PC_In <=  BA;
        pBRV3   : PC_In <=  BA;
        //
        pBTH0   : PC_In <=  (T[0] ? BA   : Next);
        pBTH1   : PC_In <=  (T[1] ? BA   : Next);
        pBTH2   : PC_In <=  (T[2] ? BA   : Next);
        pBTH3   : PC_In <=  (T[3] ? BA   : Next);
        //
        pBTL0   : PC_In <=  (T[0] ? Next : BA  );
        pBTL1   : PC_In <=  (T[1] ? Next : BA  );
        pBTL2   : PC_In <=  (T[2] ? Next : BA  );
        pBTL3   : PC_In <=  (T[3] ? Next : BA  );
        default : PC_In <=  pRst_Addrs;
    endcase
end

//  Generate Microprogram Address (Program Counter)

always @(posedge Clk)
begin
    if(MPC_Rst)
        PC <= #1 pRst_Addrs;
    else if(MPC_En)
        PC <= #1 PC_In;
end

//  Assign Memory Address Bus

always @(*)
begin
    MA <= PC_In;
end

endmodule