-------------------------------------------------------------------------------
--
-- OPB bus bridge for the XESS XSB-300E board
--
-- Includes a memory controller, a VGA framebuffer, and glue for the SRAM
--
--
-- Cristian Soviani, Dennis Lim, and Stephen A. Edwards
--
-- Modified for XESS Scorched Earch by:
-- Dennis Chua, Jeremy Chou, and Michael Sumluong
--
-------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity opb_xsb300 is
  generic (
    C_OPB_AWIDTH : integer          := 32;
    C_OPB_DWIDTH : integer          := 32;
    C_BASEADDR   : std_logic_vector := X"2000_0000";
    C_HIGHADDR   : std_logic_vector := X"2000_00FF");

  port (
    OPB_Clk        : in    std_logic;
    OPB_Rst        : in    std_logic;
    OPB_ABus       : in    std_logic_vector (31 downto 0);
    OPB_BE         : in    std_logic_vector (3 downto 0);
    OPB_DBus       : in    std_logic_vector (31 downto 0);
    OPB_RNW        : in    std_logic;
    OPB_select     : in    std_logic;
    OPB_seqAddr    : in    std_logic;
    pixel_clock    : in    std_logic;
    UIO_DBus       : out   std_logic_vector (31 downto 0);
    UIO_errAck     : out   std_logic;
    UIO_retry      : out   std_logic;
    UIO_toutSup    : out   std_logic;
    UIO_xferAck    : out   std_logic;
    PB_A           : out   std_logic_vector (19 downto 0);
    PB_UB_N        : out   std_logic;
    PB_LB_N        : out   std_logic;
    PB_WE_N        : out   std_logic;
    PB_OE_N        : out   std_logic;
    RAM_CE_N       : out   std_logic;
    VIDOUT_CLK     : out   std_logic;
    VIDOUT_RED     : out   std_logic_vector (9 downto 0);
    VIDOUT_GREEN   : out   std_logic_vector (9 downto 0);
    VIDOUT_BLUE    : out   std_logic_vector (9 downto 0);
    VIDOUT_BLANK_N : out   std_logic;
    VIDOUT_HSYNC_N : out   std_logic;
    VIDOUT_VSYNC_N : out   std_logic;
    PB_D           : inout std_logic_vector (15 downto 0));
end opb_xsb300;

architecture Behavioral of opb_xsb300 is

  -----------------------------------------------------------------------------
  -- Constant Parameters
  -----------------------------------------------------------------------------

  -- Video parameters
  constant HTOTAL       : integer := 800;
  constant HSYNC        : integer := 96;
  constant HBACK_PORCH  : integer := 48;
  constant HACTIVE      : integer := 640;
  constant HFRONT_PORCH : integer := 16;

  constant VTOTAL       : integer := 525;
  constant VSYNC        : integer := 2;
  constant VBACK_PORCH  : integer := 33;
  constant VACTIVE      : integer := 480;
  constant VFRONT_PORCH : integer := 10;

  -- Sprite parameters
  constant sprite_dimX : integer := 17;
  constant sprite_dimY : integer := 16;


  -----------------------------------------------------------------------------
  -- Component Declarations
  -----------------------------------------------------------------------------

  -- 512 X 8 dual-ported Xilinx block RAM
  component RAMB4_S8_S8
    port (
      DOA   : out std_logic_vector (7 downto 0);
      ADDRA : in  std_logic_vector (8 downto 0);
      CLKA  : in  std_logic;
      DIA   : in  std_logic_vector (7 downto 0);
      ENA   : in  std_logic;
      RSTA  : in  std_logic;
      WEA   : in  std_logic;
      DOB   : out std_logic_vector (7 downto 0);
      ADDRB : in  std_logic_vector (8 downto 0);
      CLKB  : in  std_logic;
      DIB   : in  std_logic_vector (7 downto 0);
      ENB   : in  std_logic;
      RSTB  : in  std_logic;
      WEB   : in  std_logic);
  end component;

  -- 256 X 16 dual-ported Xilinx block RAM
  -- Port A is 8 bit wide, Port B is 16 bit wide
  component RAMB4_S8_S16
    port (
      DOA   : out std_logic_vector (7 downto 0);
      ADDRA : in  std_logic_vector (8 downto 0);
      CLKA  : in  std_logic;
      DIA   : in  std_logic_vector (7 downto 0);
      ENA   : in  std_logic;
      RSTA  : in  std_logic;
      WEA   : in  std_logic;
      DOB   : out std_logic_vector (15 downto 0);
      ADDRB : in  std_logic_vector (7 downto 0);
      CLKB  : in  std_logic;
      DIB   : in  std_logic_vector (15 downto 0);
      ENB   : in  std_logic;
      RSTB  : in  std_logic;
      WEB   : in  std_logic);
  end component;

  component memoryctrl
    port (
      rst        : in  std_logic;
      clk        : in  std_logic;
      cs         : in  std_logic;
      select0    : in  std_logic;
      RNW        : in  std_logic;
      vreq       : in  std_logic;
      onecycle   : in  std_logic;
      videocycle : out std_logic;       -- try to discontinue use
      hihalf     : out std_logic;
      pb_wr      : out std_logic;
      pb_rd      : out std_logic;
      xfer       : out std_logic;
      ce0        : out std_logic;
      ce1        : out std_logic;
      rres       : out std_logic;
      video_ce   : out std_logic);      -- try to discontinue use
  end component;

  component pad_io
    port (
      clk       : in    std_logic;
      rst       : in    std_logic;
      PB_A      : out   std_logic_vector(19 downto 0);
      PB_UB_N   : out   std_logic;
      PB_LB_N   : out   std_logic;
      PB_WE_N   : out   std_logic;
      PB_OE_N   : out   std_logic;
      RAM_CE_N  : out   std_logic;
      PB_D      : inout std_logic_vector(15 downto 0);
      pb_addr   : in    std_logic_vector(19 downto 0);
      pb_ub     : in    std_logic;
      pb_lb     : in    std_logic;
      pb_wr     : in    std_logic;
      pb_rd     : in    std_logic;
      ram_ce    : in    std_logic;
      pb_dread  : out   std_logic_vector(15 downto 0);
      pb_dwrite : in    std_logic_vector(15 downto 0));
  end component;

  -- Fast low-voltage TTL-level I/O pad with 12 mA drive
  component OBUF_F_12
    port (
      O : out std_ulogic;
      I : in  std_ulogic);
  end component;

  -- Basic edge-sensitive flip-flop
  component FD
    port (
      C : in  std_logic;
      D : in  std_logic;
      Q : out std_logic);
  end component;

  -- Force instances of FD into pads for speed
  attribute iob       : string;
  attribute iob of FD : component is "true";


  -----------------------------------------------------------------------------
  -- Signal Declarations
  -----------------------------------------------------------------------------

  -- BRAM Signals
  signal RamPageAddress                 : std_logic_vector(6 downto 0);
  signal RamSelect                      : std_logic_vector (7 downto 0);
  signal DOUT0, DOUT5, DOUT6, DOUT7     : std_logic_vector(7 downto 0);
  signal DOUTB0, DOUTB5, DOUTB6, DOUTB7 : std_logic_vector(7 downto 0);
  signal RST, WE                        : std_logic_vector (7 downto 0);

  -- Signals for the video controller
  signal Hcount                : std_logic_vector(9 downto 0);  -- Horizontal position (0-800)
  signal Vcount                : std_logic_vector(9 downto 0);  -- Vertical position (0-524)
  signal Xcoord, Ycoord        : std_logic_vector(9 downto 0);
  signal HBLANK_N, VBLANK_N    : std_logic;  -- Blanking signals
  signal EndOfLine, EndOfField : std_logic;
  signal r                     : std_logic_vector (9 downto 0);
  signal g                     : std_logic_vector (9 downto 0);
  signal b                     : std_logic_vector (9 downto 0);

  -- Signals for the character generator
  signal FontLoad, LoadChar : std_logic;  -- Font/Character RAM read triggers
  signal FontAddr           : std_logic_vector(10 downto 0);
  signal CharRamPage        : std_logic_vector(2 downto 0);
  signal CharRamSelect_N    : std_logic_vector(4 downto 0);
  signal FontRamPage        : std_logic_vector(1 downto 0);
  signal FontRamSelect_N    : std_logic_vector(2 downto 0);
  signal CharAddr           : std_logic_vector(11 downto 0);
  signal CharColumn         : std_logic_vector(9 downto 0);
  signal CharRow            : std_logic_vector(9 downto 0);
  signal Column             : std_logic_vector(6 downto 0);  -- 0-79
  signal Row                : std_logic;  -- 0-1

  signal LoadNShift : std_logic;        -- Shift register control
  signal FontData   : std_logic_vector(7 downto 0);  -- Input to shift register
  signal ShiftData  : std_logic_vector(7 downto 0);  -- Shift register data
  signal TextData   : std_logic;        -- Serial out ANDed with blanking

  signal textColor : std_logic_vector (29 downto 0);

  -- Signals for the gradient background
  signal gradientStartR, gradientStartG, gradientStartB : std_logic_vector(9 downto 0);
  signal gradientIncR, gradientIncG, gradientIncB       : std_logic_vector(10 downto 0);
  signal backgroundR, backgroundG, backgroundB          : std_logic_vector(9 downto 0);
  signal gradientCtrR, gradientCtrG, gradientCtrB       : std_logic_vector(9 downto 0);

  -- Signals for the SRAM
  signal SRAM_addr_mux : std_logic_vector(19 downto 0);
  signal video_addr    : std_logic_vector (19 downto 0);  -- NOT USED ANYMORE
  signal video_data    : std_logic_vector (15 downto 0);  -- NOT USED ANYMORE
  signal video_req     : std_logic;     -- try to discontinue use
  signal video_ce      : std_logic;     -- try to discontinue use
  signal cs            : std_logic;

  signal onecycle           : std_logic;
  signal videocycle         : std_logic;  -- try to discontinue use
  signal amuxsel            : std_logic;  -- try to dicontinue use
  signal hihalf             : std_logic;
  signal rce0, rce1, rreset : std_logic;
  signal xfer               : std_logic;

  signal RNW : std_logic;

  signal ABus      : std_logic_vector (31 downto 0);
  signal DBus      : std_logic_vector (31 downto 0);
  signal wdata_mux : std_logic_vector (15 downto 0);
  signal BE        : std_logic_vector (3 downto 0);

  signal pb_bytesel   : std_logic_vector (1 downto 0);
  signal SRAM_rdata   : std_logic_vector (15 downto 0);
  signal SRAM_CE      : std_logic;
  signal pb_wr, pb_rd : std_logic;

  -- Terrain related signals
  signal terrainHeight                : std_logic_vector(9 downto 0);
  signal TERRAIN_DOUTA, TERRAIN_DOUTB : std_logic_vector(7 downto 0);
  signal drawTerrainFlag              : std_logic;
  signal terrainColor                 : std_logic_vector(29 downto 0);


  -- Sprite related signals
  signal SPRITE_DOUTA                                  : std_logic_vector(7 downto 0);  -- left hanging
  signal SPRITEAddr                                    : std_logic_vector(7 downto 0);  -- composed from WhichSprite & SpriteFontRow
  signal LeftTankFontRow, RightTankFontRow             : std_logic_vector(3 downto 0);  -- gets row 0 to 15 of sprite font
  signal CannonballFontRow, ExplosionFontRow           : std_logic_vector(3 downto 0);  -- gets row 0 to 15 of sprite font
  signal WhichSpriteLeftTank                           : std_logic_vector(3 downto 0);  -- gets the different sprite bitmaps
  signal WhichSpriteRightTank                          : std_logic_vector(3 downto 0);  -- gets the different sprite bitmaps
  signal SPRITE_DOUTB                                  : std_logic_vector(15 downto 0);  -- 16 bit read from bram
  signal LeftTankLoadNShift, RightTankLoadNShift       : std_logic;  -- 16 bit Shift register control
  signal CannonballLoadNShift, ExplosionLoadNShift     : std_logic;  -- 16 bit Shift register control
  signal ShiftLeftTankData                             : std_logic_vector(15 downto 0);  -- 16 bit Shift register data for Left Tank
  signal ShiftRightTankData                            : std_logic_vector(15 downto 0);  -- 16 bit Shift register data for Right Tank
  signal ShiftCannonballData                           : std_logic_vector(15 downto 0);  -- 16 bit Shift register data for Cannonball
  signal ShiftExplosionData                            : std_logic_vector(15 downto 0);  -- 16 bit Shift register data for Explosion
  signal VideoTankDataLeft                             : std_logic;  -- each pixel shifted out from shift register to vga
  signal VideoTankDataRight                            : std_logic;  -- each pixel shifted out from shift register to vga
  signal VideoExplosionData                            : std_logic;  -- each pixel shifted out from shift register to vga
  signal VideoCannonballData                           : std_logic;  -- each pixel shifted out from shift register to vga
  signal LeftTankSprite_x, LeftTankSprite_y            : std_logic_vector(9 downto 0);  -- x and y coordinates of the sprite
  signal RightTankSprite_x, RightTankSprite_y          : std_logic_vector(9 downto 0);  -- x and y coordinates of the sprite
  signal CannonballSprite_x, CannonballSprite_y        : std_logic_vector(9 downto 0);  -- x and y coordinates of the sprite
  signal ExplosionSprite_x, ExplosionSprite_y          : std_logic_vector(9 downto 0);  -- x and y coordinates of the sprite
  signal LeftTank_line_toggle, RightTank_line_toggle   : std_logic;  -- equals 1 when we are in range of sprite
  signal Cannonball_line_toggle, Explosion_line_toggle : std_logic;  -- equals 1 when we are in range of sprite
  signal InLeftTankRange, InRightTankRange             : std_logic;
  signal InCannonballRange, InExplosionRange           : std_logic;
  signal InLeftTankRangeAddr, InRightTankRangeAddr     : std_logic;
  signal InCannonballRangeAddr, InExplosionRangeAddr   : std_logic;



