diff --git a/gbc_snd.vhd b/gbc_snd.vhd index cbbb14c..df33ce2 100644 --- a/gbc_snd.vhd +++ b/gbc_snd.vhd @@ -38,6 +38,7 @@ architecture SYN of gbc_snd is signal en_snden4 : boolean; -- Enable at clk/4 signal en_len : boolean; -- Sample length + signal en_len_r : boolean; signal en_env : boolean; -- Envelope signal en_sweep : boolean; -- Sweep @@ -55,11 +56,13 @@ architecture SYN of gbc_snd is 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 + signal sq1_len_en_change : std_logic; -- Sq1 length off -> on 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_lenchange : std_logic; + signal sq1_lenquirk : std_logic; signal sq1_playing : std_logic; -- Sq1 channel active signal sq1_wav : std_logic_vector(5 downto 0); -- Sq1 output waveform @@ -69,6 +72,7 @@ architecture SYN of gbc_snd is signal sq2_svol : std_logic_vector(3 downto 0); -- Sq2 initial volume signal sq2_volchange : 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_freq : std_logic_vector(10 downto 0); -- Sq2 frequency @@ -83,6 +87,7 @@ architecture SYN of gbc_snd is signal wav_enable : std_logic; -- Wave enable signal wav_slen : std_logic_vector(8 downto 0); -- Wave play length signal wav_lenchange : std_logic; + signal wav_lenquirk : std_logic; signal wav_volsh : std_logic_vector(1 downto 0); -- Wave volume shift signal wav_freq : std_logic_vector(10 downto 0); -- Wave frequency signal wav_trigger : std_logic; -- Wave trigger play note @@ -95,6 +100,7 @@ architecture SYN of gbc_snd is signal noi_slen : std_logic_vector(6 downto 0); 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_envsgn : std_logic; @@ -112,6 +118,7 @@ architecture SYN of gbc_snd is signal ch_map : std_logic_vector(7 downto 0); signal ch_vol : std_logic_vector(7 downto 0); + signal framecnt : integer range 0 to 7 := 0; begin @@ -121,16 +128,16 @@ begin en_snd <= true; -- Calculate divided and frame sequencer clock enables - process(clk, en_snd, reset) + process(clk, en_snd, snd_enable, reset) variable clkcnt : unsigned(1 downto 0); variable cnt_512 : unsigned(12 downto 0); variable temp_512 : unsigned(13 downto 0); - variable framecnt : integer range 0 to 7 := 0; begin - if reset = '1' then + if reset = '1' or snd_enable='0' then --only clock frame sequencer if sound is enabled, restart at 0 clkcnt := "00"; cnt_512 := (others => '0'); - framecnt := 0; + framecnt <= 0; + en_len_r <= false; elsif rising_edge(clk) then -- Base clock divider @@ -153,8 +160,10 @@ begin en_env <= false; en_sweep <= false; if en_512 then + en_len_r <= not en_len_r; if framecnt = 0 or framecnt = 2 or framecnt = 4 or framecnt = 6 then en_len <= true; + en_len_r <= not en_len_r; end if; if framecnt = 2 or framecnt = 6 then en_sweep <= true; @@ -164,9 +173,9 @@ begin end if; if framecnt < 7 then - framecnt := framecnt + 1; + framecnt <= framecnt + 1; else - framecnt := 0; + framecnt <= 0; end if; end if; @@ -244,6 +253,12 @@ begin sq1_lenchange <= '0'; wav_lenchange <= '0'; noi_lenchange <= '0'; + sq1_lenquirk <= '0'; + sq2_lenquirk <= '0'; + wav_lenquirk <= '0'; + noi_lenquirk <= '0'; + + if s1_write = '1' then case s1_addr is @@ -266,6 +281,9 @@ begin sq1_freq(7 downto 0) <= s1_writedata; when "010100" => -- NR14 FF14 TL-- -FFF Trigger, Length enable, Frequency MSB sq1_trigger <= s1_writedata(7); + if sq1_lenchk = '0' and s1_writedata(6) = '1' and en_len_r then + sq1_lenquirk <= '1'; + end if; sq1_lenchk <= s1_writedata(6); sq1_freq(10 downto 8) <= s1_writedata(2 downto 0); @@ -284,6 +302,9 @@ begin sq2_freq(7 downto 0) <= s1_writedata; when "011001" => -- NR24 FF19 TL-- -FFF Trigger, Length enable, Frequency MSB sq2_trigger <= s1_writedata(7); + if sq2_lenchk = '0' and s1_writedata(6) = '1' and en_len_r then + sq2_lenquirk <= '1'; + end if; sq2_lenchk <= s1_writedata(6); sq2_freq(10 downto 8) <= s1_writedata(2 downto 0); @@ -300,6 +321,9 @@ begin wav_freq(7 downto 0) <= s1_writedata; when "011110" => -- NR34 FF1E TL-- -FFF Trigger, Length enable, Frequency MSB wav_trigger <= s1_writedata(7); + if wav_lenchk = '0' and s1_writedata(6) = '1' and en_len_r then + wav_lenquirk <= '1'; + end if; wav_lenchk <= s1_writedata(6); wav_freq(10 downto 8) <= s1_writedata(2 downto 0); @@ -318,6 +342,9 @@ begin noi_div <= s1_writedata(2 downto 0); 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 + noi_lenquirk <= '1'; + end if; noi_lenchk <= s1_writedata(6); -- -- Control/Status @@ -497,10 +524,13 @@ begin variable sq1_phase : integer range 0 to 7; variable sq1_len : std_logic_vector(6 downto 0); variable sq1_envcnt : std_logic_vector(2 downto 0); -- Sq1 envelope timer count - variable sq1_swcnt : std_logic_vector(2 downto 0); -- Sq1 sweep timer count + variable sq1_swcnt : std_logic_vector(3 downto 0); -- Sq1 sweep timer count variable sq1_swoffs : unsigned(11 downto 0); variable sq1_swfr : unsigned(11 downto 0); variable sq1_out : std_logic; + variable sq1_sweep_en: boolean; + variable sweep_calculate: boolean; + variable sweep_update: boolean; variable sq2_fcnt : unsigned(10 downto 0); variable sq2_phase : integer range 0 to 7; @@ -521,6 +551,7 @@ begin variable noi_len : std_logic_vector(6 downto 0); variable noi_envcnt : std_logic_vector(2 downto 0); -- Noise envelope timer count variable noi_out : std_logic; + variable noi_xor : std_logic; variable acc_fcnt : unsigned(11 downto 0); begin @@ -533,7 +564,7 @@ begin sq1_len := (others => '0'); sq1_vol <= "0000"; sq1_envcnt := "000"; - sq1_swcnt := "000"; + sq1_swcnt := "0000"; sq1_swoffs := (others => '0'); sq1_swfr := (others => '0'); sq1_out := '0'; @@ -561,6 +592,9 @@ begin noi_vol <= "0000"; noi_envcnt := "000"; noi_out := '0'; + sweep_calculate:= false; + sweep_update := false; + sq1_sweep_en := false; elsif rising_edge(clk) then if en_snd4 then @@ -599,10 +633,11 @@ begin 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(15 downto 1); + if noi_short = '1' then - noi_lfsr := (noi_lfsr(0) xor noi_lfsr(1)) & noi_lfsr(15 downto 8) & (noi_lfsr(0) xor noi_lfsr(1)) & noi_lfsr(6 downto 1); - else - noi_lfsr := (noi_lfsr(0) xor noi_lfsr(1)) & noi_lfsr(15 downto 1); + noi_lfsr(6) := noi_xor; end if; noi_out := not noi_lfsr(0); @@ -650,11 +685,68 @@ begin -- Square channel 1 -- Length counter - if en_len then + if en_len or sq1_lenquirk = '1' then if sq1_len > 0 and sq1_lenchk = '1' then sq1_len := std_logic_vector(unsigned(sq1_len) - 1); end if; end if; + + -- Sweep processing + + -- sweep counter + if en_sweep then + sq1_swcnt := std_logic_vector(unsigned(sq1_swcnt) - 1); + if sq1_swcnt = 0 then + + -- reload counter with period + if sq1_swper = "000" then + sq1_swcnt := "1000"; -- set to 8 + else + sq1_swcnt := '0' & sq1_swper; -- set to period + end if; + + -- check if update needed + if sq1_sweep_en and sq1_swper /= "000" then + sweep_calculate:= true; + sweep_update:=true; + end if; + end if; + end if; + + if sq1_sweep_en then + + -- Calculate next sweep frequency + if sweep_calculate then + case sq1_swshift is + when "000" => sq1_swoffs := unsigned('0' & sq1_fr2); + when "001" => sq1_swoffs := "00" & unsigned(sq1_fr2(10 downto 1)); + when "010" => sq1_swoffs := "000" & unsigned(sq1_fr2(10 downto 2)); + when "011" => sq1_swoffs := "0000" & unsigned(sq1_fr2(10 downto 3)); + when "100" => sq1_swoffs := "00000" & unsigned(sq1_fr2(10 downto 4)); + when "101" => sq1_swoffs := "000000" & unsigned(sq1_fr2(10 downto 5)); + when "110" => sq1_swoffs := "0000000" & unsigned(sq1_fr2(10 downto 6)); + when "111" => sq1_swoffs := "00000000" & unsigned(sq1_fr2(10 downto 7)); + when others => sq1_swoffs := unsigned('0' & sq1_fr2); + end case; + if sq1_swdir = '1' then + sq1_swfr := ('0' & unsigned(sq1_fr2)) - sq1_swoffs; + else + sq1_swfr := ('0' & unsigned(sq1_fr2)) + sq1_swoffs; + end if; + sweep_calculate:= false; + end if; + + -- update registers, and calculate next frequency + if sweep_update then + sweep_update := false; + if (sq1_swper /= "000" and sq1_swshift /= "000") then + sq1_fr2 <= std_logic_vector(sq1_swfr(10 downto 0)); + -- 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; + + end if; if sq1_playing = '1' then @@ -677,49 +769,16 @@ begin end if; end if; - -- Sweep processing - if en_sweep or sq1_trigger = '1' then - case sq1_swshift is - when "000" => sq1_swoffs := unsigned('0' & sq1_fr2); - when "001" => sq1_swoffs := "00" & unsigned(sq1_fr2(10 downto 1)); - when "010" => sq1_swoffs := "000" & unsigned(sq1_fr2(10 downto 2)); - when "011" => sq1_swoffs := "0000" & unsigned(sq1_fr2(10 downto 3)); - when "100" => sq1_swoffs := "00000" & unsigned(sq1_fr2(10 downto 4)); - when "101" => sq1_swoffs := "000000" & unsigned(sq1_fr2(10 downto 5)); - when "110" => sq1_swoffs := "0000000" & unsigned(sq1_fr2(10 downto 6)); - when "111" => sq1_swoffs := "00000000" & unsigned(sq1_fr2(10 downto 7)); - when others => sq1_swoffs := unsigned('0' & sq1_fr2); - end case; - - -- Calculate next sweep frequency - if sq1_swper /= "000" then - if sq1_swdir = '0' then - sq1_swfr := ('0' & unsigned(sq1_fr2)) - sq1_swoffs; - else - sq1_swfr := ('0' & unsigned(sq1_fr2)) + sq1_swoffs; - end if; - else -- Sweep disabled - sq1_swfr := unsigned('0' & sq1_fr2); - end if; - - -- Sweep counter - if sq1_swper /= "000" then - sq1_swcnt := std_logic_vector(unsigned(sq1_swcnt) + to_unsigned(1, sq1_swcnt'length)); - if sq1_swcnt = sq1_swper then - sq1_fr2 <= std_logic_vector(sq1_swfr(10 downto 0)); - sq1_swcnt := "000"; - end if; - end if; - end if; - -- Check for end of playing conditions --if sq1_vol = X"0" -- Volume == 0 if (sq1_lenchk = '1' and sq1_len = 0) -- Play length timer overrun - or (sq1_swper /= "000" and sq1_swfr(11) = '1') -- Sweep frequency overrun + or (sq1_sweep_en and sq1_swfr(11) = '1') -- Sweep frequency overrun then sq1_playing <= '0'; - sq1_envcnt := "000"; - sq1_swcnt := "000"; + sq1_envcnt := (others => '0'); + sq1_swcnt := (others => '0'); + sq1_swfr := (others => '0'); + sq1_sweep_en := false; --sq1_wav <= "000000"; end if; end if; @@ -738,24 +797,41 @@ begin -- Check sample trigger and start playing if sq1_trigger = '1' then - sq1_fr2 <= sq1_freq; + + 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 --- + if sq1_swshift /= "000" then + sweep_calculate := true; + end if; + if sq1_swper = "000" then + sq1_swcnt := "1000"; -- set to 8 + else + sq1_swcnt := '0' & sq1_swper; -- set to period + end if; + ---- sweep quirks --- + sq1_fcnt := unsigned(sq1_freq); if not (sq1_svol = "00000" and sq1_envsgn = '0') then -- dac enabled sq1_playing <= '1'; end if; sq1_envcnt := "000"; - sq1_swcnt := "000"; + sq1_phase := 0; - if sq1_len = 0 then - sq1_len := "1000000"; + if sq1_len = 0 then -- trigger quirks + if sq1_lenchk = '1' and en_len_r then + sq1_len := "0111111"; -- 63 + else + sq1_len := "1000000"; -- 64 + end if; end if; + end if; -- Square channel 2 -- Length counter - if en_len then + if en_len or sq2_lenquirk = '1' then if sq2_len > 0 and sq2_lenchk = '1' then - -- sq2_len := std_logic_vector(unsigned(sq2_len) + to_unsigned(1, sq2_len'length)); sq2_len := std_logic_vector(unsigned(sq2_len) - 1); end if; end if; @@ -811,15 +887,19 @@ begin end if; sq2_envcnt := "000"; sq2_phase := 0; - if sq2_len = 0 then - sq2_len := "1000000"; + if sq2_len = 0 then -- trigger quirks + if sq2_lenchk = '1' and en_len_r then + sq2_len := "0111111"; -- 63 + else + sq2_len := "1000000"; -- 64 + end if; end if; end if; -- Noise channel -- Length counter - if en_len then + if en_len or noi_lenquirk = '1' then if noi_len > 0 and noi_lenchk = '1' then noi_len := std_logic_vector(unsigned(noi_len) - 1); end if; @@ -900,8 +980,12 @@ begin noi_playing <= '1'; end if; - if noi_len = 0 then - noi_len := "1000000"; -- set to max + if noi_len = 0 then -- trigger quirks + if noi_lenchk = '1' and en_len_r then + noi_len := "0111111"; -- 63 + else + noi_len := "1000000"; -- 64 + end if; end if; end if; @@ -931,7 +1015,7 @@ begin end if; -- Length counter - if en_len then + if en_len or wav_lenquirk = '1' then if wav_len > 0 and wav_lenchk = '1' then wav_len := std_logic_vector(unsigned(wav_len) - 1); end if; @@ -955,9 +1039,14 @@ begin wav_fr2 <= wav_freq; wav_fcnt := unsigned(wav_freq); wav_playing <= '1'; - if wav_len = 0 then - wav_len := "100000000"; -- set to max + if wav_len = 0 then -- trigger quirks + if wav_lenchk = '1' and en_len_r then + wav_len := "011111111"; -- 255 + else + wav_len := "100000000"; -- 256 + end if; end if; + end if; if wav_enable = '1' and wav_volsh /= "00" then