/*  This file is part of JT12.

    JT12 is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    JT12 is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with JT12.  If not, see <http://www.gnu.org/licenses/>.

    Author: Jose Tejada Gomez. Twitter: @topapate
    Version: 1.0
    Date: 14-2-2017
    */


module jt12_mmr(
    input           rst,
    input           clk,
    input           cen /* synthesis direct_enable */,
    output          clk_en,
    output          clk_en_2,
    output          clk_en_ssg,
    output          clk_en_666,
    output          clk_en_111,
    output          clk_en_55,
    input   [7:0]   din,
    input           write,
    input   [1:0]   addr,
    output  reg     busy,
    output          ch6op,
    output  [2:0]   cur_ch,
    output  [1:0]   cur_op,
    // LFO
    output  reg [2:0]   lfo_freq,
    output  reg         lfo_en,
    // Timers
    output  reg [9:0]   value_A,
    output  reg [7:0]   value_B,
    output  reg         load_A,
    output  reg         load_B,
    output  reg         enable_irq_A,
    output  reg         enable_irq_B,
    output  reg         clr_flag_A,
    output  reg         clr_flag_B,
    output  reg         fast_timers,
    input               flag_A,
    input               overflow_A, 
    output  reg [1:0]   div_setting,
    // PCM
    output  reg [8:0]   pcm,
    output  reg         pcm_en,
    output  reg         pcm_wr, // high for one clock cycle when PCM is written
    // ADPCM-A
    output  reg  [ 7:0] aon_a,      // ON
    output  reg  [ 5:0] atl_a,      // TL
    output  reg  [15:0] addr_a,     // address latch
    output  reg  [ 7:0] lracl,      // L/R ADPCM Channel Level
    output  reg         up_start,   // write enable start address latch
    output  reg         up_end,     // write enable end address latch
    output  reg  [ 2:0] up_addr,    // write enable end address latch
    output  reg  [ 2:0] up_lracl,
    output  reg         up_aon,     // There was a write AON register
    // ADPCM-B
    output  reg         acmd_on_b,  // Control - Process start, Key On
    output  reg         acmd_rep_b, // Control - Repeat
    output  reg         acmd_rst_b, // Control - Reset
    output  reg         acmd_up_b,  // Control - New cmd received
    output  reg  [ 1:0] alr_b,      // Left / Right
    output  reg  [15:0] astart_b,   // Start address
    output  reg  [15:0] aend_b,     // End   address
    output  reg  [15:0] adeltan_b,  // Delta-N
    output  reg  [ 7:0] aeg_b,      // Envelope Generator Control
    output  reg  [ 6:0] flag_ctl,
    output  reg  [ 6:0] flag_mask,
    // Operator
    output          xuse_prevprev1,
    output          xuse_internal,
    output          yuse_internal, 
    output          xuse_prev2,
    output          yuse_prev1,
    output          yuse_prev2,
    // PG
    output  [10:0]  fnum_I,
    output  [ 2:0]  block_I,
    output  reg     pg_stop,
    // REG
    output  [ 1:0]  rl,
    output  [ 2:0]  fb_II,
    output  [ 2:0]  alg_I,
    output  [ 2:0]  pms_I,
    output  [ 1:0]  ams_IV,
    output          amsen_IV,
    output  [ 2:0]  dt1_I,
    output  [ 3:0]  mul_II,
    output  [ 6:0]  tl_IV,
    output  reg     eg_stop,

    output  [ 4:0]  ar_I,
    output  [ 4:0]  d1r_I,
    output  [ 4:0]  d2r_I,
    output  [ 3:0]  rr_I,
    output  [ 3:0]  sl_I,
    output  [ 1:0]  ks_II,
    // SSG operation
    output          ssg_en_I,
    output  [2:0]   ssg_eg_I,

    output          keyon_I,

    // Operator
    output          zero,
    output          s1_enters,
    output          s2_enters,
    output          s3_enters,
    output          s4_enters,

    // PSG interace
    output  [3:0]   psg_addr,
    output  [7:0]   psg_data,
    output  reg     psg_wr_n,
    input   [7:0]   debug_bus
);

