348 lines
10 KiB
VHDL
348 lines
10 KiB
VHDL
-------------------------------------------------------------------------------
|
|
--
|
|
-- A VGA line-doubler for an Apple ][
|
|
--
|
|
-- Stephen A. Edwards, sedwards.cs.columbia.edu
|
|
--
|
|
-- The Apple ][ uses a 14.31818 MHz master clock. It outputs a new
|
|
-- horizontal line every 65 * 14 + 2 = 912 14M cycles. The extra two
|
|
-- are from the "extended cycle" used to keep the 3.579545 MHz
|
|
-- colorburst signal in sync. Of these, 40 * 14 = 560 are active video.
|
|
--
|
|
-- In graphics mode, the Apple effectively generates 140 four-bit pixels
|
|
-- output serially (i.e., with 3.579545 MHz pixel clock). In text mode,
|
|
-- it generates 280 one-bit pixels (i.e., with a 7.15909 MHz pixel clock).
|
|
--
|
|
-- We capture 140 four-bit nibbles for each line and interpret them in
|
|
-- one of the two modes. In graphics mode, each is displayed as a
|
|
-- single pixel of one of 16 colors. In text mode, each is displayed
|
|
-- as two black or white pixels.
|
|
--
|
|
-- The VGA display is nominally 640 X 480, but we use a 14.31818 MHz
|
|
-- dot clock. To stay in sync with the Apple, we generate a new line
|
|
-- every 912 / 2 = 456 14M cycles= 31.8 us, a 31.4 kHz horizontal
|
|
-- refresh rate. Of these, 280 will be active video.
|
|
--
|
|
-- One set of suggested VGA timings:
|
|
--
|
|
-- ______________________ ________
|
|
-- ________| VIDEO |________| VIDEO
|
|
-- |-C-|----------D-----------|-E-|
|
|
-- __ ______________________________ ___________
|
|
-- |_| |_|
|
|
-- |B|
|
|
-- |---------------A----------------|
|
|
--
|
|
-- A = 31.77 us Scanline time
|
|
-- B = 3.77 us Horizontal sync time
|
|
-- C = 1.89 us Back porch
|
|
-- D = 25.17 us Active video
|
|
-- E = 0.94 us Front porch
|
|
--
|
|
-- We use A = 456 / 14.31818 MHz = 31.84 us
|
|
-- B = 54 / 14.31818 MHz = 3.77 us
|
|
-- C = 106 / 14.31818 MHz = 7.40 us
|
|
-- D = 280 / 14.31818 MHz = 19.56 us
|
|
-- E = 16 / 14.31818 MHz = 1.12 us
|
|
-------------------------------------------------------------------------------
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
entity vga_controller is
|
|
|
|
port (
|
|
CLK_14M : in std_logic; -- 14.31818 MHz master clock
|
|
nVIDEO : in std_logic;
|
|
COLOR_LINE : in std_logic;
|
|
CBL : in std_logic;
|
|
|
|
SRAM_nWE : out std_logic;
|
|
SRAM_nOE : out std_logic;
|
|
SRAM_nCE : out std_logic;
|
|
SRAM_addr : out unsigned(8 downto 0);
|
|
SRAM_data : inout unsigned(3 downto 0);
|
|
|
|
VGA_HS : out std_logic; -- Active low
|
|
VGA_VS : out std_logic; -- Active low
|
|
VGA_R : out std_logic_vector(1 downto 0);
|
|
VGA_G : out std_logic_vector(1 downto 0);
|
|
VGA_B : out std_logic_vector(1 downto 0);
|
|
|
|
MODE : in std_logic
|
|
);
|
|
|
|
end vga_controller;
|
|
|
|
architecture rtl of vga_controller is
|
|
|
|
--GE
|
|
signal HBL : std_logic := '0';
|
|
signal VBL : std_logic := '0';
|
|
|
|
signal SRAM_rd_data : unsigned(5 downto 0);
|
|
signal SRAM_wr_data : unsigned(3 downto 0);
|
|
signal SRAM_nWEi : std_logic;
|
|
signal SRAM_nOEi : std_logic;
|
|
signal SRAM_nCEi : std_logic;
|
|
|
|
signal RCOL : std_logic_vector(1 downto 0) := "00";
|
|
signal GCOL : std_logic_vector(1 downto 0) := "00";
|
|
signal BCOL : std_logic_vector(1 downto 0) := "00";
|
|
|
|
signal R : std_logic_vector(1 downto 0);
|
|
signal G : std_logic_vector(1 downto 0);
|
|
signal B : std_logic_vector(1 downto 0);
|
|
|
|
signal PIXPAT : unsigned(3 downto 0);
|
|
signal RAMPIXPAT : unsigned(5 downto 0);
|
|
signal COLPAT : unsigned(3 downto 0);
|
|
signal TEXT : std_logic;
|
|
|
|
signal ram_write_addr : unsigned(8 downto 0);
|
|
signal ram_we : std_logic;
|
|
signal ram_read_addr : unsigned(8 downto 0);
|
|
|
|
signal hcount : unsigned(9 downto 0);
|
|
signal hcount2 : unsigned(9 downto 0);
|
|
--GE signal vcount : unsigned(5 downto 0);
|
|
signal vcount : unsigned(6 downto 0);
|
|
|
|
signal even_line : std_logic := '0';
|
|
signal hactive : std_logic;
|
|
|
|
constant VGA_SCANLINE : integer := 456; -- Must be 456 (set by the Apple)
|
|
|
|
constant VGA_HSYNC : integer := 54;
|
|
constant VGA_BACK_PORCH : integer := 66;
|
|
constant VGA_ACTIVE : integer := 282; -- Must be 280 (set by the Apple)
|
|
constant VGA_FRONT_PORCH : integer := 54;
|
|
|
|
-- VGA_HSYNC + VGA_BACK_PORCH + VGA_ACTIVE + VGA_FRONT_PORCH = VGA_SCANLINE
|
|
|
|
--GE (org) constant VBL_TO_VSYNC : integer := 33;
|
|
--GE constant VBL_TO_VSYNC : integer := 80;
|
|
constant VBL_TO_VSYNC : integer := 55;
|
|
constant VGA_VSYNC_LINES : integer := 3;
|
|
|
|
signal VGA_VS_I, VGA_HS_I : std_logic;
|
|
|
|
signal video_active : std_logic;
|
|
signal vbl_delayed : std_logic;
|
|
signal color_line_delayed_1, color_line_delayed_2 : std_logic;
|
|
|
|
--GE 10/02/2009
|
|
signal cbl_last : std_logic;
|
|
signal cbl_count : unsigned(9 downto 0);
|
|
|
|
begin
|
|
|
|
--GE 10/02/2009
|
|
blank_sep2 : process(CLK_14M, CBL, cbl_last, cbl_count)
|
|
begin
|
|
if falling_edge(CLK_14M) then
|
|
cbl_count <= cbl_count + 1;
|
|
cbl_last <= CBL;
|
|
if (cbl_last = '0' and CBL = '1') then
|
|
cbl_count <= "1000100101"; -- 549
|
|
end if;
|
|
if (cbl_count = 911) then
|
|
cbl_count <= (others => '0');
|
|
VBL <= CBL;
|
|
color_line_delayed_2 <= color_line_delayed_1;
|
|
color_line_delayed_1 <= COLOR_LINE;
|
|
vbl_delayed <= VBL;
|
|
if VBL = '1' then
|
|
even_line <= '0';
|
|
vcount <= vcount + 1;
|
|
else
|
|
vcount <= (others => '0');
|
|
even_line <= not even_line;
|
|
end if;
|
|
end if;
|
|
if (cbl_count >= 549) and (cbl_count <= 899) then
|
|
HBL <= '1';
|
|
else
|
|
HBL <= '0';
|
|
end if;
|
|
end if;
|
|
end process;
|
|
hcount <= cbl_count;
|
|
|
|
hsync_gen : process (CLK_14M)
|
|
begin
|
|
if falling_edge(CLK_14M) then
|
|
if hcount = VGA_ACTIVE + VGA_FRONT_PORCH or
|
|
hcount = VGA_SCANLINE + VGA_ACTIVE + VGA_FRONT_PORCH then
|
|
VGA_HS_I <= '0';
|
|
elsif hcount = VGA_ACTIVE + VGA_FRONT_PORCH + VGA_HSYNC or
|
|
hcount = VGA_SCANLINE + VGA_ACTIVE + VGA_FRONT_PORCH + VGA_HSYNC then
|
|
VGA_HS_I <= '1';
|
|
end if;
|
|
|
|
if hcount = VGA_SCANLINE - 1 or
|
|
hcount = VGA_SCANLINE + VGA_SCANLINE - 1 then
|
|
hactive <= '1';
|
|
elsif hcount = VGA_ACTIVE or
|
|
hcount = VGA_ACTIVE + VGA_SCANLINE then
|
|
hactive <= '0';
|
|
end if;
|
|
end if;
|
|
end process hsync_gen;
|
|
|
|
VGA_HS <= VGA_HS_I;
|
|
|
|
vsync_gen : process (CLK_14M)
|
|
begin
|
|
if falling_edge(CLK_14M) then
|
|
if vcount = VBL_TO_VSYNC then
|
|
VGA_VS_I <= '0';
|
|
elsif vcount = VBL_TO_VSYNC + VGA_VSYNC_LINES then
|
|
VGA_VS_I <= '1';
|
|
end if;
|
|
end if;
|
|
end process vsync_gen;
|
|
|
|
VGA_VS <= VGA_VS_I;
|
|
|
|
-- Shift in the incoming bits to reconstruct four-bit groups
|
|
input_shift_register : process (CLK_14M)
|
|
begin
|
|
if falling_edge(CLK_14M) then
|
|
PIXPAT <= PIXPAT(2 downto 0) & nVIDEO;
|
|
end if;
|
|
end process input_shift_register;
|
|
|
|
hcount2 <= hcount - VGA_SCANLINE;
|
|
|
|
ram_read_addr <=
|
|
even_line & hcount(8 downto 1) when hcount < VGA_SCANLINE else
|
|
even_line & hcount2(8 downto 1);
|
|
|
|
ram_write_addr <= (not even_line) & hcount(9 downto 2);
|
|
ram_we <= '1' when hcount(1 downto 0) = "00" else '0';
|
|
|
|
video_active <= hactive and not vbl_delayed;
|
|
|
|
-- RGB values from Linards Ticmanis,
|
|
-- http://newsgroups.derkeiler.com/Archive/Comp/comp.sys.apple2/2005-09/msg00534.html
|
|
|
|
colorgen: process(COLPAT)
|
|
begin
|
|
case COLPAT is
|
|
when "1110" => -- 1 - 0x90 17 40
|
|
RCOL <= "10";
|
|
GCOL <= "00";
|
|
BCOL <= "01";
|
|
when "0111" => -- 2 - 0x40 2c a5
|
|
RCOL <= "01";
|
|
GCOL <= "00";
|
|
BCOL <= "10";
|
|
when "0110" => -- 3 - 0xd0 43 e5
|
|
RCOL <= "11";
|
|
GCOL <= "01";
|
|
BCOL <= "11";
|
|
when "1011" => -- 4 - 0x00 69 40
|
|
RCOL <= "00";
|
|
GCOL <= "01";
|
|
BCOL <= "01";
|
|
when "1010" => -- 5 - 0x80 80 80
|
|
RCOL <= "10";
|
|
GCOL <= "10";
|
|
BCOL <= "10";
|
|
when "0011" => -- 6 - 0x2f 95 e5
|
|
RCOL <= "00";
|
|
GCOL <= "10";
|
|
BCOL <= "11";
|
|
when "0010" => -- 7 - 0xbf ab ff
|
|
RCOL <= "11";
|
|
GCOL <= "10";
|
|
BCOL <= "11";
|
|
when "1101" => -- 8 - 0x40 54 00
|
|
RCOL <= "01";
|
|
GCOL <= "01";
|
|
BCOL <= "00";
|
|
when "1100" => -- 9 - 0xd0 6a 1a
|
|
RCOL <= "11";
|
|
GCOL <= "01";
|
|
BCOL <= "00";
|
|
when "0101" => -- 10 - 0x80 80 80
|
|
RCOL <= "01";
|
|
GCOL <= "01";
|
|
BCOL <= "01";
|
|
when "0100" => -- 11 - 0xff 96 bf
|
|
RCOL <= "11";
|
|
GCOL <= "10";
|
|
BCOL <= "11";
|
|
when "1001" => -- 12 - 0x2f bc 1a
|
|
RCOL <= "00";
|
|
GCOL <= "11";
|
|
BCOL <= "00";
|
|
when "1000" => -- 13 - 0xbf d3 5a
|
|
RCOL <= "11";
|
|
GCOL <= "11";
|
|
BCOL <= "01";
|
|
when "0001" => -- 14 - 0x6f e8 bf
|
|
RCOL <= "01";
|
|
GCOL <= "11";
|
|
BCOL <= "11";
|
|
when "0000" => -- 15 - 0xff ff ff
|
|
RCOL <= "11";
|
|
GCOL <= "11";
|
|
BCOL <= "11";
|
|
when others => -- 0 - 0x00 00 00
|
|
RCOL <= "00";
|
|
GCOL <= "00";
|
|
BCOL <= "00";
|
|
end case;
|
|
end process;
|
|
|
|
TEXT <= '1' when hcount(0) = '0' and CLK_14M = '0' and RAMPIXPAT(3) = '0'
|
|
else '1' when hcount(0) = '0' and CLK_14M = '1' and RAMPIXPAT(2) = '0'
|
|
else '1' when hcount(0) = '1' and CLK_14M = '0' and RAMPIXPAT(1) = '0'
|
|
else '1' when hcount(0) = '1' and CLK_14M = '1' and RAMPIXPAT(0) = '0'
|
|
else '0';
|
|
|
|
COLPAT <= RAMPIXPAT(3 downto 0) when hcount(0) = '1'
|
|
else RAMPIXPAT(3 downto 2) & RAMPIXPAT(5 downto 4);
|
|
|
|
--GE
|
|
SRAM_addr <= ram_read_addr when hcount(0) = '1' else ram_write_addr;
|
|
SRAM_nWEi <= not ram_we;
|
|
SRAM_nOEi <= '0' when hcount(0) = '1' else '1';
|
|
SRAM_nCEi <= CLK_14M;
|
|
process(CLK_14M)
|
|
begin
|
|
if rising_edge(CLK_14M) and SRAM_nOEi = '0' then
|
|
SRAM_rd_data <= SRAM_rd_data(1 downto 0) & SRAM_data;
|
|
end if;
|
|
end process;
|
|
process(CLK_14M)
|
|
begin
|
|
if falling_edge(CLK_14M) then
|
|
RAMPIXPAT <= SRAM_rd_data;
|
|
end if;
|
|
end process;
|
|
|
|
SRAM_wr_data <= PIXPAT;
|
|
SRAM_data <= SRAM_wr_data when SRAM_nCEi = '0' and SRAM_nWEi = '0' else "ZZZZ";
|
|
|
|
SRAM_nCE <= SRAM_nCEi;
|
|
SRAM_nOE <= SRAM_nOEi;
|
|
SRAM_nWE <= SRAM_nWEi;
|
|
|
|
R <= RCOL when video_active = '1' and color_line_delayed_2 = '1'
|
|
else TEXT & TEXT when video_active = '1' and color_line_delayed_2 = '0'
|
|
else "00";
|
|
G <= GCOL when video_active = '1' and color_line_delayed_2 = '1'
|
|
else TEXT & TEXT when video_active = '1' and color_line_delayed_2 = '0'
|
|
else "00";
|
|
B <= BCOL when video_active = '1' and color_line_delayed_2 = '1'
|
|
else TEXT & TEXT when video_active = '1' and color_line_delayed_2 = '0'
|
|
else "00";
|
|
VGA_R <= R when MODE = '1' else "00";
|
|
VGA_G <= G when MODE = '1' else TEXT & TEXT when video_active = '1' else "00";
|
|
VGA_B <= B when MODE = '1' else "00";
|
|
|
|
end rtl;
|