---- TAMF EXPERIMENT!!

  signal data_bus, data_bus_ce, data_bus_rce : std_logic_vector(31 downto 0);
  signal cs2, q2, q1, q0, ce, xfer2 : std_logic;



  -----------------------------------------------------------------------------
  -- BRAM Initialization
  -----------------------------------------------------------------------------

  attribute INIT_00 : string;
  attribute INIT_01 : string;
  attribute INIT_02 : string;
  attribute INIT_03 : string;
  attribute INIT_04 : string;
  attribute INIT_05 : string;
  attribute INIT_06 : string;
  attribute INIT_07 : string;
  attribute INIT_08 : string;
  attribute INIT_09 : string;
  attribute INIT_0a : string;
  attribute INIT_0b : string;
  attribute INIT_0c : string;
  attribute INIT_0d : string;
  attribute INIT_0e : string;
  attribute INIT_0f : string;

  -- Terrain altitude values (0-512)
  attribute INIT_00 of RAMB4_TERRAIN : label is
    "b4afaba7a39f9c9997949391908e8e8e8e8e8e9091939597999ca0a3a7abb0b4";
  attribute INIT_01 of RAMB4_TERRAIN : label is
    "b1a396918c91969a9da3a7a9abb3b4b7b6b4b3a7a4a4a4a4a4aaadb1b3b4b7b9";
  attribute INIT_02 of RAMB4_TERRAIN : label is
    "07080b0d1013161a1e22272c31373d434950585f62666e767b8994a0b2bcbab6";
  attribute INIT_03 of RAMB4_TERRAIN : label is
    "7870686058514a433d37312c27221e1a1613100d0b0907050403030303030405";
  attribute INIT_04 of RAMB4_TERRAIN : label is
    "8283848586878a8887858584848484858687898b8e90939697989b9997948a81";
  attribute INIT_05 of RAMB4_TERRAIN : label is
    "0001020304050607090a141e28323c46505a6e736e7378787a7b7c7d7e7f8081";
  attribute INIT_06 of RAMB4_TERRAIN : label is
    "6a676767676767675f564e453e362f28211b150f0a0504030201010202010000";
  attribute INIT_07 of RAMB4_TERRAIN : label is
    "9896949392919090909192939496989b9da19d9a9694928b88807b736d64676a";
  attribute INIT_08 of RAMB4_TERRAIN : label is
    "b9babdc0c3c5c7c8c9cacbcccdcecfd0d1d2d2d0cac4bfb9b4b0aba7a4a09d9a";
  attribute INIT_09 of RAMB4_TERRAIN : label is
    "b4afaba7a39f9c9997949391908f8e8e8e8e8f9091939597999ca0a3a7abb0b4";
  attribute INIT_0a of RAMB4_TERRAIN : label is
    "77797b7e8084878b8f93989da2a8aeb4b7b9bac8c8c7c6c5c4c0c0c3c4c5bfb9";
  attribute INIT_0b of RAMB4_TERRAIN : label is
    "d6d6d6cec7c0b9b2aca6a09b96928d8986827f7d7a7877757473737373747576";
  attribute INIT_0c of RAMB4_TERRAIN : label is
    "1b1d2024272b3034393f444a50575e656d757d858e97a1aad6d6d6d6d6d6d6d6";
  attribute INIT_0d of RAMB4_TERRAIN : label is
    "62615f58514b453f3a35302c2824211e1b191615131211111111111213141618";
  attribute INIT_0e of RAMB4_TERRAIN : label is
    "9597999ca0a3a7abb0b4b7b8bcbdbec3c9c8c5c1c1bbb3ada59f998f875e5d62";
  attribute INIT_0f of RAMB4_TERRAIN : label is
    "b4b4b4b4b4afafa7a8a7a39f9797949391908f8e8e8e8e8e8e8e8e8e8f909193";


  -- Sprite bitmap values for 16 bit reads
  attribute INIT_00 of RAMB4_SPRITE : label is -- Tank Angle 0 lefthalf/righthalf
    "ffffffff7ffe3ffc3ffc1f001f0007ff00000000000000000000000000000000";
  attribute INIT_01 of RAMB4_SPRITE : label is -- Tank Angle 10 lefthalf/righthalf
    "ffffffff7ffe3ffc3ffc1f001f00070000f0000f000000000000000000000000";
  attribute INIT_02 of RAMB4_SPRITE : label is -- Tank Angle 20 lefthalf/righthalf
    "ffffffff7ffe3ffc3ffc1f001f00060001800060001c00030000000000000000";
  attribute INIT_03 of RAMB4_SPRITE : label is -- Tank Angle 30 lefthalf/righthalf
    "ffffffff7ffe3ffc3ffc1f001f000400030000c0002000180006000100000000";
  attribute INIT_04 of RAMB4_SPRITE : label is -- Tank Angle 40 lefthalf/righthalf
    "ffffffff7ffe3ffc3ffc1f001f00040003000080004000200018000400020001";
  attribute INIT_05 of RAMB4_SPRITE : label is -- Tank Angle 50 lefthalf/righthalf
    "ffffffff7ffe3ffc3ffc1f001f00040002000100008000400020001000080004";
  attribute INIT_06 of RAMB4_SPRITE : label is -- Tank Angle 60 lefthalf/righthalf
    "ffffffff7ffe3ffc3ffc1f001f00040002000100008000800040002000100010";
  attribute INIT_07 of RAMB4_SPRITE : label is -- Tank Angle 70 lefthalf/righthalf
    "ffffffff7ffe3ffc3ffc1f001f00040002000200010001000080008000400040";
  attribute INIT_08 of RAMB4_SPRITE : label is -- Tank Angle 80 lefthalf/righthalf
    "ffffffff7ffe3ffc3ffc1f001f00040004000200020002000100010001000100";
  attribute INIT_09 of RAMB4_SPRITE : label is -- Tank Angle 90 lefthalf/righthalf
    "ffffffff7ffe3ffc3ffc1f001f00040004000400040004000400040004000400";
  attribute INIT_0a of RAMB4_SPRITE : label is -- Cannonball
    "000000000000000000000000000000003c007e00ff00ff00ff00ff007e003c00";
  attribute INIT_0b of RAMB4_SPRITE : label is -- Explosion
    "07e01ff83ffc7ffe7ffeffffffffffffffffffffffff7ffe7ffe3ffc1ff807e0";
  attribute INIT_0c of RAMB4_SPRITE : label is -- Square block
    "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
  attribute INIT_0d of RAMB4_SPRITE : label is -- All black
    "0000000000000000000000000000000000000000000000000000000000000000";


  -- Standard 8x16 font taken from the Linux console font file "lat0-16.psfu"
  attribute INIT_00 of RAMB4_S8_S8_5 : label is
    "000000001818001818183c3c3c18000000000000000000000000000000000000";
  attribute INIT_01 of RAMB4_S8_S8_5 : label is
    "000000006c6cfe6c6c6cfe6c6c00000000000000000000000000002466666600";
  attribute INIT_02 of RAMB4_S8_S8_5 : label is
    "0000000086c66030180cc6c200000000000010107cd616167cd0d0d67c101000";
  attribute INIT_03 of RAMB4_S8_S8_5 : label is
    "000000000000000000000030181818000000000076ccccccdc76386c6c380000";
  attribute INIT_04 of RAMB4_S8_S8_5 : label is
    "0000000030180c0c0c0c0c0c18300000000000000c18303030303030180c0000";
  attribute INIT_05 of RAMB4_S8_S8_5 : label is
    "00000000000018187e18180000000000000000000000663cff3c660000000000";
  attribute INIT_06 of RAMB4_S8_S8_5 : label is
    "0000000000000000fe0000000000000000000030181818000000000000000000";
  attribute INIT_07 of RAMB4_S8_S8_5 : label is
    "0000000000c06030180c06000000000000000000181800000000000000000000";
  attribute INIT_08 of RAMB4_S8_S8_5 : label is
    "000000007e1818181818187838180000000000007cc6e6e6d6d6cecec67c0000";
  attribute INIT_09 of RAMB4_S8_S8_5 : label is
    "000000007cc60606063c0606c67c000000000000fec6c06030180c06c67c0000";
  attribute INIT_0a of RAMB4_S8_S8_5 : label is
    "000000007cc6060606fcc0c0c0fe0000000000001e0c0c0cfecc6c3c1c0c0000";
  attribute INIT_0b of RAMB4_S8_S8_5 : label is
    "0000000030303030180c0606c6fe0000000000007cc6c6c6c6fcc0c060380000";
  attribute INIT_0c of RAMB4_S8_S8_5 : label is
    "00000000780c0606067ec6c6c67c0000000000007cc6c6c6c67cc6c6c67c0000";
  attribute INIT_0d of RAMB4_S8_S8_5 : label is
    "0000000030181800000018180000000000000000001818000000181800000000";
  attribute INIT_0e of RAMB4_S8_S8_5 : label is
    "00000000000000fe0000fe000000000000000000060c18306030180c06000000";
  attribute INIT_0f of RAMB4_S8_S8_5 : label is
    "000000001818001818180cc6c67c0000000000006030180c060c183060000000";
  attribute INIT_00 of RAMB4_S8_S8_6 : label is
    "00000000c6c6c6c6fec6c66c38100000000000007cc0dcdededec6c6c67c0000";
  attribute INIT_01 of RAMB4_S8_S8_6 : label is
    "000000003c66c2c0c0c0c0c2663c000000000000fc666666667c666666fc0000";
  attribute INIT_02 of RAMB4_S8_S8_6 : label is
    "00000000fe6662606878686266fe000000000000f86c6666666666666cf80000";
  attribute INIT_03 of RAMB4_S8_S8_6 : label is
    "000000003a66c6c6dec0c0c2663c000000000000f06060606878686266fe0000";
  attribute INIT_04 of RAMB4_S8_S8_6 : label is
    "000000003c18181818181818183c000000000000c6c6c6c6c6fec6c6c6c60000";
  attribute INIT_05 of RAMB4_S8_S8_6 : label is
    "00000000e666666c78786c6666e600000000000078cccccc0c0c0c0c0c1e0000";
  attribute INIT_06 of RAMB4_S8_S8_6 : label is
    "00000000c6c6c6c6c6d6fefeeec6000000000000fe6662606060606060f00000";
  attribute INIT_07 of RAMB4_S8_S8_6 : label is
    "000000007cc6c6c6c6c6c6c6c67c000000000000c6c6c6c6cedefef6e6c60000";
  attribute INIT_08 of RAMB4_S8_S8_6 : label is
    "00000e0c7cded6c6c6c6c6c6c67c000000000000f0606060607c666666fc0000";
  attribute INIT_09 of RAMB4_S8_S8_6 : label is
    "000000007cc6c6060c3860c6c67c000000000000e66666666c7c666666fc0000";
  attribute INIT_0a of RAMB4_S8_S8_6 : label is
    "000000007cc6c6c6c6c6c6c6c6c60000000000003c1818181818185a7e7e0000";
  attribute INIT_0b of RAMB4_S8_S8_6 : label is
    "000000006ceefed6d6d6c6c6c6c600000000000010386cc6c6c6c6c6c6c60000";
  attribute INIT_0c of RAMB4_S8_S8_6 : label is
    "000000003c181818183c66666666000000000000c6c66c7c38387c6cc6c60000";
  attribute INIT_0d of RAMB4_S8_S8_6 : label is
    "000000003c30303030303030303c000000000000fec6c26030180c86c6fe0000";
  attribute INIT_0e of RAMB4_S8_S8_6 : label is
    "000000003c0c0c0c0c0c0c0c0c3c00000000000000060c183060c00000000000";
  attribute INIT_0f of RAMB4_S8_S8_6 : label is
    "00ff0000000000000000000000000000000000000000000000000000c66c3810";
  attribute INIT_00 of RAMB4_S8_S8_7 : label is
    "0000000076cccccc7c0c78000000000000000000000000000000001830303000";
  attribute INIT_01 of RAMB4_S8_S8_7 : label is
    "000000007cc6c0c0c0c67c0000000000000000007c666666666c786060e00000";
  attribute INIT_02 of RAMB4_S8_S8_7 : label is
    "000000007cc6c0c0fec67c00000000000000000076cccccccc6c3c0c0c1c0000";
  attribute INIT_03 of RAMB4_S8_S8_7 : label is
    "0078cc0c7ccccccccccc76000000000000000000f060606060f060646c380000";
  attribute INIT_04 of RAMB4_S8_S8_7 : label is
    "000000003c181818181838001818000000000000e666666666766c6060e00000";
  attribute INIT_05 of RAMB4_S8_S8_7 : label is
    "00000000e6666c78786c666060e00000003c66660606060606060e0006060000";
  attribute INIT_06 of RAMB4_S8_S8_7 : label is
    "00000000c6d6d6d6d6feec000000000000000000183430303030303030700000";
  attribute INIT_07 of RAMB4_S8_S8_7 : label is
    "000000007cc6c6c6c6c67c000000000000000000666666666666dc0000000000";
  attribute INIT_08 of RAMB4_S8_S8_7 : label is
    "001e0c0c7ccccccccccc76000000000000f060607c6666666666dc0000000000";
  attribute INIT_09 of RAMB4_S8_S8_7 : label is
    "000000007cc60c3860c67c000000000000000000f06060606676dc0000000000";
  attribute INIT_0a of RAMB4_S8_S8_7 : label is
    "0000000076cccccccccccc0000000000000000001c3630303030fc3030100000";
  attribute INIT_0b of RAMB4_S8_S8_7 : label is
    "000000006cfed6d6d6c6c6000000000000000000183c66666666660000000000";
  attribute INIT_0c of RAMB4_S8_S8_7 : label is
    "00f80c067ec6c6c6c6c6c6000000000000000000c66c3838386cc60000000000";
  attribute INIT_0d of RAMB4_S8_S8_7 : label is
    "000000000e18181818701818180e000000000000fec6603018ccfe0000000000";
  attribute INIT_0e of RAMB4_S8_S8_7 : label is
    "0000000070181818180e18181870000000000000181818181818181818180000";
  attribute INIT_0f of RAMB4_S8_S8_7 : label is
    "000000003c1818183c66666666006600000000000000000000000000dc760000";


