import common::*;
module cpu(
    input rst_n,
    input clk28,
    input clk14,
    input clk7,
    input clk35,
    input ck14,
    input ck7,

    cpu_bus bus,

    input [8:0] vc,
    input [8:0] hc,
    input video_contention,
    input [2:0] ram_page128,
    input machine_t machine,
    input turbo_t turbo,
    input init_done_in,
    input hold,

    output reg n_rstcpu_out,
    output reg clkcpu,
    output clkcpu_ck,
    output reg n_int,
    output reg n_int_next,
    output snow,
    output contention
);


/* CONTENTION */
wire iorq_contended = bus.ioreq && (~bus.a[0] || (bus.a == 16'hBF3B) || (bus.a == 16'hFF3B)) && (machine != MACHINE_S3);
reg mreq_delayed, iorq_delayed;
always @(posedge clkcpu)
    mreq_delayed <= bus.mreq;
always @(posedge clkcpu)
    iorq_delayed <= iorq_contended;
wire contention_page = (machine == MACHINE_S3)? ram_page128[2] : ram_page128[0];
wire contention_addr = bus.a_raw[14] & (~bus.a_raw[15] | (bus.a_raw[15] & contention_page));
wire contention_mem = !iorq_contended && !mreq_delayed && contention_addr;
wire contention0 = video_contention && !iorq_delayed && (contention_mem || iorq_contended);
assign contention = clkcpu && contention0 && turbo == TURBO_NONE && (machine == MACHINE_S48 || machine == MACHINE_S128 || machine == MACHINE_S3);
assign snow = video_contention && contention_addr && bus.rfsh && bus.mreq && (machine == MACHINE_S48 || machine == MACHINE_S128);


/* CLOCK */
wire turbo_wait_cond = turbo == TURBO_14 && (bus.rd || bus.wr || (bus.iorq && bus.m1));
reg [2:0] turbo_wait_reg;
reg turbo_wait;
always @* begin
    if (bus.iorq)
        turbo_wait <= turbo_wait_cond && !turbo_wait_reg[2];
    else
        turbo_wait <= turbo_wait_cond && !turbo_wait_reg[1];
end
always @(posedge clk28) begin
    if (clkcpu != clk14)
        turbo_wait_reg <= {turbo_wait_reg[1:0], turbo_wait_cond};
end

reg clkcpu_prev;
assign clkcpu_ck = clkcpu && !clkcpu_prev;
always @(posedge clk28) begin
    clkcpu_prev <= clkcpu;
    if (contention || hold || turbo_wait)
        clkcpu <= clkcpu;
    else if (turbo == TURBO_14)
        clkcpu <= clk14;
    else if (turbo == TURBO_7 && ck14)
        clkcpu <= clk7;
    else if (turbo == TURBO_5 && ck14 && hc[1])
        clkcpu <= clk7;
    else if (turbo == TURBO_4 && ck14 && hc[1] && hc[2])
        clkcpu <= clk7;
    else if (ck7)
        clkcpu <= clk35;
end


/* INT GENERATOR */
localparam INT_V_S48       = 248;
localparam INT_H_S48       = 0;
localparam INT_L_S48       = 6'd32;
localparam INT_V_S128      = 248;
localparam INT_H_S128      = 4;
localparam INT_L_S128      = 6'd36;
localparam INT_V_PENT      = 239;
localparam INT_H_PENT      = 322;
localparam INT_L_PENT      = 6'd32;
wire int_begin =
    (machine == MACHINE_S48)?
        vc == INT_V_S48 && hc == INT_H_S48 :
    (machine == MACHINE_S128 || machine == MACHINE_S3)?
        vc == INT_V_S128 && hc == INT_H_S128 :
    // Pentagon
        vc == INT_V_PENT && hc == INT_H_PENT ;
wire [5:0] int_len =
    (machine == MACHINE_S48)?
        INT_L_S48 :
    (machine == MACHINE_S128 || machine == MACHINE_S3)?
        INT_L_S128 :
    // Pentagon
        INT_L_PENT ;

reg [5:0] int_cnt;
always @(posedge clk28 or negedge rst_n) begin
    if (!rst_n) begin
        int_cnt <= 0;
        n_int_next <= 1'b1;
    end
    else if ((int_cnt != 0 && clkcpu_ck) || (int_cnt == 0 && int_begin)) begin
        int_cnt <= int_cnt + 1'b1;
        n_int_next <= (int_cnt < int_len)? 1'b0 : 1'b1;
    end
end

always @(posedge clk28 or negedge rst_n) begin
    if (!rst_n)
        n_int <= 1'b1;
    else if (clkcpu_ck)
        n_int <= n_int_next;
end


/* RESET */
always @(posedge clk28 or negedge rst_n) begin
    if (!rst_n)
        n_rstcpu_out <= 0;
    else if (!init_done_in)
        n_rstcpu_out <= 0;
`ifdef TESTBENCH
    else if (hc[4])
        n_rstcpu_out <= 1'b1;
`endif
    else if (vc[8])
        n_rstcpu_out <= 1'b1;
end


/* T-STATES COUNTER (for debug) */
`ifdef TESTBENCH
integer tstate;
always @(posedge clkcpu) begin
    if (!n_int_next && n_int)
        tstate <= -1;
    else
        tstate <= tstate + 1;
end
`endif


endmodule
