mirror of
https://github.com/MiSTer-devel/Astrocade_MiSTer.git
synced 2026-05-24 03:03:11 +00:00
1048 lines
35 KiB
VHDL
1048 lines
35 KiB
VHDL
--
|
|
-- A simulation model of Bally Astrocade hardware
|
|
-- Copyright (c) MikeJ - Nov 2004
|
|
--
|
|
-- 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 004 spartan3e hires release
|
|
-- version 003 spartan3e release
|
|
-- version 001 initial release
|
|
--
|
|
-- microcycler not used
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.std_logic_arith.all;
|
|
use ieee.std_logic_unsigned.all;
|
|
|
|
|
|
entity BALLY_DATA is
|
|
port (
|
|
I_MXA : in std_logic_vector(15 downto 0);
|
|
I_MXD : in std_logic_vector( 7 downto 0);
|
|
O_MXD : out std_logic_vector( 7 downto 0);
|
|
O_MXD_OE_L : out std_logic;
|
|
|
|
-- cpu control signals
|
|
I_M1_L : in std_logic;
|
|
I_RD_L : in std_logic;
|
|
I_MREQ_L : in std_logic;
|
|
I_IORQ_L : in std_logic;
|
|
|
|
-- memory
|
|
O_DATEN_L : out std_logic; -- looks like the real chip
|
|
O_DATWR : out std_logic; -- ram ena to fake up write at rising edge of DATEN_L
|
|
I_MDX : in std_logic_vector( 7 downto 0); -- upper 8 bits for high res
|
|
I_MD : in std_logic_vector( 7 downto 0);
|
|
O_MD : out std_logic_vector( 7 downto 0);
|
|
O_MD_OE_L : out std_logic;
|
|
-- custom
|
|
O_MC1 : out std_logic;
|
|
O_MC0 : out std_logic;
|
|
|
|
O_HORIZ_DR : out std_logic;
|
|
O_VERT_DR : out std_logic;
|
|
I_WRCTL_L : in std_logic;
|
|
I_LTCHDO : in std_logic;
|
|
|
|
I_SERIAL1 : in std_logic;
|
|
I_SERIAL0 : in std_logic;
|
|
|
|
O_VIDEO_R : out std_logic_vector(3 downto 0);
|
|
O_VIDEO_G : out std_logic_vector(3 downto 0);
|
|
O_VIDEO_B : out std_logic_vector(3 downto 0);
|
|
O_HSYNC : out std_logic;
|
|
O_VSYNC : out std_logic;
|
|
O_HBLANK : out std_logic;
|
|
O_VBLANK : out std_logic;
|
|
O_FPSYNC : out std_logic; -- first active pixel
|
|
|
|
-- clks
|
|
O_CPU_ENA : out std_logic; -- cpu clock ena
|
|
O_PIX_ENA : out std_logic; -- pixel clock ena
|
|
ENA : in std_logic;
|
|
CLK : in std_logic
|
|
);
|
|
end;
|
|
|
|
architecture RTL of BALLY_DATA is
|
|
|
|
-- const
|
|
-- horizontal timing constants
|
|
-- approx 78 clocks blanking,34 for sync
|
|
|
|
-- original 7.159 Mhz clock
|
|
constant H_LINE_SYNCS : std_logic_vector(8 downto 0) := conv_std_logic_vector( 0,9);
|
|
constant H_LINE_SYNCR : std_logic_vector(8 downto 0) := conv_std_logic_vector( 33,9);
|
|
constant H_BLANK_N1S : std_logic_vector(8 downto 0) := conv_std_logic_vector( 0,9); -- first eq
|
|
constant H_BLANK_N1R : std_logic_vector(8 downto 0) := conv_std_logic_vector( 16,9);
|
|
constant H_BLANK_N2S : std_logic_vector(8 downto 0) := conv_std_logic_vector(227,9); -- second eq
|
|
constant H_BLANK_N2R : std_logic_vector(8 downto 0) := conv_std_logic_vector(243,9);
|
|
constant H_BLANK_B1S : std_logic_vector(8 downto 0) := conv_std_logic_vector( 0,9); -- first broad
|
|
constant H_BLANK_B1R : std_logic_vector(8 downto 0) := conv_std_logic_vector(193,9);
|
|
constant H_BLANK_B2S : std_logic_vector(8 downto 0) := conv_std_logic_vector(227,9); -- second broad
|
|
constant H_BLANK_B2R : std_logic_vector(8 downto 0) := conv_std_logic_vector(421,9);
|
|
constant H_BLANK_S : std_logic_vector(8 downto 0) := conv_std_logic_vector(444,9); -- horiz blanking
|
|
constant H_BLANK_R : std_logic_vector(8 downto 0) := conv_std_logic_vector( 67,9); -- 78 clocks, starting 12 before sync
|
|
|
|
constant H_BLANK_LR : std_logic_vector(8 downto 0) := conv_std_logic_vector(245,9); -- half line left reset
|
|
constant H_BLANK_RS : std_logic_vector(8 downto 0) := conv_std_logic_vector(225,9); -- half line right set
|
|
|
|
--constant H_DRIVE_S : std_logic_vector(8 downto 0) := conv_std_logic_vector( 60,9); -- hdrive pulse
|
|
--constant H_DRIVE_R : std_logic_vector(8 downto 0) := conv_std_logic_vector( 63,9);
|
|
--constant H_VDRIVE_R : std_logic_vector(8 downto 0) := conv_std_logic_vector( 71,9);
|
|
-- frig to get screen centered, above numbers are measured
|
|
constant H_DRIVE_S : std_logic_vector(8 downto 0) := conv_std_logic_vector( 60+8,9); -- hdrive pulse
|
|
constant H_DRIVE_R : std_logic_vector(8 downto 0) := conv_std_logic_vector( 63+8,9);
|
|
constant H_VDRIVE_R : std_logic_vector(8 downto 0) := conv_std_logic_vector( 71+8,9);
|
|
|
|
constant H_LEN : std_logic_vector(8 downto 0) := conv_std_logic_vector(453,9); -- line length (455 clocks)
|
|
constant V_LEN : std_logic_vector(10 downto 0) := conv_std_logic_vector(525,11); -- frame length
|
|
|
|
component BALLY_COL_PAL
|
|
port (
|
|
ADDR : in std_logic_vector(7 downto 0);
|
|
DATA : out std_logic_vector(11 downto 0)
|
|
);
|
|
end component;
|
|
|
|
-- Signals
|
|
type array_8x8 is array (0 to 7) of std_logic_vector(7 downto 0);
|
|
type array_bool8 is array (0 to 7) of boolean;
|
|
|
|
signal ena_cnt : std_logic_vector(1 downto 0) := "00";
|
|
signal cpu_ena : std_logic;
|
|
signal cpu_ena_t1 : std_logic;
|
|
signal pix_ena : std_logic;
|
|
signal pix_load : std_logic;
|
|
signal cs_w : std_logic;
|
|
signal cs_r : std_logic;
|
|
-- cpu if
|
|
signal col_ld : array_bool8;
|
|
signal magic_ld : boolean;
|
|
signal r_col : array_8x8 := (x"00",x"00",x"00",x"00",x"00",x"00",x"00",x"00");
|
|
signal r_hi_res : std_logic;
|
|
signal r_backgnd_col : std_logic_vector(1 downto 0) := "00";
|
|
signal r_horiz_pos : std_logic_vector(5 downto 0) := "010100"; -- 20
|
|
signal r_vert_blank : std_logic_vector(7 downto 0) := x"10"; -- line 8 (7..1)
|
|
--signal r_vert_blank : std_logic_vector(7 downto 0) := x"BF"; -- line 191 (7..1)
|
|
signal r_magic : std_logic_vector(7 downto 0) := x"00";
|
|
signal r_expand : std_logic_vector(7 downto 0) := x"00";
|
|
signal r_intercept : std_logic_vector(7 downto 0) := x"00";
|
|
-- timing
|
|
signal do_horiz_dr : std_logic;
|
|
signal do_horiz_dr_t1 : std_logic;
|
|
signal do_vert_dr : std_logic;
|
|
signal do_vert_dr_int : std_logic;
|
|
signal hsync : std_logic;
|
|
signal vsync : std_logic := '0';
|
|
signal vsync_t1 : std_logic;
|
|
signal v_1st_actv : std_logic;
|
|
|
|
signal h_count : std_logic_vector ( 8 downto 0) := (others => '0');
|
|
signal v_count : std_logic_vector (10 downto 0) := "00000000001";
|
|
signal sg_line_sync : std_logic;
|
|
signal sg_blank_n1 : std_logic;
|
|
signal sg_blank_n2 : std_logic;
|
|
signal sg_blank_b1 : std_logic;
|
|
signal sg_blank_b2 : std_logic;
|
|
signal sg_hstart : std_logic;
|
|
signal sg_hstart_t1 : std_logic;
|
|
signal sg_hblank : std_logic;
|
|
signal sg_hblank_l : std_logic;
|
|
signal sg_hblank_r : std_logic;
|
|
signal sg_vblank : std_logic;
|
|
signal sg_vstart : std_logic;
|
|
--
|
|
signal sg_blanking_EQ : std_logic;
|
|
signal sg_blanking_BRD : std_logic;
|
|
signal sg_line263 : std_logic;
|
|
signal sg_line266 : std_logic;
|
|
signal sg_line269 : std_logic;
|
|
signal sg_line272 : std_logic;
|
|
signal sg_line283 : std_logic;
|
|
signal sg_sync : std_logic;
|
|
signal sg_blank : std_logic;
|
|
signal sg_neg_sync : std_logic;
|
|
|
|
--
|
|
signal horiz_pos : std_logic_vector(7 downto 0);
|
|
signal vert_pos : std_logic_vector(7 downto 0);
|
|
signal hactv : std_logic;
|
|
signal vactv : std_logic;
|
|
|
|
-- data
|
|
signal ram_write_reg : std_logic_vector(7 downto 0);
|
|
signal datwr : std_logic;
|
|
signal ltchdo_t1 : std_logic;
|
|
signal mxd_out_ena : std_logic;
|
|
signal mxd_out_intercept : std_logic;
|
|
signal mxd_out_intercept_e1 : std_logic;
|
|
-- video
|
|
signal video_cyc : std_logic;
|
|
signal video_cyc_ras : std_logic;
|
|
signal video_cyc_ras_t1 : std_logic;
|
|
signal video_cyc_ras_t2 : std_logic;
|
|
signal video_shifter : std_logic_vector(15 downto 0);
|
|
signal video_shifter_lflag : std_logic;
|
|
signal video_shifter_actv : std_logic;
|
|
|
|
signal lflag : std_logic; -- left flag
|
|
signal lflag_inhib : std_logic;
|
|
signal lflag_e : std_logic_vector(2 downto 0);
|
|
signal hactv_e : std_logic_vector(2 downto 0);
|
|
signal col_in : std_logic_vector(7 downto 0);
|
|
signal col_out : std_logic_vector(11 downto 0);
|
|
-- datapath
|
|
signal done_magic_write : std_ulogic;
|
|
signal done_magic_write_t1 : std_ulogic;
|
|
signal expand_lower_sel : std_logic;
|
|
|
|
signal expand_out : std_logic_vector(7 downto 0);
|
|
signal flopper_out : std_logic_vector(7 downto 0);
|
|
signal pixel_collide : std_logic_vector(3 downto 0);
|
|
|
|
signal shifter_out : std_logic_vector(13 downto 0);
|
|
signal shifter_out_reg : std_logic_vector(5 downto 0);
|
|
|
|
signal rotate_cnt : std_logic_vector(2 downto 0);
|
|
signal rotate_inhibit_write : std_logic;
|
|
signal rotate_pixa : std_logic_vector(7 downto 0) := (others => '0');
|
|
signal rotate_pixb : std_logic_vector(7 downto 0) := (others => '0');
|
|
signal rotate_pixc : std_logic_vector(7 downto 0) := (others => '0');
|
|
signal rotate_pixd : std_logic_vector(7 downto 0) := (others => '0');
|
|
signal rotate_out : std_logic_vector(7 downto 0);
|
|
signal magic_final : std_logic_vector(7 downto 0);
|
|
signal ram_ip_reg : std_logic_vector(7 downto 0);
|
|
signal ram_op_reg : std_logic_vector(7 downto 0);
|
|
|
|
|
|
|
|
begin
|
|
|
|
p_chip_sel : process(cpu_ena, I_MXA)
|
|
begin
|
|
cs_w <= '0';
|
|
cs_r <= '0';
|
|
if (cpu_ena = '1') then -- cpu access
|
|
if (I_MXA(7 downto 5) = "000") then
|
|
cs_w <= '1';
|
|
end if;
|
|
end if;
|
|
if (I_MXA(7 downto 4) = "0000") then
|
|
cs_r <= '1';
|
|
end if;
|
|
|
|
end process;
|
|
|
|
--
|
|
-- registers
|
|
--
|
|
p_reg_write : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
if (I_RD_L = '1') and (I_IORQ_L = '0') and (I_M1_L = '1') and (cs_w = '1') then
|
|
case I_MXA(4 downto 0) is
|
|
when "01000" => r_hi_res <= I_MXD(0); -- 8
|
|
when "01001" => r_backgnd_col <= I_MXD(7 downto 6); -- 9
|
|
r_horiz_pos <= I_MXD(5 downto 0);
|
|
when "01010" => r_vert_blank <= I_MXD; -- A
|
|
-- B
|
|
when "01100" => r_magic <= I_MXD; -- C
|
|
when "11001" => r_expand <= I_MXD; -- 19
|
|
|
|
when others => null;
|
|
end case;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
p_reg_write_blk_decode : process(I_RD_L, I_M1_L, I_IORQ_L, cs_w, I_MXA)
|
|
begin
|
|
-- these writes will last for several cpu_ena cycles, so you
|
|
-- will get several load pulses
|
|
col_ld <= (others => false);
|
|
magic_ld <= false;
|
|
if (I_RD_L = '1') and (I_IORQ_L = '0') and (I_M1_L = '1') and (cs_w = '1') and (I_MXA(4) = '0') then
|
|
col_ld(0) <= ( I_MXA( 3 downto 0) = x"0") or
|
|
((I_MXA(10 downto 8) = "000") and (I_MXA(3 downto 0) = x"B"));
|
|
|
|
col_ld(1) <= ( I_MXA( 3 downto 0) = x"1") or
|
|
((I_MXA(10 downto 8) = "001") and (I_MXA(3 downto 0) = x"B"));
|
|
|
|
col_ld(2) <= ( I_MXA( 3 downto 0) = x"2") or
|
|
((I_MXA(10 downto 8) = "010") and (I_MXA(3 downto 0) = x"B"));
|
|
|
|
col_ld(3) <= ( I_MXA( 3 downto 0) = x"3") or
|
|
((I_MXA(10 downto 8) = "011") and (I_MXA(3 downto 0) = x"B"));
|
|
|
|
col_ld(4) <= ( I_MXA( 3 downto 0) = x"4") or
|
|
((I_MXA(10 downto 8) = "100") and (I_MXA(3 downto 0) = x"B"));
|
|
|
|
col_ld(5) <= ( I_MXA( 3 downto 0) = x"5") or
|
|
((I_MXA(10 downto 8) = "101") and (I_MXA(3 downto 0) = x"B"));
|
|
|
|
col_ld(6) <= ( I_MXA( 3 downto 0) = x"6") or
|
|
((I_MXA(10 downto 8) = "110") and (I_MXA(3 downto 0) = x"B"));
|
|
|
|
col_ld(7) <= ( I_MXA( 3 downto 0) = x"7") or
|
|
((I_MXA(10 downto 8) = "111") and (I_MXA(3 downto 0) = x"B"));
|
|
|
|
magic_ld <= ( I_MXA( 3 downto 0) = x"C");
|
|
end if;
|
|
end process;
|
|
|
|
p_reg_write_blk : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
for i in 0 to 7 loop
|
|
if col_ld(i) then r_col(i) <= I_MXD; end if;
|
|
end loop;
|
|
end if;
|
|
end process;
|
|
|
|
p_cpu_ena : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
if (do_horiz_dr = '1') then -- 3 clocks long
|
|
ena_cnt <= "00";
|
|
else
|
|
ena_cnt <= ena_cnt + "1";
|
|
end if;
|
|
|
|
cpu_ena <= '0';
|
|
if (ena_cnt = "10") then
|
|
cpu_ena <= '1';
|
|
end if;
|
|
|
|
if (r_hi_res = '1') then
|
|
pix_ena <= '1';
|
|
else
|
|
pix_ena <= ena_cnt(0);
|
|
end if;
|
|
pix_load <= cpu_ena;
|
|
end if;
|
|
end process;
|
|
O_CPU_ENA <= cpu_ena;
|
|
O_PIX_ENA <= pix_ena;
|
|
|
|
p_micro_gen : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
-- not used
|
|
O_MC1 <= '0';
|
|
O_MC0 <= '0';
|
|
end if;
|
|
end process;
|
|
--
|
|
-- sync generation
|
|
--
|
|
p_hv_count : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
if (h_count = H_LEN) then
|
|
h_count <= (others => '0');
|
|
if (v_count = V_LEN) then
|
|
v_count <= "00000000000";
|
|
else
|
|
v_count <= v_count + "1";
|
|
end if;
|
|
else
|
|
h_count <= h_count + "1";
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
p_window_h : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
-- hblank
|
|
sg_hstart <= '0';
|
|
if (h_count = H_BLANK_S) then
|
|
sg_hblank <= '1';
|
|
elsif h_count = H_BLANK_R then
|
|
sg_hstart <= '1';
|
|
sg_hblank <= '0';
|
|
end if;
|
|
|
|
if (h_count = H_BLANK_S) then
|
|
sg_hblank_l <= '1';
|
|
elsif h_count = H_BLANK_LR then
|
|
sg_hblank_l <= '0';
|
|
end if;
|
|
|
|
if (h_count = H_BLANK_RS) then
|
|
sg_hblank_r <= '1';
|
|
elsif h_count = H_BLANK_R then
|
|
sg_hblank_r <= '0';
|
|
end if;
|
|
|
|
if (h_count = H_DRIVE_S) then
|
|
do_horiz_dr <= '1';
|
|
elsif h_count = H_DRIVE_R then
|
|
do_horiz_dr <= '0';
|
|
end if;
|
|
|
|
if (h_count = H_DRIVE_S) then
|
|
do_vert_dr <= '1';
|
|
elsif h_count = H_VDRIVE_R then
|
|
do_vert_dr <= '0';
|
|
end if;
|
|
|
|
-- low res 40 bytes / line (160 pixels, 2 bits per pixel)
|
|
-- vert res 102 lines
|
|
-- two clocks / pixel
|
|
end if;
|
|
end process;
|
|
|
|
p_sync_h : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
-- negative sync pulse
|
|
if (h_count = H_LINE_SYNCS) then
|
|
sg_line_sync <= '1';
|
|
elsif h_count = H_LINE_SYNCR then
|
|
sg_line_sync <= '0';
|
|
end if;
|
|
|
|
-- blanking narrow1
|
|
if (h_count = H_BLANK_N1S) then
|
|
sg_blank_n1 <= '1';
|
|
elsif h_count = H_BLANK_N1R then
|
|
sg_blank_n1 <= '0';
|
|
end if;
|
|
|
|
-- blanking narrow2
|
|
if (h_count = H_BLANK_N2S) then
|
|
sg_blank_n2 <= '1';
|
|
elsif (h_count = H_BLANK_N2R) then
|
|
sg_blank_n2 <= '0';
|
|
end if;
|
|
|
|
-- blanking broad1
|
|
if (h_count = H_BLANK_B1S) then
|
|
sg_blank_b1 <= '1';
|
|
elsif (h_count = H_BLANK_B1R) then
|
|
sg_blank_b1 <= '0';
|
|
end if;
|
|
|
|
-- blanking broad2
|
|
if h_count = H_BLANK_B2S then
|
|
sg_blank_b2 <= '1';
|
|
elsif (h_count = H_BLANK_B2R) then
|
|
sg_blank_b2 <= '0';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
p_window_v : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
-- line 21 first f1 nonblanked video line ** bally chip starts at 20 with a half line
|
|
-- line 44 first f1 video line
|
|
-- line 234 last f1 video line (power up menu)
|
|
-- line 263 last f1 nonblanked video line (half line)
|
|
|
|
-- line 283 first f2 nonblanked video line (half line)
|
|
-- line 307 first f2 video line (power up menu)
|
|
-- line 497 last f2 video line
|
|
-- line 525 last f2 nonblanked video line
|
|
|
|
-- (vblank reg is written as 191 for boot menu)
|
|
-- there are 191 active video lines read from the video store per field (when displaying menu)
|
|
-- The bally data chip seems to get the half lines wrong however (283 whole line)
|
|
-- and it puts a half line on 20. Or, I've missread the field sync pulses.
|
|
-- doesn't really matter as we are driving a vga screen for now ..
|
|
|
|
-- 14.2857MHz (7.1428)
|
|
-- line / 455 = 15.698 K = 29.9 frames
|
|
if (ENA = '1') then
|
|
|
|
sg_vstart <= '0';
|
|
if (v_count = conv_std_logic_vector( 21,11)) then
|
|
sg_vblank <= '0';
|
|
sg_vstart <= '1'; -- field one sync to scan converter
|
|
elsif (v_count = conv_std_logic_vector( 264,11)) then
|
|
sg_vblank <= '1';
|
|
elsif (v_count = conv_std_logic_vector( 283,11)) then
|
|
sg_vblank <= '0';
|
|
elsif (v_count = conv_std_logic_vector( 1,11)) then
|
|
sg_vblank <= '1';
|
|
end if;
|
|
|
|
if (v_count = conv_std_logic_vector( 4,11)) then
|
|
vsync <= '1';
|
|
elsif (v_count = conv_std_logic_vector( 7,11)) then
|
|
vsync <= '0';
|
|
elsif (v_count = conv_std_logic_vector( 267,11)) then
|
|
vsync <= '1';
|
|
elsif (v_count = conv_std_logic_vector( 270,11)) then
|
|
vsync <= '0';
|
|
end if;
|
|
|
|
v_1st_actv <= '0';
|
|
if (v_count = conv_std_logic_vector( 44,11)) or
|
|
(v_count = conv_std_logic_vector( 307,11)) then
|
|
v_1st_actv <= '1';
|
|
end if;
|
|
-- timing from rising edge of v_sync
|
|
-- 4 21 263
|
|
-- 1 18 260
|
|
|
|
-- 267 283 525
|
|
-- 1 17 259
|
|
|
|
-- so field 2 is displayed above field 1 - this is the same as ntsc and seems
|
|
-- to be necessary to get a correct picture on my monitor taking it's sync from vsync
|
|
end if;
|
|
end process;
|
|
O_VBLANK <= sg_vblank;
|
|
|
|
|
|
p_sync_v : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
sg_blanking_eq <= '0';
|
|
if ( (v_count = conv_std_logic_vector( 1,11)) or
|
|
(v_count = conv_std_logic_vector( 2,11)) or
|
|
(v_count = conv_std_logic_vector( 3,11)) or
|
|
(v_count = conv_std_logic_vector( 7,11)) or
|
|
(v_count = conv_std_logic_vector( 8,11)) or
|
|
(v_count = conv_std_logic_vector( 9,11)) or
|
|
(v_count = conv_std_logic_vector(264,11)) or
|
|
(v_count = conv_std_logic_vector(265,11)) or
|
|
(v_count = conv_std_logic_vector(270,11)) or
|
|
(v_count = conv_std_logic_vector(271,11)) ) then
|
|
sg_blanking_eq <= '1';
|
|
end if;
|
|
|
|
sg_blanking_brd <= '0';
|
|
if ( (v_count = conv_std_logic_vector( 4,11)) or
|
|
(v_count = conv_std_logic_vector( 5,11)) or
|
|
(v_count = conv_std_logic_vector( 6,11)) or
|
|
(v_count = conv_std_logic_vector(267,11)) or
|
|
(v_count = conv_std_logic_vector(268,11)) ) then
|
|
sg_blanking_brd <= '1';
|
|
end if;
|
|
|
|
sg_line263 <= '0';
|
|
if (v_count = (conv_std_logic_vector(263,11))) then
|
|
sg_line263 <= '1';
|
|
end if;
|
|
|
|
sg_line266 <= '0';
|
|
if (v_count = (conv_std_logic_vector(266,11))) then
|
|
sg_line266 <= '1';
|
|
end if;
|
|
|
|
sg_line269 <= '0';
|
|
if (v_count = (conv_std_logic_vector(269,11))) then
|
|
sg_line269 <= '1';
|
|
end if;
|
|
|
|
sg_line272 <= '0';
|
|
if (v_count = (conv_std_logic_vector(272,11))) then
|
|
sg_line272 <= '1';
|
|
end if;
|
|
|
|
sg_line283 <= '0';
|
|
if (v_count = (conv_std_logic_vector(283,11))) then
|
|
sg_line283 <= '1';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
p_syncgen : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
sg_sync <= '0';
|
|
|
|
if (sg_blanking_eq = '1') then
|
|
sg_sync <= sg_blank_n1 or sg_blank_n2;
|
|
|
|
elsif (sg_blanking_brd = '1') then
|
|
sg_sync <= sg_blank_b1 or sg_blank_b2;
|
|
|
|
elsif (sg_line263 = '1') then
|
|
sg_sync <= sg_blank_n2 or sg_line_sync;
|
|
|
|
elsif (sg_line266 = '1') then
|
|
sg_sync <= sg_blank_n1 or sg_blank_b2;
|
|
|
|
elsif (sg_line269 = '1') then
|
|
sg_sync <= sg_blank_b1 or sg_blank_n2;
|
|
|
|
elsif (sg_line272 = '1') then
|
|
sg_sync <= sg_blank_n1;
|
|
|
|
else
|
|
sg_sync <= sg_line_sync; -- normal line.
|
|
end if;
|
|
|
|
sg_blank <= sg_hblank or sg_vblank;
|
|
if (sg_line263 = '1') then
|
|
sg_blank <= sg_hblank_r; -- left half line
|
|
elsif (sg_line283 = '1') then
|
|
sg_blank <= sg_hblank_l; -- right half line
|
|
end if;
|
|
|
|
hsync <= sg_line_sync;
|
|
vsync_t1 <= vsync;
|
|
end if;
|
|
end process;
|
|
O_HBLANK <= sg_hblank;
|
|
|
|
-- code duplicated in addr chip
|
|
p_active_picture : process
|
|
variable vcomp : std_logic_vector(7 downto 0);
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
if (do_horiz_dr = '1') then
|
|
horiz_pos <= (others => '0');
|
|
elsif (cpu_ena = '1') then -- clk phi
|
|
horiz_pos <= horiz_pos + "1";
|
|
end if;
|
|
|
|
do_horiz_dr_t1 <= do_horiz_dr;
|
|
if (do_vert_dr_int = '1') then
|
|
vert_pos <= (others => '0');
|
|
elsif (do_horiz_dr = '1') and (do_horiz_dr_t1 = '0') then -- rising edge
|
|
if (vert_pos = x"ff") then
|
|
null;
|
|
else
|
|
vert_pos <= vert_pos + "1";
|
|
end if;
|
|
end if;
|
|
|
|
-- bit of guesswork here
|
|
if (cpu_ena = '1') then
|
|
if (horiz_pos = x"01") then
|
|
hactv <= '1';
|
|
elsif (horiz_pos = x"51") then
|
|
hactv <= '0';
|
|
end if;
|
|
end if;
|
|
vcomp := r_vert_blank(7 downto 0);
|
|
-- DATA chip does line pairs only in low res mode
|
|
if (r_hi_res = '0') then
|
|
vcomp(0) := '0';
|
|
end if;
|
|
|
|
vactv <= '0';
|
|
if (vert_pos < vcomp) then
|
|
vactv <= '1';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
p_video_cyc : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
if (cpu_ena = '1') then
|
|
video_cyc <= '0';
|
|
if (hactv = '1') and (vactv = '1') and (horiz_pos(0) = '0') then
|
|
video_cyc <= '1';
|
|
end if;
|
|
video_cyc_ras <= video_cyc;
|
|
end if;
|
|
video_cyc_ras_t1 <= video_cyc_ras;
|
|
video_cyc_ras_t2 <= video_cyc_ras_t1; -- match delay to rams
|
|
end if;
|
|
end process;
|
|
|
|
p_left_flag : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
-- apparently horiz_pos 40 or above do not have any effect
|
|
if (do_horiz_dr = '1') then
|
|
lflag_inhib <= '0';
|
|
elsif (cpu_ena = '1') and (horiz_pos(6 downto 0) = ("1001111")) then
|
|
lflag_inhib <= '1';
|
|
end if;
|
|
|
|
if (do_horiz_dr = '1') then
|
|
lflag <= '1';
|
|
elsif (cpu_ena = '1') and (horiz_pos(6 downto 0) = (r_horiz_pos & '1')) and (lflag_inhib = '0') then
|
|
lflag <= '0';
|
|
end if;
|
|
|
|
-- pipeline delay
|
|
if (do_horiz_dr = '1') then
|
|
lflag_e <= "111";
|
|
hactv_e <= "000";
|
|
|
|
elsif (cpu_ena = '1') then
|
|
lflag_e(0) <= lflag;
|
|
lflag_e(2 downto 1) <= lflag_e(1 downto 0);
|
|
hactv_e(0) <= hactv;
|
|
hactv_e(2 downto 1) <= hactv_e(1 downto 0);
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
p_video_shifter : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
-- video cyc, grab from ram
|
|
if (pix_ena = '1') then
|
|
if (pix_load = '1') and (video_cyc_ras_t2 = '1') then
|
|
video_shifter <= I_MD(7 downto 0) & I_MDX(7 downto 0);
|
|
else
|
|
-- top left pixel is bits 7,6
|
|
video_shifter <= video_shifter(13 downto 0) & "00";
|
|
end if;
|
|
|
|
end if;
|
|
|
|
if (do_horiz_dr = '1') then
|
|
video_shifter_lflag <= '1';
|
|
video_shifter_actv <= '0'; -- background col
|
|
elsif (pix_ena = '1') and (pix_load = '1') then
|
|
video_shifter_lflag <= lflag_e(2);
|
|
video_shifter_actv <= hactv_e(2) and vactv;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
p_col_sel : process(video_shifter, video_shifter_lflag, video_shifter_actv,
|
|
r_col, r_backgnd_col)
|
|
variable sel : std_logic_vector(2 downto 0);
|
|
begin
|
|
if (video_shifter_actv = '0') then
|
|
sel := video_shifter_lflag & r_backgnd_col;
|
|
else
|
|
sel := video_shifter_lflag & video_shifter(7+8 downto 6+8);
|
|
end if;
|
|
|
|
col_in <= (others => '0');
|
|
case sel is
|
|
when "000" => col_in <= r_col(0);
|
|
when "001" => col_in <= r_col(1);
|
|
when "010" => col_in <= r_col(2);
|
|
when "011" => col_in <= r_col(3);
|
|
when "100" => col_in <= r_col(4);
|
|
when "101" => col_in <= r_col(5);
|
|
when "110" => col_in <= r_col(6);
|
|
when "111" => col_in <= r_col(7);
|
|
when others => null;
|
|
end case;
|
|
end process;
|
|
|
|
u_col : entity work.BALLY_COL_PAL
|
|
port map (
|
|
ADDR => col_in,
|
|
DATA => col_out
|
|
);
|
|
|
|
p_video_out : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
--sg_neg_sync <= not sg_sync;
|
|
O_HSYNC <= hsync;
|
|
O_VSYNC <= vsync_t1;
|
|
|
|
if (sg_blank = '1') then
|
|
O_VIDEO_R <= "0000";
|
|
O_VIDEO_G <= "0000";
|
|
O_VIDEO_B <= "0000";
|
|
else
|
|
O_VIDEO_R <= col_out(11 downto 8);
|
|
O_VIDEO_G <= col_out( 7 downto 4);
|
|
O_VIDEO_B <= col_out( 3 downto 0);
|
|
end if;
|
|
do_vert_dr_int <= v_1st_actv and do_vert_dr;
|
|
sg_hstart_t1 <= sg_hstart;
|
|
O_FPSYNC <= sg_hstart_t1 and sg_vstart;
|
|
end if;
|
|
end process;
|
|
O_VERT_DR <= do_vert_dr_int;
|
|
O_HORIZ_DR <= do_horiz_dr;
|
|
--
|
|
-- data path
|
|
--
|
|
p_ramin : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
ram_write_reg <= I_MXD;
|
|
|
|
done_magic_write <= '0';
|
|
if (I_WRCTL_L = '0') and (I_MXA(15 downto 14) = "00") and (cpu_ena = '1') then
|
|
done_magic_write <= '1';
|
|
end if;
|
|
done_magic_write_t1 <= done_magic_write; -- make sure we have finished ram write
|
|
end if;
|
|
end process;
|
|
|
|
p_expand : process(ram_write_reg, r_magic, r_expand, expand_lower_sel)
|
|
variable expand_sel : std_logic_vector(3 downto 0);
|
|
begin
|
|
if (expand_lower_sel = '1') then -- reg cleared by magic, upper when 0
|
|
expand_sel := ram_write_reg(3 downto 0);
|
|
else
|
|
expand_sel := ram_write_reg(7 downto 4);
|
|
end if;
|
|
|
|
if (r_magic(3) = '0') then -- bypass
|
|
expand_out <= ram_write_reg;
|
|
else
|
|
for i in 0 to 3 loop
|
|
if (expand_sel(i) = '0') then
|
|
expand_out((i*2)+1 downto i*2) <= r_expand(1 downto 0);
|
|
else
|
|
expand_out((i*2)+1 downto i*2) <= r_expand(3 downto 2);
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
end process;
|
|
|
|
p_rotate_reg : process
|
|
variable shift : std_logic;
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
if magic_ld then
|
|
rotate_cnt <= "000";
|
|
expand_lower_sel <= '0';
|
|
shifter_out_reg <= (others => '0');
|
|
elsif (done_magic_write_t1 = '1') then
|
|
rotate_cnt <= rotate_cnt + "1";
|
|
expand_lower_sel <= not expand_lower_sel;
|
|
shifter_out_reg <= shifter_out(5 downto 0);
|
|
end if;
|
|
|
|
rotate_inhibit_write <= '0';
|
|
shift := '0';
|
|
if (I_MXA(15 downto 14) = "00") then
|
|
if (rotate_cnt(2) = '0') then
|
|
rotate_inhibit_write <= r_magic(2); -- only if using rotate
|
|
if (cpu_ena = '1') and (I_WRCTL_L = '0') then
|
|
shift := '1';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
if (shift = '1') then
|
|
rotate_pixa <= expand_out(7 downto 6) & rotate_pixa(7 downto 2); -- top
|
|
rotate_pixb <= expand_out(5 downto 4) & rotate_pixb(7 downto 2);
|
|
rotate_pixc <= expand_out(3 downto 2) & rotate_pixc(7 downto 2);
|
|
rotate_pixd <= expand_out(1 downto 0) & rotate_pixd(7 downto 2);
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
p_rotate_shifter : process(expand_out, shifter_out_reg, r_magic,
|
|
rotate_cnt, rotate_pixa, rotate_pixb, rotate_pixc, rotate_pixd)
|
|
begin
|
|
-- r_magic bits 1,0
|
|
shifter_out <= (others => '0'); -- default
|
|
case r_magic(1 downto 0) is
|
|
when "00" => shifter_out <= expand_out(7 downto 0) & "000000";
|
|
when "01" => shifter_out <= shifter_out_reg(5 downto 4) & expand_out(7 downto 0) & "0000" ;
|
|
when "10" => shifter_out <= shifter_out_reg(5 downto 2) & expand_out(7 downto 0) & "00" ;
|
|
when "11" => shifter_out <= shifter_out_reg(5 downto 0) & expand_out(7 downto 0);
|
|
when others => null;
|
|
end case;
|
|
|
|
rotate_out <= (others => '0'); -- default
|
|
case rotate_cnt(1 downto 0) is
|
|
when "00" => rotate_out <= rotate_pixa;
|
|
when "01" => rotate_out <= rotate_pixb;
|
|
when "10" => rotate_out <= rotate_pixc;
|
|
when "11" => rotate_out <= rotate_pixd;
|
|
when others => null;
|
|
end case;
|
|
end process;
|
|
|
|
p_flopper : process(shifter_out, rotate_out, r_magic)
|
|
variable flopper_src : std_logic_vector(7 downto 0);
|
|
begin
|
|
if (r_magic(2) = '0') then -- rotate bypass
|
|
flopper_src := shifter_out(13 downto 6);
|
|
else
|
|
flopper_src := rotate_out;
|
|
end if;
|
|
|
|
if (r_magic(6) = '0') then -- flopper bypass
|
|
flopper_out <= flopper_src;
|
|
else
|
|
flopper_out(7 downto 6) <= flopper_src(1 downto 0);
|
|
flopper_out(5 downto 4) <= flopper_src(3 downto 2);
|
|
flopper_out(3 downto 2) <= flopper_src(5 downto 4);
|
|
flopper_out(1 downto 0) <= flopper_src(7 downto 6);
|
|
end if;
|
|
end process;
|
|
|
|
p_or_xor : process(flopper_out, ram_ip_reg, r_magic)
|
|
variable result_or : std_logic_vector(7 downto 0);
|
|
variable result_xor : std_logic_vector(7 downto 0);
|
|
begin
|
|
result_or := flopper_out or ram_ip_reg;
|
|
result_xor := flopper_out xor ram_ip_reg;
|
|
|
|
magic_final <= (others => '0');
|
|
case r_magic(5 downto 4) is
|
|
when "00" => magic_final <= flopper_out; -- none
|
|
when "01" => magic_final <= result_or; -- or
|
|
when "10" => magic_final <= result_xor; -- xor
|
|
when "11" => magic_final <= result_xor; -- xor and or
|
|
when others => null;
|
|
end case;
|
|
end process;
|
|
|
|
p_intercept : process(flopper_out, ram_ip_reg)
|
|
begin
|
|
pixel_collide <= (others => '0');
|
|
for i in 0 to 3 loop
|
|
if (flopper_out((i*2)+1 downto (i*2)) /= "00") and
|
|
(ram_ip_reg((i*2)+1 downto (i*2)) /= "00") then
|
|
pixel_collide(i) <= '1';
|
|
end if;
|
|
end loop;
|
|
|
|
end process;
|
|
|
|
p_intercept_reg : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
if (cpu_ena = '1') then
|
|
mxd_out_intercept_e1 <= mxd_out_intercept;
|
|
end if;
|
|
|
|
-- reset
|
|
if (mxd_out_intercept = '0') and (mxd_out_intercept_e1 = '1') and (cpu_ena = '1') then -- end of read
|
|
r_intercept(3 downto 0) <= "0000";
|
|
elsif (datwr = '1') and (I_MXA(15 downto 14) = "00") and (rotate_inhibit_write = '0') then
|
|
-- write
|
|
if (r_magic(5) = '1') or (r_magic(4) = '1') then -- or/xor write only
|
|
r_intercept(0) <= r_intercept(0) or pixel_collide(3);
|
|
r_intercept(1) <= r_intercept(1) or pixel_collide(2);
|
|
r_intercept(2) <= r_intercept(2) or pixel_collide(1);
|
|
r_intercept(3) <= r_intercept(3) or pixel_collide(0);
|
|
|
|
r_intercept(4) <= pixel_collide(3);
|
|
r_intercept(5) <= pixel_collide(2);
|
|
r_intercept(6) <= pixel_collide(1);
|
|
r_intercept(7) <= pixel_collide(0);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
p_output_reg : process
|
|
begin
|
|
wait until rising_edge(CLK);
|
|
if (ENA = '1') then
|
|
if (cpu_ena = '1') then
|
|
ltchdo_t1 <= I_LTCHDO;
|
|
ram_ip_reg <= I_MD(7 downto 0); -- used for or / xor
|
|
end if;
|
|
|
|
if (I_MXA(15 downto 14) = "00") then -- magic write
|
|
ram_op_reg <= magic_final;
|
|
else
|
|
ram_op_reg <= ram_write_reg;
|
|
end if;
|
|
|
|
cpu_ena_t1 <= cpu_ena;
|
|
datwr <= cpu_ena_t1 and (not I_WRCTL_L);
|
|
end if;
|
|
end process;
|
|
O_DATWR <= datwr;
|
|
-- ram out
|
|
|
|
p_ramout : process(I_WRCTL_L, ram_op_reg, rotate_inhibit_write)
|
|
begin
|
|
O_MD_OE_L <= '1';
|
|
O_DATEN_L <= '1';
|
|
O_MD <= (others => 'X'); -- debug
|
|
if (I_WRCTL_L = '0') and (rotate_inhibit_write = '0') then
|
|
O_MD <= ram_op_reg;
|
|
O_MD_OE_L <= '0';
|
|
O_DATEN_L <= '0';
|
|
end if;
|
|
end process;
|
|
|
|
p_mxd_out_ena : process(I_LTCHDO, ltchdo_t1, I_RD_L, I_IORQ_L, cs_r, I_MXA)
|
|
begin
|
|
|
|
mxd_out_ena <= '0';
|
|
mxd_out_intercept <= '0';
|
|
|
|
if (I_LTCHDO = '1') or (ltchdo_t1 = '1') then
|
|
mxd_out_ena <= '1';
|
|
else
|
|
if (I_RD_L = '0') and (I_IORQ_L = '0') and (cs_r = '1') then
|
|
if (I_MXA(3 downto 0) = x"8") then -- intercept
|
|
mxd_out_ena <= '1';
|
|
mxd_out_intercept <= '1';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
p_mxout : process(mxd_out_ena, mxd_out_intercept, I_MD, r_intercept)
|
|
begin
|
|
O_MXD <= (others => 'X');
|
|
O_MXD_OE_L <= '1';
|
|
if (mxd_out_ena = '1') then
|
|
O_MXD_OE_L <= '0';
|
|
if (mxd_out_intercept = '1') then
|
|
O_MXD <= r_intercept;
|
|
else
|
|
O_MXD <= I_MD(7 downto 0);
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
end architecture RTL;
|
|
|