terminal/framebuffer.vhd
Klemens Schölhorn 1c22e73128 Implement scrolling for the terminal
The line where the cursor located is always displayed at the bottom and
old content scrolls up until disappearing eventually at the top of the
screen.

This is implemented using a ring buffer of all rows, where old chars
are only overwritten when writing new ones. To avoid displaying stale
data at the end of rows, we save the length of every row and hide chars
that were not overwritten (yet).
2018-04-25 23:50:09 +02:00

149 lines
4.6 KiB
VHDL

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;