module snd_vrc6(
input  M2,
input  RW,
input  [7:0]DB,
input  [15:0]AB,
input  RES,
output [5:0]snd_vol
);

assign snd_vol[5:0] = pgen_out1[3:0] + pgen_out2[3:0] + sw_out[4:0];
reg [2:0]freq_ctrl;
reg [7:0]pulse1;
reg [11:0]freq1;
reg chan_on1;
reg [7:0]pulse2;
reg [11:0]freq2;
reg chan_on2;
reg [5:0]sw_arate;
reg [11:0]sw_freq;
reg sw_chan_on;
always @(negedge M2) begin
        if(RES) {freq_ctrl[2:0], chan_on1, chan_on2, sw_chan_on} <= 6'h00;
        else if(~RW) begin
        case(AB)
         16'h9000: pulse1[7:0]    <= DB[7:0];
         16'h9001: freq1[7:0]     <= DB[7:0];
         16'h9002: {chan_on1, freq1[11:8]} <= {DB[7], DB[3:0]};
         16'h9003: freq_ctrl[2:0] <= DB[2:0];
         16'hA000: pulse2[7:0]    <= DB[7:0];
         16'hA001: freq2[7:0]     <= DB[7:0];
         16'hA002: {chan_on2, freq2[11:8]} <= {DB[7], DB[3:0]};
         16'hB000: sw_arate[5:0]  <= DB[5:0];
         16'hB001: sw_freq[7:0]   <= DB[7:0];
         16'hB002: {sw_chan_on, sw_freq[11:8]} <= {DB[7], DB[3:0]};
        endcase
                      end
                       end

wire [3:0]freq_rs;
assign freq_rs[3:0] = freq_ctrl[2] ? 4'h8 : freq_ctrl[1] ? 4'h4 : 4'h0;
wire [3:0]pgen_out1, pgen_out2;
wire [4:0]sw_out;

pulse_gen pulse_gen1(
.M2(M2),
.chan_on(chan_on1),
.pulse(pulse1),
.freq(freq1 >> freq_rs),
.halt(freq_ctrl[0]),
.snd(pgen_out1)
);

pulse_gen pulse_gen2(
.M2(M2),
.chan_on(chan_on2),
.pulse(pulse2),
.freq(freq2 >> freq_rs),
.halt(freq_ctrl[0]),
.snd(pgen_out2)
);

sawtooth sawtooth(
.M2(M2),
.chan_on(sw_chan_on),
.arate(sw_arate),
.freq(sw_freq >> freq_rs),
.halt(freq_ctrl[0]),
.snd(sw_out)
);
endmodule

module pulse_gen(
input  M2,
input  chan_on,
input  [7:0]pulse,
input  [11:0]freq,
input  halt,
output [3:0]snd
);
assign snd[3:0] = ~chan_on | ~chan_state ? 4'h0 : vol[3:0];
wire [3:0]vol;
wire [2:0]duty;
wire mode;
assign { mode, duty[2:0], vol[3:0] } = pulse[7:0];
reg [11:0]freq_ctr;
reg [3:0]duty_ctr;
reg chan_state;

always @(negedge M2) begin
                if(~halt) begin
                if(freq_ctr == 12'h000) begin
                             freq_ctr[11:0] <= freq[11:0];
                             duty_ctr[3:0]  <= duty_ctr[3:0] + 4'h1;
                                         end
                else freq_ctr[11:0] <= freq_ctr[11:0] - 12'h001;
                chan_state  <= mode ? 1'b1 : duty_ctr[3:0] > duty[2:0];
                          end
                     end
endmodule

module sawtooth(
input  M2,
input  chan_on,
input  [5:0]arate,
input  [11:0]freq,
input  halt,
output [4:0]snd
);

assign snd = ~chan_on ? 5'h00 : accum[7:3];
reg [2:0]ctr;
reg [7:0]accum;
reg [11:0]freq_ctr;
reg strobe;
always @(negedge M2) begin
      if(~halt) begin
      if(freq_ctr == 12'h000) begin
      freq_ctr[11:0] <= freq[11:0];
      strobe         <= ~strobe;
           if(strobe) begin
                      ctr[2:0]   <= ctr[2:0] == 3'h6 ? 3'h0  : ctr[2:0] + 3'h1;
                      accum[7:0] <= ctr[2:0] == 3'h0 ? 8'h00 : accum[7:0] + arate[5:0];
                      end
                               end
      else freq_ctr[11:0] <= freq_ctr[11:0] - 12'h001;
                 end
                      end
endmodule
