-- 
-- megaram.vhd
--   Mega-ROM emulation, ASC8K/16K/SCC+(8Mbits)
--   Revision 1.00
-- 
-- Copyright (c) 2006 Kazuhiro Tsujikawa (ESE Artists' factory)
-- All rights reserved.
-- 
-- Redistribution and use of this source code or any derivative works, are 
-- permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice, 
--    this list of conditions and the following disclaimer.
-- 2. Redistributions in binary 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.
-- 3. Redistributions may not be sold, nor may they be used in a commercial 
--    product or activity without specific prior written permission.
--
-- THIS SOFTWARE 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 COPYRIGHT OWNER 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.
-- 

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity megaram is
  port(
    clk21m  : in std_logic;
    reset   : in std_logic;
    clkena  : in std_logic;
    req     : in std_logic;
    ack     : out std_logic;
    wrt     : in std_logic;
    adr     : in std_logic_vector(15 downto 0);
    dbi     : out std_logic_vector(7 downto 0);
    dbo     : in std_logic_vector(7 downto 0);

    ramreq  : out std_logic;
    ramwrt  : out std_logic;
    ramadr  : out std_logic_vector(19 downto 0);
    ramdbi  : in std_logic_vector(7 downto 0);
    ramdbo  : out std_logic_vector(7 downto 0);

    mapsel  : in std_logic_vector(1 downto 0);  -- "-0":SCC+, "01":ASC8K, "11":ASC16K

    wavl    : out std_logic_vector(7 downto 0);
    wavr    : out std_logic_vector(7 downto 0)
 );
end megaram;

architecture rtl of megaram is

  component scc_wave
    port(
      clk21m  : in std_logic;
      reset   : in std_logic;
      clkena  : in std_logic;
      req     : in std_logic;
      ack     : out std_logic;
      wrt     : in std_logic;
      adr     : in std_logic_vector(7 downto 0);
      dbi     : out std_logic_vector(7 downto 0);
      dbo     : in std_logic_vector(7 downto 0);
      wave    : out std_logic_vector(7 downto 0)
    );
  end component;

  signal SccSel      : std_logic_vector(1 downto 0);
  signal Dec1FFE     : std_logic;
  signal DecSccA     : std_logic;
  signal DecSccB     : std_logic;

  signal WavCpy      : std_logic;
  signal WavReq      : std_logic;
--signal WavWrtL     : std_logic;
--signal WavWrtR     : std_logic;
  signal WavAckL     : std_logic;
  signal WavAckR     : std_logic;
  signal WavAdr      : std_logic_vector(7 downto 0);
  signal WavDbiL     : std_logic_vector(7 downto 0);
  signal WavDbiR     : std_logic_vector(7 downto 0);

  signal SccBank0    : std_logic_vector(7 downto 0);
  signal SccBank1    : std_logic_vector(7 downto 0);
  signal SccBank2    : std_logic_vector(7 downto 0);
  signal SccBank3    : std_logic_vector(7 downto 0);
  signal SccModeA    : std_logic_vector(7 downto 0);
  signal SccModeB    : std_logic_vector(7 downto 0);

  signal SccAmpL     : std_logic_vector(7 downto 0);
  signal SccAmpR     : std_logic_vector(7 downto 0);

begin

  ----------------------------------------------------------------
  -- SCC access decoder
  ----------------------------------------------------------------
  process(clk21m, reset)

    variable flag : std_logic;

  begin

    if (reset = '1') then

      flag   := '0';
      WavCpy <= '0';

    elsif (clk21m'event and clk21m = '1') then

      -- SCC wave memory copy (ch.D > ch.E)
      if (WavReq = '1' and (WavAckL or WavAckR) = '1') then
        if (wrt = '1' and adr(7 downto 5) = "011" and DecSccA = '1' and flag = '0') then
          flag := '1';
        else
          flag := '0';
        end if;
        WavCpy <= '0';
      elsif (flag = '1' and (WavAckL or WavAckR) = '0') then
        WavCpy <= '1';
      end if;

    end if;

  end process;

  -- SCC acknowledge
  ack     <= req  when SccSel = "00" else (WavAckL or WavAckR);

  -- RAM request
  RamReq  <= req  when SccSel = "01" else '0';
  RamWrt  <= wrt;

  --ram address (MSB is fixed to '0' in 6000-7FFFh)
  RamAdr(19)          <= SccModeA(6) when adr(14 downto 13) /= "11" and mapsel(0) = '0' else
                         '0'         when                               mapsel(0) = '0' else
                         SccBank0(6) when adr(14 downto 13) = "10"   else
                         SccBank1(6) when adr(14 downto 13) = "11"   else
                         SccBank2(6) when adr(14 downto 13) = "00"   else
                         SccBank3(6);
  RamAdr(18 downto 0) <= SccBank0(5 downto 0) & adr(12 downto 0) when adr(14 downto 13) = "10" else
                         SccBank1(5 downto 0) & adr(12 downto 0) when adr(14 downto 13) = "11" else
                         SccBank2(5 downto 0) & adr(12 downto 0) when adr(14 downto 13) = "00" else
                         SccBank3(5 downto 0) & adr(12 downto 0);

  -- Mapped I/O port access on 9800-98FFh / B800-B8FFh ... Wave memory
  WavReq  <= (req or WavCpy) when SccSel(1) = '1' else '0';
--WavWrtL <= wrt when adr(9 downto 8) /= "11" else '0';
--WavWrtR <= wrt when adr(9 downto 8) /= "10" else '0';

  -- exchange B8A0-B8BF <> 9880-989F (wave_ch.E) / B8C0-B8DF <> 98E0-98FF (mode register)
  WavAdr  <= "100" & adr(4 downto 0) when WavCpy = '1' else
             (adr(7 downto 0) xor X"20") when (adr(13) = '0' and adr(7)  = '1') else
             adr(7 downto 0);

  -- SCC data bus control
  RamDbo  <= dbo;
  dbi     <= RamDbi  when SccSel = "01" else
             WavDbiL when SccSel = "10" else
             WavDbiR when SccSel = "11" else
             (others => '1');

  -- SCC address decoder
  SccSel <= "10" when   -- memory access (scc_wave_Lch)
                        (adr(8) = '0' and SccModeB(4) = '0' and mapsel(0) = '0' and 
                        (DecSccA = '1' or DecSccB = '1')) else
            "11" when   -- memory access (scc_wave_Rch)
                        (adr(8) = '1' and SccModeB(4) = '0' and mapsel(0) = '0' and 
                        (DecSccA = '1' or DecSccB = '1')) else
            "01" when   -- memory access (MEGA-ROM)
                        -- 4000-7FFFh(R/-, ASC8K/16K)
                        (adr(15 downto 14) = "01"  and mapsel(0) = '1'  and                     wrt = '0') or
                        -- 8000-BFFFh(R/-, ASC8K/16K)
                        (adr(15 downto 14) = "10"  and mapsel(0) = '1'  and                     wrt = '0') or
                        -- 4000-5FFFh(R/W, ASC8K/16K)
                        (adr(15 downto 13) = "010" and mapsel(0) = '1'  and SccBank0(7) = '1'            ) or
                        -- 8000-9FFFh(R/W, ASC8K/16K)
                        (adr(15 downto 13) = "100" and mapsel(0) = '1'  and SccBank2(7) = '1'            ) or
                        -- A000-BFFFh(R/W, ASC8K/16K)
                        (adr(15 downto 13) = "101" and mapsel(0) = '1'  and SccBank3(7) = '1'            ) or
                        -- 4000-5FFFh(R/-, SCC)
                        (adr(15 downto 13) = "010" and SccModeA(6) = '0' and                   wrt = '0') or
                        -- 6000-7FFFh(R/-, SCC)
                        (adr(15 downto 13) = "011" and                                         wrt = '0') or
                        -- 8000-9FFFh(R/-, SCC)
                        (adr(15 downto 13) = "100" and                       DecSccA = '0' and wrt = '0') or
                        -- A000-BFFFh(R/-, SCC)
                        (adr(15 downto 13) = "101" and SccModeA(6) = '0' and DecSccB = '0' and wrt = '0') or
                        -- 4000-5FFFh(R/W) ESCC-RAM
                        (adr(15 downto 13) = "010" and SccModeA(4) = '1') or
                        -- 6000-7FFDh(R/W) ESCC-RAM
                        (adr(15 downto 13) = "011" and SccModeA(4) = '1' and Dec1FFE /= '1') or
                        -- 4000-7FFFh(R/W) SNATCHER
                        (adr(15 downto 14) = "01"  and SccModeB(4) = '1') or
                        -- 8000-9FFFh(R/W) SNATCHER
                        (adr(15 downto 13) = "100" and SccModeB(4) = '1') or
                        -- A000-BFFDh(R/W) SNATCHER
                        (adr(15 downto 13) = "101" and SccModeB(4) = '1' and Dec1FFE /= '1')                 else
            "00";       -- MEGA-ROM bank register access

  -- Mapped I/O port access on 7FFE-7FFFh / BFFE-BFFFh ... Write protect / SPC mode register
  Dec1FFE <= '1' when adr(12 downto 1) = "111111111111" else '0';
  -- Mapped I/O port access on 9800-9FFFh ... Wave memory
  DecSccA <= '1' when adr(15 downto 11) = "10011" and SccModeB(5) = '0' and SccBank2(5 downto 0) = "111111" else '0';
  -- Mapped I/O port access on B800-BFFFh ... Wave memory
  DecSccB <= '1' when adr(15 downto 11) = "10111" and SccModeB(5) = '1' and SccBank3(7) = '1' else '0';

  ----------------------------------------------------------------
  -- SCC bank register
  ----------------------------------------------------------------
  process(clk21m, reset)

  begin

    if (reset = '1') then

      SccBank0   <= X"00";
      SccBank1   <= X"01";
      SccBank2   <= X"02";
      SccBank3   <= X"03";
      SccModeA   <= (others => '0');
      SccModeB   <= (others => '0');

    elsif (clk21m'event and clk21m = '1') then

      if (mapsel(0) = '0') then

        -- Mapped I/O port access on 5000-57FFh ... Bank register write
        if (req = '1' and SccSel = "00" and wrt = '1' and adr(15 downto 11) = "01010" and 
            SccModeA(6) = '0' and SccModeA(4) = '0' and SccModeB(4) = '0') then
          SccBank0 <= dbo;
        end if;
        -- Mapped I/O port access on 7000-77FFh ... Bank register write
        if (req = '1' and SccSel = "00" and wrt = '1' and adr(15 downto 11) = "01110" and 
            SccModeA(6) = '0' and SccModeA(4) = '0' and SccModeB(4) = '0') then
          SccBank1 <= dbo;
        end if;
        -- Mapped I/O port access on 9000-97FFh ... Bank register write
        if (req = '1' and SccSel = "00" and wrt = '1' and adr(15 downto 11) = "10010" and 
            SccModeB(4) = '0') then
          SccBank2 <= dbo;
        end if;
        -- Mapped I/O port access on B000-B7FFh ... Bank register write
        if (req = '1' and SccSel = "00" and wrt = '1' and adr(15 downto 11) = "10110" and 
            SccModeA(6) = '0' and SccModeA(4) = '0' and SccModeB(4) = '0') then
          SccBank3 <= dbo;
        end if;
        -- Mapped I/O port access on 7FFE-7FFFh ... Register write
        if (req = '1' and SccSel = "00" and wrt = '1' and adr(15 downto 13) = "011" and 
            Dec1FFE = '1' and SccModeB(5 downto 4) = "00") then 
          SccModeA <= dbo;
        end if;
        -- Mapped I/O port access on BFFE-BFFFh ... Register write
        if (req = '1' and SccSel = "00" and wrt = '1' and adr(15 downto 13) = "101" and 
            Dec1FFE = '1' and SccModeA(6) = '0' and SccModeA(4) = '0') then 
          SccModeB <= dbo;
        end if;

      else

        -- Mapped I/O port access on 6000-6FFFh ... Bank register write
        if (req = '1' and SccSel = "00" and wrt = '1' and adr(15 downto 12) = "0110") then
          -- ASC8K / 6000-67FFh
          if (mapsel(1) = '0' and adr(11) = '0') then
            SccBank0 <= dbo;
          -- ASC8K / 6800-6FFFh
          elsif (mapsel(1) = '0' and adr(11) = '1') then
            SccBank1 <= dbo;
          -- ASC16K / 6000-67FFh
          elsif (adr(11) = '0') then
            SccBank0 <= dbo(7) & dbo(5 downto 0) & '0';
            SccBank1 <= dbo(7) & dbo(5 downto 0) & '1';
          end if;
        end if;

        -- Mapped I/O port access on 7000-7FFFh ... Bank register write
        if (req = '1' and SccSel = "00" and wrt = '1' and adr(15 downto 12) = "0111") then
          -- ASC8K / 7000-77FFh
          if (mapsel(1) = '0' and adr(11) = '0') then
            SccBank2 <= dbo;
          -- ASC8K / 7800-7FFFh
          elsif (mapsel(1) = '0' and adr(11) = '1') then
            SccBank3 <= dbo;
          -- ASC16K / 7000-77FFh
          elsif (adr(11) = '0') then
            SccBank2 <= dbo(7) & dbo(5 downto 0) & '0';
            SccBank3 <= dbo(7) & dbo(5 downto 0) & '1';
          end if;
        end if;

      end if;

    end if;

  end process;

  ----------------------------------------------------------------
  -- SPC access (reserved)
  ----------------------------------------------------------------
  -- SCSI controller access request (4000-5FFFh/A000-BFFFh)
--SpcReq <= req when SccModeA(6) = '1' and SccModeA(4) = '0' and
--                   (adr(15 downto 13) = "010" or adr(15 downto 13) = "101") else '0';

  ----------------------------------------------------------------
  -- Connect components
  ----------------------------------------------------------------
  SccCh  : scc_wave
    port map(clk21m, reset, clkena, WavReq, WavAckL, wrt,     WavAdr, WavDbiL, dbo, SccAmpL);
    WavAckR <= WavAckL;
    WavDbiR <= WavDbiL;
    SccAmpR <= SccAmpL;

--SccChL : scc_wave
--  port map(clk21m, reset, clkena, WavReq, WavAckL, WavWrtL, WavAdr, WavDbiL, dbo, SccAmpL);

--SccChR : scc_wave
--  port map(clk21m, reset, clkena, WavReq, WavAckR, WavWrtR, WavAdr, WavDbiR, dbo, SccAmpR);

  ----------------------------------------------------------------
  -- Wave output (L / R)
  ----------------------------------------------------------------
  wavl <= SccAmpL;
  wavr <= SccAmpR;

end rtl;
