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).
This commit is contained in:
Klemens Schölhorn 2018-04-25 23:50:09 +02:00
parent 2f858a6764
commit 1c22e73128
2 changed files with 107 additions and 39 deletions

View File

@ -18,9 +18,16 @@ entity framebuffer is
rgb: out std_logic_vector(23 downto 0); rgb: out std_logic_vector(23 downto 0);
write_enable: std_logic; cursor_row: in unsigned(5 downto 0);
write_address: std_logic_vector(12 downto 0); cursor_col: in unsigned(6 downto 0);
write_data: std_logic_vector(7 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; end framebuffer;
@ -45,10 +52,18 @@ architecture logic of framebuffer is
constant font: rom_type := read_font("font.hex"); 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 read_address: std_logic_vector(12 downto 0);
signal current_char: std_logic_vector(7 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: std_logic_vector(63 downto 0);
signal current_glyph_valid: boolean;
-- delay by 2 cycles to match the delay of x/y -> rgb -- delay by 2 cycles to match the delay of x/y -> rgb
constant glyph_pos_length: integer := 2; constant glyph_pos_length: integer := 2;
@ -56,7 +71,16 @@ architecture logic of framebuffer is
signal glyph_pos: glyph_pos_type; signal glyph_pos: glyph_pos_type;
begin begin
read_address <= x(9 downto 3) & y(8 downto 3); 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 ( terminal_buffer: entity work.ram_2port generic map (
WIDTH_BITS => 8, WIDTH_BITS => 8,
@ -65,11 +89,32 @@ begin
clk => clk, clk => clk,
ra => read_address, ra => read_address,
do => current_char, do => current_char,
we => write_enable, we => charbuf_we,
wa => write_address, wa => charbuf_wa,
di => write_data 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) process(clk)
variable current_glyph_pos: integer range 0 to 127; variable current_glyph_pos: integer range 0 to 127;
begin begin
@ -95,8 +140,9 @@ begin
end process delay_glyph_pos; end process delay_glyph_pos;
-- actually currently BRG -- actually currently BRG
rgb <= with current_glyph(glyph_pos(1)) = '1' and current_glyph_valid
"111111111111111111111111" when current_glyph(glyph_pos(1)) = '1' else select rgb <=
"000000000000000000000000"; "111111111111111111111111" when true,
"000000000000000000000000" when false;
end logic; end logic;

View File

@ -35,50 +35,67 @@ architecture syn of terminal is
signal image_y: std_logic_vector(8 downto 0); signal image_y: std_logic_vector(8 downto 0);
signal pixel_rgb: std_logic_vector(23 downto 0); signal pixel_rgb: std_logic_vector(23 downto 0);
signal fb_write_enable: std_logic; -- the cursor points to the position where the next char will be written
signal fb_write_address: std_logic_vector(12 downto 0); signal cursor_row: unsigned(5 downto 0);
signal fb_write_data: std_logic_vector(7 downto 0); signal cursor_col: unsigned(6 downto 0);
signal write_x: unsigned(6 downto 0); signal charbuf_we: std_logic;
signal write_y: unsigned(5 downto 0); signal charbuf_wa: std_logic_vector(12 downto 0);
signal charbuf_di: std_logic_vector(7 downto 0);
signal rowlen_we: std_logic;
signal rowlen_wa: std_logic_vector(5 downto 0);
signal rowlen_di: std_logic_vector(6 downto 0);
begin begin
fb_write_address <= std_logic_vector(write_x) & std_logic_vector(write_y); -- writes the next character, advances the cursor and saves the length of
-- the current row before jumping to the next one
process(clk) process(clk)
variable next_line: unsigned(5 downto 0); variable next_line: unsigned(5 downto 0);
begin begin
if rising_edge(clk) then if rising_edge(clk) then
fb_write_enable <= '0'; -- we write to the current cursor position and simply pass the data
fb_write_data <= write_data; -- through (but CR and LF are ignored, so charbuf_we is 0 by default)
charbuf_we <= '0';
charbuf_wa <= std_logic_vector(cursor_col) & std_logic_vector(cursor_row);
charbuf_di <= write_data;
-- we save the length of the current row before advancing to the
-- next row (the current row length is the cursor column when using
-- LF the go to the next line or one more when wrapping around)
rowlen_we <= '0';
rowlen_wa <= std_logic_vector(cursor_row);
rowlen_di <= std_logic_vector(cursor_col);
-- calculate next line -- calculate next line
if write_y = 59 then if cursor_row = 59 then
next_line := (others => '0'); next_line := (others => '0');
else else
next_line := write_y + 1; next_line := cursor_row + 1;
end if; end if;
if write_enable = '1' then if write_enable = '1' then
fb_write_enable <= '1';
if write_x = 79 then
write_x <= (others => '0');
write_y <= next_line;
else
write_x <= write_x + 1;
end if;
-- carriage return -- carriage return
if write_data = x"0d" then if write_data = x"0d" then
fb_write_enable <= '0'; cursor_col <= (others => '0');
write_x <= (others => '0');
end if;
-- line feed (implicit CR) -- line feed (implicit CR)
if write_data = x"0a" then elsif write_data = x"0a" then
fb_write_enable <= '0'; cursor_col <= (others => '0');
write_x <= (others => '0'); cursor_row <= next_line;
write_y <= next_line; rowlen_we <= '1'; -- save row length
-- normal characters
else
charbuf_we <= '1'; -- write normal characters
if cursor_col = 79 then
cursor_col <= (others => '0');
cursor_row <= next_line;
rowlen_we <= '1'; -- save row length
rowlen_di <= "1010000"; -- 80 (cursor_col is only 79)
else
cursor_col <= cursor_col + 1;
end if;
end if; end if;
end if; end if;
end if; end if;
@ -120,9 +137,14 @@ begin
x => image_x, x => image_x,
y => image_y, y => image_y,
rgb => pixel_rgb, rgb => pixel_rgb,
write_enable => fb_write_enable, cursor_row => cursor_row,
write_address => fb_write_address, cursor_col => cursor_col,
write_data => fb_write_data charbuf_we => charbuf_we,
charbuf_wa => charbuf_wa,
charbuf_di => charbuf_di,
rowlen_we => rowlen_we,
rowlen_wa => rowlen_wa,
rowlen_di => rowlen_di
); );
end syn; end syn;