forked from Apple-2-HW/Apple_II_vhdl
222 lines
6.9 KiB
VHDL
222 lines
6.9 KiB
VHDL
-------------------------------------------------------------------------------
|
|
--
|
|
-- Apple ][ Video Generation Logic
|
|
--
|
|
-- Stephen A. Edwards, sedwards@cs.columbia.edu
|
|
--
|
|
-- This takes data from memory and various mode switches to produce the
|
|
-- serial one-bit video data stream.
|
|
--
|
|
-------------------------------------------------------------------------------
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
entity video_generator is
|
|
|
|
port (
|
|
CLK_14M : in std_logic; -- 14.31818 MHz master clock
|
|
CLK_7M : in std_logic;
|
|
AX : in std_logic;
|
|
CAS_N : in std_logic;
|
|
TEXT_MODE : in std_logic;
|
|
PAGE2 : in std_logic;
|
|
HIRES_MODE : in std_logic;
|
|
MIXED_MODE : in std_logic;
|
|
H0 : in std_logic;
|
|
VA : in std_logic;
|
|
VB : in std_logic;
|
|
VC : in std_logic;
|
|
V2 : in std_logic;
|
|
V4 : in std_logic;
|
|
BLANK : in std_logic;
|
|
DL : in unsigned(7 downto 0); -- Data from RAM
|
|
LDPS_N : in std_logic;
|
|
LD194 : in std_logic;
|
|
FLASH_CLK : in std_logic; -- Low-frequency flashing text clock
|
|
HIRES : out std_logic;
|
|
VIDEO : out std_logic;
|
|
COLOR_LINE : out std_logic
|
|
);
|
|
|
|
end video_generator;
|
|
|
|
architecture rtl of video_generator is
|
|
|
|
signal char_rom_addr : unsigned(8 downto 0);
|
|
signal char_rom_out : unsigned(4 downto 0);
|
|
signal text_shiftreg : unsigned(5 downto 0);
|
|
signal invert_character : std_logic;
|
|
signal text_pixel : std_logic; -- B2 p11
|
|
signal blank_delayed : std_logic;
|
|
signal video_sig : std_logic; -- output of B10 p5
|
|
signal graph_shiftreg : unsigned(7 downto 0);
|
|
signal graphics_time_1, graphics_time_2,
|
|
graphics_time_3 : std_logic; -- B5 p2, B8 p15, B8 p2
|
|
signal lores_time : std_logic; -- A11 p6
|
|
signal pixel_select : std_logic_vector(1 downto 0); -- A10 p14, A10 p15
|
|
signal hires_delayed : std_logic; -- A11 p9
|
|
|
|
begin
|
|
|
|
-----------------------------------------------------------------------------
|
|
--
|
|
-- Text Mode Circuitry
|
|
--
|
|
-- The character ROM drives a parallel-to-serial shift register
|
|
-- whose output is selectively inverted by inverted or flashing text
|
|
--
|
|
-----------------------------------------------------------------------------
|
|
|
|
char_rom_addr <= DL(5 downto 0) & VC & VB & VA;
|
|
|
|
thecharrom : entity work.character_rom
|
|
port map(
|
|
addr => char_rom_addr,
|
|
clk => CLK_14M, -- FIXME: a lower frequency?
|
|
dout => char_rom_out
|
|
);
|
|
|
|
-- Parallel-to-serial shifter for text mode
|
|
-- The Apple actually used LDPS_N as the clock, not 14M; this is equivalent
|
|
A3_74166: process (CLK_14M)
|
|
begin
|
|
if rising_edge(CLK_14M) then
|
|
if CLK_7M = '0' then
|
|
if LDPS_N = '0' then -- load
|
|
text_shiftreg <= char_rom_out & "0";
|
|
else -- shift
|
|
text_shiftreg <= '0' & text_shiftreg(5 downto 1);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Latch and decoder for flashing/inverted text
|
|
-- Comprises part of B11, B13, and A10
|
|
flash_invert : process (CLK_14M)
|
|
begin
|
|
if rising_edge(CLK_14M) then
|
|
if LD194 = '0' then
|
|
invert_character <= not (DL(7) or (DL(6) and FLASH_CLK));
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
text_pixel <= text_shiftreg(0) xor invert_character;
|
|
|
|
-----------------------------------------------------------------------------
|
|
--
|
|
-- Lores and Hires Mode Circuitry
|
|
--
|
|
-- An eight-bit shift register that either shifts (hires mode) or rotates
|
|
-- the two nibbles (lores) followed by a mux that selects the video
|
|
-- data from the text mode display, the hires shift register (possibly
|
|
-- delayed by a 14M clock pulse), or one of the bits in the lores shift
|
|
-- register.
|
|
--
|
|
-----------------------------------------------------------------------------
|
|
|
|
-- Original Apple clocked this shift register on the rising edge of RAS_N
|
|
B5B8_74LS174 : process (CLK_14M)
|
|
begin
|
|
if rising_edge(CLK_14M) then
|
|
if AX = '1' and CAS_N = '0' then
|
|
graphics_time_3 <= graphics_time_2;
|
|
graphics_time_2 <= graphics_time_1;
|
|
graphics_time_1 <= not (TEXT_MODE or (V2 and V4 and MIXED_MODE));
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
COLOR_LINE <= graphics_time_1;
|
|
|
|
HIRES <= HIRES_MODE and graphics_time_3; -- to address generator
|
|
|
|
lores_time <= not HIRES_MODE and graphics_time_3;
|
|
|
|
A8A10_74LS194 : process (CLK_14M)
|
|
begin
|
|
if rising_edge(CLK_14M) then
|
|
if LD194 = '0' then
|
|
if lores_time = '1' then -- LORES mode
|
|
pixel_select <= VC & H0;
|
|
else -- HIRES mode
|
|
pixel_select <= graphics_time_1 & DL(7);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Shift hires pixels by one 14M cycle to get orange and blue
|
|
A11_74LS74 : process (CLK_14M)
|
|
begin
|
|
if rising_edge(CLK_14M) then
|
|
hires_delayed <= graph_shiftreg(0);
|
|
end if;
|
|
end process;
|
|
|
|
-- A pair of four-bit universal shift registers that either
|
|
-- shift the whole byte (hires mode) or rotate the two nibbles (lores mode)
|
|
B4B9_74LS194 : process (CLK_14M)
|
|
begin
|
|
if rising_edge(CLK_14M) then
|
|
if LD194 = '0' then
|
|
graph_shiftreg <= DL;
|
|
else
|
|
if lores_time = '1' then -- LORES configuration
|
|
graph_shiftreg <= graph_shiftreg(4) & graph_shiftreg(7 downto 5) &
|
|
graph_shiftreg(0) & graph_shiftreg(3 downto 1);
|
|
else -- HIRES configuration
|
|
if CLK_7M = '0' then
|
|
graph_shiftreg <= graph_shiftreg(4) & graph_shiftreg(7 downto 1);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Synchronize BLANK to LD194
|
|
A10_74LS194: process (CLK_14M)
|
|
begin
|
|
if rising_edge(CLK_14M) then
|
|
if LD194 = '0' then
|
|
blank_delayed <= BLANK;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Video output mux and flip-flop
|
|
A9B10_74LS151 : process (CLK_14M)
|
|
begin
|
|
if rising_edge(CLK_14M) then
|
|
if blank_delayed = '0' then
|
|
if lores_time = '1' then -- LORES mode
|
|
case pixel_select is
|
|
when "00" => video_sig <= graph_shiftreg(0);
|
|
when "01" => video_sig <= graph_shiftreg(2);
|
|
when "10" => video_sig <= graph_shiftreg(4);
|
|
when "11" => video_sig <= graph_shiftreg(6);
|
|
when others => video_sig <= 'X';
|
|
end case;
|
|
else
|
|
if pixel_select(1) = '0' then -- TEXT mode
|
|
video_sig <= text_pixel;
|
|
else -- HIRES mode
|
|
if pixel_select(0) = '1' then
|
|
video_sig <= hires_delayed;
|
|
else
|
|
video_sig <= graph_shiftreg(0);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
else
|
|
video_sig <= '0';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
VIDEO <= video_sig;
|
|
|
|
end rtl;
|