term: Support for reverse scroll, save/restore, and bug fixes
diff --git a/stage23/drivers/vga_textmode.h b/stage23/drivers/vga_textmode.h
index 8800e9df..1c76c357 100644
--- a/stage23/drivers/vga_textmode.h
+++ b/stage23/drivers/vga_textmode.h
@@ -23,7 +23,10 @@ bool text_scroll_disable(void);
void text_scroll_enable(void);
void text_move_character(size_t new_x, size_t new_y, size_t old_x, size_t old_y);
void text_scroll(void);
+void text_revscroll(void);
void text_swap_palette(void);
+void text_save_state(void);
+void text_restore_state(void);
void text_double_buffer(bool state);
void text_double_buffer_flush(void);
diff --git a/stage23/drivers/vga_textmode.s2.c b/stage23/drivers/vga_textmode.s2.c
index f3a97cfb..1982f11b 100644
--- a/stage23/drivers/vga_textmode.s2.c
+++ b/stage23/drivers/vga_textmode.s2.c
@@ -29,6 +29,11 @@ static struct context {
#define text_palette context.text_palette
bool scroll_enabled;
#define scroll_enabled context.scroll_enabled
+
+ uint8_t saved_state_text_palette;
+#define saved_state_text_palette context.saved_state_text_palette
+ size_t saved_state_cursor_offset;
+#define saved_state_cursor_offset context.saved_state_cursor_offset
} context;
static size_t old_cursor_offset = 0;
@@ -38,6 +43,16 @@ static void draw_cursor(void) {
video_mem[cursor_offset + 1] = ((pal & 0xf0) >> 4) | ((pal & 0x0f) << 4);
}
+void text_save_state(void) {
+ saved_state_text_palette = text_palette;
+ saved_state_cursor_offset = cursor_offset;
+}
+
+void text_restore_state(void) {
+ text_palette = saved_state_text_palette;
+ cursor_offset = saved_state_cursor_offset;
+}
+
void text_swap_palette(void) {
text_palette = (text_palette << 4) | (text_palette >> 4);
}
@@ -66,6 +81,22 @@ void text_scroll(void) {
}
}
+void text_revscroll(void) {
+ // move the text up by one row
+ for (size_t i = (term_context.scroll_bottom_margin - 1) * VD_COLS - 2; ; i--) {
+ back_buffer[i + VD_COLS] = back_buffer[i];
+ if (i == term_context.scroll_top_margin * VD_COLS) {
+ break;
+ }
+ }
+ // clear the first line of the screen
+ for (size_t i = term_context.scroll_top_margin * VD_COLS;
+ i < (term_context.scroll_top_margin + 1) * VD_COLS; i += 2) {
+ back_buffer[i] = ' ';
+ back_buffer[i + 1] = text_palette;
+ }
+}
+
void text_clear(bool move) {
for (size_t i = 0; i < VIDEO_BOTTOM; i += 2) {
back_buffer[i] = ' ';
diff --git a/stage23/lib/gterm.c b/stage23/lib/gterm.c
index 94681c3d..ce685547 100644
--- a/stage23/lib/gterm.c
+++ b/stage23/lib/gterm.c
@@ -89,19 +89,40 @@ static struct context {
#define cursor_x context.cursor_x
size_t cursor_y;
#define cursor_y context.cursor_y
+ bool scroll_enabled;
+#define scroll_enabled context.scroll_enabled
+
+ uint32_t saved_state_text_fg;
+#define saved_state_text_fg context.saved_state_text_fg
+ uint32_t saved_state_text_bg;
+#define saved_state_text_bg context.saved_state_text_bg
+ size_t saved_state_cursor_x;
+#define saved_state_cursor_x context.saved_state_cursor_x
+ size_t saved_state_cursor_y;
+#define saved_state_cursor_y context.saved_state_cursor_y
} context;
static size_t old_cursor_x = 0;
static size_t old_cursor_y = 0;
+void gterm_save_state(void) {
+ saved_state_text_fg = text_fg;
+ saved_state_text_bg = text_bg;
+ saved_state_cursor_x = cursor_x;
+ saved_state_cursor_y = cursor_y;
+}
+
+void gterm_restore_state(void) {
+ text_fg = saved_state_text_fg;
+ text_bg = saved_state_text_bg;
+ cursor_x = saved_state_cursor_x;
+ cursor_y = saved_state_cursor_y;
+}
+
void gterm_swap_palette(void) {
uint32_t tmp = text_bg;
text_bg = text_fg;
- if (tmp == 0xffffffff) {
- text_fg = default_bg;
- } else {
- text_fg = tmp;
- }
+ text_fg = tmp;
}
#define A(rgb) (uint8_t)(rgb >> 24)
@@ -353,8 +374,6 @@ static void push_to_queue(struct gterm_char *c, size_t x, size_t y) {
q->c = *c;
}
-static bool scroll_enabled = true;
-
bool gterm_scroll_disable(void) {
bool ret = scroll_enabled;
scroll_enabled = false;
@@ -365,6 +384,32 @@ void gterm_scroll_enable(void) {
scroll_enabled = true;
}
+void gterm_revscroll(void) {
+ for (size_t i = (term_context.scroll_bottom_margin - 1) * cols - 1; ; i--) {
+ struct gterm_char *c;
+ struct queue_item *q = map[i];
+ if (q != NULL) {
+ c = &q->c;
+ } else {
+ c = &grid[i];
+ }
+ push_to_queue(c, (i + cols) % cols, (i + cols) / cols);
+ if (i == term_context.scroll_top_margin * cols) {
+ break;
+ }
+ }
+
+ // Clear the first line of the screen.
+ struct gterm_char empty;
+ empty.c = ' ';
+ empty.fg = text_fg;
+ empty.bg = text_bg;
+ for (size_t i = term_context.scroll_top_margin * cols;
+ i < (term_context.scroll_top_margin + 1) * cols; i++) {
+ push_to_queue(&empty, i % cols, i / cols);
+ }
+}
+
void gterm_scroll(void) {
for (size_t i = (term_context.scroll_top_margin + 1) * cols;
i < term_context.scroll_bottom_margin * cols; i++) {
@@ -578,6 +623,7 @@ bool gterm_init(size_t *_rows, size_t *_cols, size_t width, size_t height) {
return false;
cursor_status = true;
+ scroll_enabled = true;
// default scheme
margin = 64;
diff --git a/stage23/lib/gterm.h b/stage23/lib/gterm.h
index 65a39282..d37aaa13 100644
--- a/stage23/lib/gterm.h
+++ b/stage23/lib/gterm.h
@@ -27,7 +27,10 @@ bool gterm_scroll_disable(void);
void gterm_scroll_enable(void);
void gterm_move_character(size_t new_x, size_t new_y, size_t old_x, size_t old_y);
void gterm_scroll(void);
+void gterm_revscroll(void);
void gterm_swap_palette(void);
+void gterm_save_state(void);
+void gterm_restore_state(void);
void gterm_double_buffer_flush(void);
diff --git a/stage23/lib/term.c b/stage23/lib/term.c
index 21fa0dea..91e612ee 100644
--- a/stage23/lib/term.c
+++ b/stage23/lib/term.c
@@ -51,7 +51,10 @@ void term_vbe(size_t width, size_t height) {
scroll_enable = gterm_scroll_enable;
term_move_character = gterm_move_character;
term_scroll = gterm_scroll;
+ term_revscroll = gterm_revscroll;
term_swap_palette = gterm_swap_palette;
+ term_save_state = gterm_save_state;
+ term_restore_state = gterm_restore_state;
term_double_buffer_flush = gterm_double_buffer_flush;
diff --git a/stage23/lib/term.h b/stage23/lib/term.h
index 13a9a2d3..c113f669 100644
--- a/stage23/lib/term.h
+++ b/stage23/lib/term.h
@@ -30,6 +30,11 @@ extern struct term_context {
size_t scroll_top_margin;
size_t scroll_bottom_margin;
uint32_t esc_values[MAX_ESC_VALUES];
+
+ bool saved_state_bold;
+ bool saved_state_reverse_video;
+ size_t saved_state_current_charset;
+ size_t saved_state_current_primary;
} term_context;
enum {
@@ -71,7 +76,10 @@ extern bool (*scroll_disable)(void);
extern void (*scroll_enable)(void);
extern void (*term_move_character)(size_t new_x, size_t new_y, size_t old_x, size_t old_y);
extern void (*term_scroll)(void);
+extern void (*term_revscroll)(void);
extern void (*term_swap_palette)(void);
+extern void (*term_save_state)(void);
+extern void (*term_restore_state)(void);
extern void (*term_double_buffer_flush)(void);
diff --git a/stage23/lib/term.s2.c b/stage23/lib/term.s2.c
index c0543796..4622877c 100644
--- a/stage23/lib/term.s2.c
+++ b/stage23/lib/term.s2.c
@@ -66,7 +66,10 @@ void term_notready(void) {
scroll_enable = notready_void;
term_move_character = notready_move_character;
term_scroll = notready_void;
+ term_revscroll = notready_void;
term_swap_palette = notready_void;
+ term_save_state = notready_void;
+ term_restore_state = notready_void;
term_double_buffer_flush = notready_void;
term_context_size = notready_context_size;
term_context_save = notready_uint64_t;
@@ -207,7 +210,10 @@ bool (*scroll_disable)(void);
void (*scroll_enable)(void);
void (*term_move_character)(size_t new_x, size_t new_y, size_t old_x, size_t old_y);
void (*term_scroll)(void);
+void (*term_revscroll)(void);
void (*term_swap_palette)(void);
+void (*term_save_state)(void);
+void (*term_restore_state)(void);
void (*term_double_buffer_flush)(void);
@@ -241,6 +247,11 @@ struct term_context term_context;
#define charsets term_context.charsets
#define g_select term_context.g_select
+#define saved_state_bold term_context.saved_state_bold
+#define saved_state_reverse_video term_context.saved_state_reverse_video
+#define saved_state_current_charset term_context.saved_state_current_charset
+#define saved_state_current_primary term_context.saved_state_current_primary
+
#define CHARSET_DEFAULT 0
#define CHARSET_DEC_SPECIAL 1
@@ -296,7 +307,10 @@ void term_textmode(void) {
scroll_enable = text_scroll_enable;
term_move_character = text_move_character;
term_scroll = text_scroll;
+ term_revscroll = text_revscroll;
term_swap_palette = text_swap_palette;
+ term_save_state = text_save_state;
+ term_restore_state = text_restore_state;
term_double_buffer_flush = text_double_buffer_flush;
@@ -897,6 +911,24 @@ cleanup:
escape = false;
}
+static void restore_state(void) {
+ bold = saved_state_bold;
+ reverse_video = saved_state_reverse_video;
+ current_charset = saved_state_current_charset;
+ current_primary = saved_state_current_primary;
+
+ term_restore_state();
+}
+
+static void save_state(void) {
+ term_save_state();
+
+ saved_state_bold = bold;
+ saved_state_reverse_video = reverse_video;
+ saved_state_current_charset = current_charset;
+ saved_state_current_primary = current_primary;
+}
+
static void escape_parse(uint8_t c) {
escape_offset++;
@@ -922,6 +954,12 @@ is_csi:
rrr = false;
control_sequence = true;
return;
+ case '7':
+ save_state();
+ break;
+ case '8':
+ restore_state();
+ break;
case 'c':
term_reinit();
clear(true);
@@ -944,7 +982,12 @@ is_csi:
break;
case 'M':
// "Reverse linefeed"
- set_cursor_pos(x, y - 1);
+ if (y == scroll_top_margin) {
+ term_revscroll();
+ set_cursor_pos(0, y);
+ } else {
+ set_cursor_pos(0, y - 1);
+ }
break;
case 'Z':
if (term_callback != NULL) {
