module zx_multisound(
    input rst_n,
    input clk32,
    input clkx,

    input [4:0] cfg,

    input [15:0] zxa,
    inout [7:0] zxd,
    input zxrd_n,
    input zxwr_n,
    input zxiorq_n,
    input zxmreq_n,
    input zxm1_n,
    output zxwait_n,
    output zxiorqge_n,
    input zxdos_n,
    input zxiodos_n,

    output aa0,
    inout [7:0] ad,
    output rstout_n,
    output ard_n,
    output awr_n,
    output ym_m,
    output ym1_cs_n,
    output ym2_cs_n,
    output reg fm1_ena,
    output reg fm2_ena,
    output saa_cs_n,
    output saa_clk,
    output midi_clk,

    input [15:0] ga,
    inout [7:0] gd,
    output grst_n,
    output gclk,
    output reg gint_n,
    input grd_n,
    input gwr_n,
    input gm1_n,
    input gmreq_n,
    input giorq_n,
    output grom_n,
    output gram1_n,
    output gram2_n,
    output gram3_n,
    output gram4_n,
    output [18:15] gma,

    output dac0_out,
    output dac1_out,
    output dac2_out,
    output dac3_out
);

assign rstout_n = rst_n;

// iorq_n are useless in zxevo :(
// so we're detecting iorq_n cycle by rd_n/wr_n signal asserted without m1_n/mreq_n
reg ioreq, ioreq_prev;
always @(negedge clk32) begin
    ioreq_prev <= ioreq;
    // ioreq <= zxiorq_n == 1'b0 && zxm1_n == 1'b1 && zxdos_n == 1'b1 && zxiodos_n == 1'b1;
    ioreq <= zxm1_n == 1'b1 && zxmreq_n == 1'b1 && (zxrd_n == 1'b0 || zxwr_n == 1'b0);
end
wire ioreq_rd = ioreq && zxrd_n == 1'b0;
wire ioreq_wr = ioreq && zxwr_n == 1'b0;

// dos_n are useless in zxevo :(
// so we're just lock some ports access when instruction has been fetched from rom
reg rom_m1_access;
always @(negedge clk32 or negedge rst_n) begin
    if (!rst_n)
        rom_m1_access <= 0;
    else if (zxm1_n == 0)
        rom_m1_access <= zxa[15:14] == 2'b00;
end



/* CONFIGURATION */
wire ym_ena  = cfg[0];
wire saa_ena = cfg[1];
wire gs_ena  = cfg[2];
wire sd_ena  = cfg[3];


/* CLOCKS */
reg [5:0] clk3_5_cnt = 0;
reg [1:0] clk8_cnt   = 0;
reg [2:0] clk12_cnt  = 0;
always @(posedge clk32) clk3_5_cnt <= clk3_5_cnt + 6'd7;
always @(posedge clk32) clk8_cnt   <= clk8_cnt   + 1'b1;
always @(posedge clk32) clk12_cnt  <= clk12_cnt  + 3'd3;
wire clk3_5 = clk3_5_cnt[5];
wire clk8   = clk8_cnt[1];
wire clk12  = clk12_cnt[2];
wire clk16  = clk8_cnt[0];


/* TURBO SOUND FM */
wire port_bffd      = zxa[15:14] == 2'b10  && zxa[3:0] == 4'b1101 && ym_ena;
wire port_fffd      = zxa[15:14] == 2'b11  && zxa[3:0] == 4'b1101 && ym_ena;
wire port_fffd_full = zxa[15:13] == 3'b111 && zxa[3:0] == 4'b1101 && ym_ena; // required for compatibility with #dffd port
reg ym_chip_sel, ym_get_stat;
wire ym_a0 = (~zxrd_n & zxa[14] & ~ym_get_stat) | (~zxwr_n & ~zxa[14]);
assign ym1_cs_n = ~(~ym_chip_sel && (port_bffd || port_fffd));
assign ym2_cs_n = ~( ym_chip_sel && (port_bffd || port_fffd));

