module M65C02_MPC #(
    parameter pAddrWidth = 10,          // Original F9408 => 10-bit Address
    parameter pRst_Addrs = 0            // Reset Address
)(
    input   Rst,                        // Module Reset (Synchronous)
    input   Clk,                        // Module Clock
    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
    input   En,                         // Enable Ready
    input   Rdy,                        // Ready
    input   PLS,                        // Pipeline Mode Select
    output  reg [(pAddrWidth-1):0] MA   // Microprogram Address
);

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

localparam RTS  =  0;   // Return from Subroutine
localparam BSR  =  1;   // Branch to Subroutine
localparam FTCH =  2;   // Fetch Next Instruction
localparam BMW  =  3;   // Multi-way Branch
localparam BRV0 =  4;   // Branch Via External Branch Address Source #0
localparam BRV1 =  5;   // Branch Via External Branch Address Source #1
localparam BRV2 =  6;   // Branch Via External Branch Address Source #2
localparam BRV3 =  7;   // Branch Via External Branch Address Source #3
localparam BTH0 =  8;   // Branch if T[0] is Logic 1, else fetch next instr.
localparam BTH1 =  9;   // Branch if T[1] is Logic 1, else fetch next instr.
localparam BTH2 = 10;   // Branch if T[2] is Logic 1, else fetch next instr.
localparam BTH3 = 11;   // Branch if T[3] is Logic 1, else fetch next instr.
localparam BTL0 = 12;   // Branch if T[0] is Logic 0, else fetch next instr.
localparam BTL1 = 13;   // Branch if T[1] is Logic 0, else fetch next instr.
localparam BTL2 = 14;   // Branch if T[2] is Logic 0, else fetch next instr.
localparam BTL3 = 15;   // Branch if T[3] is Logic 0, else fetch next instr.

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

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     dRst;                             // Reset stretcher
wire    MPC_Rst;                          // Internal MPC Reset signal

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

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

assign MPC_Rst = ((PLS) ? (Rst | dRst) : Rst);

//  Implement 4-Level LIFO Stack

always @(posedge Clk)
begin
    if(MPC_Rst)
        {D, C, B, A} <= #1 0;
    else if(I == BSR)
        {D, C, B, A} <= #1 {C, B, A, Next};
    else if(I == RTS)
        {D, C, B, A} <= #1 {{pAddrWidth{1'b0}}, D, C, B};
end

//  Program Counter Incrementer

assign Next = PC + 1;

//  Generate Unconditional Branch Address Select

assign Via = {((I == BRV2) | (I == BRV3)), ((I == BRV3) | (I == BRV1))};       

//  Generate Program Counter Input Signal

always @(*)
begin
    case({MPC_Rst, I})
        RTS     : PC_In <=  A;
        BSR     : PC_In <=  BA;
        FTCH    : PC_In <=  Next;
        BMW     : PC_In <=  {BA[(pAddrWidth - 1):3], MW};
        //
        BRV0    : PC_In <=  BA;
        BRV1    : PC_In <=  BA;
        BRV2    : PC_In <=  BA;
        BRV3    : PC_In <=  BA;
        //
        BTH0    : PC_In <=  (T[0] ? BA   : Next);
        BTH1    : PC_In <=  (T[1] ? BA   : Next);
        BTH2    : PC_In <=  (T[2] ? BA   : Next);
        BTH3    : PC_In <=  (T[3] ? BA   : Next);
        //
        BTL0    : PC_In <=  (T[0] ? Next : BA  );
        BTL1    : PC_In <=  (T[1] ? Next : BA  );
        BTL2    : PC_In <=  (T[2] ? Next : BA  );
        BTL3    : 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
        PC <= #1 ((En) ? ((Rdy) ? PC_In : PC) : PC_In);
end

//  Assign Memory Address Bus

always @(*)
begin
    MA <= ((PLS) ? ((En) ? ((Rdy) ? PC_In : PC) : PC_In) : PC);
end

endmodule