module M65C02_AddrGen(
    input   Rst,                    // System Reset
    input   Clk,                    // System Clock

    input   [15:0] Vector,          // Interrupt/Trap Vector

    input   [3:0] NA_Op,            // Next Address Operation
    input   [1:0] PC_Op,            // Program Counter Operation
    input   [1:0] Stk_Op,           // Stack Pointer Operation
    
    input   ZP,                     // Modulo 256 Control Input
    
    input   CC,                     // Conditional Branch Input Flag
    input   BRV3,                   // Interrupt or Next Instruction Select
    input   Int,                    // Unmasked Interrupt Request Input

    input   Rdy,                    // Ready Input
    
    input   [7:0] DI,               // Memory Data Input
    input   [7:0] OP1,              // Operand Register 1 Input
    input   [7:0] OP2,              // Operand Register 2 Input
    input   [7:0] StkPtr,           // Stack Pointer Input
    
    input   [7:0] X,                // X Index Register Input
    input   [7:0] Y,                // Y Index Register Input

    output  reg [15:0] AO,          // Address Output

    output  reg [15:0] AL,          // Address Generator Left Operand
    output  reg [15:0] AR,          // Address Generator Right Operand
    output  reg [15:0] NA,          // Address Generator Output - Next Address
    output  reg [15:0] MAR,         // Memory Address Register
    output  reg [15:0] PC,          // Program Counter
    output  reg [15:0] dPC          // Delayed Program Counter - Interrupt Adr
);

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

localparam  pNA_Inc  = 4'h1;    // NA <= PC + 1
localparam  pNA_MAR  = 4'h2;    // NA <= MAR + 0
localparam  pNA_Nxt  = 4'h3;    // NA <= MAR + 1
localparam  pNA_Stk  = 4'h4;    // NA <= SP + 0
localparam  pNA_DPN  = 4'h5;    // NA <= {0, OP1} + 0
localparam  pNA_DPX  = 4'h6;    // NA <= {0, OP1} + {0, X}
localparam  pNA_DPY  = 4'h7;    // NA <= {0, OP1} + {0, Y}
localparam  pNA_LDA  = 4'h8;    // NA <= {OP2, OP1} + 0
//
//
//
//
//
localparam  pNA_LDAX = 4'hE;    // NA <= {OP2, OP1} + {0, X}
localparam  pNA_LDAY = 4'hF;    // NA <= {OP2, OP1} + {0, Y}

////////////////////////////////////////////////////////////////////////////////
//
//  Module Declarations
//

reg     [ 6:0] Op_Sel;          // ROM Decoder for Next Address Operation
wire    CE_MAR;                 // Memory Address Register Clock Enable

reg     [ 4:0] PC_Sel;          // ROM Decoder for Program Counter Updates
wire    [15:0] Rel;             // Branch Address Sign-Extended Offset
reg     [15:0] PCL, PCR;        // Program Counter Left and Right Operands
wire    CE_PC;                  // Program Counter Clock Enable

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

//  Next Address Generator

always @(*)                 // PMSO XY C
begin                       // CAtP    0
    case(NA_Op)             //  Rk
        4'b0000 : Op_Sel <= 7'b1000_00_0;   // NA <= PC                  + 0
        4'b0001 : Op_Sel <= 7'b1000_00_1;   // NA <= PC                  + 1
        4'b0010 : Op_Sel <= 7'b0100_00_0;   // NA <= MAR                 + 0
        4'b0011 : Op_Sel <= 7'b0100_00_1;   // NA <= MAR                 + 1
        4'b0100 : Op_Sel <= 7'b0010_00_0;   // NA <= {  1, SP }          + 0
        4'b0101 : Op_Sel <= 7'b0001_00_0;   // NA <= {  0, OP1}          + 0
        4'b0110 : Op_Sel <= 7'b0001_10_0;   // NA <= {  0, OP1} + {0, X} + 0
        4'b0111 : Op_Sel <= 7'b0001_01_0;   // NA <= {  0, OP1} + {0, Y} + 0
        4'b1000 : Op_Sel <= 7'b0001_00_0;   // NA <= {OP2, OP1}          + 0
        4'b1001 : Op_Sel <= 7'b0001_00_0;   // NA <= {OP2, OP1}          + 0
        4'b1010 : Op_Sel <= 7'b0001_00_0;   // NA <= {OP2, OP1}          + 0
        4'b1011 : Op_Sel <= 7'b0001_00_0;   // NA <= {OP2, OP1}          + 0
        4'b1100 : Op_Sel <= 7'b0001_00_0;   // NA <= {OP2, OP1}          + 0
        4'b1101 : Op_Sel <= 7'b0001_00_0;   // NA <= {OP2, OP1}          + 0
        4'b1110 : Op_Sel <= 7'b0001_10_0;   // NA <= {OP2, OP1} + {0, X} + 0
        4'b1111 : Op_Sel <= 7'b0001_01_0;   // NA <= {OP2, OP1} + {0, Y} + 0
    endcase