begin

  -----------------------------------------------------------------------------
  --
  -- Instances of the block RAMs
  --
  -----------------------------------------------------------------------------

  -- Port A is used for communication with the OPB
  -- Port B is for video
  RAMB4_S8_S8_0 : RAMB4_S8_S8
    port map (
      DOA   => DOUT0,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(0),
      WEA   => WE(0),
      DOB   => DOUTB0,
      ADDRB => CharAddr(8 downto 0),
      CLKB  => Pixel_Clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => CharRamSelect_N(0),
      WEB   => '0');

  -- Port A: C program I/O
  -- Port B: VHDL video subsystem I/O
  RAMB4_TERRAIN : RAMB4_S8_S8
    port map (
      DOA   => TERRAIN_DOUTA,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(1),
      WEA   => WE(1),
      DOB   => TERRAIN_DOUTB,
      ADDRB => Xcoord (9 downto 1),
      CLKB  => Pixel_Clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => '0',
      WEB   => '0');


  RAMB4_SPRITE : RAMB4_S8_S16
    port map (
      DOA   => SPRITE_DOUTA,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => '0',
      WEA   => '0',
      DOB   => SPRITE_DOUTB,            --16 bit data out
      ADDRB => SPRITEAddr,              --8 bit address
      CLKB  => Pixel_Clock,
      DIB   => X"0000",
      ENB   => '1',
      RSTB  => '0',
      WEB   => '0');

  RAMB4_S8_S8_5 : RAMB4_S8_S8
    port map (
      DOA   => DOUT5,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(5),
      WEA   => WE(5),
      DOB   => DOUTB5,
      ADDRB => FontAddr(8 downto 0),
      CLKB  => pixel_clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => FontRamSelect_N(0),
      WEB   => '0');

  RAMB4_S8_S8_6 : RAMB4_S8_S8
    port map (
      DOA   => DOUT6,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(6),
      WEA   => WE(6),
      DOB   => DOUTB6,
      ADDRB => FontAddr(8 downto 0),
      CLKB  => pixel_clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => FontRamSelect_N(1),
      WEB   => '0');

  RAMB4_S8_S8_7 : RAMB4_S8_S8
    port map (
      DOA   => DOUT7,
      ADDRA => ABus(8 downto 0),
      CLKA  => OPB_Clk,
      DIA   => DBus(7 downto 0),
      ENA   => '1',
      RSTA  => RST(7),
      WEA   => WE(7),
      DOB   => DOUTB7,
      ADDRB => FontAddr(8 downto 0),
      CLKB  => pixel_clock,
      DIB   => X"00",
      ENB   => '1',
      RSTB  => FontRamSelect_N(2),
      WEB   => '0');

