mirror of
https://github.com/MiSTer-devel/Gameboy_MiSTer.git
synced 2026-05-24 03:03:25 +00:00
SOUND: sq1 sweep unit rewrite, trigger and length quirks implemented
This commit is contained in:
217
gbc_snd.vhd
217
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
|
||||
|
||||
Reference in New Issue
Block a user