end

//  Generate Left Address Operand

always @(*) AL <= (  ((Op_Sel[ 6]) ? PC              : 0)
                   | ((Op_Sel[ 5]) ? MAR             : 0)
                   | ((Op_Sel[ 4]) ? {8'h01, StkPtr} : 0)
                   | ((Op_Sel[ 3]) ? {OP2  , OP1   } : 0));
                   
//  Generate Right Address Operand

always @(*) AR <= (  ((Op_Sel[ 2]) ? {8'h00, X}      : 0)
                   | ((Op_Sel[ 1]) ? {8'h00, Y}      : 0));

//  Compute Next Address

always @(*) NA <= (AL + AR + (Op_Sel[0] ? 1'b1 : 1'b0));

//  Generate Address Output - Truncate Next Address when ZP asserted

always @(*) AO <= NA & ((ZP) ? 16'h00FF : 16'hFFFF);

//  Memory Address Register

assign CE_MAR = (|NA_Op) & Rdy;

always @(posedge Clk)
begin
    if(Rst)
        MAR <= #1 Vector;
    else if(CE_MAR)
        MAR <= #1 AO;
end

//  Program Counter

assign CE_PC = ((BRV3) ? ((|PC_Op) & ~Int) : (|PC_Op)) & Rdy;

//  Generate Relative Address

assign Rel = ((CC) ? {{8{DI[7]}}, DI} : 0);

//  Determine the operands for Program Counter updates

always @(*)                 // PDO R C
begin                       // PIP e 0
    case({PC_Op, Stk_Op})   //   2 l
        4'b0000 : PC_Sel <= 5'b100_0_0; // NOP: PC      PC <= PC         + 0
        4'b0001 : PC_Sel <= 5'b100_0_0; // NOP: PC      PC <= PC         + 0
        4'b0010 : PC_Sel <= 5'b100_0_0; // NOP: PC      PC <= PC         + 0
        4'b0011 : PC_Sel <= 5'b100_0_0; // NOP: PC      PC <= PC         + 0
        4'b0100 : PC_Sel <= 5'b100_0_1; // Pls: PC + 1  PC <= PC         + 1
        4'b0101 : PC_Sel <= 5'b100_0_1; // Pls: PC + 1  PC <= PC         + 1
        4'b0110 : PC_Sel <= 5'b100_0_1; // Pls: PC + 1  PC <= PC         + 1
        4'b0111 : PC_Sel <= 5'b100_0_1; // Pls: PC + 1  PC <= PC         + 1
        4'b1000 : PC_Sel <= 5'b010_0_0; // Jmp: JMP     PC <= { DI, OP1} + 0
        4'b1001 : PC_Sel <= 5'b010_0_0; // Jmp: JMP     PC <= { DI, OP1} + 0
        4'b1010 : PC_Sel <= 5'b001_0_0; // Jmp: JSR     PC <= {OP2, OP1} + 0
        4'b1011 : PC_Sel <= 5'b010_0_1; // Jmp: RTS/RTI PC <= { DI, OP1} + 1
        4'b1100 : PC_Sel <= 5'b100_1_1; // Rel: Bcc     PC <= PC + Rel   + 1
        4'b1101 : PC_Sel <= 5'b100_1_1; // Rel: Bcc     PC <= PC + Rel   + 1
        4'b1110 : PC_Sel <= 5'b100_1_1; // Rel: Bcc     PC <= PC + Rel   + 1
        4'b1111 : PC_Sel <= 5'b100_1_1; // Rel: Bcc     PC <= PC + Rel   + 1
    endcase
end

always @(*) PCL <= (  ((PC_Sel[4]) ? PC         : 0)
                    | ((PC_Sel[3]) ? { DI, OP1} : 0)
                    | ((PC_Sel[2]) ? {OP2, OP1} : 0));

always @(*) PCR <=    ((PC_Sel[1]) ? Rel        : 0);

//  Implement Program Counter

always @(posedge Clk)
begin
    if(Rst)
        PC <= #1 Vector;
    else if(CE_PC)
        PC <= #1 (PCL + PCR + PC_Sel[0]);
end

//  Track past values of the PC for interrupt handling
//      past value of PC required to correctly determine the address of the
//      instruction at which the interrupt trap was taken. The automatic incre-
//      ment of the return address following RTS/RTI will advance the address 
//      so that it points to the correct instruction.

always @(posedge Clk)
begin
    if(Rst)
        dPC <= #1 Vector;
    else if(CE_PC)
        dPC <= #1 PC;
end

endmodule