/* This file is part of JT12.

 
    JT12 program 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 program 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_reg(
    input           rst,
    input           clk,
    input           clk_en /* synthesis direct_enable */,
    
    input   [2:0]   ch,     // channel to update
    input   [1:0]   op,
    
    input           csm,
    input           flag_A,
    input           overflow_A,

    // channel udpates
    input   [2:0]   ch_sel,
    input   [7:0]   ch_din,
    input           up_alg, 
    input           up_fnumlo,
    // operator updates
    input   [7:0]   din,
    input           up_keyon,
    input           up_pms,
    input           up_dt1,
    input           up_tl,
    input           up_ks_ar,
    input           up_amen_dr,
    input           up_sr,
    input           up_sl_rr,
    input           up_ssgeg,

    output reg       ch6op,  // 1 when the operator belongs to CH6
    output reg [2:0] cur_ch,
    output reg [1:0] cur_op,
    
    // CH3 Effect-mode operation
    input           effect,
    input   [10:0]  fnum_ch3op2,
    input   [10:0]  fnum_ch3op3, 
    input   [10:0]  fnum_ch3op1,
    input   [ 2:0]  block_ch3op2,
    input   [ 2:0]  block_ch3op3,
    input   [ 2:0]  block_ch3op1,
    input   [ 5:0]  latch_fnum,
    // Pipeline order
    output  reg     zero,
    output          s1_enters,
    output          s2_enters,
    output          s3_enters,
    output          s4_enters,
    
    // 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,
    // channel configuration
    output      [1:0]   rl,
    output reg  [2:0]   fb_II,
    output      [2:0]   alg_I,
    // Operator multiplying
    output      [ 3:0]  mul_II,
    // Operator detuning
    output      [ 2:0]  dt1_I,
    
    // EG
    output      [4:0]   ar_I,   // attack  rate
    output      [4:0]   d1r_I, // decay   rate
    output      [4:0]   d2r_I, // sustain rate
    output      [3:0]   rr_I,   // release rate
    output      [3:0]   sl_I,   // sustain level
    output      [1:0]   ks_II,     // key scale
    output              ssg_en_I,
    output      [2:0]   ssg_eg_I,
    output      [6:0]   tl_IV,
    output      [2:0]   pms_I,
    output      [1:0]   ams_IV,
    output              amsen_IV,

    // envelope operation
    output          keyon_I
);

parameter num_ch=6; // Use only 3 (YM2203/YM2610) or 6 (YM2612/YM2608)


reg  [1:0] next_op;
reg  [2:0] next_ch;
reg last;

`ifdef SIMULATION
// These signals need to operate during rst
// initial state is not relevant (or critical) in real life
// but we need a clear value during simulation
// This does not work with NCVERILOG
initial begin
    cur_op = 2'd0;
    cur_ch = 3'd0;
    next_op = 2'd0;
    next_ch = 3'd1;
    last    = 1'b0;
    zero    = 1'b1;
end
`endif

assign s1_enters = cur_op == 2'b00;
assign s3_enters = cur_op == 2'b01;
assign s2_enters = cur_op == 2'b10;
assign s4_enters = cur_op == 2'b11;

wire [4:0] next = { next_op, next_ch };
wire [4:0] cur  = {  cur_op,  cur_ch };

wire [2:0] fb_I;

always @(posedge clk) if( clk_en ) begin
    fb_II <= fb_I;
    ch6op <= next_ch==3'd6;
end 

