library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.numeric_std.all; use std.textio.all; use ieee.std_logic_textio.all; entity framebuffer is generic ( input_clk: integer ); port ( clk: in std_logic; x: in std_logic_vector(9 downto 0); y: in std_logic_vector(8 downto 0); rgb: out std_logic_vector(23 downto 0); cursor_row: in unsigned(5 downto 0); cursor_col: in unsigned(6 downto 0); -- write access to the character and row length rams charbuf_we: in std_logic; charbuf_wa: in std_logic_vector(12 downto 0); charbuf_di: in std_logic_vector(7 downto 0); rowlen_we: in std_logic; rowlen_wa: in std_logic_vector(5 downto 0); rowlen_di: in std_logic_vector(6 downto 0) ); end framebuffer; architecture logic of framebuffer is type rom_type is array(0 to 127) of std_logic_vector(63 downto 0); impure function read_font(filename: in string) return rom_type is file rom_file: text is in filename; variable rom_line: line; variable rom: rom_type; begin -- skip comment in first line readline(rom_file, rom_line); for i in rom_type'range loop readline(rom_file, rom_line); hread(rom_line, rom(i)); end loop; return rom; end function; constant font: rom_type := read_font("font.hex"); signal row: unsigned(5 downto 0); signal col: unsigned(6 downto 0); signal eff_row_wide: unsigned(6 downto 0); signal eff_row: unsigned(5 downto 0); signal read_address: std_logic_vector(12 downto 0); signal current_char: std_logic_vector(7 downto 0); signal current_row_length: std_logic_vector(6 downto 0); signal current_glyph: std_logic_vector(63 downto 0); signal current_glyph_valid: boolean; -- delay by 2 cycles to match the delay of x/y -> rgb constant glyph_pos_length: integer := 2; type glyph_pos_type is array(1 to glyph_pos_length) of integer range 0 to 63; signal glyph_pos: glyph_pos_type; begin row <= unsigned(y(8 downto 3)); col <= unsigned(x(9 downto 3)); -- calculate effective row with respect to the cursor; this keeps the -- cursor always at the bottom of the screen and old contents scoll up eff_row_wide <= resize(cursor_row, 7) + row + 1; eff_row <= resize(eff_row_wide, 6) when eff_row_wide < "0111100" else -- 60 resize(eff_row_wide - "0111100", 6); -- 60 read_address <= std_logic_vector(col) & std_logic_vector(eff_row); terminal_buffer: entity work.ram_2port generic map ( WIDTH_BITS => 8, DEPTH_BITS => 13 ) port map ( clk => clk, ra => read_address, do => current_char, we => charbuf_we, wa => charbuf_wa, di => charbuf_di ); -- store the length of every row so that we don't have to overwrite -- existing rows when wrapping around and can simply disable display -- of non-overwritten characters row_lengths: entity work.ram_2port generic map ( WIDTH_BITS => 7, DEPTH_BITS => 6 ) port map ( clk => clk, ra => std_logic_vector(eff_row), do => current_row_length, we => rowlen_we, wa => rowlen_wa, di => rowlen_di ); -- as the row length is only saved after switching to the next row, we use -- the current cursor col position in the case of the current row, which -- is always at the bottom, so it is always row 59 current_glyph_valid <= col < cursor_col when row = "111011" else -- 59 col < unsigned(current_row_length); process(clk) variable current_glyph_pos: integer range 0 to 127; begin if rising_edge(clk) then current_glyph_pos := to_integer(unsigned(current_char)); current_glyph <= font(current_glyph_pos); end if; end process; delay_glyph_pos: process(clk) variable combined: std_logic_vector(5 downto 0); begin if rising_edge(clk) then combined := y(2 downto 0) & x(2 downto 0); glyph_pos(glyph_pos_length) <= to_integer(unsigned(combined)); -- move all elements one index down for i in 1 to glyph_pos_length - 1 loop glyph_pos(i) <= glyph_pos(i + 1); end loop; end if; end process delay_glyph_pos; -- actually currently BRG with current_glyph(glyph_pos(1)) = '1' and current_glyph_valid select rgb <= "111111111111111111111111" when true, "000000000000000000000000" when false; end logic;