module RAM2E(C14M, PHI1, LED, 
             nWE, nWE80, nEN80, nC07X,
             Ain, Din, Dout, nDOE, Vout, nVOE,
             CKEout, nCSout, nRASout, nCASout, nRWEout,
             BA, RAout, DQML, DQMH, RD);

    /* Clocks */
    input C14M, PHI1;
    
    /* Control inputs */
    input nWE, nWE80, nEN80, nC07X;

    /* "Fast" (init) state counter */
    reg [15:0] FS = 0; always @(posedge C14M) FS <= FS+16'h0001;
    reg Ready = 0;
    always @(posedge C14M) if (FS[15:0]==16'hFFFF) Ready <= 1'b1;

    /* IIe state counter */
    reg [3:0] S = 0;
    reg PHI1r = 0; always @(posedge C14M) PHI1r <= PHI1;
    always @(posedge C14M) begin
        S <= (PHI1 && !PHI1r && Ready) ? 4'h1 :
             (S==4'h0) ? 4'h0 :
             (S==4'hF) ? 4'hF : S+4'h1;
    end

    /* Refresh counter */
    reg [2:0] RC;
    reg RefReq;
    always @(negedge PHI1) begin
        if (RC[2] && RC[1]) RC <= 0; // RC==6 || RC==7
        else RC <= RC+3'h1;
        RefReq <= RC==0;
    end

    /* Activity LED */
    wire LEDEN;
    output LED; assign LED = !(!nEN80 && LEDEN && Ready);

    /* DRAM multiplexed address bus input */
    input [7:0] Ain;
    
    /* 6502 data bus input/output */
    input [7:0] Din;
    reg DOEEN; 
    always @(posedge C14M) begin
        DOEEN <= S==4'hB || S==4'hC || S==4'hD || S==4'hE || S==4'hF;
    end
    output nDOE; assign nDOE = !(!nEN80 && nWE && DOEEN);
    output [7:0] Dout; assign Dout[7:0] = RD[7:0];
    
    /* Video Data Bus */
	reg VOE;
    always @(negedge C14M) begin
        VOE <= S==4'h7 || S==4'h8 || S==4'h9 || S==4'hA || S==4'hB || S==4'hC;
    end
    output nVOE; assign nVOE = !VOE;
    output reg [7:0] Vout; // Video data bus
    always @(negedge C14M) if (S==4'h6) Vout[7:0] <= RD[7:0];

    /* SDRAM bus */
    reg CKE = 1, nRAS = 1, nCAS = 1, nRWE = 1;
    output reg [1:0] BA;
    reg [11:0] RA;
    output reg DQML = 1, DQMH = 1;
    inout [7:0] RD;
    wire [7:0] RDout = Ready ? Din[7:0] : 8'h00;
    reg RDOE;
    always @(posedge C14M) begin
        RDOE <= (!Ready) || (!nEN80 && !nWE && (S==4'hA || S==4'hB));
    end
    assign RD[7:0] = RDOE ? RDout[7:0] : 8'bZ;

    /* SDRAM falling edge command outputs */
    output nCSout; assign nCSout = 0;
    output reg CKEout = 1, nRASout = 1, nCASout = 1, nRWEout = 1;
    always @(negedge C14M) begin
        CKEout <= CKE;
        nRASout <= nRAS;
        nCASout <= nCAS;
        nRWEout <= nRWE;
    end

    /* SDRAM address outputs */
    output [11:0] RAout;
    reg [11:0] RAr; always @(negedge C14M) RAr <= RA;
    reg RAT; always @(negedge C14M) RAT <= S==4'hA;
    assign RAout[11:8] = RAr[11:8];
    assign RAout[7:0] = RAT ? Ain[7:0] : RAr[7:0];
    
    /* RAMWorks Bank Register and Capacity Mask */
    reg [7:0] RWBank = 0; // RAMWorks bank register
    wire [7:0] RWMask;
    reg RWSel = 0; // RAMWorks bank register select
    always @(posedge C14M) begin
        if (S==4'h9) RWSel <= RA[0] && !RA[3] && !nWE && !nC07X;
    end
    reg CmdRWMaskSet = 0; // RAMWorks Mask register set flag
    reg CmdSetRWBankFFLED = 0;
    reg CmdLEDSet = 0;
    reg CmdLEDGet = 0;
    
    /* Command Sequence Detector */
    reg [2:0] CS = 0; // Command sequence state
    reg [2:0] CmdTout = 0; // Command sequence timeout

    /* Command sequence control */
    always @(posedge C14M) begin
        if (S==4'hC) begin
            if (RWSel) begin
                CmdTout <= 0; // Reset command timeout if RWSel accessed
                // Recognize command sequence and advance CS state
                if ((CS==3'h0 && Din[7:0]==8'hFF) ||
                    (CS==3'h1 && Din[7:0]==8'h00) ||
                    (CS==3'h2 && Din[7:0]==8'h55) ||
                    (CS==3'h3 && Din[7:0]==8'hAA) ||
                    (CS==3'h4 && Din[7:0]==8'hC1) ||
                    (CS==3'h5 && Din[7:0]==8'hAD) ||
                     CS==3'h6 || CS==3'h7) CS <= CS+3'h1;
                else CS <= 0; // Back to beginning if it's not right
            end else begin
                CmdTout <= CmdTout+3'h1; // Increment command timeout
                // If command sequence times out, reset sequence state
                if (CmdTout==3'h7) CS <= 0;
            end
        end
    end

    /* Chip-specific UFM interface */
    wire [7:0] ChipCmdNum;
    RAM2E_UFM ram2e_ufm (
        .C14M(C14M), .S(S), .FS(FS), .CS(CS),
        .RWSel(RWSel), .D(Din),
        .RWMask(RWMask), .LEDEN(LEDEN),
        .CmdRWMaskSet(CmdRWMaskSet), .CmdLEDSet(CmdLEDSet),
        .ChipCmdNum(ChipCmdNum));

    /* RAMWorks register control - bank, LED, etc. */
    reg CmdSetRWBankFFChip;
    always @(posedge C14M) begin
        if (S==4'hC && RWSel) begin
            // Latch RAMWorks bank if accessed
            if ((CmdSetRWBankFFLED) || (CmdSetRWBankFFChip) ||
                (CmdLEDGet && LEDEN)) RWBank <= 8'hFF;
            else RWBank <= Din[7:0] & {RWMask[7], ~RWMask[6:0]};
            
            if (CS==3'h6) begin // Recognize and submit command in CS6
                // Chip detection command
                CmdSetRWBankFFChip <= Din[7:0]==ChipCmdNum[7:0];
                // LED exists detect command
                CmdSetRWBankFFLED <=  Din[7:0]==8'hF0;
                // Volatile settings commands
                CmdRWMaskSet <=       Din[7:0]==8'hE0;
                CmdLEDSet <=          Din[7:0]==8'hE2;
                CmdLEDGet <=          Din[7:0]==8'hE3;
            end else begin // Reset command triggers
                CmdSetRWBankFFChip <= 0;
                CmdSetRWBankFFLED <= 0;
                CmdRWMaskSet <= 0;
                CmdLEDSet <= 0;
                CmdLEDGet <= 0;
            end
        end
    end

    /* SDRAM Control */
    always @(posedge C14M) case (S)
        4'h0: begin
            CKE <= 1'b1;
            if (!FS[15] || FS[0]) begin
                // NOP
                nRAS <= 1'b1;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
            end else case (FS[4:1])
                4'h0: begin
                    // PC all
                    nRAS <= 1'b0;
                    nCAS <= 1'b1;
                    nRWE <= 1'b0;
                end 4'h1: begin
                    // LDM
                    nRAS <= 1'b0;
                    nCAS <= 1'b0;
                    nRWE <= 1'b0;
                end 4'h2: begin
                    // NOP
                    nRAS <= 1'b1;
                    nCAS <= 1'b1;
                    nRWE <= 1'b1;
                end 4'h3, 4'h4, 4'h5, 4'h6, 
                    4'h7, 4'h8, 4'h9, 4'hA: begin
                    // AREF
                    nRAS <= 1'b0;
                    nCAS <= 1'b0;
                    nRWE <= 1'b1;
                end 4'hB: begin
                    // ACT
                    nRAS <= 1'b0;
                    nCAS <= 1'b1;
                    nRWE <= 1'b1;
                end 4'hC, 4'hD: begin
                    // WR
                    nRAS <= 1'b1;
                    nCAS <= 1'b0;
                    nRWE <= 1'b0;
                end 4'hE: begin
                    // NOP
                    nRAS <= 1'b1;
                    nCAS <= 1'b1;
                    nRWE <= 1'b1;
                end 4'hF: begin
                    // PC all
                    nRAS <= 1'b0;
                    nCAS <= 1'b1;
                    nRWE <= 1'b0;
                end
            endcase
            BA[1:0] <= 2'b00;
            case (FS[4:3])
                2'b00, 2'b01: begin
                    // Mode register contents
                    RA[11] <= 1'b0;     // Reserved
                    RA[10] <= !FS[1];   // reserved / "all"
                    RA[9] <= 1'b1;      // "1" for single write mode
                    RA[8] <= 1'b0;      // Reserved
                    RA[7] <= 1'b0;      // "0" for not test mode
                    RA[6:4] <= 3'b010;  // "2" for CAS latency 2
                    RA[3] <= 1'b0;      // "0" for sequential burst (not used)
                    RA[2:0] <= 3'b000;  // "0" for burst length 1 (no burst)
                end 2'b10: begin
                    RA[11:8] <= 4'h0;
                    RA[7:0] <= FS[14:7];
                end 2'b11: begin
                    RA[11:3] <= 9'h000;
                    RA[2:1] <= FS[6:5];
                    RA[0] <= FS[1];
                end
            endcase
            DQML <= !FS[15];
            DQMH <= !FS[15];
        end 4'h1: begin
            // NOP CKE
            CKE <= 1'b1;
            nRAS <= 1'b1;
            nCAS <= 1'b1;
            nRWE <= 1'b1;
            BA[1:0] <= 2'b00;
            RA[11:8] <= 4'b0000;
            // Hold RA[7:0]
            DQML <= 1'b0;
            DQMH <= 1'b1;
        end 4'h2: begin
            // ACT CKE
            CKE <= 1'b1;
            nRAS <= 1'b0;
            nCAS <= 1'b1;
            nRWE <= 1'b1;
            // Hold BA
            // Hold RA
            // Hold DQMs
        end 4'h3: begin
            // RD CKE
            CKE <= 1'b1;
            nRAS <= 1'b1;
            nCAS <= 1'b0;
            nRWE <= 1'b1;
            // Hold BA
            // Hold RA[11:8]
            RA[7:0] <= Ain[7:0];
            // Hold DQMs
        end 4'h4: begin
            if (RefReq) begin // Refresh request
                // PC all CKE
                CKE <= 1'b1;
                nRAS <= 1'b0;
                nCAS <= 1'b1;
                nRWE <= 1'b0;
            end else begin // No refresh request
                // PC all CKD
                CKE <= 1'b0;
                nRAS <= 1'b0;
                nCAS <= 1'b1;
                nRWE <= 1'b0;
            end
            // Hold BA
            // Hold RA[11,9:0]
            RA[10] <= 1'b1; // "all"
            // Hold DQMs
        end 4'h5: begin
            if (RefReq) begin // Refresh request
                // AREF CKE
                CKE <= 1'b1;
                nRAS <= 1'b0;
                nCAS <= 1'b0;
                nRWE <= 1'b1;
            end else begin // No refresh request
                // NOP CKD
                CKE <= 1'b0;
                nRAS <= 1'b1;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
            end
            // Hold BA
            // Hold RA
            // Hold DQMs
        end 4'h6: begin
            // NOP CKD
            CKE <= 1'b0;
            nRAS <= 1'b1;
            nCAS <= 1'b1;
            nRWE <= 1'b1;
            // Hold BA
            // Hold RA
            // Hold DQMs
        end 4'h7: begin
            // Can't check EN80 at this time
            // NOP CKE
            CKE <= 1'b1;
            nRAS <= 1'b1;
            nCAS <= 1'b1;
            nRWE <= 1'b1;
            // Hold BA
            // Hold RA[11:8]
            RA[7:0] <= Ain[7:0];
            // Hold DQMs
        end 4'h8: begin
            if (nEN80) begin // Idle
                // NOP CKD
                CKE <= 1'b0;
                nRAS <= 1'b1;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
            end else if (nWE) begin // Read
                // ACT CKE
                CKE <= 1'b1;
                nRAS <= 1'b0;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
            end else begin // Write
                // ACT CKD
                CKE <= 1'b0;
                nRAS <= 1'b0;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
            end
            BA[1:0] <= RWBank[6:5];
            RA[11:8] <= RWBank[4:1];
            // Hold RA[7:0]
            // Hold DQMs
        end 4'h9: begin
            if (nEN80) begin // Idle
                // NOP CKD
                CKE <= 1'b0;
                nRAS <= 1'b1;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
            end else if (nWE) begin // Read
                // RD CKE
                CKE <= 1'b1;
                nRAS <= 1'b1;
                nCAS <= 1'b0;
                nRWE <= 1'b1;
            end else begin // Write
                // NOP CKE
                CKE <= 1'b1;
                nRAS <= 1'b1;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
            end
            // Hold BA
            RA[11:9] <= 3'b000; // no auto-precharge
            RA[8] <= RWBank[7];
            // RA[7:0] is transparent
            DQML <=  RWBank[0];
            DQMH <= !RWBank[0];
        end 4'hA: begin
            if (nEN80) begin // Idle
                // NOP CKD
                CKE <= 1'b0;
                nRAS <= 1'b1;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
                RA[10] <= 1'b0;
            end else if (nWE) begin // Read
                // PC all CKD
                CKE <= 1'b0;
                nRAS <= 1'b0;
                nCAS <= 1'b1;
                nRWE <= 1'b0;
                RA[10] <= 1'b1; // "all"
            end else begin // Write
                // WR CKE
                CKE <= 1'b1;
                nRAS <= 1'b1;
                nCAS <= 1'b0;
                nRWE <= 1'b0;
                RA[10] <= 1'b0; // no auto-precharge
            end
            // Hold BA
            // Hold RA[11,9:8]
            RA[7:0] <= Ain[7:0];
            // Hold DQMs
        end 4'hB: begin
            if (nEN80) begin // Idle
                // NOP CKD
                CKE <= 1'b0;
                nRAS <= 1'b1;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
            end else if (nWE) begin // Read
                // NOP CKD
                CKE <= 1'b0;
                nRAS <= 1'b1;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
            end else begin // Write
                // NOP CKE
                CKE <= 1'b1;
                nRAS <= 1'b1;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
            end
            // Hold BA
            // Hold RA[11:0]
            // Hold DQMs
        end 4'hC: begin
            if (nEN80) begin // Idle
                // NOP CKD
                CKE <= 1'b0;
                nRAS <= 1'b1;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
                // Hold RA[10]
            end else if (nWE) begin // Read
                // NOP CKD
                CKE <= 1'b0;
                nRAS <= 1'b1;
                nCAS <= 1'b1;
                nRWE <= 1'b1;
                // Hold RA[10]
            end else begin // Write
                // PC all CKD
                CKE <= 1'b0;
                nRAS <= 1'b0;
                nCAS <= 1'b1;
                nRWE <= 1'b0;
                RA[10] <= 1'b1; // "all"
            end
            // Hold BA
            // Hold RA[11,9:0]
            // Hold RA[7:0]
            // Hold DQMs
        end 4'hD: begin
            // NOP CKD
            CKE <= 1'b0;
            nRAS <= 1'b1;
            nCAS <= 1'b1;
            nRWE <= 1'b1;
            // Hold BA
            // Hold RA[11:0]
            // Hold DQMs
        end 4'hE, 4'hF: begin
            // NOP CKD
            CKE <= 1'b0;
            nRAS <= 1'b1;
            nCAS <= 1'b1;
            nRWE <= 1'b1;
            // Hold BA
            // Hold RA[11:8]
            RA[7:0] <= Ain[7:0]; // Latch row address for next video read
            // Hold DQMs
        end
    endcase
endmodule
