diff --git a/gbc_snd.vhd b/gbc_snd.vhd index 1a00f63..0bca6c1 100644 --- a/gbc_snd.vhd +++ b/gbc_snd.vhd @@ -53,7 +53,9 @@ architecture SYN of gbc_snd is signal sq1_svol : std_logic_vector(3 downto 0); -- Sq1 initial volume signal sq1_envsgn : std_logic; -- Sq1 envelope sign + signal sq1_envsgn_old: std_logic; -- Sq1 old envelope sign (used in zombie mode) signal sq1_envper : std_logic_vector(2 downto 0); -- Sq1 envelope period + signal sq1_envper_old: std_logic_vector(2 downto 0); -- Sq1 old envelope period (used in zombie mode) signal sq1_freq : std_logic_vector(10 downto 0); -- Sq1 frequency signal sq1_trigger : std_logic; -- Sq1 trigger play note signal sq1_lenchk : std_logic; -- Sq1 length check enable @@ -61,7 +63,7 @@ architecture SYN of gbc_snd is signal sq1_fr2 : std_logic_vector(10 downto 0); -- Sq1 frequency (shadow copy) signal sq1_vol : std_logic_vector(3 downto 0); -- Sq1 initial volume - signal sq1_volchange : std_logic; + signal sq1_nr2change : std_logic; signal sq1_lenchange : std_logic; signal sq1_lenquirk : std_logic; signal sq1_freqchange : std_logic; @@ -72,11 +74,13 @@ architecture SYN of gbc_snd is signal sq2_duty : std_logic_vector(1 downto 0); -- Sq2 duty cycle signal sq2_slen : std_logic_vector(6 downto 0); -- Sq2 play length signal sq2_svol : std_logic_vector(3 downto 0); -- Sq2 initial volume - signal sq2_volchange : std_logic; + signal sq2_nr2change : std_logic; signal sq2_lenchange : std_logic; signal sq2_lenquirk : std_logic; signal sq2_envsgn : std_logic; -- Sq2 envelope sign signal sq2_envper : std_logic_vector(2 downto 0); -- Sq2 envelope period + signal sq2_envsgn_old: std_logic; -- Sq2 old envelope sign (used in zombie mode) + signal sq2_envper_old: std_logic_vector(2 downto 0); -- Sq2 old envelope period (used in zombie mode) signal sq2_freq : std_logic_vector(10 downto 0); -- Sq2 frequency signal sq2_trigger : std_logic; -- Sq2 trigger play note signal sq2_lenchk : std_logic; -- Sq2 length check enable @@ -102,16 +106,18 @@ architecture SYN of gbc_snd is signal noi_lenchange : std_logic; signal noi_lenquirk : std_logic; signal noi_svol : std_logic_vector(3 downto 0); - signal noi_volchange : std_logic; + signal noi_nr2change : std_logic; signal noi_envsgn : std_logic; signal noi_envper : std_logic_vector(2 downto 0); + signal noi_envsgn_old: std_logic; -- noi old envelope sign (used in zombie mode) + signal noi_envper_old: std_logic_vector(2 downto 0); -- noi old envelope period (used in zombie mode) signal noi_freqsh : std_logic_vector(3 downto 0); + signal noi_freqchange: std_logic; signal noi_short : std_logic; signal noi_div : std_logic_vector(2 downto 0); signal noi_trigger : std_logic; signal noi_lenchk : std_logic; - signal noi_fr2 : std_logic_vector(10 downto 0); -- Noise frequency (shadow copy) signal noi_vol : std_logic_vector(3 downto 0); -- Noise initial volume signal noi_playing : std_logic; -- Noise channel active signal noi_wav : std_logic_vector(5 downto 0); -- Noise output waveform @@ -245,9 +251,10 @@ begin noi_trigger <= '0'; end if; - sq2_volchange <= '0'; - sq1_volchange <= '0'; - noi_volchange <= '0'; + sq2_nr2change <= '0'; + sq1_nr2change <= '0'; + noi_nr2change <= '0'; + noi_freqchange<= '0'; sq2_lenchange <= '0'; sq1_lenchange <= '0'; @@ -275,12 +282,15 @@ begin sq1_swshift <= s1_writedata(2 downto 0); when "010001" => -- NR11 FF11 DDLL LLLL Duty, Length load (64-L) sq1_duty <= s1_writedata(7 downto 6); - -- sq1_slen <= s1_writedata(5 downto 0); sq1_slen <= std_logic_vector("1000000" - unsigned(s1_writedata(5 downto 0))); sq1_lenchange <= '1'; when "010010" => -- NR12 FF12 VVVV APPP Starting volume, Envelope add mode, period + -- zombie mode copy old values + sq1_envsgn_old <= sq1_envsgn; + sq1_envper_old <= sq1_envper; + -- write to registers + sq1_nr2change <= '1'; sq1_svol <= s1_writedata(7 downto 4); - sq1_volchange <= '1'; sq1_envsgn <= s1_writedata(3); sq1_envper <= s1_writedata(2 downto 0); when "010011" => -- NR13 FF13 FFFF FFFF Frequency LSB @@ -296,12 +306,15 @@ begin -- Square 2 when "010110" => -- NR21 FF16 DDLL LLLL Duty, Length load (64-L) sq2_duty <= s1_writedata(7 downto 6); - -- sq2_slen <= s1_writedata(5 downto 0); sq2_slen <= std_logic_vector("1000000" - unsigned(s1_writedata(5 downto 0))); sq2_lenchange <= '1'; when "010111" => -- NR22 FF17 VVVV APPP Starting volume, Envelope add mode, period + -- zombie mode copy old values + sq2_envsgn_old <= sq2_envsgn; + sq2_envper_old <= sq2_envper; + -- write to registers sq2_svol <= s1_writedata(7 downto 4); - sq2_volchange <= '1'; + sq2_nr2change <= '1'; sq2_envsgn <= s1_writedata(3); sq2_envper <= s1_writedata(2 downto 0); when "011000" => -- NR23 FF18 FFFF FFFF Frequency LSB @@ -338,14 +351,19 @@ begin noi_slen <= std_logic_vector("1000000" - unsigned(s1_writedata(5 downto 0))); noi_lenchange <= '1'; when "100001" => -- NR42 FF21 VVVV APPP Starting volume, Envelope add mode, period + -- zombie mode copy old values + noi_envsgn_old <= noi_envsgn; + noi_envper_old <= noi_envper; + -- write to registers noi_svol <= s1_writedata(7 downto 4); - noi_volchange <= '1'; + noi_nr2change <= '1'; noi_envsgn <= s1_writedata(3); noi_envper <= s1_writedata(2 downto 0); when "100010" => -- NR43 FF22 SSSS WDDD Clock shift, Width mode of LFSR, Divisor code noi_freqsh <= s1_writedata(7 downto 4); noi_short <= s1_writedata(3); noi_div <= s1_writedata(2 downto 0); + noi_freqchange <= '1'; when "100011" => -- NR44 FF23 TL-- ---- Trigger, Length enable noi_trigger <= s1_writedata(7); if noi_lenchk = '0' and s1_writedata(6) = '1' and en_len_r then @@ -541,11 +559,13 @@ begin variable sweep_calculate: boolean; variable sweep_update: boolean; variable sweep_negate: boolean; + variable sq1_envoff : boolean; -- check if envelope is on (zombiemode) variable sq2_fcnt : unsigned(10 downto 0); variable sq2_phase : integer range 0 to 7; variable sq2_len : std_logic_vector(6 downto 0); variable sq2_envcnt : std_logic_vector(3 downto 0); -- Sq2 envelope timer count + variable sq2_envoff : boolean; -- check if envelope is on (zombiemode) variable sq2_out : std_logic; variable wav_fcnt : unsigned(10 downto 0); @@ -555,13 +575,16 @@ begin variable wav_index : unsigned(4 downto 0); variable noi_divisor : unsigned(10 downto 0); -- Noise frequency divisor - variable noi_freq : unsigned(10 downto 0); -- Noise frequency (calculated) + variable noi_period : unsigned(10 downto 0); -- Noise period (calculated) variable noi_fcnt : unsigned(10 downto 0); variable noi_lfsr : unsigned(14 downto 0); -- 15 bits variable noi_len : std_logic_vector(6 downto 0); variable noi_envcnt : std_logic_vector(3 downto 0); -- Noise envelope timer count + variable noi_envoff : boolean; -- check if envelope is on (zombiemode) variable noi_out : std_logic; variable noi_xor : std_logic; + + variable tmp_volume : unsigned(7 downto 0); -- used in zombie mode variable acc_fcnt : unsigned(11 downto 0); begin @@ -578,9 +601,9 @@ begin sq1_swoffs := (others => '0'); sq1_swfr := (others => '0'); sq1_out := '0'; + sq2_playing <= '0'; - -- sq2_fr2 <= (others => '0'); sq2_fcnt := (others => '0'); sq2_phase := 0; sq2_len := (others => '0'); @@ -595,7 +618,6 @@ begin wav_index := (others => '0'); noi_playing <= '0'; - noi_fr2 <= (others => '0'); noi_fcnt := (others => '0'); noi_lfsr := (others => '1'); noi_len := (others => '0'); @@ -606,10 +628,19 @@ begin sweep_update := false; sq1_sweep_en := false; sweep_negate := false; + + -- zombie mode check if env is still updating + sq2_envoff := false; + sq1_envoff := false; + noi_envoff := false; elsif rising_edge(clk) then + + + ----------------------- Square channel 1 ----------------------------- + if en_snd4 then - -- Sq1 frequency timer + -- Sq1 frequency timer Frequency = 131072/(2048-x) Hz if sq1_playing = '1' then acc_fcnt := ('0'&sq1_fcnt) + to_unsigned(1, acc_fcnt'length); if acc_fcnt(acc_fcnt'high) = '1' then @@ -623,41 +654,7 @@ begin sq1_fcnt := acc_fcnt(sq1_fcnt'range); end if; end if; - - -- Sq2 frequency timer - if sq2_playing = '1' then - acc_fcnt := ('0'&sq2_fcnt) + to_unsigned(1, acc_fcnt'length); - if acc_fcnt(acc_fcnt'high) = '1' then - if sq2_phase < 7 then - sq2_phase := sq2_phase + 1; - else - sq2_phase := 0; - end if; - sq2_fcnt := unsigned(sq2_freq); - else - sq2_fcnt := acc_fcnt(sq2_fcnt'range); - end if; - end if; - - -- Noi frequency timer - if noi_playing = '1' then - acc_fcnt := ('0'&noi_fcnt) + to_unsigned(1, acc_fcnt'length); - if acc_fcnt(acc_fcnt'high) = '1' then - -- Noise LFSR - noi_xor := noi_lfsr(0) xor noi_lfsr(1); - noi_lfsr := noi_xor & noi_lfsr(14 downto 1); - - if noi_short = '1' then - noi_lfsr(6) := noi_xor; - end if; - - noi_out := not noi_lfsr(0); - noi_fcnt := unsigned(noi_fr2); - else - noi_fcnt := acc_fcnt(noi_fcnt'range); - end if; - end if; - + case sq1_duty is when "00" => sq1_out := duty_0(sq1_phase); when "01" => sq1_out := duty_1(sq1_phase); @@ -670,31 +667,9 @@ begin sq1_wav <= sq1_vol & "00"; else sq1_wav <= "000000"; - end if; - - case sq2_duty is - when "00" => sq2_out := duty_0(sq2_phase); - when "01" => sq2_out := duty_1(sq2_phase); - when "10" => sq2_out := duty_2(sq2_phase); - when "11" => sq2_out := duty_3(sq2_phase); - when others => null; - end case; - - if sq2_out = '1' then - sq2_wav <= sq2_vol & "00"; - else - sq2_wav <= "000000"; - end if; - - if noi_out = '1' then - noi_wav <= noi_vol & "00"; - else - noi_wav <= "000000"; - end if; - - end if; - - -- Square channel 1 + end if; + end if; + -- Length counter if en_len or sq1_lenquirk = '1' then if sq1_len > 0 and sq1_lenchk = '1' then @@ -757,7 +732,6 @@ begin if (sq1_swper /= "000" and sq1_swshift /= "000") then sq1_fr2 <= std_logic_vector(sq1_swfr(10 downto 0)); sq1_freqchange <= '1'; - -- sq1_freq <= std_logic_vector(sq1_swfr(10 downto 0)); TODO: update reg sweep_calculate:= true; -- when updating calculate 2nd time end if; end if; @@ -775,10 +749,14 @@ begin if sq1_envsgn = '1' then if sq1_vol /= "1111" then -- sq1_vol < 15 sq1_vol <= std_logic_vector(unsigned(sq1_vol) + 1); + else + sq1_envoff := true; -- envelope done end if; else if sq1_vol /= "0000" then -- sq1_vol > sq1_vol <= std_logic_vector(unsigned(sq1_vol) - 1); + else + sq1_envoff := true; -- envelope done end if; end if; @@ -802,16 +780,33 @@ begin sq1_swcnt := (others => '0'); sq1_swfr := (others => '0'); sq1_sweep_en := false; - --sq1_wav <= "000000"; end if; end if; - if sq1_trigger = '1' or sq1_volchange = '1' then - sq1_vol <= sq1_svol; + if sq1_trigger = '1' or sq1_nr2change = '1' then + + -- "zombie" mode + tmp_volume := "0000"&unsigned(sq1_vol); + if sq1_envper_old="000" and not sq1_envoff then + tmp_volume := "0000"&unsigned(sq1_vol) + 1; + else + if sq1_envsgn_old = '0' then + tmp_volume := "0000"&unsigned(sq1_vol) + 2; + end if; + end if; + + if (sq1_envsgn xor sq1_envsgn_old) = '1' then + tmp_volume := X"10" - tmp_volume; + end if; + + sq1_vol <= std_logic_vector(tmp_volume(3 downto 0)); + + -- check if dac is enabled if sq1_svol = "00000" and sq1_envsgn = '0' then -- dac disabled sq1_playing <= '0'; end if; end if; + if sq1_lenchange = '1' then sq1_len := sq1_slen; @@ -820,7 +815,7 @@ begin -- Check sample trigger and start playing if sq1_trigger = '1' then - + sq1_vol <= sq1_svol; sq1_fr2 <= sq1_freq; -- shadow frequency register for sweep unit sq1_sweep_en := sq1_swper /= "000" or sq1_swshift /= "000" ; -- sweep unit enabled ? ---- sweep quirks --- @@ -846,6 +841,8 @@ begin else sq1_envcnt := '0' & sq1_envper; -- set to period end if; + sq1_envoff := false; + sq1_phase := 0; if sq1_len = 0 then -- trigger quirks @@ -858,7 +855,39 @@ begin end if; - -- Square channel 2 + ----------------------- Square channel 2 ----------------------------- + + if en_snd4 then + -- Sq2 frequency timer Frequency = 131072/(2048-x) Hz + if sq2_playing = '1' then + acc_fcnt := ('0'&sq2_fcnt) + to_unsigned(1, acc_fcnt'length); + if acc_fcnt(acc_fcnt'high) = '1' then + if sq2_phase < 7 then + sq2_phase := sq2_phase + 1; + else + sq2_phase := 0; + end if; + sq2_fcnt := unsigned(sq2_freq); + else + sq2_fcnt := acc_fcnt(sq2_fcnt'range); + end if; + end if; + + case sq2_duty is + when "00" => sq2_out := duty_0(sq2_phase); + when "01" => sq2_out := duty_1(sq2_phase); + when "10" => sq2_out := duty_2(sq2_phase); + when "11" => sq2_out := duty_3(sq2_phase); + when others => null; + end case; + + if sq2_out = '1' then + sq2_wav <= sq2_vol & "00"; + else + sq2_wav <= "000000"; + end if; + end if; + -- Length counter if en_len or sq2_lenquirk = '1' then if sq2_len > 0 and sq2_lenchk = '1' then @@ -877,10 +906,14 @@ begin if sq2_envsgn = '1' then if sq2_vol /= "1111" then -- sq2_vol < 15 sq2_vol <= std_logic_vector(unsigned(sq2_vol) + 1); + else + sq2_envoff := true; end if; else if sq2_vol /= "0000" then -- sq2_vol > 0 sq2_vol <= std_logic_vector(unsigned(sq2_vol) - 1); + else + sq2_envoff := true; end if; end if; @@ -899,12 +932,28 @@ begin then sq2_playing <= '0'; sq2_envcnt := "0000"; - --sq2_wav <= "000000"; end if; end if; - if sq2_volchange ='1' or sq2_trigger= '1' then - sq2_vol <= sq2_svol; + if sq2_nr2change ='1' or sq2_trigger= '1' then + + -- "zombie" mode + tmp_volume := "0000"&unsigned(sq2_vol); + if sq2_envper_old="000" and not sq2_envoff then + tmp_volume := "0000"&unsigned(sq2_vol) + 1; + else + if sq2_envsgn_old = '0' then + tmp_volume := "0000"&unsigned(sq2_vol) + 2; + end if; + end if; + + if (sq2_envsgn xor sq2_envsgn_old) = '1' then + tmp_volume := X"10" - tmp_volume; + end if; + + sq2_vol <= std_logic_vector(tmp_volume(3 downto 0)); + + -- check if dac is enabled if sq2_svol = "00000" and sq2_envsgn = '0' then -- dac disabled sq2_playing <= '0'; end if; @@ -916,6 +965,7 @@ begin -- Check sample trigger and start playing if sq2_trigger = '1' then + sq2_vol <= sq2_svol; -- sq2_fr2 <= sq2_freq; sq2_fcnt := unsigned(sq2_freq); @@ -930,6 +980,7 @@ begin else sq2_envcnt := '0' & sq2_envper; -- set to period end if; + sq2_envoff := false; sq2_phase := 0; if sq2_len = 0 then -- trigger quirks @@ -942,7 +993,35 @@ begin end if; - -- Noise channel + ----------------------- Noise channel ----------------------------- + + -- Noi frequency timer Frequency = 524288 Hz / r / 2^(s+1) ;For r=0 assume r=0.5 instead + if en_snd4 then + if noi_playing = '1' then + acc_fcnt := ('0'&noi_fcnt) - to_unsigned(1, acc_fcnt'length); + if acc_fcnt = 0 then + -- Noise LFSR + noi_xor := noi_lfsr(0) xor noi_lfsr(1); + noi_lfsr := noi_xor & noi_lfsr(14 downto 1); + + if noi_short = '1' then + noi_lfsr(6) := noi_xor; + end if; + + noi_out := not noi_lfsr(0); + noi_fcnt := noi_period; + else + noi_fcnt := acc_fcnt(noi_fcnt'range); + end if; + end if; + + if noi_out = '1' then + noi_wav <= noi_vol & "00"; + else + noi_wav <= "000000"; + end if; + end if; + -- Length counter if en_len or noi_lenquirk = '1' then if noi_len > 0 and noi_lenchk = '1' then @@ -961,10 +1040,14 @@ begin if noi_envsgn = '1' then if noi_vol /= "1111" then -- noi_vol < 15 noi_vol <= std_logic_vector(unsigned(noi_vol) + 1); + else + noi_envoff := true; end if; else if noi_vol /= "0000" then -- noi_vol > 0 noi_vol <= std_logic_vector(unsigned(noi_vol) - 1); + else + noi_envoff := true; end if; end if; @@ -986,8 +1069,25 @@ begin end if; end if; - if noi_volchange ='1' or noi_trigger= '1' then - noi_vol <= noi_svol; + if noi_nr2change ='1' or noi_trigger= '1' then + + -- "zombie" mode + tmp_volume := "0000"&unsigned(noi_vol); + if noi_envper_old="000" and not noi_envoff then + tmp_volume := "0000"&unsigned(noi_vol) + 1; + else + if noi_envsgn_old = '0' then + tmp_volume := "0000"&unsigned(noi_vol) + 2; + end if; + end if; + + if (noi_envsgn xor noi_envsgn_old) = '1' then + tmp_volume := X"10" - tmp_volume; + end if; + + noi_vol <= std_logic_vector(tmp_volume(3 downto 0)); + + -- check if dac is enabled if noi_svol = "00000" and noi_envsgn = '0' then -- dac disabled noi_playing <= '0'; end if; @@ -996,36 +1096,32 @@ begin if noi_lenchange = '1' then noi_len := noi_slen; end if; - - -- Check sample trigger and start playing - if noi_trigger = '1' then + + if noi_trigger = '1' or noi_freqchange = '1' then + -- Calculate noise frequency case noi_div is - when "000" => noi_divisor := to_unsigned(2048 - 8, noi_divisor'length); - when "001" => noi_divisor := to_unsigned(2048 - 16, noi_divisor'length); - when "010" => noi_divisor := to_unsigned(2048 - 32, noi_divisor'length); - when "011" => noi_divisor := to_unsigned(2048 - 48, noi_divisor'length); - when "100" => noi_divisor := to_unsigned(2048 - 64, noi_divisor'length); - when "101" => noi_divisor := to_unsigned(2048 - 80, noi_divisor'length); - when "110" => noi_divisor := to_unsigned(2048 - 96, noi_divisor'length); - when others => noi_divisor := to_unsigned(2048 - 112, noi_divisor'length); + when "000" => noi_period := to_unsigned(2, noi_period'length); + when "001" => noi_period := to_unsigned(4, noi_period'length); + when "010" => noi_period := to_unsigned(8, noi_period'length); + when "011" => noi_period := to_unsigned(12, noi_period'length); + when "100" => noi_period := to_unsigned(16, noi_period'length); + when "101" => noi_period := to_unsigned(20, noi_period'length); + when "110" => noi_period := to_unsigned(24, noi_period'length); + when others => noi_period := to_unsigned(28, noi_divisor'length); end case; + + noi_period := noi_period sll to_integer(unsigned(noi_freqsh)); + noi_fcnt := noi_period; + + end if; + + + -- Check sample trigger and start playing + if noi_trigger = '1' then + noi_vol <= noi_svol; + noi_lfsr := (others => '1'); --- case noi_freqsh is --- when "000" => noi_freq := unsigned(noi_divisor); --- when "001" => noi_freq := '0' & unsigned(noi_divisor(10 downto 1)); --- when "010" => noi_freq := "00" & unsigned(noi_divisor(10 downto 2)); --- when "011" => noi_freq := "000" & unsigned(noi_divisor(10 downto 3)); --- when "100" => noi_freq := "0000" & unsigned(noi_divisor(10 downto 4)); --- when "101" => noi_freq := "00000" & unsigned(noi_divisor(10 downto 5)); --- when "110" => noi_freq := "000000" & unsigned(noi_divisor(10 downto 6)); --- when "111" => noi_freq := "0000000" & unsigned(noi_divisor(10 downto 7)); --- when others => noi_freq := unsigned(noi_divisor); --- end case; - noi_freq := noi_divisor sll to_integer(unsigned(noi_freqsh)); - - noi_fr2 <= std_logic_vector(noi_freq); - noi_fcnt := noi_freq; -- reload envelope counter with period if noi_envper = "000" then @@ -1033,6 +1129,7 @@ begin else noi_envcnt := '0' & noi_envper; -- set to period end if; + noi_envoff := false; if not (noi_svol = "00000" and noi_envsgn = '0') then -- dac enabled noi_playing <= '1'; @@ -1046,9 +1143,11 @@ begin end if; end if; end if; - + + ----------------------------- Wave channel ----------------------------------- + if en_snd2 then - -- Wave frequency timer + -- Wave frequency timer Frequency = 4194304/(64*(2048-x)) Hz = 65536/(2048-x) Hz wav_shift := false; if wav_playing = '1' then acc_fcnt := ('0'&wav_fcnt) + to_unsigned(1, acc_fcnt'length); @@ -1079,7 +1178,6 @@ begin end if; end if; - -- Wave channel if wav_playing = '1' then -- Check for end of playing conditions if (wav_lenchk = '1' and wav_len = 0) or wav_enable = '0' then