always @(posedge clk32 or negedge rst_n) begin
    if (!rst_n) begin
        ym_chip_sel <= 0;
        ym_get_stat <= 0;
        fm1_ena <= 0;
        fm2_ena <= 0;
    end
    else if (port_fffd && ioreq_wr && zxd[7:4] == 4'b1111) begin
        ym_chip_sel <= zxd[0];
        ym_get_stat <= ~zxd[1];
        fm1_ena <= zxd[2]? 1'b0 : 1'bz;
        fm2_ena <= zxd[2]? 1'b0 : 1'bz;
    end
end

assign ym_m = clk3_5;


/* SAA1099 */
wire port_ff = zxa[7:0] == 8'hFF && saa_ena && !rom_m1_access;
assign saa_cs_n = ~(port_ff && ioreq_wr);
wire saa_a0 = zxa[8];

wire port_fffd_saa = zxa[15:14] == 2'b11 && zxa[3:0] == 4'b1101 && saa_ena;
reg saa_clk_en;
always @(posedge clk32 or negedge rst_n) begin
    if (!rst_n)
        saa_clk_en <= 0;
    else if (port_fffd_saa && ioreq_wr && zxd[7:4] == 4'b1111)
        saa_clk_en <= ~zxd[3];
end

assign saa_clk = saa_clk_en? clk8 : 1'b0;


/* MIDI */
assign midi_clk = clk12;


/* GENERAL SOUND */
assign gclk = clk16;
assign grst_n = rstout_n;

reg gioreq, gioreq_prev;
always @(posedge clk32) begin
    gioreq_prev <= gioreq;
    gioreq <= giorq_n == 1'b0 && gm1_n == 1'b1;
end

reg [8:0] g_int_cnt;
wire g_int_reload = g_int_cnt[8:6] == 4'b101;
always @(posedge clk12 or negedge rst_n) begin
    if (!rst_n) begin
        g_int_cnt <= 0;
        gint_n <= 1'b1;
    end
    else begin
        if (g_int_reload)
            g_int_cnt <= 0;
        else
            g_int_cnt <= g_int_cnt + 1'b1;

        if (g_int_reload)
            gint_n <= 1'b0;
        else if (g_int_cnt[5])
            gint_n <= 1'b1;
    end
end

/* GS EXTERNAL REGISTERS */
reg [7:0] gs_regdata, gs_regcmd;
wire port_b3 = zxa[7:0] == 8'hB3 && gs_ena;
wire port_bb = zxa[7:0] == 8'hBB && gs_ena;
always @(posedge clk32 or negedge rst_n) begin
    if (!rst_n) begin
        gs_regdata <= 0;
        gs_regcmd <= 0;
    end
    else begin
        if (port_b3 && ioreq_wr)
            gs_regdata <= zxd;
        if (port_bb && ioreq_wr)
            gs_regcmd <= zxd;
    end
end

/* GS INTERNAL REGISTERS */
reg [7:0] gs_reg00, gs_reg_out;
wire [6:0] gs_page = gs_reg00[6:0];
always @(posedge clk32 or negedge rst_n) begin
    if (!rst_n) begin
        gs_reg00 <= 0;
        gs_reg_out <= 0;
    end
    else if (~giorq_n && ~gwr_n) begin
        if (ga[3:0] == 4'h0) gs_reg00 <= gd;
        if (ga[3:0] == 4'h3) gs_reg_out <= gd;
    end
end

/* GS DAC REGISTERS */
reg gs_vol0_cs; always @(posedge clk32) gs_vol0_cs = ~giorq_n && ga[3:0] == 4'h6;
reg gs_vol1_cs; always @(posedge clk32) gs_vol1_cs = ~giorq_n && ga[3:0] == 4'h7;
reg gs_vol2_cs; always @(posedge clk32) gs_vol2_cs = ~giorq_n && ga[3:0] == 4'h8;
reg gs_vol3_cs; always @(posedge clk32) gs_vol3_cs = ~giorq_n && ga[3:0] == 4'h9;
reg gs_dac0_cs; always @(posedge clk32) gs_dac0_cs = ~gmreq_n && ga[15:13] == 3'b011 && ga[9:8] == 2'd0;
reg gs_dac1_cs; always @(posedge clk32) gs_dac1_cs = ~gmreq_n && ga[15:13] == 3'b011 && ga[9:8] == 2'd1;
reg gs_dac2_cs; always @(posedge clk32) gs_dac2_cs = ~gmreq_n && ga[15:13] == 3'b011 && ga[9:8] == 2'd2;
reg gs_dac3_cs; always @(posedge clk32) gs_dac3_cs = ~gmreq_n && ga[15:13] == 3'b011 && ga[9:8] == 2'd3;
wire gs_vol0_wr = gs_vol0_cs && ~gwr_n;
wire gs_vol1_wr = gs_vol1_cs && ~gwr_n;
wire gs_vol2_wr = gs_vol2_cs && ~gwr_n;
wire gs_vol3_wr = gs_vol3_cs && ~gwr_n;
wire gs_dac0_wr = gs_dac0_cs && ~grd_n;
wire gs_dac1_wr = gs_dac1_cs && ~grd_n;
wire gs_dac2_wr = gs_dac2_cs && ~grd_n;
wire gs_dac3_wr = gs_dac3_cs && ~grd_n;

/* GS STATUS REGISTER */
reg gs_flag_cmd, gs_flag_data;
wire [7:0] gs_status = {gs_flag_data, 6'b111111, gs_flag_cmd};

always @(posedge clk32 or negedge rst_n) begin
    if (!rst_n)
        gs_flag_data <= 1'b0;
    else if (ioreq_rd && !ioreq_prev && port_b3)
        gs_flag_data <= 1'b0;
    else if (ioreq_wr && !ioreq_prev && port_b3)
        gs_flag_data <= 1'b1;
    else if (gioreq && !gioreq_prev && ga[3:0] == 4'h2)
        gs_flag_data <= 1'b0;
    else if (gioreq && !gioreq_prev && ga[3:0] == 4'h3)
        gs_flag_data <= 1'b1;
    else if (gioreq && !gioreq_prev && ga[3:0] == 4'hA)
        gs_flag_data <= ~gs_reg00[0];
end

always @(posedge clk32 or negedge rst_n) begin
    if (!rst_n)
        gs_flag_cmd <= 1'b0;
    else if (ioreq_wr && !ioreq_prev && port_bb)
        gs_flag_cmd <= 1'b1;
    else if (gioreq && !gioreq_prev && ga[3:0] == 4'h5)
        gs_flag_cmd <= 1'b0;
    else if (gioreq && !gioreq_prev && ga[3:0] == 4'hB)
        gs_flag_cmd <= vol3[5];
end

/* GS BUS CONTROLLER */
assign grom_n = (~gmreq_n && ((ga[15:14] == 2'b00) || (ga[15] && gs_page == 0)))? 1'b0 : 1'b1;
`ifdef GS_RAM_2MB
    assign gram1_n = (~gmreq_n && grom_n && ((gs_page[5:4] == 2'd0) || ~ga[15]))? 1'b0 : 1'b1;
    assign gram2_n = (~gmreq_n && grom_n &&  (gs_page[5:4] == 2'd1) &&  ga[15] )? 1'b0 : 1'b1;
    assign gram3_n = (~gmreq_n && grom_n &&  (gs_page[5:4] == 2'd2) &&  ga[15] )? 1'b0 : 1'b1;
    assign gram4_n = (~gmreq_n && grom_n &&  (gs_page[5:4] == 2'd3) &&  ga[15] )? 1'b0 : 1'b1;
`else
    assign gram1_n = (~gmreq_n && grom_n && (~gs_page[4] || ~ga[15]))? 1'b0 : 1'b1;
    assign gram2_n = (~gmreq_n && grom_n &&   gs_page[4] &&  ga[15] )? 1'b0 : 1'b1;
    assign gram3_n = 1'b1;
    assign gram4_n = 1'b1;
`endif
assign gma = (ga[15] == 1'b0)? 4'b0001 : gs_page[3:0];
assign gd =
    (~giorq_n && ~grd_n && ga[3:0] == 4'h4)? gs_status :
    (~giorq_n && ~grd_n && ga[3:0] == 4'h2)? gs_regdata :
    (~giorq_n && ~grd_n && ga[3:0] == 4'h1)? gs_regcmd :
    (~giorq_n && (~grd_n || ~gm1_n))?        {8{1'b1}} :
                                             {8{1'bz}} ;


/* SOUNDRIVE */
wire port_xf = sd_ena && zxa[7] == 1'b0 && zxa[5] == 1'b0 && zxa[3:0] == 4'hF && !rom_m1_access;
wire [1:0] port_xf_chn = {zxa[6],zxa[4]};
reg sd_dac0_cs; always @(posedge clk32) sd_dac0_cs = ioreq && port_xf && port_xf_chn == 2'd0;
reg sd_dac1_cs; always @(posedge clk32) sd_dac1_cs = ioreq && port_xf && port_xf_chn == 2'd1;
reg sd_dac2_cs; always @(posedge clk32) sd_dac2_cs = ioreq && port_xf && port_xf_chn == 2'd2;
reg sd_dac3_cs; always @(posedge clk32) sd_dac3_cs = ioreq && port_xf && port_xf_chn == 2'd3;
wire sd_dac0_wr = sd_dac0_cs && ~zxwr_n;
wire sd_dac1_wr = sd_dac1_cs && ~zxwr_n;
wire sd_dac2_wr = sd_dac2_cs && ~zxwr_n;
wire sd_dac3_wr = sd_dac3_cs && ~zxwr_n;


/* DAC */
reg [5:0] vol0, vol1, vol2, vol3;
always @(posedge clk32 or negedge rst_n) begin
    if (!rst_n) begin
        vol0 <= 0;
        vol1 <= 0;
        vol2 <= 0;
        vol3 <= 0;
    end
    else begin
        if      (sd_dac0_wr) vol0 <= 6'b111111;
        else if (gs_vol0_wr) vol0 <= gd[5:0];
        if      (sd_dac1_wr) vol1 <= 6'b111111;
        else if (gs_vol1_wr) vol1 <= gd[5:0];
        if      (sd_dac2_wr) vol2 <= 6'b111111;
        else if (gs_vol2_wr) vol2 <= gd[5:0];
        if      (sd_dac3_wr) vol3 <= 6'b111111;
        else if (gs_vol3_wr) vol3 <= gd[5:0];
    end
end

reg [7:0] dac0, dac1, dac2, dac3;
always @(posedge clk32 or negedge rst_n) begin
    if (!rst_n) begin
        dac0 <= 0;
        dac1 <= 0;
        dac2 <= 0;
        dac3 <= 0;
    end
    else begin
        // quartus bug(?): without second condition inside "IF" expression incorrect design may be generated
        if (sd_dac0_wr && !gs_dac0_wr) dac0 <= (zxd[7]? zxd : {zxd[7],~zxd[6:0]});
        else if           (gs_dac0_wr) dac0 <= ( gd[7]? gd  : { gd[7],~gd[6:0]});
        if (sd_dac1_wr && !gs_dac1_wr) dac1 <= (zxd[7]? zxd : {zxd[7],~zxd[6:0]});
        else if           (gs_dac1_wr) dac1 <= ( gd[7]? gd  : { gd[7],~gd[6:0]});
        if (sd_dac2_wr && !gs_dac2_wr) dac2 <= (zxd[7]? zxd : {zxd[7],~zxd[6:0]});
        else if           (gs_dac2_wr) dac2 <= ( gd[7]? gd  : { gd[7],~gd[6:0]});
        if (sd_dac3_wr && !gs_dac3_wr) dac3 <= (zxd[7]? zxd : {zxd[7],~zxd[6:0]});
        else if           (gs_dac3_wr) dac3 <= ( gd[7]? gd  : { gd[7],~gd[6:0]});
    end
end

reg vol0_en, vol1_en, vol2_en, vol3_en;
reg [5:0] vol_cnt;
reg [7:0] dac0_cnt, dac1_cnt, dac2_cnt, dac3_cnt;
assign dac0_out = dac0_cnt[7]? dac0[7] : clk32;
assign dac1_out = dac1_cnt[7]? dac1[7] : clk32;
assign dac2_out = dac2_cnt[7]? dac2[7] : clk32;
assign dac3_out = dac3_cnt[7]? dac3[7] : clk32;
always @(posedge clk32) begin
    vol_cnt <= vol_cnt + 6'd31;
    vol0_en <= (vol_cnt < vol0) || (&vol0);
    vol1_en <= (vol_cnt < vol1) || (&vol1);
    vol2_en <= (vol_cnt < vol2) || (&vol2);
    vol3_en <= (vol_cnt < vol3) || (&vol3);
    if (vol0_en) dac0_cnt <= dac0_cnt[6:0] + dac0[6:0]; else dac0_cnt[7] <= 0;
    if (vol1_en) dac1_cnt <= dac1_cnt[6:0] + dac1[6:0]; else dac1_cnt[7] <= 0;
    if (vol2_en) dac2_cnt <= dac2_cnt[6:0] + dac2[6:0]; else dac2_cnt[7] <= 0;
    if (vol3_en) dac3_cnt <= dac3_cnt[6:0] + dac3[6:0]; else dac3_cnt[7] <= 0;
end


/* BUS CONTROLLER */
assign ard_n = ~ioreq_rd;
assign awr_n = ~ioreq_wr;
assign aa0 = zxa[1]? saa_a0 : ym_a0 ;
assign ad = ioreq_wr && (port_fffd || port_bffd || port_ff)? zxd : 8'bzzzzzzzz;

assign zxwait_n = 1'bz;
assign zxiorqge_n = (zxm1_n && (port_fffd_full || port_bffd || port_b3 || port_bb))? 1'b0 : 1'b1;
assign zxd =
    ioreq_rd && port_fffd? ad :
    ioreq_rd && port_b3? gs_reg_out :
    ioreq_rd && port_bb? gs_status :
    8'bzzzzzzzz;


endmodule