parameter use_ssg=0, num_ch=6, use_pcm=1, use_adpcm=0, mask_div=1;

jt12_div #(.use_ssg(use_ssg)) u_div (
    .rst            ( rst             ),
    .clk            ( clk             ),
    .cen            ( cen             ),
    .div_setting    ( div_setting     ),
    .clk_en         ( clk_en          ),
    .clk_en_2       ( clk_en_2        ),
    .clk_en_ssg     ( clk_en_ssg      ),
    .clk_en_666     ( clk_en_666      ),
    .clk_en_111     ( clk_en_111      ),
    .clk_en_55      ( clk_en_55       )
);

reg [7:0]   selected_register;

/*
reg     irq_zero_en, irq_brdy_en, irq_eos_en,
        irq_tb_en, irq_ta_en;
        */
reg [6:0] up_opreg; // hot-one encoding. tells which operator register gets updated next
reg [2:0] up_chreg; // hot-one encoding. tells which channel register gets updated next
reg up_keyon;

localparam  REG_TESTYM  =   8'h21,
            REG_LFO     =   8'h22,
            REG_CLKA1   =   8'h24,
            REG_CLKA2   =   8'h25,
            REG_CLKB    =   8'h26,
            REG_TIMER   =   8'h27,
            REG_KON     =   8'h28,
            REG_IRQMASK =   8'h29,
            REG_PCM     =   8'h2A,
            REG_PCM_EN  =   8'h2B,
            REG_DACTEST =   8'h2C,
            REG_CLK_N6  =   8'h2D,
            REG_CLK_N3  =   8'h2E,
            REG_CLK_N2  =   8'h2F,
            // ADPCM (YM2610)
            REG_ADPCMA_ON   = 8'h00,
            REG_ADPCMA_TL   = 8'h01,
            REG_ADPCMA_TEST = 8'h02;

reg csm, effect;

reg [ 2:0] block_ch3op2,  block_ch3op3,  block_ch3op1;
reg [10:0] fnum_ch3op2, fnum_ch3op3, fnum_ch3op1;
reg [ 5:0] latch_fnum;


reg [2:0] up_ch;
reg [1:0] up_op;

reg [7:0] op_din, ch_din;

generate
    if( use_ssg ) begin
        assign psg_addr = selected_register[3:0];
        assign psg_data = ch_din;
    end else begin
        assign psg_addr = 4'd0;
        assign psg_data = 8'd0;
    end
endgenerate

reg part;

`ifdef SIMULATION
always @(posedge clk) if( write && rst ) begin
    $display("WARNING [JT12]: detected write request while in reset.\nThis is likely a glue-logic error in the CPU-FM module.");
    $finish;
end
`endif

wire [2:0] ch_sel = {part, selected_register[1:0]};

