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:
parent
2f858a6764
commit
1c22e73128
@ -18,9 +18,16 @@ entity framebuffer is
|
||||
|
||||
rgb: out std_logic_vector(23 downto 0);
|
||||
|
||||
write_enable: std_logic;
|
||||
write_address: std_logic_vector(12 downto 0);
|
||||
write_data: std_logic_vector(7 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;
|
||||
|
||||
@ -45,10 +52,18 @@ architecture logic of framebuffer is
|
||||
|
||||
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;
|
||||
@ -56,7 +71,16 @@ architecture logic of framebuffer is
|
||||
signal glyph_pos: glyph_pos_type;
|
||||
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 (
|
||||
WIDTH_BITS => 8,
|
||||
@ -65,11 +89,32 @@ begin
|
||||
clk => clk,
|
||||
ra => read_address,
|
||||
do => current_char,
|
||||
we => write_enable,
|
||||
wa => write_address,
|
||||
di => write_data
|
||||
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
|
||||
@ -95,8 +140,9 @@ begin
|
||||
end process delay_glyph_pos;
|
||||
|
||||
-- actually currently BRG
|
||||
rgb <=
|
||||
"111111111111111111111111" when current_glyph(glyph_pos(1)) = '1' else
|
||||
"000000000000000000000000";
|
||||
with current_glyph(glyph_pos(1)) = '1' and current_glyph_valid
|
||||
select rgb <=
|
||||
"111111111111111111111111" when true,
|
||||
"000000000000000000000000" when false;
|
||||
|
||||
end logic;
|
||||
|
80
terminal.vhd
80
terminal.vhd
@ -35,50 +35,67 @@ architecture syn of terminal is
|
||||
signal image_y: std_logic_vector(8 downto 0);
|
||||
signal pixel_rgb: std_logic_vector(23 downto 0);
|
||||
|
||||
signal fb_write_enable: std_logic;
|
||||
signal fb_write_address: std_logic_vector(12 downto 0);
|
||||
signal fb_write_data: std_logic_vector(7 downto 0);
|
||||
-- the cursor points to the position where the next char will be written
|
||||
signal cursor_row: unsigned(5 downto 0);
|
||||
signal cursor_col: unsigned(6 downto 0);
|
||||
|
||||
signal write_x: unsigned(6 downto 0);
|
||||
signal write_y: unsigned(5 downto 0);
|
||||
signal charbuf_we: std_logic;
|
||||
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
|
||||
|
||||
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)
|
||||
variable next_line: unsigned(5 downto 0);
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
fb_write_enable <= '0';
|
||||
fb_write_data <= write_data;
|
||||
-- we write to the current cursor position and simply pass the 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
|
||||
if write_y = 59 then
|
||||
if cursor_row = 59 then
|
||||
next_line := (others => '0');
|
||||
else
|
||||
next_line := write_y + 1;
|
||||
next_line := cursor_row + 1;
|
||||
end if;
|
||||
|
||||
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
|
||||
if write_data = x"0d" then
|
||||
fb_write_enable <= '0';
|
||||
write_x <= (others => '0');
|
||||
end if;
|
||||
cursor_col <= (others => '0');
|
||||
|
||||
-- line feed (implicit CR)
|
||||
if write_data = x"0a" then
|
||||
fb_write_enable <= '0';
|
||||
write_x <= (others => '0');
|
||||
write_y <= next_line;
|
||||
elsif write_data = x"0a" then
|
||||
cursor_col <= (others => '0');
|
||||
cursor_row <= 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;
|
||||
@ -120,9 +137,14 @@ begin
|
||||
x => image_x,
|
||||
y => image_y,
|
||||
rgb => pixel_rgb,
|
||||
write_enable => fb_write_enable,
|
||||
write_address => fb_write_address,
|
||||
write_data => fb_write_data
|
||||
cursor_row => cursor_row,
|
||||
cursor_col => cursor_col,
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user