Apple1_MiST/rtl/tms9918/vdp18/vdp18_cpuio.vhd

571 lines
18 KiB
VHDL

-------------------------------------------------------------------------------
--
-- Synthesizable model of TI's TMS9918A, TMS9928A, TMS9929A.
--
-- $Id: vdp18_cpuio.vhd,v 1.17 2006/06/18 10:47:01 arnim Exp $
--
-- CPU I/O Interface Module
--
-------------------------------------------------------------------------------
--
-- Copyright (c) 2006, Arnim Laeuger (arnim.laeuger@gmx.net)
--
-- 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 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 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.
--
-- Please report bugs to the author, but before you do so, please
-- make sure that this is not a derivative work and that
-- you have the latest version of this file.
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use work.vdp18_pack.access_t;
use work.vdp18_pack.opmode_t;
entity vdp18_cpuio is
port (
clk_i : in std_logic;
clk_en_10m7_i : in boolean;
clk_en_acc_i : in boolean;
reset_i : in boolean;
rd_i : in boolean;
wr_i : in boolean;
mode_i : in std_logic;
cd_i : in std_logic_vector(0 to 7);
cd_o : out std_logic_vector(0 to 7);
cd_oe_o : out std_logic;
access_type_i : in access_t;
opmode_o : out opmode_t;
vram_we_o : out std_logic;
vram_a_o : out std_logic_vector(0 to 13);
vram_d_o : out std_logic_vector(0 to 7);
vram_d_i : in std_logic_vector(0 to 7);
spr_coll_i : in boolean;
spr_5th_i : in boolean;
spr_5th_num_i : in std_logic_vector(0 to 4);
reg_ev_o : out boolean;
reg_16k_o : out boolean;
reg_blank_o : out boolean;
reg_size1_o : out boolean;
reg_mag1_o : out boolean;
reg_ntb_o : out std_logic_vector(0 to 3);
reg_ctb_o : out std_logic_vector(0 to 7);
reg_pgb_o : out std_logic_vector(0 to 2);
reg_satb_o : out std_logic_vector(0 to 6);
reg_spgb_o : out std_logic_vector(0 to 2);
reg_col1_o : out std_logic_vector(0 to 3);
reg_col0_o : out std_logic_vector(0 to 3);
irq_i : in boolean;
int_n_o : out std_logic
);
end vdp18_cpuio;
library ieee;
use ieee.numeric_std.all;
use work.vdp18_pack.all;
architecture rtl of vdp18_cpuio is
type state_t is (ST_IDLE,
ST_RD_MODE0, ST_WR_MODE0,
ST_RD_MODE1,
ST_WR_MODE1_1ST, ST_WR_MODE1_1ST_IDLE,
ST_WR_MODE1_2ND_VREAD, ST_WR_MODE1_2ND_VWRITE,
ST_WR_MODE1_2ND_RWRITE);
signal state_s,
state_q : state_t;
signal buffer_q : std_logic_vector(0 to 7);
signal addr_q : unsigned(0 to 13);
signal incr_addr_s,
load_addr_s : boolean;
signal wrbuf_cpu_s : boolean;
signal sched_rdvram_s,
rdvram_sched_q,
rdvram_q : boolean;
signal abort_wrvram_s,
sched_wrvram_s,
wrvram_sched_q,
wrvram_q : boolean;
signal write_tmp_s : boolean;
signal tmp_q : std_logic_vector(0 to 7);
signal write_reg_s : boolean;
-- control register bits ----------------------------------------------------
type ctrl_reg_t is array (natural range 7 downto 0) of
std_logic_vector(0 to 7);
signal ctrl_reg_q : ctrl_reg_t;
-- status register ----------------------------------------------------------
signal status_reg_s : std_logic_vector(0 to 7);
signal destr_rd_status_s : boolean;
signal sprite_5th_q : boolean;
signal sprite_5th_num_q : std_logic_vector(0 to 4);
signal sprite_coll_q : boolean;
signal int_n_q : std_logic;
type read_mux_t is (RDMUX_STATUS, RDMUX_READAHEAD);
signal read_mux_s : read_mux_t;
begin
-----------------------------------------------------------------------------
-- Process seq
--
-- Purpose:
-- Implements the sequential elements.
--
seq: process (clk_i, reset_i)
variable incr_addr_v : boolean;
begin
if reset_i then
state_q <= ST_IDLE;
buffer_q <= (others => '0');
addr_q <= (others => '0');
rdvram_sched_q <= false;
rdvram_q <= false;
wrvram_sched_q <= false;
wrvram_q <= false;
elsif clk_i'event and clk_i = '1' then
-- default assignments
incr_addr_v := incr_addr_s;
if clk_en_10m7_i then
-- update state vector ------------------------------------------------
state_q <= state_s;
-- buffer and flag control --------------------------------------------
if wrbuf_cpu_s then
-- write read-ahead buffer from CPU bus
buffer_q <= cd_i;
-- immediately stop read-ahead
rdvram_sched_q <= false;
rdvram_q <= false;
elsif clk_en_acc_i and
rdvram_q and
access_type_i = AC_CPU then
-- write read-ahead buffer from VRAM during CPU access slot
buffer_q <= vram_d_i;
-- stop scanning for CPU data
rdvram_q <= false;
-- increment read-ahead address
incr_addr_v := true;
end if;
if sched_rdvram_s then
-- immediately stop write-back
wrvram_sched_q <= false;
wrvram_q <= false;
-- schedule read-ahead
rdvram_sched_q <= true;
end if;
if sched_wrvram_s then
-- schedule write-back
wrvram_sched_q <= true;
end if;
if abort_wrvram_s then
-- stop scanning for write-back
wrvram_q <= false;
end if;
if rdvram_sched_q and clk_en_acc_i then
-- align scheduled read-ahead with access slot phase
rdvram_sched_q <= false;
rdvram_q <= true;
end if;
if wrvram_sched_q and clk_en_acc_i then
-- align scheduled write-back with access slot phase
wrvram_sched_q <= false;
wrvram_q <= true;
end if;
-- manage address -----------------------------------------------------
if load_addr_s then
addr_q(6 to 13) <= unsigned(tmp_q);
addr_q(0 to 5) <= unsigned(cd_i(2 to 7));
elsif incr_addr_v then
addr_q <= addr_q + 1;
end if;
end if;
end if;
end process seq;
--
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Process wback_ctrl
--
-- Purpose:
-- Write-back control.
--
wback_ctrl: process (clk_en_acc_i,
access_type_i,
wrvram_q)
begin
-- default assignments
abort_wrvram_s <= false;
incr_addr_s <= false;
vram_we_o <= '0';
if wrvram_q then
if access_type_i = AC_CPU then
-- signal write access to VRAM
vram_we_o <= '1';
if clk_en_acc_i then
-- clear write-back flag and increment address
abort_wrvram_s <= true;
incr_addr_s <= true;
end if;
end if;
end if;
end process wback_ctrl;
--
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Process reg_if
--
-- Purpose:
-- Implements the register interface.
--
reg_if: process (clk_i, reset_i)
variable reg_addr_v : unsigned(0 to 2);
begin
if reset_i then
tmp_q <= (others => '0');
ctrl_reg_q <= (others => (others => '0'));
sprite_coll_q <= false;
sprite_5th_q <= false;
sprite_5th_num_q <= (others => '0');
int_n_q <= '1';
elsif clk_i'event and clk_i = '1' then
if clk_en_10m7_i then
-- Temporary register -------------------------------------------------
if write_tmp_s then
tmp_q <= cd_i;
end if;
-- Registers 0 to 7 ---------------------------------------------------
if write_reg_s then
reg_addr_v := unsigned(cd_i(5 to 7));
ctrl_reg_q(to_integer(reg_addr_v)) <= tmp_q;
end if;
end if;
-- Fifth sprite handling ------------------------------------------------
if spr_5th_i and not sprite_5th_q then
sprite_5th_q <= true;
sprite_5th_num_q <= spr_5th_num_i;
elsif destr_rd_status_s then
sprite_5th_q <= false;
end if;
-- Sprite collision flag ------------------------------------------------
if spr_coll_i then
sprite_coll_q <= true;
elsif destr_rd_status_s then
sprite_coll_q <= false;
end if;
-- Interrupt ------------------------------------------------------------
if irq_i then
int_n_q <= '0';
elsif destr_rd_status_s then
int_n_q <= '1';
end if;
end if;
end process reg_if;
--
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Process access_ctrl
--
-- Purpose:
-- Implements the combinational logic for the CPU I/F FSM.
-- Decodes the CPU I/F FSM state and generates the control signals for the
-- register and VRAM logic.
--
access_ctrl: process (state_q,
rd_i, wr_i,
mode_i,
cd_i)
type transfer_mode_t is (TM_NONE,
TM_RD_MODE0, TM_WR_MODE0,
TM_RD_MODE1, TM_WR_MODE1);
variable transfer_mode_v : transfer_mode_t;
begin
-- default assignments
state_s <= state_q;
sched_rdvram_s <= false;
sched_wrvram_s <= false;
wrbuf_cpu_s <= false;
write_tmp_s <= false;
write_reg_s <= false;
load_addr_s <= false;
read_mux_s <= RDMUX_STATUS;
destr_rd_status_s <= false;
-- determine transfer mode
transfer_mode_v := TM_NONE;
if mode_i = '0' then
if rd_i then
transfer_mode_v := TM_RD_MODE0;
end if;
if wr_i then
transfer_mode_v := TM_WR_MODE0;
end if;
else
if rd_i then
transfer_mode_v := TM_RD_MODE1;
end if;
if wr_i then
transfer_mode_v := TM_WR_MODE1;
end if;
end if;
-- FSM state transitions
case state_q is
-- ST_IDLE: waiting for CPU access --------------------------------------
when ST_IDLE =>
case transfer_mode_v is
when TM_RD_MODE0 =>
state_s <= ST_RD_MODE0;
when TM_WR_MODE0 =>
state_s <= ST_WR_MODE0;
when TM_RD_MODE1 =>
state_s <= ST_RD_MODE1;
when TM_WR_MODE1 =>
state_s <= ST_WR_MODE1_1ST;
when others =>
null;
end case;
-- ST_RD_MODE0: read from VRAM ------------------------------------------
when ST_RD_MODE0 =>
-- set read mux
read_mux_s <= RDMUX_READAHEAD;
if transfer_mode_v = TM_NONE then
-- CPU finished read access:
-- schedule new read-ahead and return to idle
state_s <= ST_IDLE;
sched_rdvram_s <= true;
end if;
-- ST_WR_MODE0: write to VRAM -------------------------------------------
when ST_WR_MODE0 =>
-- write data from CPU to write-back/read-ahead buffer
wrbuf_cpu_s <= true;
if transfer_mode_v = TM_NONE then
-- CPU finished write access:
-- schedule new write-back and return to idle
state_s <= ST_IDLE;
sched_wrvram_s <= true;
end if;
-- ST_RD_MODE1: read from status register -------------------------------
when ST_RD_MODE1 =>
-- set read mux
read_mux_s <= RDMUX_STATUS;
if transfer_mode_v = TM_NONE then
-- CPU finished read access:
-- destructive read of status register and return to IDLE
destr_rd_status_s <= true;
state_s <= ST_IDLE;
end if;
-- ST_WR_MODE1_1ST: save first byte -------------------------------------
when ST_WR_MODE1_1ST =>
-- update temp register
write_tmp_s <= true;
if transfer_mode_v = TM_NONE then
-- CPU finished write access:
-- become idle but remember that the first byte of a paired write
-- has been written
state_s <= ST_WR_MODE1_1ST_IDLE;
end if;
-- ST_WR_MODE1_1ST_IDLE: wait for next access ---------------------------
when ST_WR_MODE1_1ST_IDLE =>
-- determine type of next access
case transfer_mode_v is
when TM_RD_MODE0 =>
state_s <= ST_RD_MODE0;
when TM_WR_MODE0 =>
state_s <= ST_WR_MODE0;
when TM_RD_MODE1 =>
state_s <= ST_RD_MODE1;
when TM_WR_MODE1 =>
case cd_i(0 to 1) is
when "00" =>
state_s <= ST_WR_MODE1_2ND_VREAD;
when "01" =>
state_s <= ST_WR_MODE1_2ND_VWRITE;
when "10" | "11" =>
state_s <= ST_WR_MODE1_2ND_RWRITE;
when others =>
null;
end case;
when others =>
null;
end case;
-- ST_WR_MODE1_2ND_VREAD: write second byte of address, then read ahead -
when ST_WR_MODE1_2ND_VREAD =>
load_addr_s <= true;
if transfer_mode_v = TM_NONE then
-- CPU finished write access:
-- schedule new read-ahead and return to idle
sched_rdvram_s <= true;
state_s <= ST_IDLE;
end if;
-- ST_WR_MODE1_2ND_VWRITE: write second byte of address
when ST_WR_MODE1_2ND_VWRITE =>
load_addr_s <= true;
if transfer_mode_v = TM_NONE then
-- CPU finished write access:
-- return to idle
state_s <= ST_IDLE;
end if;
-- ST_WR_MODE1_2ND_RWRITE: write to register ----------------------------
when ST_WR_MODE1_2ND_RWRITE =>
write_reg_s <= true;
if transfer_mode_v = TM_NONE then
-- CPU finished write access:
-- return to idle
state_s <= ST_IDLE;
end if;
when others =>
null;
end case;
end process access_ctrl;
--
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Process mode_decode
--
-- Purpose:
-- Decodes the display mode from the M1, M2, M3 bits.
--
mode_decode: process (ctrl_reg_q)
variable mode_v : std_logic_vector(0 to 2);
begin
mode_v := ctrl_reg_q(1)(3) & -- M1
ctrl_reg_q(1)(4) & -- M2
ctrl_reg_q(0)(6); -- M3
case mode_v is
when "000" =>
opmode_o <= OPMODE_GRAPH1;
when "001" =>
opmode_o <= OPMODE_GRAPH2;
when "010" =>
opmode_o <= OPMODE_MULTIC;
when "100" =>
opmode_o <= OPMODE_TEXTM;
when others =>
opmode_o <= OPMODE_TEXTM;
end case;
end process mode_decode;
--
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Build status register
-----------------------------------------------------------------------------
status_reg_s <= not int_n_q &
to_std_logic_f(sprite_5th_q) &
to_std_logic_f(sprite_coll_q) &
sprite_5th_num_q;
-----------------------------------------------------------------------------
-- Output mapping
-----------------------------------------------------------------------------
vram_a_o <= std_logic_vector(addr_q);
vram_d_o <= buffer_q;
cd_o <= buffer_q
when read_mux_s = RDMUX_READAHEAD else
status_reg_s;
cd_oe_o <= '1'
when rd_i else
'0';
reg_ev_o <= to_boolean_f(ctrl_reg_q(0)(7));
reg_16k_o <= to_boolean_f(ctrl_reg_q(1)(0));
reg_blank_o <= not to_boolean_f(ctrl_reg_q(1)(1));
reg_size1_o <= to_boolean_f(ctrl_reg_q(1)(6));
reg_mag1_o <= to_boolean_f(ctrl_reg_q(1)(7));
reg_ntb_o <= ctrl_reg_q(2)(4 to 7);
reg_ctb_o <= ctrl_reg_q(3);
reg_pgb_o <= ctrl_reg_q(4)(5 to 7);
reg_satb_o <= ctrl_reg_q(5)(1 to 7);
reg_spgb_o <= ctrl_reg_q(6)(5 to 7);
reg_col1_o <= ctrl_reg_q(7)(0 to 3);
reg_col0_o <= ctrl_reg_q(7)(4 to 7);
int_n_o <= int_n_q or not ctrl_reg_q(1)(2);
end rtl;