// this runs at clk speed, no clock gating here
// if I try to make this an async rst it fails to map it
// as flip flops but uses latches instead. So I keep it as sync. reset
always @(posedge clk) begin : memory_mapped_registers
    if( rst ) begin
        selected_register   <= 0;
        div_setting         <= 2'b10; // FM=1/6, SSG=1/4
        up_ch               <= 0;
        up_op               <= 0;
        up_keyon            <= 0;
        up_opreg            <= 0;
        up_chreg            <= 0;
        // IRQ Mask
        /*{ irq_zero_en, irq_brdy_en, irq_eos_en,
            irq_tb_en, irq_ta_en } = 5'h1f; */
        // timers
        { value_A, value_B } <= 0;
        { clr_flag_B, clr_flag_A,
        enable_irq_B, enable_irq_A, load_B, load_A } <= 0;
        fast_timers <= 0;
        // LFO
        lfo_freq    <= 0;
        lfo_en      <= 0;
        csm         <= 0;
        effect      <= 0;
        // PCM
        pcm         <= 0;
        pcm_en      <= 0;
        pcm_wr      <= 0;
        // ADPCM-A
        aon_a       <= 0;
        atl_a       <= 0;
        up_start    <= 0;
        up_end      <= 0;
        up_addr     <= 7;
        up_lracl    <= 7;
        up_aon      <= 0;
        lracl       <= 0;
        addr_a      <= 0;
        // ADPCM-B
        acmd_on_b   <= 0;
        acmd_rep_b  <= 0;
        acmd_rst_b  <= 0;
        alr_b       <= 0;
        flag_ctl    <= 0;
        astart_b    <= 0;
        aend_b      <= 0;
        adeltan_b   <= 0;
        flag_mask   <= 0;
        aeg_b       <= 8'hff;
        // Original test features
        eg_stop     <= 0;
        pg_stop     <= 0;
        psg_wr_n    <= 1;
        // 
        { block_ch3op1, fnum_ch3op1 } <= {3'd0, 11'd0 };
        { block_ch3op3, fnum_ch3op3 } <= {3'd0, 11'd0 };
        { block_ch3op2, fnum_ch3op2 } <= {3'd0, 11'd0 };
        latch_fnum <= 0;
        op_din     <= 0;
        part       <= 0;
    end else begin
        up_chreg   <= 0;
        // WRITE IN REGISTERS
        if( write ) begin
            if( !addr[0] ) begin
                selected_register <= din;  
                part <= addr[1];        
                if (!mask_div)
                case(din)
                    // clock divider: should work only for ym2203
                    // and ym2608.
                    // clock divider works just by selecting the register
                    REG_CLK_N6: div_setting[1] <= 1'b1; // 2D
                    REG_CLK_N3: div_setting[0] <= 1'b1; // 2E
                    REG_CLK_N2: div_setting    <= 2'b0; // 2F
                    default:;
                endcase
            end else begin
                // Global registers
                ch_din <= din;
                if( selected_register == REG_KON && !part ) begin
                    up_keyon <= 1;
                    op_din   <= din;
                end else begin
                    up_keyon <= 0;
                end
                // General control (<0x20 registers and A0==0)
                if(!part) begin
                    casez( selected_register)
                        //REG_TEST: lfo_rst <= 1'b1; // regardless of din
                        8'h0?: psg_wr_n <= 1'b0;
                        REG_TESTYM: begin
                            eg_stop <= din[5];
                            pg_stop <= din[3];
                            fast_timers <= din[2];
                            end
                        REG_CLKA1:  value_A[9:2]<= din;
                        REG_CLKA2:  value_A[1:0]<= din[1:0];
                        REG_CLKB:   value_B     <= din;
                        REG_TIMER: begin
                            effect  <= |din[7:6];
                            csm     <= din[7:6] == 2'b10;
                            { clr_flag_B, clr_flag_A,
                              enable_irq_B, enable_irq_A,
                              load_B, load_A } <= din[5:0];
                            end
                        `ifndef NOLFO                   
                        REG_LFO:    { lfo_en, lfo_freq } <= din[3:0];
                        `endif
                        default:;
                    endcase
                end

                // CH3 special registers
                casez( selected_register)
                    8'hA9: { block_ch3op1, fnum_ch3op1 } <= { latch_fnum, din };
                    8'hA8: { block_ch3op3, fnum_ch3op3 } <= { latch_fnum, din };
                    8'hAA: { block_ch3op2, fnum_ch3op2 } <= { latch_fnum, din };
                    // According to http://www.mjsstuf.x10host.com/pages/vgmPlay/vgmPlay.htm
                    // There is a single fnum latch for all channels
                    8'hA4, 8'hA5, 8'hA6, 8'hAD, 8'hAC, 8'hAE: latch_fnum <= din[5:0];
                    default:;   // avoid incomplete-case warning
                endcase

                // YM2612 PCM support
                if( use_pcm==1 ) begin
                    casez( selected_register)
                        REG_DACTEST: pcm[0] <= din[3];
                        REG_PCM:
                            pcm <= { ~din[7], din[6:0], 1'b1 };
                        REG_PCM_EN:  pcm_en  <= din[7];
                        default:;
                    endcase
                    pcm_wr <= selected_register==REG_PCM;
                end
                if( use_adpcm==1 ) begin
                    // YM2610 ADPCM-A support, A1=1, regs 0-2D
                    if(part && selected_register[7:6]==2'b0) begin
                        casez( selected_register[5:0] )
                            6'h0: begin
                                aon_a  <= din;
                                up_aon <= 1'b1;
                            end
                            6'h1: atl_a <= din[5:0];
                            // LRACL
                            6'h8, 6'h9, 6'hA, 6'hB, 6'hC, 6'hD: begin
                                lracl <= din;
                                up_lracl <= selected_register[2:0];
                            end
                            6'b01_????, 6'b10_????: begin
                                if( !selected_register[3] ) addr_a[ 7:0] <= din;
                                if( selected_register[3]  ) addr_a[15:8] <= din;
                                case( selected_register[5:4] )
                                    2'b01, 2'b10: begin
                                        {up_end, up_start } <= selected_register[5:4];
                                        up_addr <= selected_register[2:0];
                                    end
                                    default: begin
                                        up_start <= 1'b0;
                                        up_end   <= 1'b0;
                                    end
                                endcase
                            end
                            default:;
                        endcase
                    end
                    if( !part && selected_register[7:4]==4'h1 ) begin
                        // YM2610 ADPCM-B support, A1=0, regs 1x
                        case(selected_register[3:0])
                            4'd0: {acmd_up_b, acmd_on_b, acmd_rep_b,acmd_rst_b} <= {1'd1,din[7],din[4],din[0]};
                            4'd1: alr_b  <= din[7:6];
                            4'd2: astart_b [ 7:0] <= din;
                            4'd3: astart_b [15:8] <= din;
                            4'd4: aend_b   [ 7:0] <= din;
                            4'd5: aend_b   [15:8] <= din;
                            4'h9: adeltan_b[ 7:0] <= din;
                            4'ha: adeltan_b[15:8] <= din;
                            4'hb: aeg_b           <= din;
                            4'hc: begin
                               flag_mask   <= ~{din[7],din[5:0]};
                               flag_ctl    <= {din[7],din[5:0]}; // this lasts a single clock cycle
                           end
                            default:;
                        endcase
                    end
                end
                if( selected_register[1:0]==2'b11 ) 
                    { up_chreg, up_opreg } <= { 3'h0, 7'h0 };
                else begin
                    casez( selected_register )
                        // channel registers
                        8'hA0, 8'hA1, 8'hA2:    { up_chreg, up_opreg } <= { 3'h1, 7'd0 }; // up_fnumlo
                        // FB + Algorithm
                        8'hB0, 8'hB1, 8'hB2: { up_chreg, up_opreg } <= { 3'h2, 7'd0 }; // up_alg
                        8'hB4, 8'hB5, 8'hB6: { up_chreg, up_opreg } <= { 3'h4, 7'd0 }; // up_pms
                        // operator registers
                        8'h3?: { up_chreg, up_opreg } <= { 3'h0, 7'h01 }; // up_dt1
                        8'h4?: { up_chreg, up_opreg } <= { 3'h0, 7'h02 }; // up_tl
                        8'h5?: { up_chreg, up_opreg } <= { 3'h0, 7'h04 }; // up_ks_ar
                        8'h6?: { up_chreg, up_opreg } <= { 3'h0, 7'h08 }; // up_amen_dr
                        8'h7?: { up_chreg, up_opreg } <= { 3'h0, 7'h10 }; // up_sr
                        8'h8?: { up_chreg, up_opreg } <= { 3'h0, 7'h20 }; // up_sl
                        8'h9?: { up_chreg, up_opreg } <= { 3'h0, 7'h40 }; // up_ssgeg
                        default: { up_chreg, up_opreg } <= { 3'h0, 7'h0 };
                    endcase // selected_register
                    if( selected_register[7:4]>=3 && selected_register[7:4]<=9 ) begin
                        op_din <= din;
                        up_ch <= {part, selected_register[1:0]};
                        up_op <= selected_register[3:2]; // 0=S1,1=S3,2=S2,3=S4
                    end
                end
            end
        end
        else if(clk_en) begin /* clear once-only bits */
            // lfo_rst <= 1'b0;
            { clr_flag_B, clr_flag_A } <= 2'd0;
            psg_wr_n <= 1'b1;
            pcm_wr   <= 1'b0;
            flag_ctl <= 'd0;
            up_aon   <= 1'b0;
            acmd_up_b <= 1'b0;
        end
    end
end

reg  [4:0] busy_cnt; // busy lasts for 32 synthesizer clock cycles
wire [5:0] nx_busy = {1'd0,busy_cnt}+{5'd0,busy};

always @(posedge clk, posedge rst) begin
    if( rst ) begin
        busy     <= 0;
        busy_cnt <= 0;
    end else begin
        if( write&addr[0] ) begin
            busy     <= 1;
            busy_cnt <= 0;
        end else if(clk_en) begin
            busy     <= ~nx_busy[5] & busy;
            busy_cnt <=  nx_busy[4:0];
        end
    end
end

jt12_reg #(.num_ch(num_ch)) u_reg(
    .rst        ( rst       ),
    .clk        ( clk       ),      // P1
    .clk_en     ( clk_en    ),

    // channel udpates
    .ch_sel     ( ch_sel        ),
    .ch_din     ( ch_din        ),
    .up_fnumlo  ( up_chreg[0]   ),
    .up_alg     ( up_chreg[1]   ),
    .up_pms     ( up_chreg[2]   ),

    // operator updates
    .din        ( op_din        ),
    .up_keyon   ( up_keyon      ),
    .up_dt1     ( up_opreg[0]   ),
    .up_tl      ( up_opreg[1]   ),
    .up_ks_ar   ( up_opreg[2]   ),
    .up_amen_dr ( up_opreg[3]   ),
    .up_sr      ( up_opreg[4]   ),
    .up_sl_rr   ( up_opreg[5]   ),
    .up_ssgeg   ( up_opreg[6]   ),

    .op         ( up_op     ),      // operator to update
    .ch         ( up_ch     ),      // channel to update

    .csm        ( csm       ),
    .flag_A     ( flag_A    ),
    .overflow_A ( overflow_A),

    .ch6op      ( ch6op     ),
    .cur_ch     ( cur_ch    ),
    .cur_op     ( cur_op    ),
    // CH3 Effect-mode operation
    .effect     ( effect    ),      // allows independent freq. for CH 3
    .fnum_ch3op2( fnum_ch3op2 ),
    .fnum_ch3op3( fnum_ch3op3 ),
    .fnum_ch3op1( fnum_ch3op1 ),
    .block_ch3op2( block_ch3op2 ),
    .block_ch3op3( block_ch3op3 ),
    .block_ch3op1( block_ch3op1 ),
    .latch_fnum ( latch_fnum    ),
    // Operator
    .xuse_prevprev1 ( xuse_prevprev1  ),
    .xuse_internal  ( xuse_internal   ),
    .yuse_internal  ( yuse_internal   ),  
    .xuse_prev2     ( xuse_prev2      ),
    .yuse_prev1     ( yuse_prev1      ),
    .yuse_prev2     ( yuse_prev2      ),
    // PG
    .fnum_I     (   fnum_I  ),
    .block_I    (   block_I ),
    .mul_II     (   mul_II  ),
    .dt1_I      (   dt1_I   ),

    // EG
    .ar_I       (ar_I       ), // attack  rate
    .d1r_I      (d1r_I      ), // decay   rate
    .d2r_I      (d2r_I      ), // sustain rate
    .rr_I       (rr_I       ), // release rate
    .sl_I       (sl_I       ), // sustain level
    .ks_II      (ks_II      ), // key scale
    // SSG operation
    .ssg_en_I   ( ssg_en_I  ),
    .ssg_eg_I   ( ssg_eg_I  ),
    // envelope number
    .tl_IV      (tl_IV      ),
    .pms_I      (pms_I      ),
    .ams_IV     (ams_IV     ),
    .amsen_IV   (amsen_IV   ),
    // channel configuration
    .rl         ( rl        ),
    .fb_II      ( fb_II     ),
    .alg_I      ( alg_I     ),
    .keyon_I    ( keyon_I   ),

    .zero       ( zero      ),
    .s1_enters  ( s1_enters ),
    .s2_enters  ( s2_enters ),
    .s3_enters  ( s3_enters ),
    .s4_enters  ( s4_enters )
);

endmodule
