From 8e49e9b08708f1a5a9711e89a9424b4295773225 Mon Sep 17 00:00:00 2001 From: the great greene arkleseizure Date: Sat, 12 Oct 2013 08:17:43 +0100 Subject: fish --- vidproc.vhd | 279 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 vidproc.vhd (limited to 'vidproc.vhd') diff --git a/vidproc.vhd b/vidproc.vhd new file mode 100644 index 0000000..6d5f91f --- /dev/null +++ b/vidproc.vhd @@ -0,0 +1,279 @@ +-- BBC Micro for Altera DE1 +-- +-- Copyright (c) 2011 Mike Stirling +-- +-- 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 agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- 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. +-- +-- BBC Micro "VIDPROC" Video ULA +-- +-- Synchronous implementation for FPGA +-- +-- (C) 2011 Mike Stirling +-- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity vidproc is +port ( + CLOCK : in std_logic; + -- Clock enable qualifies display cycles (interleaved with CPU cycles) + CLKEN : in std_logic; + nRESET : in std_logic; + + -- Clock enable output to CRTC + CLKEN_CRTC : out std_logic; + + -- Bus interface + ENABLE : in std_logic; + A0 : in std_logic; + -- CPU data bus (for register writes) + DI_CPU : in std_logic_vector(7 downto 0); + -- Display RAM data bus (for display data fetch) + DI_RAM : in std_logic_vector(7 downto 0); + + -- Control interface + nINVERT : in std_logic; + DISEN : in std_logic; + CURSOR : in std_logic; + + -- Video in (teletext mode) + R_IN : in std_logic; + G_IN : in std_logic; + B_IN : in std_logic; + + -- Video out + R : out std_logic; + G : out std_logic; + B : out std_logic + ); +end entity; + +architecture rtl of vidproc is +-- Write-only registers +signal r0_cursor0 : std_logic; +signal r0_cursor1 : std_logic; +signal r0_cursor2 : std_logic; +signal r0_crtc_2mhz : std_logic; +signal r0_pixel_rate : std_logic_vector(1 downto 0); +signal r0_teletext : std_logic; +signal r0_flash : std_logic; + +type palette_t is array(0 to 15) of std_logic_vector(3 downto 0); +signal palette : palette_t; + +-- Pixel shift register +signal shiftreg : std_logic_vector(7 downto 0); +-- Delayed display enable +signal delayed_disen : std_logic; + +-- Internal clock enable generation +signal clken_pixel : std_logic; +signal clken_fetch : std_logic; +signal clken_counter : unsigned(3 downto 0); + +-- Cursor generation - can span up to 32 pixels +-- Segments 0 and 1 are 8 pixels wide +-- Segment 2 is 16 pixels wide +signal cursor_invert : std_logic; +signal cursor_active : std_logic; +signal cursor_counter : unsigned(1 downto 0); + +begin + -- Synchronous register access, enabled on every clock + process(CLOCK,nRESET) + begin + if nRESET = '0' then + r0_cursor0 <= '0'; + r0_cursor1 <= '0'; + r0_cursor2 <= '0'; + r0_crtc_2mhz <= '0'; + r0_pixel_rate <= "00"; + r0_teletext <= '0'; + r0_flash <= '0'; + + for colour in 0 to 15 loop + palette(colour) <= (others => '0'); + end loop; + elsif rising_edge(CLOCK) then + if ENABLE = '1' then + if A0 = '0' then + -- Access control register + r0_cursor0 <= DI_CPU(7); + r0_cursor1 <= DI_CPU(6); + r0_cursor2 <= DI_CPU(5); + r0_crtc_2mhz <= DI_CPU(4); + r0_pixel_rate <= DI_CPU(3 downto 2); + r0_teletext <= DI_CPU(1); + r0_flash <= DI_CPU(0); + else + -- Access palette register + palette(to_integer(unsigned(DI_CPU(7 downto 4)))) <= DI_CPU(3 downto 0); + end if; + end if; + end if; + end process; + + -- Clock enable generation. + -- Pixel clock can be divided by 1,2,4 or 8 depending on the value + -- programmed at r0_pixel_rate + -- 00 = /8, 01 = /4, 10 = /2, 11 = /1 + clken_pixel <= + CLKEN when r0_pixel_rate = "11" else + (CLKEN and not clken_counter(0)) when r0_pixel_rate = "10" else + (CLKEN and not (clken_counter(0) or clken_counter(1))) when r0_pixel_rate = "01" else + (CLKEN and not (clken_counter(0) or clken_counter(1) or clken_counter(2))); + -- The CRT controller is always enabled in the 15th cycle, so that the result + -- is ready for latching into the shift register in cycle 0. If 2 MHz mode is + -- selected then the CRTC is also enabled in the 7th cycle + CLKEN_CRTC <= CLKEN and + clken_counter(0) and clken_counter(1) and clken_counter(2) and + (clken_counter(3) or r0_crtc_2mhz); + -- The result is fetched from the CRTC in cycle 0 and also cycle 8 if 2 MHz + -- mode is selected. This is used for reloading the shift register as well as + -- counting cursor pixels + clken_fetch <= CLKEN and not (clken_counter(0) or clken_counter(1) or clken_counter(2) or + (clken_counter(3) and not r0_crtc_2mhz)); + + process(CLOCK,nRESET) + begin + if nRESET = '0' then + clken_counter <= (others => '0'); + elsif rising_edge(CLOCK) and CLKEN = '1' then + -- Increment internal cycle counter during each video clock + clken_counter <= clken_counter + 1; + end if; + end process; + + -- Fetch control + process(CLOCK,nRESET) + begin + if nRESET = '0' then + shiftreg <= (others => '0'); + elsif rising_edge(CLOCK) and clken_pixel = '1' then + if clken_fetch = '1' then + -- Fetch next byte from RAM into shift register. This always occurs in + -- cycle 0, and also in cycle 8 if the CRTC is clocked at double rate. + shiftreg <= DI_RAM; + else + -- Clock shift register and input '1' at LSB + shiftreg <= shiftreg(6 downto 0) & "1"; + end if; + end if; + end process; + + -- Cursor generation + cursor_invert <= cursor_active and + ((r0_cursor0 and not (cursor_counter(0) or cursor_counter(1))) or + (r0_cursor1 and cursor_counter(0) and not cursor_counter(1)) or + (r0_cursor2 and cursor_counter(1))); + process(CLOCK,nRESET) + begin + if nRESET = '0' then + cursor_active <= '0'; + cursor_counter <= (others => '0'); + elsif rising_edge(CLOCK) and clken_fetch = '1' then + if CURSOR = '1' or cursor_active = '1' then + -- Latch cursor + cursor_active <= '1'; + + -- Reset on counter wrap + if cursor_counter = "11" then + cursor_active <= '0'; + end if; + + -- Increment counter + if cursor_active = '0' then + -- Reset + cursor_counter <= (others => '0'); + else + -- Increment + cursor_counter <= cursor_counter + 1; + end if; + end if; + end if; + end process; + + -- Pixel generation + -- The new shift register contents are loaded during + -- cycle 0 (and 8) but will not be read here until the next cycle. + -- By running this process on every single video tick instead of at + -- the pixel rate we ensure that the resulting delay is minimal and + -- constant (running this at the pixel rate would cause + -- the display to move slightly depending on which mode was selected). + process(CLOCK,nRESET) + variable palette_a : std_logic_vector(3 downto 0); + variable dot_val : std_logic_vector(3 downto 0); + variable red_val : std_logic; + variable green_val : std_logic; + variable blue_val : std_logic; + begin + if nRESET = '0' then + R <= '0'; + G <= '0'; + B <= '0'; + delayed_disen <= '0'; + elsif rising_edge(CLOCK) and CLKEN = '1' then + -- Look up dot value in the palette. Bits are as follows: + -- bit 3 - FLASH + -- bit 2 - Not BLUE + -- bit 1 - Not GREEN + -- bit 0 - Not RED + palette_a := shiftreg(7) & shiftreg(5) & shiftreg(3) & shiftreg(1); + dot_val := palette(to_integer(unsigned(palette_a))); + + -- Apply flash inversion if required + red_val := (dot_val(3) and r0_flash) xor not dot_val(0); + green_val := (dot_val(3) and r0_flash) xor not dot_val(1); + blue_val := (dot_val(3) and r0_flash) xor not dot_val(2); + + -- To output + -- FIXME: INVERT option + if r0_teletext = '0' then + -- Cursor can extend outside the bounds of the screen, so + -- it is not affected by DISEN + R <= (red_val and delayed_disen) xor cursor_invert; + G <= (green_val and delayed_disen) xor cursor_invert; + B <= (blue_val and delayed_disen) xor cursor_invert; + else + R <= R_IN xor cursor_invert; + G <= G_IN xor cursor_invert; + B <= B_IN xor cursor_invert; + end if; + + -- Display enable signal delayed by one clock + delayed_disen <= DISEN; + end if; + end process; +end architecture; + -- cgit v1.2.3