Difference between revisions of "Ym2149"
From CPCWiki - THE Amstrad CPC encyclopedia!
| Line 1: | Line 1: | ||
Ym2149 chip created by MikeJ in VHDL, compatible with Xilinx and Altera. | Ym2149 chip created by MikeJ in VHDL, compatible with Xilinx and Altera. | ||
| − | Not published, original version (coming from FPGAmstrad NEXYS4 v0 backup), just a very little patched to run in Amstrad (see "freemac" occurrence in code) | + | Not published, YM2149_linmix.vhd original version (coming from FPGAmstrad NEXYS4 v0 backup), just a very little patched to run in Amstrad (see "freemac" occurrence in code) : |
| + | |||
| + | -- | ||
| + | -- A simulation model of YM2149 (AY-3-8910 with bells on) | ||
| + | |||
| + | -- Copyright (c) MikeJ - Jan 2005 | ||
| + | -- | ||
| + | -- All rights reserved | ||
| + | -- | ||
| + | -- Redistribution and use in source and synthezised forms, with or without | ||
| + | -- modification, are permitted provided that the following conditions are met: | ||
| + | -- | ||
| + | -- Redistributions of source code must retain the above copyright notice, | ||
| + | -- this list of conditions and the following disclaimer. | ||
| + | -- | ||
| + | -- Redistributions in synthesized form must reproduce the above copyright | ||
| + | -- notice, this list of conditions and the following disclaimer in the | ||
| + | -- documentation and/or other materials provided with the distribution. | ||
| + | -- | ||
| + | -- Neither the name of the author nor the names of other contributors may | ||
| + | -- be used to endorse or promote products derived from this software without | ||
| + | -- specific prior written permission. | ||
| + | -- | ||
| + | -- THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| + | -- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | ||
| + | -- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| + | -- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE | ||
| + | -- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| + | -- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| + | -- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| + | -- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| + | -- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| + | -- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| + | -- POSSIBILITY OF SUCH DAMAGE. | ||
| + | -- | ||
| + | -- You are responsible for any legal issues arising from your use of this code. | ||
| + | -- | ||
| + | -- The latest version of this file can be found at: www.fpgaarcade.com | ||
| + | -- | ||
| + | -- Email support@fpgaarcade.com | ||
| + | -- | ||
| + | -- Revision list | ||
| + | -- | ||
| + | -- version 001 initial release | ||
| + | -- | ||
| + | -- Clues from MAME sound driver and Kazuhiro TSUJIKAWA | ||
| + | -- | ||
| + | -- These are the measured outputs from a real chip for a single Isolated channel into a 1K load (V) | ||
| + | -- vol 15 .. 0 | ||
| + | -- 3.27 2.995 2.741 2.588 2.452 2.372 2.301 2.258 2.220 2.198 2.178 2.166 2.155 2.148 2.141 2.132 | ||
| + | -- As the envelope volume is 5 bit, I have fitted a curve to the not quite log shape in order | ||
| + | -- to produced all the required values. | ||
| + | -- (The first part of the curve is a bit steeper and the last bit is more linear than expected) | ||
| + | -- | ||
| + | -- NOTE, this component uses LINEAR mixing of the three analogue channels, and is only | ||
| + | -- accurate for designs where the outputs are buffered and not simply wired together. | ||
| + | -- The ouput level is more complex in that case and requires a larger table. | ||
| + | |||
| + | library ieee; | ||
| + | use ieee.std_logic_1164.all; | ||
| + | use ieee.std_logic_arith.all; | ||
| + | use ieee.std_logic_unsigned.all; | ||
| + | |||
| + | entity YM2149 is | ||
| + | generic ( | ||
| + | MOCK:boolean:=false | ||
| + | ); | ||
| + | port ( | ||
| + | -- data bus | ||
| + | I_DA : in std_logic_vector(7 downto 0); | ||
| + | O_DA : out std_logic_vector(7 downto 0); | ||
| + | O_DA_OE_L : out std_logic; | ||
| + | -- control | ||
| + | I_A9_L : in std_logic; | ||
| + | I_A8 : in std_logic; | ||
| + | I_BDIR : in std_logic; | ||
| + | I_BC2 : in std_logic; | ||
| + | I_BC1 : in std_logic; | ||
| + | I_SEL_L : in std_logic; | ||
| + | |||
| + | O_AUDIO : out std_logic_vector(7 downto 0); | ||
| + | -- port a | ||
| + | I_IOA : in std_logic_vector(7 downto 0); | ||
| + | -- O_IOA : out std_logic_vector(7 downto 0); | ||
| + | -- O_IOA_OE_L : out std_logic; | ||
| + | -- port b | ||
| + | -- I_IOB : in std_logic_vector(7 downto 0); | ||
| + | -- O_IOB : out std_logic_vector(7 downto 0); | ||
| + | -- O_IOB_OE_L : out std_logic; | ||
| + | |||
| + | ENA : in std_logic; -- clock enable for higher speed operation | ||
| + | RESET_L : in std_logic; | ||
| + | CLK : in std_logic -- note 6 Mhz | ||
| + | ); | ||
| + | end; | ||
| + | |||
| + | architecture RTL of YM2149 is | ||
| + | --type array_16x8 is array (0 to 15) of std_logic_vector(7 downto 0); | ||
| + | --type array_16x8 is array (0 to 14) of std_logic_vector(7 downto 0); | ||
| + | type array_16x8 is array (0 to 13) of std_logic_vector(7 downto 0); | ||
| + | type array_3x12 is array (1 to 3) of std_logic_vector(11 downto 0); | ||
| + | |||
| + | signal cnt_div : std_logic_vector(3 downto 0) := (others => '0'); | ||
| + | signal noise_div : std_logic := '0'; | ||
| + | signal ena_div : std_logic; | ||
| + | signal ena_div_noise : std_logic; | ||
| + | signal poly17 : std_logic_vector(16 downto 0) := (others => '0'); | ||
| + | |||
| + | -- registers | ||
| + | signal addr : std_logic_vector(7 downto 0); | ||
| + | signal busctrl_addr : std_logic; | ||
| + | signal busctrl_we : std_logic; | ||
| + | signal busctrl_re : std_logic; | ||
| + | |||
| + | signal reg : array_16x8; | ||
| + | signal env_reset : std_logic; | ||
| + | signal ioa_inreg : std_logic_vector(7 downto 0); | ||
| + | --signal iob_inreg : std_logic_vector(7 downto 0); | ||
| + | |||
| + | signal noise_gen_cnt : std_logic_vector(4 downto 0); | ||
| + | signal noise_gen_op : std_logic; | ||
| + | signal tone_gen_cnt : array_3x12 := (others => (others => '0')); | ||
| + | signal tone_gen_op : std_logic_vector(3 downto 1) := "000"; | ||
| + | |||
| + | signal env_gen_cnt : std_logic_vector(15 downto 0); | ||
| + | signal env_ena : std_logic; | ||
| + | signal env_hold : std_logic; | ||
| + | signal env_inc : std_logic; | ||
| + | signal env_vol : std_logic_vector(4 downto 0); | ||
| + | |||
| + | signal tone_ena_l : std_logic; | ||
| + | signal tone_src : std_logic; | ||
| + | signal noise_ena_l : std_logic; | ||
| + | signal chan_vol : std_logic_vector(4 downto 0); | ||
| + | |||
| + | signal dac_amp : std_logic_vector(7 downto 0); | ||
| + | signal audio_mix : std_logic_vector(9 downto 0); | ||
| + | signal audio_final : std_logic_vector(9 downto 0); | ||
| + | begin | ||
| + | |||
| + | do_mock:if MOCK generate | ||
| + | O_DA<=I_IOA; | ||
| + | end generate; | ||
| + | |||
| + | dont_mock:if not(MOCK) generate | ||
| + | |||
| + | -- cpu i/f | ||
| + | p_busdecode : process(I_BDIR, I_BC2, I_BC1, addr, I_A9_L, I_A8) | ||
| + | variable cs : std_logic; | ||
| + | variable sel : std_logic_vector(2 downto 0); | ||
| + | begin | ||
| + | -- BDIR BC2 BC1 MODE | ||
| + | -- 0 0 0 inactive | ||
| + | -- 0 0 1 address | ||
| + | -- 0 1 0 inactive | ||
| + | -- 0 1 1 read | ||
| + | -- 1 0 0 address | ||
| + | -- 1 0 1 inactive | ||
| + | -- 1 1 0 write | ||
| + | -- 1 1 1 read | ||
| + | busctrl_addr <= '0'; | ||
| + | busctrl_we <= '0'; | ||
| + | busctrl_re <= '0'; | ||
| + | |||
| + | cs := '0'; | ||
| + | if (I_A9_L = '0') and (I_A8 = '1') and (addr(7 downto 4) = "0000") then | ||
| + | cs := '1'; | ||
| + | end if; | ||
| + | |||
| + | sel := (I_BDIR & I_BC2 & I_BC1); | ||
| + | case sel is | ||
| + | when "000" => null; | ||
| + | when "001" => busctrl_addr <= '1'; | ||
| + | when "010" => null; | ||
| + | when "011" => busctrl_re <= cs; | ||
| + | when "100" => busctrl_addr <= '1'; | ||
| + | when "101" => null; | ||
| + | when "110" => busctrl_we <= cs; | ||
| + | when "111" => busctrl_addr <= '1'; | ||
| + | when others => null; | ||
| + | end case; | ||
| + | end process; | ||
| + | |||
| + | p_oe : process(busctrl_re) | ||
| + | begin | ||
| + | -- if we are emulating a real chip, maybe clock this to fake up the tristate typ delay of 100ns | ||
| + | O_DA_OE_L <= not (busctrl_re); | ||
| + | end process; | ||
| + | |||
| + | -- | ||
| + | -- CLOCKED | ||
| + | -- | ||
| + | --p_waddr : process | ||
| + | --begin | ||
| + | ---- looks like registers are latches in real chip, but the address is caught at the end of the address state. | ||
| + | --wait until rising_edge(CLK); | ||
| + | |||
| + | --if (RESET_L = '0') then | ||
| + | --addr <= (others => '0'); | ||
| + | --else | ||
| + | --if (busctrl_addr = '1') then | ||
| + | --addr <= I_DA; | ||
| + | --end if; | ||
| + | --end if; | ||
| + | --end process; | ||
| + | |||
| + | --p_wdata : process | ||
| + | --begin | ||
| + | ---- looks like registers are latches in real chip, but the address is caught at the end of the address state. | ||
| + | --wait until rising_edge(CLK); | ||
| + | --env_reset <= '0'; | ||
| + | |||
| + | --if (RESET_L = '0') then | ||
| + | --reg <= (others => (others => '0')); | ||
| + | --env_reset <= '1'; | ||
| + | --else | ||
| + | --env_reset <= '0'; | ||
| + | --if (busctrl_we = '1') then | ||
| + | --case addr(3 downto 0) is | ||
| + | --when x"0" => reg(0) <= I_DA; | ||
| + | --when x"1" => reg(1) <= I_DA; | ||
| + | --when x"2" => reg(2) <= I_DA; | ||
| + | --when x"3" => reg(3) <= I_DA; | ||
| + | --when x"4" => reg(4) <= I_DA; | ||
| + | --when x"5" => reg(5) <= I_DA; | ||
| + | --when x"6" => reg(6) <= I_DA; | ||
| + | --when x"7" => reg(7) <= I_DA; | ||
| + | --when x"8" => reg(8) <= I_DA; | ||
| + | --when x"9" => reg(9) <= I_DA; | ||
| + | --when x"A" => reg(10) <= I_DA; | ||
| + | --when x"B" => reg(11) <= I_DA; | ||
| + | --when x"C" => reg(12) <= I_DA; | ||
| + | --when x"D" => reg(13) <= I_DA; env_reset <= '1'; | ||
| + | --when x"E" => reg(14) <= I_DA; | ||
| + | --when x"F" => reg(15) <= I_DA; | ||
| + | --when others => null; | ||
| + | --end case; | ||
| + | --end if; | ||
| + | --end if; | ||
| + | --end process; | ||
| + | |||
| + | -- | ||
| + | -- LATCHED, useful when emulating a real chip in circuit. Nasty as gated clock. | ||
| + | -- | ||
| + | p_waddr : process(reset_l, busctrl_addr) | ||
| + | begin | ||
| + | -- looks like registers are latches in real chip, but the address is caught at the end of the address state. | ||
| + | if (RESET_L = '0') then | ||
| + | addr <= (others => '0'); | ||
| + | elsif falling_edge(busctrl_addr) then -- yuk | ||
| + | addr <= I_DA; | ||
| + | end if; | ||
| + | end process; | ||
| + | |||
| + | p_wdata : process(reset_l, clk, addr) --process(reset_l, busctrl_we, addr) | ||
| + | begin | ||
| + | if (RESET_L = '0') then | ||
| + | reg <= (others => (others => '0')); | ||
| + | elsif rising_edge(clk) then | ||
| + | if busctrl_we='1' then --falling_edge(busctrl_we) then | ||
| + | case addr(3 downto 0) is | ||
| + | when x"0" => reg(0) <= I_DA; | ||
| + | when x"1" => reg(1) <= I_DA; | ||
| + | when x"2" => reg(2) <= I_DA; | ||
| + | when x"3" => reg(3) <= I_DA; | ||
| + | when x"4" => reg(4) <= I_DA; | ||
| + | when x"5" => reg(5) <= I_DA; | ||
| + | when x"6" => reg(6) <= I_DA; | ||
| + | when x"7" => reg(7) <= I_DA; | ||
| + | when x"8" => reg(8) <= I_DA; | ||
| + | when x"9" => reg(9) <= I_DA; | ||
| + | when x"A" => reg(10) <= I_DA; | ||
| + | when x"B" => reg(11) <= I_DA; | ||
| + | when x"C" => reg(12) <= I_DA; | ||
| + | when x"D" => reg(13) <= I_DA; | ||
| + | -- when x"E" => reg(14) <= I_DA; | ||
| + | -- when x"F" => reg(15) <= I_DA; | ||
| + | when others => null; | ||
| + | end case; | ||
| + | end if; | ||
| + | end if; | ||
| + | |||
| + | env_reset <= '0'; | ||
| + | if (busctrl_we = '1') and (addr(3 downto 0) = x"D") then | ||
| + | env_reset <= '1'; | ||
| + | end if; | ||
| + | end process; | ||
| + | |||
| + | p_rdata : process(busctrl_re, addr, reg) | ||
| + | begin | ||
| + | O_DA <= (others => '0'); -- 'X' | ||
| + | if (busctrl_re = '1') then -- not necessary, but useful for putting 'X's in the simulator | ||
| + | case addr(3 downto 0) is | ||
| + | when x"0" => O_DA <= reg(0) ; | ||
| + | when x"1" => O_DA <= "0000" & reg(1)(3 downto 0) ; | ||
| + | when x"2" => O_DA <= reg(2) ; | ||
| + | when x"3" => O_DA <= "0000" & reg(3)(3 downto 0) ; | ||
| + | when x"4" => O_DA <= reg(4) ; | ||
| + | when x"5" => O_DA <= "0000" & reg(5)(3 downto 0) ; | ||
| + | when x"6" => O_DA <= "000" & reg(6)(4 downto 0) ; | ||
| + | when x"7" => O_DA <= reg(7) ; | ||
| + | when x"8" => O_DA <= "000" & reg(8)(4 downto 0) ; | ||
| + | when x"9" => O_DA <= "000" & reg(9)(4 downto 0) ; | ||
| + | when x"A" => O_DA <= "000" & reg(10)(4 downto 0) ; | ||
| + | when x"B" => O_DA <= reg(11); | ||
| + | when x"C" => O_DA <= reg(12); | ||
| + | when x"D" => O_DA <= "0000" & reg(13)(3 downto 0); | ||
| + | when x"E" => if (reg(7)(6) = '0') then -- input | ||
| + | O_DA <= ioa_inreg; | ||
| + | else | ||
| + | O_DA <= ioa_inreg; -- freemac hack reg(14); -- read output reg | ||
| + | end if; | ||
| + | -- when x"F" => if (Reg(7)(7) = '0') then | ||
| + | -- O_DA <= iob_inreg; | ||
| + | -- else | ||
| + | -- O_DA <= reg(15); | ||
| + | -- end if; | ||
| + | when others => null; | ||
| + | end case; | ||
| + | end if; | ||
| + | end process; | ||
| + | -- | ||
| + | p_divider : process | ||
| + | begin | ||
| + | wait until rising_edge(CLK); | ||
| + | -- / 8 when SEL is high and /16 when SEL is low | ||
| + | if (ENA = '1') then | ||
| + | ena_div <= '0'; | ||
| + | ena_div_noise <= '0'; | ||
| + | if (cnt_div = "0000") then | ||
| + | cnt_div <= (not I_SEL_L) & "111"; | ||
| + | ena_div <= '1'; | ||
| + | |||
| + | noise_div <= not noise_div; | ||
| + | if (noise_div = '1') then | ||
| + | ena_div_noise <= '1'; | ||
| + | end if; | ||
| + | else | ||
| + | cnt_div <= cnt_div - "1"; | ||
| + | end if; | ||
| + | end if; | ||
| + | end process; | ||
| + | |||
| + | p_noise_gen : process | ||
| + | variable noise_gen_comp : std_logic_vector(4 downto 0); | ||
| + | variable poly17_zero : std_logic; | ||
| + | begin | ||
| + | wait until rising_edge(CLK); | ||
| + | |||
| + | if (reg(6)(4 downto 0) = "00000") then | ||
| + | noise_gen_comp := "00000"; | ||
| + | else | ||
| + | noise_gen_comp := (reg(6)(4 downto 0) - "1"); | ||
| + | end if; | ||
| + | |||
| + | poly17_zero := '0'; | ||
| + | if (poly17 = "00000000000000000") then poly17_zero := '1'; end if; | ||
| + | |||
| + | if (ENA = '1') then | ||
| + | |||
| + | if (ena_div_noise = '1') then -- divider ena | ||
| + | |||
| + | if (noise_gen_cnt >= noise_gen_comp) then | ||
| + | noise_gen_cnt <= "00000"; | ||
| + | poly17 <= (poly17(0) xor poly17(2) xor poly17_zero) & poly17(16 downto 1); | ||
| + | else | ||
| + | noise_gen_cnt <= (noise_gen_cnt + "1"); | ||
| + | end if; | ||
| + | end if; | ||
| + | end if; | ||
| + | end process; | ||
| + | noise_gen_op <= poly17(0); | ||
| + | |||
| + | p_tone_gens : process | ||
| + | variable tone_gen_freq : array_3x12; | ||
| + | variable tone_gen_comp : array_3x12; | ||
| + | begin | ||
| + | wait until rising_edge(CLK); | ||
| + | |||
| + | -- looks like real chips count up - we need to get the Exact behaviour .. | ||
| + | tone_gen_freq(1) := reg(1)(3 downto 0) & reg(0); | ||
| + | tone_gen_freq(2) := reg(3)(3 downto 0) & reg(2); | ||
| + | tone_gen_freq(3) := reg(5)(3 downto 0) & reg(4); | ||
| + | -- period 0 = period 1 | ||
| + | for i in 1 to 3 loop | ||
| + | if (tone_gen_freq(i) = x"000") then | ||
| + | tone_gen_comp(i) := x"000"; | ||
| + | else | ||
| + | tone_gen_comp(i) := (tone_gen_freq(i) - "1"); | ||
| + | end if; | ||
| + | end loop; | ||
| + | |||
| + | if (ENA = '1') then | ||
| + | for i in 1 to 3 loop | ||
| + | if (ena_div = '1') then -- divider ena | ||
| + | |||
| + | if (tone_gen_cnt(i) >= tone_gen_comp(i)) then | ||
| + | tone_gen_cnt(i) <= x"000"; | ||
| + | tone_gen_op(i) <= not tone_gen_op(i); | ||
| + | else | ||
| + | tone_gen_cnt(i) <= (tone_gen_cnt(i) + "1"); | ||
| + | end if; | ||
| + | end if; | ||
| + | end loop; | ||
| + | end if; | ||
| + | end process; | ||
| + | |||
| + | p_envelope_freq : process | ||
| + | variable env_gen_freq : std_logic_vector(15 downto 0); | ||
| + | variable env_gen_comp : std_logic_vector(15 downto 0); | ||
| + | begin | ||
| + | wait until rising_edge(CLK); | ||
| + | env_gen_freq := reg(12) & reg(11); | ||
| + | -- envelope freqs 1 and 0 are the same. | ||
| + | if (env_gen_freq = x"0000") then | ||
| + | env_gen_comp := x"0000"; | ||
| + | else | ||
| + | env_gen_comp := (env_gen_freq - "1"); | ||
| + | end if; | ||
| + | |||
| + | if (ENA = '1') then | ||
| + | env_ena <= '0'; | ||
| + | if (ena_div = '1') then -- divider ena | ||
| + | if (env_gen_cnt >= env_gen_comp) then | ||
| + | env_gen_cnt <= x"0000"; | ||
| + | env_ena <= '1'; | ||
| + | else | ||
| + | env_gen_cnt <= (env_gen_cnt + "1"); | ||
| + | end if; | ||
| + | end if; | ||
| + | end if; | ||
| + | end process; | ||
| + | |||
| + | p_envelope_shape : process(env_reset, CLK) | ||
| + | variable is_bot : boolean; | ||
| + | variable is_bot_p1 : boolean; | ||
| + | variable is_top_m1 : boolean; | ||
| + | variable is_top : boolean; | ||
| + | begin | ||
| + | -- envelope shapes | ||
| + | -- C AtAlH | ||
| + | -- 0 0 x x \___ | ||
| + | -- | ||
| + | -- 0 1 x x /___ | ||
| + | -- | ||
| + | -- 1 0 0 0 \\\\ | ||
| + | -- | ||
| + | -- 1 0 0 1 \___ | ||
| + | -- | ||
| + | -- 1 0 1 0 \/\/ | ||
| + | -- ___ | ||
| + | -- 1 0 1 1 \ | ||
| + | -- | ||
| + | -- 1 1 0 0 //// | ||
| + | -- ___ | ||
| + | -- 1 1 0 1 / | ||
| + | -- | ||
| + | -- 1 1 1 0 /\/\ | ||
| + | -- | ||
| + | -- 1 1 1 1 /___ | ||
| + | if (env_reset = '1') then | ||
| + | -- load initial state | ||
| + | if (reg(13)(2) = '0') then -- attack | ||
| + | env_vol <= "11111"; | ||
| + | env_inc <= '0'; -- -1 | ||
| + | else | ||
| + | env_vol <= "00000"; | ||
| + | env_inc <= '1'; -- +1 | ||
| + | end if; | ||
| + | env_hold <= '0'; | ||
| + | |||
| + | elsif rising_edge(CLK) then | ||
| + | is_bot := (env_vol = "00000"); | ||
| + | is_bot_p1 := (env_vol = "00001"); | ||
| + | is_top_m1 := (env_vol = "11110"); | ||
| + | is_top := (env_vol = "11111"); | ||
| + | |||
| + | if (ENA = '1') then | ||
| + | if (env_ena = '1') then | ||
| + | if (env_hold = '0') then | ||
| + | if (env_inc = '1') then | ||
| + | env_vol <= (env_vol + "00001"); | ||
| + | else | ||
| + | env_vol <= (env_vol + "11111"); | ||
| + | end if; | ||
| + | end if; | ||
| + | |||
| + | -- envelope shape control. | ||
| + | if (reg(13)(3) = '0') then | ||
| + | if (env_inc = '0') then -- down | ||
| + | if is_bot_p1 then env_hold <= '1'; end if; | ||
| + | else | ||
| + | if is_top then env_hold <= '1'; end if; | ||
| + | end if; | ||
| + | else | ||
| + | if (reg(13)(0) = '1') then -- hold = 1 | ||
| + | if (env_inc = '0') then -- down | ||
| + | if (reg(13)(1) = '1') then -- alt | ||
| + | if is_bot then env_hold <= '1'; end if; | ||
| + | else | ||
| + | if is_bot_p1 then env_hold <= '1'; end if; | ||
| + | end if; | ||
| + | else | ||
| + | if (reg(13)(1) = '1') then -- alt | ||
| + | if is_top then env_hold <= '1'; end if; | ||
| + | else | ||
| + | if is_top_m1 then env_hold <= '1'; end if; | ||
| + | end if; | ||
| + | end if; | ||
| + | |||
| + | elsif (reg(13)(1) = '1') then -- alternate | ||
| + | if (env_inc = '0') then -- down | ||
| + | if is_bot_p1 then env_hold <= '1'; end if; | ||
| + | if is_bot then env_hold <= '0'; env_inc <= '1'; end if; | ||
| + | else | ||
| + | if is_top_m1 then env_hold <= '1'; end if; | ||
| + | if is_top then env_hold <= '0'; env_inc <= '0'; end if; | ||
| + | end if; | ||
| + | end if; | ||
| + | |||
| + | end if; | ||
| + | end if; | ||
| + | end if; | ||
| + | end if; | ||
| + | end process; | ||
| + | |||
| + | p_chan_mixer : process(cnt_div, reg, tone_gen_op) | ||
| + | begin | ||
| + | tone_ena_l <= '1'; tone_src <= '1'; | ||
| + | noise_ena_l <= '1'; chan_vol <= "00000"; | ||
| + | case cnt_div(1 downto 0) is | ||
| + | when "00" => | ||
| + | tone_ena_l <= reg(7)(0); tone_src <= tone_gen_op(1); chan_vol <= reg(8)(4 downto 0); | ||
| + | noise_ena_l <= reg(7)(3); | ||
| + | when "01" => | ||
| + | tone_ena_l <= reg(7)(1); tone_src <= tone_gen_op(2); chan_vol <= reg(9)(4 downto 0); | ||
| + | noise_ena_l <= reg(7)(4); | ||
| + | when "10" => | ||
| + | tone_ena_l <= reg(7)(2); tone_src <= tone_gen_op(3); chan_vol <= reg(10)(4 downto 0); | ||
| + | noise_ena_l <= reg(7)(5); | ||
| + | when "11" => null; -- tone gen outputs become valid on this clock | ||
| + | when others => null; | ||
| + | end case; | ||
| + | end process; | ||
| + | |||
| + | p_op_mixer : process | ||
| + | variable chan_mixed : std_logic; | ||
| + | variable chan_amp : std_logic_vector(4 downto 0); | ||
| + | begin | ||
| + | wait until rising_edge(CLK); | ||
| + | if (ENA = '1') then | ||
| + | |||
| + | chan_mixed := (tone_ena_l or tone_src) and (noise_ena_l or noise_gen_op); | ||
| + | |||
| + | chan_amp := (others => '0'); | ||
| + | if (chan_mixed = '1') then | ||
| + | if (chan_vol(4) = '0') then | ||
| + | if (chan_vol(3 downto 0) = "0000") then -- nothing is easy ! make sure quiet is quiet | ||
| + | chan_amp := "00000"; | ||
| + | else | ||
| + | chan_amp := chan_vol(3 downto 0) & '1'; -- make sure level 31 (env) = level 15 (tone) | ||
| + | end if; | ||
| + | else | ||
| + | chan_amp := env_vol(4 downto 0); | ||
| + | end if; | ||
| + | end if; | ||
| + | |||
| + | dac_amp <= x"00"; | ||
| + | case chan_amp is | ||
| + | when "11111" => dac_amp <= x"FF"; | ||
| + | when "11110" => dac_amp <= x"D9"; | ||
| + | when "11101" => dac_amp <= x"BA"; | ||
| + | when "11100" => dac_amp <= x"9F"; | ||
| + | when "11011" => dac_amp <= x"88"; | ||
| + | when "11010" => dac_amp <= x"74"; | ||
| + | when "11001" => dac_amp <= x"63"; | ||
| + | when "11000" => dac_amp <= x"54"; | ||
| + | when "10111" => dac_amp <= x"48"; | ||
| + | when "10110" => dac_amp <= x"3D"; | ||
| + | when "10101" => dac_amp <= x"34"; | ||
| + | when "10100" => dac_amp <= x"2C"; | ||
| + | when "10011" => dac_amp <= x"25"; | ||
| + | when "10010" => dac_amp <= x"1F"; | ||
| + | when "10001" => dac_amp <= x"1A"; | ||
| + | when "10000" => dac_amp <= x"16"; | ||
| + | when "01111" => dac_amp <= x"13"; | ||
| + | when "01110" => dac_amp <= x"10"; | ||
| + | when "01101" => dac_amp <= x"0D"; | ||
| + | when "01100" => dac_amp <= x"0B"; | ||
| + | when "01011" => dac_amp <= x"09"; | ||
| + | when "01010" => dac_amp <= x"08"; | ||
| + | when "01001" => dac_amp <= x"07"; | ||
| + | when "01000" => dac_amp <= x"06"; | ||
| + | when "00111" => dac_amp <= x"05"; | ||
| + | when "00110" => dac_amp <= x"04"; | ||
| + | when "00101" => dac_amp <= x"03"; | ||
| + | when "00100" => dac_amp <= x"03"; | ||
| + | when "00011" => dac_amp <= x"02"; | ||
| + | when "00010" => dac_amp <= x"02"; | ||
| + | when "00001" => dac_amp <= x"01"; | ||
| + | when "00000" => dac_amp <= x"00"; | ||
| + | when others => null; | ||
| + | end case; | ||
| + | |||
| + | if (cnt_div(1 downto 0) = "10") then | ||
| + | audio_mix <= (others => '0'); | ||
| + | audio_final <= audio_mix; | ||
| + | else | ||
| + | audio_mix <= audio_mix + ("00" & dac_amp); | ||
| + | end if; | ||
| + | |||
| + | if (RESET_L = '0') then | ||
| + | O_AUDIO(7 downto 0) <= "00000000"; | ||
| + | else | ||
| + | if (audio_final(9) = '0') then | ||
| + | O_AUDIO(7 downto 0) <= audio_final(8 downto 1); | ||
| + | else -- clip | ||
| + | O_AUDIO(7 downto 0) <= x"FF"; | ||
| + | end if; | ||
| + | end if; | ||
| + | end if; | ||
| + | end process; | ||
| + | |||
| + | -- p_io_ports : process(reg) | ||
| + | -- begin | ||
| + | -- --O_IOA <= reg(14); | ||
| + | -- | ||
| + | -- --O_IOA_OE_L <= not reg(7)(6); | ||
| + | ---- O_IOB <= reg(15); | ||
| + | ---- O_IOB_OE_L <= not reg(7)(7); | ||
| + | -- end process; | ||
| + | |||
| + | p_io_ports_inreg : process | ||
| + | begin | ||
| + | wait until rising_edge(CLK); | ||
| + | ioa_inreg <= I_IOA; | ||
| + | -- iob_inreg <= I_IOB; | ||
| + | end process; | ||
| + | |||
| + | end generate; | ||
| + | end architecture RTL; | ||
| + | |||
MikeJ is also author of : | MikeJ is also author of : | ||
* [http://www.fpgaarcade.com/platforms/ FPGAArcade] platform (Xilinx final platform running AGA Amiga) | * [http://www.fpgaarcade.com/platforms/ FPGAArcade] platform (Xilinx final platform running AGA Amiga) | ||
* [[T80]] chip (VHDL) used also in FPGAmstrad project | * [[T80]] chip (VHDL) used also in FPGAmstrad project | ||
Revision as of 08:17, 5 March 2017
Ym2149 chip created by MikeJ in VHDL, compatible with Xilinx and Altera.
Not published, YM2149_linmix.vhd original version (coming from FPGAmstrad NEXYS4 v0 backup), just a very little patched to run in Amstrad (see "freemac" occurrence in code) :
--
-- A simulation model of YM2149 (AY-3-8910 with bells on)
-- Copyright (c) MikeJ - Jan 2005
--
-- All rights reserved
--
-- Redistribution and use in source and synthezised forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
--
-- Redistributions in synthesized form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
--
-- Neither the name of the author nor the names of other contributors may
-- be used to endorse or promote products derived from this software without
-- specific prior written permission.
--
-- THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--
-- You are responsible for any legal issues arising from your use of this code.
--
-- The latest version of this file can be found at: www.fpgaarcade.com
--
-- Email support@fpgaarcade.com
--
-- Revision list
--
-- version 001 initial release
--
-- Clues from MAME sound driver and Kazuhiro TSUJIKAWA
--
-- These are the measured outputs from a real chip for a single Isolated channel into a 1K load (V)
-- vol 15 .. 0
-- 3.27 2.995 2.741 2.588 2.452 2.372 2.301 2.258 2.220 2.198 2.178 2.166 2.155 2.148 2.141 2.132
-- As the envelope volume is 5 bit, I have fitted a curve to the not quite log shape in order
-- to produced all the required values.
-- (The first part of the curve is a bit steeper and the last bit is more linear than expected)
--
-- NOTE, this component uses LINEAR mixing of the three analogue channels, and is only
-- accurate for designs where the outputs are buffered and not simply wired together.
-- The ouput level is more complex in that case and requires a larger table.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity YM2149 is
generic (
MOCK:boolean:=false
);
port (
-- data bus
I_DA : in std_logic_vector(7 downto 0);
O_DA : out std_logic_vector(7 downto 0);
O_DA_OE_L : out std_logic;
-- control
I_A9_L : in std_logic;
I_A8 : in std_logic;
I_BDIR : in std_logic;
I_BC2 : in std_logic;
I_BC1 : in std_logic;
I_SEL_L : in std_logic;
O_AUDIO : out std_logic_vector(7 downto 0);
-- port a
I_IOA : in std_logic_vector(7 downto 0);
-- O_IOA : out std_logic_vector(7 downto 0);
-- O_IOA_OE_L : out std_logic;
-- port b
-- I_IOB : in std_logic_vector(7 downto 0);
-- O_IOB : out std_logic_vector(7 downto 0);
-- O_IOB_OE_L : out std_logic;
ENA : in std_logic; -- clock enable for higher speed operation
RESET_L : in std_logic;
CLK : in std_logic -- note 6 Mhz
);
end;
architecture RTL of YM2149 is
--type array_16x8 is array (0 to 15) of std_logic_vector(7 downto 0);
--type array_16x8 is array (0 to 14) of std_logic_vector(7 downto 0);
type array_16x8 is array (0 to 13) of std_logic_vector(7 downto 0);
type array_3x12 is array (1 to 3) of std_logic_vector(11 downto 0);
signal cnt_div : std_logic_vector(3 downto 0) := (others => '0');
signal noise_div : std_logic := '0';
signal ena_div : std_logic;
signal ena_div_noise : std_logic;
signal poly17 : std_logic_vector(16 downto 0) := (others => '0');
-- registers
signal addr : std_logic_vector(7 downto 0);
signal busctrl_addr : std_logic;
signal busctrl_we : std_logic;
signal busctrl_re : std_logic;
signal reg : array_16x8;
signal env_reset : std_logic;
signal ioa_inreg : std_logic_vector(7 downto 0);
--signal iob_inreg : std_logic_vector(7 downto 0);
signal noise_gen_cnt : std_logic_vector(4 downto 0);
signal noise_gen_op : std_logic;
signal tone_gen_cnt : array_3x12 := (others => (others => '0'));
signal tone_gen_op : std_logic_vector(3 downto 1) := "000";
signal env_gen_cnt : std_logic_vector(15 downto 0);
signal env_ena : std_logic;
signal env_hold : std_logic;
signal env_inc : std_logic;
signal env_vol : std_logic_vector(4 downto 0);
signal tone_ena_l : std_logic;
signal tone_src : std_logic;
signal noise_ena_l : std_logic;
signal chan_vol : std_logic_vector(4 downto 0);
signal dac_amp : std_logic_vector(7 downto 0);
signal audio_mix : std_logic_vector(9 downto 0);
signal audio_final : std_logic_vector(9 downto 0);
begin
do_mock:if MOCK generate
O_DA<=I_IOA;
end generate;
dont_mock:if not(MOCK) generate
-- cpu i/f
p_busdecode : process(I_BDIR, I_BC2, I_BC1, addr, I_A9_L, I_A8)
variable cs : std_logic;
variable sel : std_logic_vector(2 downto 0);
begin
-- BDIR BC2 BC1 MODE
-- 0 0 0 inactive
-- 0 0 1 address
-- 0 1 0 inactive
-- 0 1 1 read
-- 1 0 0 address
-- 1 0 1 inactive
-- 1 1 0 write
-- 1 1 1 read
busctrl_addr <= '0';
busctrl_we <= '0';
busctrl_re <= '0';
cs := '0';
if (I_A9_L = '0') and (I_A8 = '1') and (addr(7 downto 4) = "0000") then
cs := '1';
end if;
sel := (I_BDIR & I_BC2 & I_BC1);
case sel is
when "000" => null;
when "001" => busctrl_addr <= '1';
when "010" => null;
when "011" => busctrl_re <= cs;
when "100" => busctrl_addr <= '1';
when "101" => null;
when "110" => busctrl_we <= cs;
when "111" => busctrl_addr <= '1';
when others => null;
end case;
end process;
p_oe : process(busctrl_re)
begin
-- if we are emulating a real chip, maybe clock this to fake up the tristate typ delay of 100ns
O_DA_OE_L <= not (busctrl_re);
end process;
--
-- CLOCKED
--
--p_waddr : process
--begin
---- looks like registers are latches in real chip, but the address is caught at the end of the address state.
--wait until rising_edge(CLK);
--if (RESET_L = '0') then
--addr <= (others => '0');
--else
--if (busctrl_addr = '1') then
--addr <= I_DA;
--end if;
--end if;
--end process;
--p_wdata : process
--begin
---- looks like registers are latches in real chip, but the address is caught at the end of the address state.
--wait until rising_edge(CLK);
--env_reset <= '0';
--if (RESET_L = '0') then
--reg <= (others => (others => '0'));
--env_reset <= '1';
--else
--env_reset <= '0';
--if (busctrl_we = '1') then
--case addr(3 downto 0) is
--when x"0" => reg(0) <= I_DA;
--when x"1" => reg(1) <= I_DA;
--when x"2" => reg(2) <= I_DA;
--when x"3" => reg(3) <= I_DA;
--when x"4" => reg(4) <= I_DA;
--when x"5" => reg(5) <= I_DA;
--when x"6" => reg(6) <= I_DA;
--when x"7" => reg(7) <= I_DA;
--when x"8" => reg(8) <= I_DA;
--when x"9" => reg(9) <= I_DA;
--when x"A" => reg(10) <= I_DA;
--when x"B" => reg(11) <= I_DA;
--when x"C" => reg(12) <= I_DA;
--when x"D" => reg(13) <= I_DA; env_reset <= '1';
--when x"E" => reg(14) <= I_DA;
--when x"F" => reg(15) <= I_DA;
--when others => null;
--end case;
--end if;
--end if;
--end process;
--
-- LATCHED, useful when emulating a real chip in circuit. Nasty as gated clock.
--
p_waddr : process(reset_l, busctrl_addr)
begin
-- looks like registers are latches in real chip, but the address is caught at the end of the address state.
if (RESET_L = '0') then
addr <= (others => '0');
elsif falling_edge(busctrl_addr) then -- yuk
addr <= I_DA;
end if;
end process;
p_wdata : process(reset_l, clk, addr) --process(reset_l, busctrl_we, addr)
begin
if (RESET_L = '0') then
reg <= (others => (others => '0'));
elsif rising_edge(clk) then
if busctrl_we='1' then --falling_edge(busctrl_we) then
case addr(3 downto 0) is
when x"0" => reg(0) <= I_DA;
when x"1" => reg(1) <= I_DA;
when x"2" => reg(2) <= I_DA;
when x"3" => reg(3) <= I_DA;
when x"4" => reg(4) <= I_DA;
when x"5" => reg(5) <= I_DA;
when x"6" => reg(6) <= I_DA;
when x"7" => reg(7) <= I_DA;
when x"8" => reg(8) <= I_DA;
when x"9" => reg(9) <= I_DA;
when x"A" => reg(10) <= I_DA;
when x"B" => reg(11) <= I_DA;
when x"C" => reg(12) <= I_DA;
when x"D" => reg(13) <= I_DA;
-- when x"E" => reg(14) <= I_DA;
-- when x"F" => reg(15) <= I_DA;
when others => null;
end case;
end if;
end if;
env_reset <= '0';
if (busctrl_we = '1') and (addr(3 downto 0) = x"D") then
env_reset <= '1';
end if;
end process;
p_rdata : process(busctrl_re, addr, reg)
begin
O_DA <= (others => '0'); -- 'X'
if (busctrl_re = '1') then -- not necessary, but useful for putting 'X's in the simulator
case addr(3 downto 0) is
when x"0" => O_DA <= reg(0) ;
when x"1" => O_DA <= "0000" & reg(1)(3 downto 0) ;
when x"2" => O_DA <= reg(2) ;
when x"3" => O_DA <= "0000" & reg(3)(3 downto 0) ;
when x"4" => O_DA <= reg(4) ;
when x"5" => O_DA <= "0000" & reg(5)(3 downto 0) ;
when x"6" => O_DA <= "000" & reg(6)(4 downto 0) ;
when x"7" => O_DA <= reg(7) ;
when x"8" => O_DA <= "000" & reg(8)(4 downto 0) ;
when x"9" => O_DA <= "000" & reg(9)(4 downto 0) ;
when x"A" => O_DA <= "000" & reg(10)(4 downto 0) ;
when x"B" => O_DA <= reg(11);
when x"C" => O_DA <= reg(12);
when x"D" => O_DA <= "0000" & reg(13)(3 downto 0);
when x"E" => if (reg(7)(6) = '0') then -- input
O_DA <= ioa_inreg;
else
O_DA <= ioa_inreg; -- freemac hack reg(14); -- read output reg
end if;
-- when x"F" => if (Reg(7)(7) = '0') then
-- O_DA <= iob_inreg;
-- else
-- O_DA <= reg(15);
-- end if;
when others => null;
end case;
end if;
end process;
--
p_divider : process
begin
wait until rising_edge(CLK);
-- / 8 when SEL is high and /16 when SEL is low
if (ENA = '1') then
ena_div <= '0';
ena_div_noise <= '0';
if (cnt_div = "0000") then
cnt_div <= (not I_SEL_L) & "111";
ena_div <= '1';
noise_div <= not noise_div;
if (noise_div = '1') then
ena_div_noise <= '1';
end if;
else
cnt_div <= cnt_div - "1";
end if;
end if;
end process;
p_noise_gen : process
variable noise_gen_comp : std_logic_vector(4 downto 0);
variable poly17_zero : std_logic;
begin
wait until rising_edge(CLK);
if (reg(6)(4 downto 0) = "00000") then
noise_gen_comp := "00000";
else
noise_gen_comp := (reg(6)(4 downto 0) - "1");
end if;
poly17_zero := '0';
if (poly17 = "00000000000000000") then poly17_zero := '1'; end if;
if (ENA = '1') then
if (ena_div_noise = '1') then -- divider ena
if (noise_gen_cnt >= noise_gen_comp) then
noise_gen_cnt <= "00000";
poly17 <= (poly17(0) xor poly17(2) xor poly17_zero) & poly17(16 downto 1);
else
noise_gen_cnt <= (noise_gen_cnt + "1");
end if;
end if;
end if;
end process;
noise_gen_op <= poly17(0);
p_tone_gens : process
variable tone_gen_freq : array_3x12;
variable tone_gen_comp : array_3x12;
begin
wait until rising_edge(CLK);
-- looks like real chips count up - we need to get the Exact behaviour ..
tone_gen_freq(1) := reg(1)(3 downto 0) & reg(0);
tone_gen_freq(2) := reg(3)(3 downto 0) & reg(2);
tone_gen_freq(3) := reg(5)(3 downto 0) & reg(4);
-- period 0 = period 1
for i in 1 to 3 loop
if (tone_gen_freq(i) = x"000") then
tone_gen_comp(i) := x"000";
else
tone_gen_comp(i) := (tone_gen_freq(i) - "1");
end if;
end loop;
if (ENA = '1') then
for i in 1 to 3 loop
if (ena_div = '1') then -- divider ena
if (tone_gen_cnt(i) >= tone_gen_comp(i)) then
tone_gen_cnt(i) <= x"000";
tone_gen_op(i) <= not tone_gen_op(i);
else
tone_gen_cnt(i) <= (tone_gen_cnt(i) + "1");
end if;
end if;
end loop;
end if;
end process;
p_envelope_freq : process
variable env_gen_freq : std_logic_vector(15 downto 0);
variable env_gen_comp : std_logic_vector(15 downto 0);
begin
wait until rising_edge(CLK);
env_gen_freq := reg(12) & reg(11);
-- envelope freqs 1 and 0 are the same.
if (env_gen_freq = x"0000") then
env_gen_comp := x"0000";
else
env_gen_comp := (env_gen_freq - "1");
end if;
if (ENA = '1') then
env_ena <= '0';
if (ena_div = '1') then -- divider ena
if (env_gen_cnt >= env_gen_comp) then
env_gen_cnt <= x"0000";
env_ena <= '1';
else
env_gen_cnt <= (env_gen_cnt + "1");
end if;
end if;
end if;
end process;
p_envelope_shape : process(env_reset, CLK)
variable is_bot : boolean;
variable is_bot_p1 : boolean;
variable is_top_m1 : boolean;
variable is_top : boolean;
begin
-- envelope shapes
-- C AtAlH
-- 0 0 x x \___
--
-- 0 1 x x /___
--
-- 1 0 0 0 \\\\
--
-- 1 0 0 1 \___
--
-- 1 0 1 0 \/\/
-- ___
-- 1 0 1 1 \
--
-- 1 1 0 0 ////
-- ___
-- 1 1 0 1 /
--
-- 1 1 1 0 /\/\
--
-- 1 1 1 1 /___
if (env_reset = '1') then
-- load initial state
if (reg(13)(2) = '0') then -- attack
env_vol <= "11111";
env_inc <= '0'; -- -1
else
env_vol <= "00000";
env_inc <= '1'; -- +1
end if;
env_hold <= '0';
elsif rising_edge(CLK) then
is_bot := (env_vol = "00000");
is_bot_p1 := (env_vol = "00001");
is_top_m1 := (env_vol = "11110");
is_top := (env_vol = "11111");
if (ENA = '1') then
if (env_ena = '1') then
if (env_hold = '0') then
if (env_inc = '1') then
env_vol <= (env_vol + "00001");
else
env_vol <= (env_vol + "11111");
end if;
end if;
-- envelope shape control.
if (reg(13)(3) = '0') then
if (env_inc = '0') then -- down
if is_bot_p1 then env_hold <= '1'; end if;
else
if is_top then env_hold <= '1'; end if;
end if;
else
if (reg(13)(0) = '1') then -- hold = 1
if (env_inc = '0') then -- down
if (reg(13)(1) = '1') then -- alt
if is_bot then env_hold <= '1'; end if;
else
if is_bot_p1 then env_hold <= '1'; end if;
end if;
else
if (reg(13)(1) = '1') then -- alt
if is_top then env_hold <= '1'; end if;
else
if is_top_m1 then env_hold <= '1'; end if;
end if;
end if;
elsif (reg(13)(1) = '1') then -- alternate
if (env_inc = '0') then -- down
if is_bot_p1 then env_hold <= '1'; end if;
if is_bot then env_hold <= '0'; env_inc <= '1'; end if;
else
if is_top_m1 then env_hold <= '1'; end if;
if is_top then env_hold <= '0'; env_inc <= '0'; end if;
end if;
end if;
end if;
end if;
end if;
end if;
end process;
p_chan_mixer : process(cnt_div, reg, tone_gen_op)
begin
tone_ena_l <= '1'; tone_src <= '1';
noise_ena_l <= '1'; chan_vol <= "00000";
case cnt_div(1 downto 0) is
when "00" =>
tone_ena_l <= reg(7)(0); tone_src <= tone_gen_op(1); chan_vol <= reg(8)(4 downto 0);
noise_ena_l <= reg(7)(3);
when "01" =>
tone_ena_l <= reg(7)(1); tone_src <= tone_gen_op(2); chan_vol <= reg(9)(4 downto 0);
noise_ena_l <= reg(7)(4);
when "10" =>
tone_ena_l <= reg(7)(2); tone_src <= tone_gen_op(3); chan_vol <= reg(10)(4 downto 0);
noise_ena_l <= reg(7)(5);
when "11" => null; -- tone gen outputs become valid on this clock
when others => null;
end case;
end process;
p_op_mixer : process
variable chan_mixed : std_logic;
variable chan_amp : std_logic_vector(4 downto 0);
begin
wait until rising_edge(CLK);
if (ENA = '1') then
chan_mixed := (tone_ena_l or tone_src) and (noise_ena_l or noise_gen_op);
chan_amp := (others => '0');
if (chan_mixed = '1') then
if (chan_vol(4) = '0') then
if (chan_vol(3 downto 0) = "0000") then -- nothing is easy ! make sure quiet is quiet
chan_amp := "00000";
else
chan_amp := chan_vol(3 downto 0) & '1'; -- make sure level 31 (env) = level 15 (tone)
end if;
else
chan_amp := env_vol(4 downto 0);
end if;
end if;
dac_amp <= x"00";
case chan_amp is
when "11111" => dac_amp <= x"FF";
when "11110" => dac_amp <= x"D9";
when "11101" => dac_amp <= x"BA";
when "11100" => dac_amp <= x"9F";
when "11011" => dac_amp <= x"88";
when "11010" => dac_amp <= x"74";
when "11001" => dac_amp <= x"63";
when "11000" => dac_amp <= x"54";
when "10111" => dac_amp <= x"48";
when "10110" => dac_amp <= x"3D";
when "10101" => dac_amp <= x"34";
when "10100" => dac_amp <= x"2C";
when "10011" => dac_amp <= x"25";
when "10010" => dac_amp <= x"1F";
when "10001" => dac_amp <= x"1A";
when "10000" => dac_amp <= x"16";
when "01111" => dac_amp <= x"13";
when "01110" => dac_amp <= x"10";
when "01101" => dac_amp <= x"0D";
when "01100" => dac_amp <= x"0B";
when "01011" => dac_amp <= x"09";
when "01010" => dac_amp <= x"08";
when "01001" => dac_amp <= x"07";
when "01000" => dac_amp <= x"06";
when "00111" => dac_amp <= x"05";
when "00110" => dac_amp <= x"04";
when "00101" => dac_amp <= x"03";
when "00100" => dac_amp <= x"03";
when "00011" => dac_amp <= x"02";
when "00010" => dac_amp <= x"02";
when "00001" => dac_amp <= x"01";
when "00000" => dac_amp <= x"00";
when others => null;
end case;
if (cnt_div(1 downto 0) = "10") then
audio_mix <= (others => '0');
audio_final <= audio_mix;
else
audio_mix <= audio_mix + ("00" & dac_amp);
end if;
if (RESET_L = '0') then
O_AUDIO(7 downto 0) <= "00000000";
else
if (audio_final(9) = '0') then
O_AUDIO(7 downto 0) <= audio_final(8 downto 1);
else -- clip
O_AUDIO(7 downto 0) <= x"FF";
end if;
end if;
end if;
end process;
-- p_io_ports : process(reg)
-- begin
-- --O_IOA <= reg(14);
--
-- --O_IOA_OE_L <= not reg(7)(6);
---- O_IOB <= reg(15);
---- O_IOB_OE_L <= not reg(7)(7);
-- end process;
p_io_ports_inreg : process
begin
wait until rising_edge(CLK);
ioa_inreg <= I_IOA;
-- iob_inreg <= I_IOB;
end process;
end generate;
end architecture RTL;
MikeJ is also author of :
- FPGAArcade platform (Xilinx final platform running AGA Amiga)
- T80 chip (VHDL) used also in FPGAmstrad project