-- Memory control/arbitration state machine

  memoryctrl1 : memoryctrl port map (
    rst        => OPB_Rst,
    clk        => OPB_Clk,
    cs         => cs,
    select0    => OPB_select,
    RNW        => RNW,
    vreq       => video_req,            -- try to discontinue use
    onecycle   => onecycle,
    videocycle => videocycle,           -- try to discontinue use
    hihalf     => hihalf,
    pb_wr      => pb_wr,
    pb_rd      => pb_rd,
    xfer       => xfer,
    ce0        => rce0,
    ce1        => rce1,
    rres       => rreset,
    video_ce   => video_ce);            -- try to dicontinue use

-- I/O pads

  pad_io1 : pad_io port map (
    clk       => OPB_Clk,
    rst       => OPB_Rst,
    PB_A      => PB_A,
    PB_UB_N   => PB_UB_N,
    PB_LB_N   => PB_LB_N,
    PB_WE_N   => PB_WE_N,
    PB_OE_N   => PB_OE_N,
    RAM_CE_N  => RAM_CE_N,
    PB_D      => PB_D,
    pb_addr   => SRAM_addr_mux,
    pb_rd     => pb_rd,
    pb_wr     => pb_wr,
    pb_ub     => pb_bytesel(1),
    pb_lb     => pb_bytesel(0),
    ram_ce    => SRAM_CE,
    pb_dread  => SRAM_rdata,
    pb_dwrite => wdata_mux);


  -----------------------------------------------------------------------------
  --
  -- SRAM Memory / OPB Stuff
  --
  -----------------------------------------------------------------------------

  LatchOPBSignals : process (OPB_Clk)
  begin
    if OPB_Clk'event and OPB_Clk = '1' then
      if OPB_RST = '1' then
        ABus <= ( others => '0' );
        DBus <= ( others => '0' );
        RNW  <= '1';
        BE   <= ( others => '0' );
      else
        ABus <= OPB_ABus;
        DBus <= OPB_DBus;
        RNW  <= OPB_RNW;
        BE   <= OPB_BE;
      end if;
    end if;
  end process;


  SRAM_CE <= pb_rd or pb_wr;

  amuxsel <= videocycle;                -- try to disconinue use

  SRAM_addr_mux <= video_addr when (amuxsel = '1')  -- try to discontinue use
                   else (ABus(20 downto 2) & (ABus(1) or hihalf));

  onecycle <= (not BE(3)) or (not BE(2)) or (not BE(1)) or (not BE(0));

  wdata_mux <= DBus(15 downto 0) when ((ABus(1) or hihalf) = '1')
               else DBus(31 downto 16);

  process(videocycle, BE, ABus(1), hihalf, pb_rd, pb_wr)
  begin
    if videocycle = '1' then            -- try to discontinue use
      pb_bytesel   <= "11";
    elsif pb_rd = '1' or pb_wr = '1' then
      if ABus(1) = '1' or hihalf = '1' then
        pb_bytesel <= BE(1 downto 0);
      else
        pb_bytesel <= BE(3 downto 2);
      end if;
    else
      pb_bytesel   <= "00";
    end if;
  end process;

  cs <= OPB_select when ABus(31 downto 20) = X"008" else '0';


  -- NOT USED ANYMORE
  process (OPB_Clk)
  begin
    if OPB_Clk'event and OPB_Clk = '1' then
      if video_ce = '1' then            -- try to discontinue use
        video_data <= SRAM_rdata;
      end if;
    end if;
  end process;

  -- Write the low two bytes if rce0 or rce1 (so that all 32 bits are on the data bus) is enabled
  process (OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      data_bus_rce(15 downto 0)   <= X"0000";
    elsif OPB_Clk'event and OPB_Clk = '1' then
      if rreset = '1' then
        data_bus_rce(15 downto 0) <= X"0000";
      elsif (rce1 or rce0) = '1' then
        data_bus_rce(15 downto 0) <= SRAM_rdata(15 downto 0);
      end if;
    end if;
  end process;

  -- Write the high two bytes if rce0 is enabled
  process (OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      data_bus_rce(31 downto 16)   <= X"0000";
    elsif OPB_Clk'event and OPB_Clk = '1' then
      if rreset = '1' then
        data_bus_rce(31 downto 16) <= X"0000";
      elsif rce0 = '1' then
        data_bus_rce(31 downto 16) <= SRAM_rdata(15 downto 0);
      end if;
    end if;
  end process;


  -----------------------------------------------------------------------------
  --
  -- Data Registers latched from the OPB data bus
  --
  -----------------------------------------------------------------------------

----- TAMF EXPERIMENT ----

  cs2 <= OPB_select when OPB_ABus(31 downto 16) = X"0180" else '0';
  ce <= (q2 and not q1) or (q0);

  process (OPB_Clk)
  begin
    if OPB_Clk'event and OPB_Clk='1' then
      q2 <= (not q2 and q1) or (q2 and not q1);
      q1 <= (cs2 and not q2 and not q1) or (q2 and not q1);
      q0 <= q2 and not q1;
    end if;
  end process;

  ReadDataFromMemory : process (OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      data_bus_ce <= X"0000_0000";
    elsif OPB_Clk'event and OPB_Clk = '1' then
      if ce='1' and RNW='1' then
        if RST(0) = '0' then
	  data_bus_ce <= DOUT0 & DOUT0 & DOUT0 & DOUT0;
        elsif RST(1) = '0' then
          data_bus_ce <= TERRAIN_DOUTA & TERRAIN_DOUTA & TERRAIN_DOUTA & TERRAIN_DOUTA;
        else
	  case ABus(15 downto 0) is
            when X"FFA1"                                    => data_bus_ce <= "00" & textColor;
            when X"FFA2"                                    => data_bus_ce <= "00" & X"00000" & gradientStartR;
            when X"FFA3"                                    => data_bus_ce <= "00" & X"00000" & gradientStartG;
            when X"FFA4"                                    => data_bus_ce <= "00" & X"00000" & gradientStartB;
            when X"FFA5"                                    => data_bus_ce <= "0" & X"00000" & gradientIncR;
            when X"FFA6"                                    => data_bus_ce <= "0" & X"00000" & gradientIncG;
            when X"FFA7"                                    => data_bus_ce <= "0" & X"00000" & gradientIncB;
            when X"FFB1"                                    => data_bus_ce <= "00" & X"00000" & LeftTankSprite_x;
            when X"FFB2"                                    => data_bus_ce <= "00" & X"00000" & LeftTankSprite_y;
            when X"FFB3"                                    => data_bus_ce <= "00" & X"00000" & RightTankSprite_x;
            when X"FFB4"                                    => data_bus_ce <= "00" & X"00000" & RightTankSprite_y;
            when X"FFB5"                                    => data_bus_ce <= "00" & X"00000" & CannonballSprite_x;
            when X"FFB6"                                    => data_bus_ce <= "00" & X"00000" & CannonballSprite_y;
            when X"FFB7"                                    => data_bus_ce <= "00" & X"00000" & ExplosionSprite_x;
            when X"FFB8"                                    => data_bus_ce <= "00" & X"00000" & ExplosionSprite_y;
            when X"FFB9"                                    => data_bus_ce <= X"0000_000" & WhichSpriteLeftTank;
            when X"FFBA"                                    => data_bus_ce <= X"0000_000" & WhichSpriteRightTank;
	    when X"FFBB"                                    => data_bus_ce <= "00" & terrainColor;
            when others                                     => data_bus_ce <= X"0000_0000";
          end case;
	end if;
      else
        data_bus_ce <= X"0000_0000";
      end if;
    end if;
  end process;


  WriteDataToMemory : process (OPB_Clk, OPB_Rst)
  begin
    if OPB_Rst='1' then
      textColor                                                                    <= (others => '1');
      gradientStartR                                                               <= (others => '0');
      gradientStartG                                                               <= (others => '0');
      gradientStartB                                                               <= (others => '0');
      gradientIncR                                                                 <= (others => '0');
      gradientIncG                                                                 <= (others => '0');
      gradientIncB                                                                 <= (others => '0');
      LeftTankSprite_x                                                             <= (others => '0');
      LeftTankSprite_y                                                             <= (others => '0');
      RightTankSprite_x                                                            <= (others => '0');
      RightTankSprite_y                                                            <= (others => '0');
      CannonballSprite_x                                                           <= (others => '0');
      CannonballSprite_y                                                           <= (others => '0');
      ExplosionSprite_x                                                            <= (others => '0');
      ExplosionSprite_y                                                            <= (others => '0');
      WhichSpriteLeftTank                                                          <= (others => '0');
      WhichSpriteRightTank                                                         <= (others => '0');
      terrainColor                                                                 <= (others => '0');
    elsif OPB_Clk'event and OPB_Clk='1' then
      if ce='1' and rnw='0' then
        case ABus(15 downto 0) is
          when X"FFA1"                                    => textColor            <= DBus(29 downto 0);
          when X"FFA2"                                    => gradientStartR       <= DBus(9 downto 0);
          when X"FFA3"                                    => gradientStartG       <= DBus(9 downto 0);
          when X"FFA4"                                    => gradientStartB       <= DBus(9 downto 0);
          when X"FFA5"                                    => gradientIncR         <= DBus(10 downto 0);
          when X"FFA6"                                    => gradientIncG         <= DBus(10 downto 0);
          when X"FFA7"                                    => gradientIncB         <= DBus(10 downto 0);
          when X"FFB1"                                    => LeftTankSprite_x     <= DBus(9 downto 0);
          when X"FFB2"                                    => LeftTankSprite_y     <= DBus(9 downto 0);
          when X"FFB3"                                    => RightTankSprite_x    <= DBus(9 downto 0);
          when X"FFB4"                                    => RightTankSprite_y    <= DBus(9 downto 0);
          when X"FFB5"                                    => CannonballSprite_x   <= DBus(9 downto 0);
          when X"FFB6"                                    => CannonballSprite_y   <= DBus(9 downto 0);
          when X"FFB7"                                    => ExplosionSprite_x    <= DBus(9 downto 0);
          when X"FFB8"                                    => ExplosionSprite_y    <= DBus(9 downto 0);
          when X"FFB9"                                    => WhichSpriteLeftTank  <= DBus(3 downto 0);
          when X"FFBA"                                    => WhichSpriteRightTank <= DBus(3 downto 0);
	  when X"FFBB"                                    => terrainColor         <= DBus(29 downto 0);
          when others                                     =>
        end case;
      end if;
    end if;
  end process;

  xfer2 <= q0;
  data_bus <= data_bus_ce when ce='1' else data_bus_rce;
  UIO_DBus <= data_bus;
  UIO_xferAck <= xfer when ce='0' else xfer2;


  -- Unused Slave OPB outputs
  UIO_errAck  <= '0';
  UIO_retry   <= '0';
  UIO_toutSup <= '0';


----- TAMF EXPERIMENT ----

  RamPageAddress <= ABus(15 downto 9);

  RamSelect <=
    "00000001" when RamPageAddress = "0000000" else
    "00000010" when RamPageAddress = "0000001" else
    --"00000100" when RamPageAddress = "010" else
    --"00001000" when RamPageAddress = "011" else
    --"00010000" when RamPageAddress = "100" else
    --"00100000" when RamPageAddress = "101" else
    --"01000000" when RamPageAddress = "110" else
    --"10000000" when RamPageAddress = "111" else
    "00000000";

  WE <=
    RamSelect when cs2 = '1' and RNW = '0' and OPB_Rst = '0' else
    "00000000";

  RST <=
    not RamSelect when cs2 = '1' and RNW = '1' and OPB_Rst = '0' else
    "11111111";

  -----------------------------------------------------------------------------
  --
  -- Background Gradient Processes
  --
  -----------------------------------------------------------------------------

  BackgroundGradient : process (pixel_clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      backgroundR           <= (others => '0');
      backgroundG           <= (others => '0');
      backgroundB           <= (others => '0');
    elsif pixel_clock'event and pixel_clock = '1' then
      if EndOfLine = '1' then
        if EndOfField = '1' then
          backgroundR       <= gradientStartR;
          backgroundG       <= gradientStartG;
          backgroundB       <= gradientStartB;
        else
          -- RED
          if gradientIncR(10) = '1' then  -- Inc/Dec each line by x
            if gradientIncR(9) = '1' then
              if backgroundR > (backgroundR - not(gradientIncR(9 downto 0) - 1)) then
                backgroundR <= backgroundR - not(gradientIncR(9 downto 0) - 1);
              end if;
            else
              if backgroundR < backgroundR + gradientIncR(9 downto 0) then
                backgroundR <= backgroundR + gradientIncR(9 downto 0);
              end if;
            end if;
          else
            if gradientIncR(9) = '1' then
              if ( ( (not(gradientIncR(9 downto 0) - 1) + 1) = gradientCtrR(9 downto 0) ) and ( (backgroundR-1) < backgroundR ) ) then
                backgroundR <= backgroundR - 1;
              end if;
            elsif ( (gradientIncR(9 downto 0) /= ("00" & X"00")) and ( (backgroundR+1) > backgroundR ) ) then
              backgroundR   <= backgroundR + 1;
            end if;
          end if;

          -- GREEN
          if gradientIncG(10) = '1' then  -- Inc/Dec each line by x
            if gradientIncG(9) = '1' then
              if backgroundG > (backgroundG - not(gradientIncG(9 downto 0) - 1)) then
                backgroundG <= backgroundG - not(gradientIncG(9 downto 0) - 1);
              end if;
            else
              if backgroundG < backgroundG + gradientIncG(9 downto 0) then
                backgroundG <= backgroundG + gradientIncG(9 downto 0);
              end if;
            end if;
          else
            if gradientIncG(9) = '1' then
              if ( ( (not(gradientIncG(9 downto 0) - 1) + 1) = gradientCtrG(9 downto 0) ) and ( (backgroundG-1) < backgroundG ) ) then
                backgroundG <= backgroundG - 1;
              end if;
            elsif ( (gradientIncG(9 downto 0) /= ("00" & X"00")) and ( (backgroundG+1) > backgroundG ) ) then
              backgroundG   <= backgroundG + 1;
            end if;
          end if;

          -- BLUE
          if gradientIncB(10) = '1' then  -- Inc/Dec each line by x
            if gradientIncB(9) = '1' then
              if backgroundB > (backgroundB - not(gradientIncB(9 downto 0) - 1)) then
                backgroundB <= backgroundB - not(gradientIncB(9 downto 0) - 1);
              end if;
            else
              if backgroundB < backgroundB + gradientIncB(9 downto 0) then
                backgroundB <= backgroundB + gradientIncB(9 downto 0);
              end if;
            end if;
          else
            if gradientIncB(9) = '1' then
              if ( ( (not(gradientIncB(9 downto 0) - 1) + 1) = gradientCtrB(9 downto 0) ) and ( (backgroundB-1) < backgroundB ) ) then
                backgroundB <= backgroundB - 1;
              end if;
            elsif ( (gradientIncB(9 downto 0) /= ("00" & X"00")) and ( (backgroundB+1) > backgroundB ) ) then
              backgroundB   <= backgroundB + 1;
            end if;
          end if;

        end if;
      end if;
    end if;
  end process;

  BackgroundGradient2 : process (pixel_clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      gradientCtrR           <= (others => '0');
      gradientCtrG           <= (others => '0');
      gradientCtrB           <= (others => '0');
    elsif pixel_clock'event and pixel_clock = '1' then
      if EndOfLine = '1' then
        if EndOfField = '1' then
          gradientCtrR       <= (others => '0');
          gradientCtrG       <= (others => '0');
          gradientCtrB       <= (others => '0');
        else
          -- RED
          if gradientIncR(10) = '0' then  -- Inc/Dec by 1 when line = x            
            if gradientIncR(9) = '1' then
              -- Do subtraction
              -- Get the unsigned version of gradient (which is in 2's compl) by
              -- subtracting 1 then complementing the result
              if ( not(gradientIncR(9 downto 0) - 1) = gradientCtrR(9 downto 0) ) then
                gradientCtrR <= "00" & X"00";
              else
                gradientCtrR <= gradientCtrR + 1;
              end if;
            elsif gradientIncR(9 downto 0) /= ("00" & X"00") then
              -- Do addition
              if ( gradientIncR(9 downto 0) = gradientCtrR(9 downto 0) ) then
                gradientCtrR <= "00" & X"00";
              else
                gradientCtrR <= gradientCtrR + 1;
              end if;
            else
              gradientCtrR   <= gradientCtrR + 1;
            end if;
          end if;

          -- GREEN
          if gradientIncG(10) = '0' then  -- Inc/Dec by 1 when line = x            
            if gradientIncG(9) = '1' then
              -- Do subtraction
              -- Get the unsigned version of gradient (which is in 2's compl) by
              -- subtracting 1 then complementing the result
              if ( not(gradientIncG(9 downto 0) - 1) = gradientCtrG(9 downto 0) ) then
                gradientCtrG <= "00" & X"00";
              else
                gradientCtrG <= gradientCtrG + 1;
              end if;
            elsif gradientIncG(9 downto 0) /= ("00" & X"00") then
              -- Do addition
              if ( gradientIncG(9 downto 0) = gradientCtrG(9 downto 0) ) then
                gradientCtrG <= "00" & X"00";
              else
                gradientCtrG <= gradientCtrG + 1;
              end if;
            else
              gradientCtrG   <= gradientCtrG + 1;
            end if;
          end if;

          -- BLUE
          if gradientIncB(10) = '0' then  -- Inc/Dec by 1 when line = x
            if gradientIncB(9) = '1' then
              -- Do subtraction
              -- Get the unsigned version of gradient (which is in 2's compl) by
              -- subtracting 1 then complementing the result
              if ( not(gradientIncB(9 downto 0) - 1) = gradientCtrB(9 downto 0) ) then
                gradientCtrB <= "00" & X"00";
              else
                gradientCtrB <= gradientCtrB + 1;
              end if;
            elsif gradientIncB(9 downto 0) /= ("00" & X"00") then
              -- Do addition
              if ( gradientIncB(9 downto 0) = gradientCtrB(9 downto 0) ) then
                gradientCtrB <= "00" & X"00";
              else
                gradientCtrB <= gradientCtrB + 1;
              end if;
            else
              gradientCtrB   <= gradientCtrB + 1;
            end if;
          end if;

        end if;
      end if;
    end if;
  end process;




  ----------------------------------------------------------------------------
  --
  -- Video controller
  --
  -----------------------------------------------------------------------------

  -- Horizontal and vertical counters

  HCounter : process (pixel_clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      Hcount   <= (others => '0');
    elsif pixel_clock'event and pixel_clock = '1' then
      if EndOfLine = '1' then
        Hcount <= (others => '0');
      else
        Hcount <= Hcount + 1;
      end if;
    end if;
  end process HCounter;

  EndOfLine <= '1' when Hcount = HTOTAL - 1 else '0';

  VCounter : process (pixel_clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      Vcount     <= (others => '0');
    elsif pixel_clock'event and pixel_clock = '1' then
      if EndOfLine = '1' then
        if EndOfField = '1' then
          Vcount <= (others => '0');
        else
          Vcount <= Vcount + 1;
        end if;
      end if;
    end if;
  end process VCounter;

  EndOfField <= '1' when Vcount = VTOTAL - 1 else '0';

  -- State machines to generate HSYNC, VSYNC, HBLANK, and VBLANK

  HSyncGen : process (pixel_clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      VIDOUT_HSYNC_N   <= '0';
    elsif pixel_clock'event and pixel_clock = '1' then
      if EndOfLine = '1' then
        VIDOUT_HSYNC_N <= '0';
      elsif Hcount = HSYNC - 1 then
        VIDOUT_HSYNC_N <= '1';
      end if;
    end if;
  end process HSyncGen;

  -- The -1 correction doesn't appear here to correct for the
  -- registered video signal outputs.

  HBlankGen : process (pixel_clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      HBLANK_N   <= '0';
    elsif pixel_clock'event and pixel_clock = '1' then
      if Hcount = HSYNC + HBACK_PORCH then
        HBLANK_N <= '1';
      elsif Hcount = HSYNC + HBACK_PORCH + HACTIVE then
        HBLANK_N <= '0';
      end if;
    end if;
  end process HBlankGen;

  VSyncGen : process (pixel_clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      VIDOUT_VSYNC_N     <= '0';
    elsif pixel_clock'event and pixel_clock = '1' then
      if EndOfLine = '1' then
        if EndOfField = '1' then
          VIDOUT_VSYNC_N <= '0';
        elsif VCount = VSYNC - 1 then
          VIDOUT_VSYNC_N <= '1';
        end if;
      end if;
    end if;
  end process VSyncGen;

  VBlankGen : process (pixel_clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      VBLANK_N     <= '0';
    elsif pixel_clock'event and pixel_clock = '1' then
      if EndOfLine = '1' then
        if (Vcount = VSYNC + VBACK_PORCH - 1) then
          VBLANK_N <= '1';
        elsif VCount = VSYNC + VBACK_PORCH + VACTIVE - 1 then
          VBLANK_N <= '0';
        end if;
      end if;
    end if;
  end process VBlankGen;

  Xcoord <= Hcount - HSYNC - HBACK_PORCH;
  Ycoord <= Vcount - VSYNC - VBACK_PORCH;




  -----------------------------------------------------------------------------
  --
  -- Text Generation from the Character Buffer and Font Bitmaps
  --
  -----------------------------------------------------------------------------

  -- RAM read triggers and shift register control

  LoadChar   <= '1' when Hcount(2 downto 0) = X"5" else '0';
  FontLoad   <= '1' when Hcount(2 downto 0) = X"6" else '0';
  LoadNShift <= '1' when Hcount(2 downto 0) = X"7" else '0';

  -- Correction of 4 needed to calculate the character address before the
  -- character is displayed
  CharColumn <= Hcount - HSYNC - HBACK_PORCH + 4;
  Column     <= CharColumn(9 downto 3);  -- /8
  CharRow    <= Vcount - VSYNC - VBACK_PORCH;
  Row        <= CharRow(4);              -- / 16

  -- Column + Row * 80
  CharAddr <= Column + ("00000" & Row & "000000") + ("0000000" & Row & "0000") when (Row <= '1') else "000000000000";

  CharRamPage     <= CharAddr(11 downto 9);
  CharRamSelect_N <= "11110" when CharRamPage = "000" else "11111";

  -- Most significant bit of character ignored
  FontAddr(10 downto 4) <= DOUTB0(6 downto 0);
  FontAddr(3 downto 0)  <= CharRow(3 downto 0);

  -- Unusual addressing: font only holds 96 of 128 possible characters
  -- First 32 characters appear twice
  FontRamPage     <= FontAddr(10 downto 9);
  FontRamSelect_N <=
    "110" when FontRamPage = "00" else
    "110" when FontRamPage = "01" else
    "101" when FontRamPage = "10" else
    "011" when FontRamPage = "11" else
    "111";

  FontData <= DOUTB5 or DOUTB6 or DOUTB7;

  -- Shift register
  ShiftRegister : process (pixel_clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      ShiftData   <= X"AB";
    elsif pixel_clock'event and pixel_clock = '1' then
      if LoadNShift = '1' then
        ShiftData <= FontData;
      else
        ShiftData <= ShiftData(6 downto 0) & ShiftData(7);
      end if;
    end if;
  end process ShiftRegister;

  TextData <= ShiftData(7);


  -----------------------------------------------------------------------------
  --
  -- Terrain Generation
  --
  -----------------------------------------------------------------------------

  -- Latch the terrain y-coordiate data from RAMB4_TERRAIN
  terrainHeight <=  "00" & TERRAIN_DOUTB;

  drawTerrainFlag <= '1' when ( (Xcoord >= 0) and (Xcoord < 640) ) and ( Ycoord >= (terrainHeight + 250) )
                      else '0';


  -----------------------------------------------------------------------------
  --
  -- Sprite Generation
  --
  -----------------------------------------------------------------------------

  -- 16 bit shift register control
  LeftTankLoadNShift <= '1' when ((Xcoord = (LeftTankSprite_x)) and
                                  ((Ycoord >= LeftTankSprite_y) and
                                   (Ycoord < (LeftTankSprite_y+sprite_dimY))))
                        else '0';

  RightTankLoadNShift <= '1' when ((Xcoord = (RightTankSprite_x)) and
                                   ((Ycoord >= RightTankSprite_y) and
                                    (Ycoord < (RightTankSprite_y+sprite_dimY))))
                         else '0';

  CannonballLoadNShift <= '1' when ((Xcoord = (CannonballSprite_x)) and
                                    ((Ycoord >= CannonballSprite_y) and
                                     (Ycoord < (CannonballSprite_y+sprite_dimY))))
                          else '0';

  ExplosionLoadNShift <= '1' when ((Xcoord = (ExplosionSprite_x)) and
                                   ((Ycoord >= ExplosionSprite_y) and
                                    (Ycoord < (ExplosionSprite_y+sprite_dimY))))
                         else '0';


  LeftTank_line_toggle <= '1' when ((Ycoord >= LeftTankSprite_y) and
                                    (Ycoord < (LeftTankSprite_y+sprite_dimY)))
                          else '0';

  RightTank_line_toggle <= '1' when ((Ycoord >= RightTankSprite_y) and
                                     (Ycoord < (RightTankSprite_y+sprite_dimY)))
                           else '0';

  Cannonball_line_toggle <= '1' when ((Ycoord >= CannonballSprite_y) and
                                      (Ycoord < (CannonballSprite_y+sprite_dimY)))
                            else '0';

  Explosion_line_toggle <= '1' when ((Ycoord >= ExplosionSprite_y) and
                                     (Ycoord < (ExplosionSprite_y+sprite_dimY)))
                           else '0';


  InLeftTankRange <= '1' when (((Xcoord >= (LeftTankSprite_x)) and
                                (Xcoord < (LeftTankSprite_x+sprite_dimX))) and
                               ((Ycoord >= LeftTankSprite_y) and
                                (Ycoord < (LeftTankSprite_y+sprite_dimY))))
                     else '0';

  InRightTankRange <= '1' when (((Xcoord >= (RightTankSprite_x)) and
                                 (Xcoord < (RightTankSprite_x+sprite_dimX))) and
                                ((Ycoord >= RightTankSprite_y) and
                                 (Ycoord < (RightTankSprite_y+sprite_dimY))))
                      else '0';

  InCannonballRange <= '1' when (((Xcoord >= (CannonballSprite_x)) and
                                  (Xcoord < (CannonballSprite_x+sprite_dimX))) and
                                 ((Ycoord >= CannonballSprite_y) and
                                  (Ycoord < (CannonballSprite_y+sprite_dimY))))
                       else '0';

  InExplosionRange <= '1' when (((Xcoord >= (ExplosionSprite_x)) and
                                 (Xcoord < (ExplosionSprite_x+sprite_dimX))) and
                                ((Ycoord >= ExplosionSprite_y) and
                                 (Ycoord < (ExplosionSprite_y+sprite_dimY))))
                      else '0';

  InLeftTankRangeAddr <= '1' when (((Xcoord >= (LeftTankSprite_x-1)) and
                                    (Xcoord < (LeftTankSprite_x+sprite_dimX))) and
                                   ((Ycoord >= LeftTankSprite_y) and
                                    (Ycoord < (LeftTankSprite_y+sprite_dimY))))
                         else '0';

  InRightTankRangeAddr <= '1' when (((Xcoord >= (RightTankSprite_x-1)) and
                                     (Xcoord < (RightTankSprite_x+sprite_dimX))) and
                                    ((Ycoord >= RightTankSprite_y) and
                                     (Ycoord < (RightTankSprite_y+sprite_dimY))))
                          else '0';

  InCannonballRangeAddr <= '1' when (((Xcoord >= (CannonballSprite_x-1)) and
                                      (Xcoord < (CannonballSprite_x+sprite_dimX))) and
                                     ((Ycoord >= CannonballSprite_y) and
                                      (Ycoord < (CannonballSprite_y+sprite_dimY))))
                           else '0';

  InExplosionRangeAddr <= '1' when (((Xcoord >= (ExplosionSprite_x-1)) and
                                     (Xcoord < (ExplosionSprite_x+sprite_dimX))) and
                                    ((Ycoord >= ExplosionSprite_y) and
                                     (Ycoord < (ExplosionSprite_y+sprite_dimY))))
                          else '0';


  SpriteIncrementer : process (OPB_Rst, Pixel_Clock)
  begin
    if (OPB_Rst = '1') then
      LeftTankFontRow     <= X"0";
      RightTankFontRow    <= X"0";
      CannonballFontRow   <= X"0";
      ExplosionFontRow    <= X"0";
    elsif Pixel_Clock'event and Pixel_Clock = '1' then
      if (LeftTank_line_toggle = '1') and (EndOfLine = '1') then
        LeftTankFontRow   <= LeftTankFontRow + 1;
      elsif (LeftTank_line_toggle = '0') then
        LeftTankFontRow   <= X"0";
      end if;
      if (RightTank_line_toggle = '1') and (EndOfLine = '1') then
        RightTankFontRow  <= RightTankFontRow + 1;
      elsif (RightTank_line_toggle = '0') then
        RightTankFontRow  <= X"0";
      end if;
      if (Cannonball_line_toggle = '1') and (EndOfLine = '1') then
        CannonballFontRow <= CannonballFontRow + 1;
      elsif (Cannonball_line_toggle = '0') then
        CannonballFontRow <= X"0";
      end if;
      if (Explosion_line_toggle = '1') and (EndOfLine = '1') then
        ExplosionFontRow  <= ExplosionFontRow + 1;
      elsif (Explosion_line_toggle = '0') then
        ExplosionFontRow  <= X"0";
      end if;
    end if;
  end process;

  -- Picks correct sprite to display
  SPRITEAddr <= X"B" & ExplosionFontRow                 when InExplosionRangeAddr = '1'  else
                X"A" & CannonballFontRow                when InCannonballRangeAddr = '1' else
                WhichSpriteRightTank & RightTankFontRow when InRightTankRangeAddr = '1'  else
                WhichSpriteLeftTank & LeftTankFontRow   when InLeftTankRangeAddr = '1'   else
                X"C0";


  -- Shift register 16 bit for Sprites
  ShiftRegister16 : process (Pixel_Clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      ShiftLeftTankData     <= X"0000";  -- X"AB";
      ShiftRightTankData    <= X"0000";  -- X"AB";
      ShiftCannonballData   <= X"0000";
      ShiftExplosionData    <= X"0000";
    elsif Pixel_Clock'event and Pixel_Clock = '1' then
      if LeftTankLoadNShift = '1' then
        ShiftLeftTankData   <= SPRITE_DOUTB;
      else
        ShiftLeftTankData   <= ShiftLeftTankData(14 downto 0) & "0";
      end if;
      if RightTankLoadNShift = '1' then
        ShiftRightTankData  <= SPRITE_DOUTB;
      else
        ShiftRightTankData  <= "0" & ShiftRightTankData(15 downto 1);
      end if;
      if CannonballLoadNShift = '1' then
        ShiftCannonballData <= SPRITE_DOUTB;
      else
        ShiftCannonballData <= ShiftCannonballData(14 downto 0) & "0";
      end if;
      if ExplosionLoadNShift = '1' then
        ShiftExplosionData  <= SPRITE_DOUTB;
      else
        ShiftExplosionData  <= ShiftExplosionData(14 downto 0) & "0";
      end if;
    end if;
  end process ShiftRegister16;

  VideoTankDataLeft   <= ShiftLeftTankData(15);
  VideoTankDataRight  <= ShiftRightTankData(0);
  VideoCannonballData <= ShiftCannonballData(15);
  VideoExplosionData  <= ShiftExplosionData(15);



  -----------------------------------------------------------------------------
  --
  -- Video Output
  --
  -----------------------------------------------------------------------------

  -- Registered video signals going to the video DAC
  VideoOut : process (pixel_clock, OPB_Rst)
  begin
    if OPB_Rst = '1' then
      VIDOUT_BLANK_N <= '0';
      r              <= (others => '0');
      g              <= (others => '0');
      b              <= (others => '0');
    elsif pixel_clock'event and pixel_clock = '1' then
      VIDOUT_BLANK_N <= VBLANK_N and HBLANK_N;

      if( (InExplosionRange = '1') and (VideoExplosionData = '1') ) then
        r <= "1111111111";
        g <= "0000000000";
        b <= "1111111111";
      elsif( (InCannonballRange = '1') and (VideoCannonballData = '1') and (InExplosionRange = '0') ) then
        r <= "1111111111";
        g <= "1111111111";
        b <= "0000000000";
      elsif( (InLeftTankRange = '1') and (VideoTankDataLeft = '1') and
             (InExplosionRange = '0') and (InCannonballRange = '0') ) then
        r <= "1111111111";
        g <= "0000000000";
        b <= "0000000000";
      elsif( (InRightTankRange = '1') and (VideoTankDataRight = '1') and
             (InExplosionRange = '0') and (InCannonballRange = '0') ) then
        r <= "0000000000";
        g <= "0000000000";
        b <= "1111111111";
      elsif ( TextData = '1' ) and ( Ycoord < 32 ) then
        -- Text
        r <= textColor(29 downto 20);
        g <= textColor(19 downto 10);
        b <= textColor(9 downto 0);
      elsif ( drawTerrainFlag = '1' ) then
        -- Terrain
        r <= terrainColor(29 downto 20);
        g <= terrainColor(19 downto 10);
        b <= terrainColor(9 downto 0);
      else
        -- Gradient background
        r <= backgroundR;
        g <= backgroundG;
        b <= backgroundB;
      end if;
    end if;
  end process VideoOut;


  VIDOUT_RED   <= r;
  VIDOUT_GREEN <= g;
  VIDOUT_BLUE  <= b;

  -- Video clock I/O pad to the DAC
  vidclk : OBUF_F_12 port map (
    O => VIDOUT_CLK,
    I => pixel_clock);

end Behavioral;