// FNUM and BLOCK
wire    [10:0]  fnum_I_raw;
wire    [ 2:0]  block_I_raw;
wire    effect_on = effect && (cur_ch==3'd2);
wire    effect_on_s1 = effect_on && (cur_op == 2'd0 );
wire    effect_on_s3 = effect_on && (cur_op == 2'd1 );
wire    effect_on_s2 = effect_on && (cur_op == 2'd2 );
wire    noeffect     = ~|{effect_on_s1, effect_on_s3, effect_on_s2};
assign fnum_I = ( {11{effect_on_s1}} & fnum_ch3op1 ) |
                ( {11{effect_on_s2}} & fnum_ch3op2 ) |
                ( {11{effect_on_s3}} & fnum_ch3op3 ) |
                ( {11{noeffect}}     & fnum_I_raw  );

assign block_I =( {3{effect_on_s1}} & block_ch3op1 ) |
                ( {3{effect_on_s2}} & block_ch3op2 ) |
                ( {3{effect_on_s3}} & block_ch3op3 ) |
                ( {3{noeffect}}  & block_I_raw  );
                
wire [4:0] req_opch_I = { op, ch };
wire [4:0]  req_opch_II, req_opch_III, 
            req_opch_IV, req_opch_V; //, req_opch_VI;
                
jt12_sumch #(.num_ch(num_ch)) u_opch_II ( .chin(req_opch_I  ), .chout(req_opch_II ) );
jt12_sumch #(.num_ch(num_ch)) u_opch_III( .chin(req_opch_II ), .chout(req_opch_III) );
jt12_sumch #(.num_ch(num_ch)) u_opch_IV ( .chin(req_opch_III), .chout(req_opch_IV ) );
jt12_sumch #(.num_ch(num_ch)) u_opch_V  ( .chin(req_opch_IV ), .chout(req_opch_V  ) );
// jt12_sumch #(.num_ch(num_ch)) u_opch_VI ( .chin(req_opch_V  ), .chout(req_opch_VI)  );

wire update_op_I  = cur == req_opch_I;
wire update_op_II = cur == req_opch_II;
// wire update_op_III= cur == req_opch_III;
wire update_op_IV = cur == req_opch_IV;
// wire update_op_V  = cur == req_opch_V;
// wire update_op_VI = cur == opch_VI;
// wire [2:0] op_plus1 = op+2'd1;
// wire update_op_VII= cur == { op_plus1[1:0], ch };

// key on/off
wire    [3:0]   keyon_op = din[7:4];
wire    [2:0]   keyon_ch = din[2:0];

always @(*) begin
    // next = cur==5'd23 ? 5'd0 : cur +1'b1;
    if( num_ch==6 ) begin
        next_op = cur_ch==3'd6 ? cur_op+1'b1 : cur_op;
        next_ch = cur_ch[1:0]==2'b10 ? cur_ch+2'd2 : cur_ch+1'd1;
    end else begin // 3 channels
        next_op = cur_ch==3'd2 ? cur_op+1'b1 : cur_op;
        next_ch = cur_ch[1:0]==2'b10 ? 3'd0 : cur_ch+1'd1;      
    end
end

always @(posedge clk) begin : up_counter
    if( clk_en ) begin
        { cur_op, cur_ch }  <= { next_op, next_ch };
        zero    <= next == 5'd0;
    end
end

`ifndef NOFM
jt12_kon #(.num_ch(num_ch)) u_kon(
    .rst        ( rst       ),
    .clk        ( clk       ),
    .clk_en     ( clk_en    ),
    .keyon_op   ( keyon_op  ),
    .keyon_ch   ( keyon_ch  ),
    .next_op    ( next_op   ),
    .next_ch    ( next_ch   ),
    .up_keyon   ( up_keyon  ),
    .csm        ( csm       ),
    // .flag_A      ( flag_A    ),
    .overflow_A ( overflow_A),
    
    .keyon_I    ( keyon_I   )
);

jt12_mod #(.num_ch(num_ch)) u_mod(
    .alg_I      ( alg_I     ),
    .s1_enters  ( s1_enters ),
    .s3_enters  ( s3_enters ),
    .s2_enters  ( s2_enters ),
    .s4_enters  ( s4_enters ),
    
    .xuse_prevprev1 ( xuse_prevprev1  ),
    .xuse_internal  ( xuse_internal   ),
    .yuse_internal  ( yuse_internal   ),  
    .xuse_prev2     ( xuse_prev2      ),
    .yuse_prev1     ( yuse_prev1      ),
    .yuse_prev2     ( yuse_prev2      )
);

wire [43:0] shift_out;

generate
    if( num_ch==6 ) begin
        // YM2612 / YM3438: Two CSR.
        wire [43:0] shift_middle;

        jt12_csr u_csr0(
            .rst            ( rst           ),
            .clk            ( clk           ),
            .clk_en         ( clk_en        ),
            .din            ( din           ),
            .shift_in       ( shift_out     ),
            .shift_out      ( shift_middle  ),
            .up_tl          ( up_tl         ),     
            .up_dt1         ( up_dt1        ),    
            .up_ks_ar       ( up_ks_ar      ),  
            .up_amen_dr     ( up_amen_dr    ),
            .up_sr          ( up_sr         ),     
            .up_sl_rr       ( up_sl_rr      ),  
            .up_ssgeg       ( up_ssgeg      ),  
            .update_op_I    ( update_op_I   ),
            .update_op_II   ( update_op_II  ),
            .update_op_IV   ( update_op_IV  )
        );

        wire up_midop_I  = { ~cur[4], cur[3:0] } == req_opch_I;
        wire up_midop_II = { ~cur[4], cur[3:0] } == req_opch_II;
        wire up_midop_IV = { ~cur[4], cur[3:0] } == req_opch_IV;

        jt12_csr u_csr1(
            .rst            ( rst           ),
            .clk            ( clk           ),
            .clk_en         ( clk_en        ),
            .din            ( din           ),
            .shift_in       ( shift_middle  ),
            .shift_out      ( shift_out     ),
            .up_tl          ( up_tl         ),     
            .up_dt1         ( up_dt1        ),    
            .up_ks_ar       ( up_ks_ar      ),  
            .up_amen_dr     ( up_amen_dr    ),
            .up_sr          ( up_sr         ),     
            .up_sl_rr       ( up_sl_rr      ),  
            .up_ssgeg       ( up_ssgeg      ),  
            // update in the middle:
            .update_op_I    ( up_midop_I    ),
            .update_op_II   ( up_midop_II   ),
            .update_op_IV   ( up_midop_IV   )
        );
    end
    else begin // YM2203 only has one CSR
        jt12_csr u_csr0(
            .rst            ( rst           ),
            .clk            ( clk           ),
            .clk_en         ( clk_en        ),
            .din            ( din           ),
            .shift_in       ( shift_out     ),
            .shift_out      ( shift_out     ),
            .up_tl          ( up_tl         ),     
            .up_dt1         ( up_dt1        ),    
            .up_ks_ar       ( up_ks_ar      ),  
            .up_amen_dr     ( up_amen_dr    ),
            .up_sr          ( up_sr         ),     
            .up_sl_rr       ( up_sl_rr      ),  
            .up_ssgeg       ( up_ssgeg      ),  
            .update_op_I    ( update_op_I   ),
            .update_op_II   ( update_op_II  ),
            .update_op_IV   ( update_op_IV  )
        );
    end // else
endgenerate

assign { tl_IV,   dt1_I,    mul_II,    ks_II, 
         ar_I,    amsen_IV, d1r_I,     d2r_I, 
         sl_I,    rr_I,     ssg_en_I,  ssg_eg_I } = shift_out;


// memory for CH registers
jt12_reg_ch #(.NUM_CH(num_ch)) u_regch(
    .rst        ( rst           ),
    .clk        ( clk           ),
    .cen        ( clk_en        ),
    .din        ( ch_din        ),

    .up_ch      ( ch_sel        ),
    .latch_fnum ( latch_fnum    ),
    .up_fnumlo  ( up_fnumlo     ),
    .up_alg     ( up_alg        ),
    .up_pms     ( up_pms        ),

    .ch         ( next_ch       ), // next active channel
    .block      ( block_I_raw   ),
    .fnum       ( fnum_I_raw    ),
    .fb         ( fb_I          ),
    .alg        ( alg_I         ),
    .rl         ( rl            ),
    .ams_IV     ( ams_IV        ),
    .pms        ( pms_I         )
);

`endif
endmodule
