library ieee;
use ieee.std_logic_1164.all;

-- this address decoder inserts dummy setup time on write.
entity address_decoder is
generic (abus_size : integer := 16; dbus_size : integer := 8);
    port (  phi2        : in std_logic; --dropping edge syncronized clock.
            mem_clk     : in std_logic;
            R_nW        : in std_logic; -- active high on read / active low on write.
            addr        : in std_logic_vector (abus_size - 1 downto 0);
            d_io        : in std_logic_vector (dbus_size - 1 downto 0);
            rom_ce_n    : out std_logic;
            ram_ce_n    : out std_logic;
            ppu_ce_n    : out std_logic;
            apu_ce_n    : out std_logic
        );
end address_decoder;

--/*
-- * NES memory map
-- * 0x0000   -   0x07FF      RAM
-- * 0x0800   -   0x1FFF      mirror RAM
-- * 0x2000   -   0x2007      I/O PPU
-- * 0x4000   -   0x401F      I/O APU
-- * 0x6000   -   0x7FFF      battery backup ram
-- * 0x8000   -   0xFFFF      PRG-ROM
-- * */

architecture rtl of address_decoder is

component ram_ctrl
    port (  
            clk              : in std_logic;
            ce_n, oe_n, we_n : in std_logic;
            sync_ce_n        : out std_logic
        );
end component;

signal ram_ce_n_in    : std_logic;
signal r_n            : std_logic;

begin

    rom_ce_n <= '0' when (addr(15) = '1' and R_nW = '1') else
             '1' ;

    ppu_ce_n <= '0'
            when (addr(15) = '0' and addr(14) = '0' and addr(13) = '1')  else
                '1';

    apu_ce_n <= '0'
            when (addr(15) = '0' and addr(14) = '1' and addr(13) = '0')  else
                '1';

    r_n <= not r_nw;
    ram_ctl_inst : ram_ctrl
            port map (mem_clk, ram_ce_n_in, r_n, r_nw, ram_ce_n);

    --ram io timing.
    main_p : process (phi2, addr, d_io, R_nW)
    begin
            -- ram range : 0 - 0x2000.
            -- 0x2000 is 0010_0000_0000_0000
        if ((addr(15) or addr(14) or addr(13)) = '0') then
        --if (addr < "0010000000000000") then
            if (R_nW = '0') then
                --write
                --write timing slided by half clock.
                ram_ce_n_in <= not phi2;
            elsif (R_nW = '1') then 
                --read
                ram_ce_n_in <= '0';
            else
                ram_ce_n_in <= '1';
            end if;
        else
            ram_ce_n_in <= '1';
        end if;
    end process;

end rtl;



-----------------------------------------------------
-----------------------------------------------------
---------- VRAM / CHR ROM Address Decoder -----------
-----------------------------------------------------
-----------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;

entity v_address_decoder is
generic (abus_size : integer := 14; dbus_size : integer := 8);
    port (  clk         : in std_logic; 
            mem_clk     : in std_logic;
            rd_n        : in std_logic;
            wr_n        : in std_logic;
            ale         : in std_logic;
            v_addr      : in std_logic_vector (13 downto 0);
            v_data      : in std_logic_vector (7 downto 0);
            nt_v_mirror : in std_logic;
            pt_ce_n     : out std_logic;
            nt0_ce_n    : out std_logic;
            nt1_ce_n    : out std_logic
        );
end v_address_decoder;

-- Address      Size    Description
-- $0000-$0FFF  $1000   Pattern Table 0 [lower CHR bank]
-- $1000-$1FFF  $1000   Pattern Table 1 [upper CHR bank]
-- $2000-$23FF  $0400   Name Table #0
-- $2400-$27FF  $0400   Name Table #1
-- $2800-$2BFF  $0400   Name Table #2
-- $2C00-$2FFF  $0400   Name Table #3
-- $3000-$3EFF  $0F00   Mirrors of $2000-$2FFF
-- $3F00-$3F1F  $0020   Palette RAM indexes [not RGB values]
-- $3F20-$3FFF  $0080   Mirrors of $3F00-$3F1F

architecture rtl of v_address_decoder is

component ram_ctrl
    port (  
            clk              : in std_logic;
            ce_n, oe_n, we_n : in std_logic;
            sync_ce_n        : out std_logic
        );
end component;

signal nt0_ce_n_in    : std_logic;
signal nt1_ce_n_in    : std_logic;
begin

    --pattern table
    pt_ce_n <= '0' when (v_addr(13) = '0' and rd_n = '0') else
             '1' ;

    nt0_ram_ctl : ram_ctrl
            port map (mem_clk, nt0_ce_n_in, rd_n, wr_n, nt0_ce_n);
    nt1_ram_ctl : ram_ctrl
            port map (mem_clk, nt1_ce_n_in, rd_n, wr_n, nt1_ce_n);

    --ram io timing.
    main_p : process (clk, v_addr, v_data, wr_n)
    begin
        if (v_addr(13) = '1') then
            ---name tbl
            if ((v_addr(12) and v_addr(11) and v_addr(10) 
                        and v_addr(9) and v_addr(8)) = '0') then
                if (nt_v_mirror = '1') then
                    --bit 10 is the name table selector.
                    if (v_addr(10) = '0') then
                        --name table 0 enable.
                        nt1_ce_n_in <= '1';
                        if (wr_n = '0') then
                            --write
                            nt0_ce_n_in <= clk;
                        elsif (rd_n = '0') then 
                            --read
                            nt0_ce_n_in <= '0';
                        else
                            nt0_ce_n_in <= '1';
                        end if;
                    else
                        --name table 1 enable.
                        nt0_ce_n_in <= '1';
                        if (wr_n = '0') then
                            --write
                            nt1_ce_n_in <= clk;
                        elsif (rd_n = '0') then 
                            --read
                            nt1_ce_n_in <= '0';
                        else
                            nt1_ce_n_in <= '1';
                        end if;
                    end if;
                else
                    --horizontal mirror.
                    --bit 11 is the name table selector.
                    if (v_addr(11) = '0') then
                        --name table 0 enable.
                        nt1_ce_n_in <= '1';
                        if (wr_n = '0') then
                            --write
                            nt0_ce_n_in <= clk;
                        elsif (rd_n = '0') then 
                            --read
                            nt0_ce_n_in <= '0';
                        else
                            nt0_ce_n_in <= '1';
                        end if;
                    else
                        --name table 1 enable.
                        nt0_ce_n_in <= '1';
                        if (wr_n = '0') then
                            --write
                            nt1_ce_n_in <= clk;
                        elsif (rd_n = '0') then 
                            --read
                            nt1_ce_n_in <= '0';
                        else
                            nt1_ce_n_in <= '1';
                        end if;
                    end if;
                end if; --if (nt_v_mirror = '1') then
            else
                nt0_ce_n_in <= '1';
                nt1_ce_n_in <= '1';
            end if;
        else
            nt0_ce_n_in <= '1';
            nt1_ce_n_in <= '1';
        end if; --if (v_addr(13) = '1') then
    end process;

end rtl;

