These two patches implement a software based cursor for the "coreboot native vga" seavgabios implementation. It implements the cursor by inverting the pixels in the framebuffer at the character cell that contains the cursor.
This is a bit of a failed experiment - I do not intend to commit it to the main git repo as is. The feature works, but it seems to slow down screen writing significantly. I'm posting this here in case anyone is interested in playing with it.
I've also put this up at: https://github.com/KevinOConnor/seabios/tree/testing-cbvga
I suspect the reason for the slow down is that the majority of character writes to the screen end up moving the cursor, and on each cursor move it's required to remove the inverse in the old position and then add the inverse in the new position. The inverse is implemented as an xor which requires a cell read followed by a cell write. Thus, a typical character write used to require writing one cell's worth of data to the framebuffer, but with this patch takes 5 times more IO (a read/write, a write, a read/write).
If I get time I may play with other approaches. (One I was thinking of is hooking the BIOS timer irq and only enabling the cursor from that irq - which should dramatically reduce the amount of cursor updates when lots of characters are being rendered in bulk.)
-Kevin
Kevin O'Connor (2): vgabios: Add stubs for software cursor enable/disable vgabios: Add software cursor capability
vgasrc/Kconfig | 5 +++++ vgasrc/vgabios.c | 40 ++++++++++++++++++++++++++++++++++------ vgasrc/vgabios.h | 2 ++ vgasrc/vgafb.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 6 deletions(-)
Add stub function vgafb_set_swcursor(), and add calls to it any place that may read/write the framebuffer or may modify the cursor shape/position.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- vgasrc/vgabios.c | 38 +++++++++++++++++++++++++++++++++----- vgasrc/vgabios.h | 1 + vgasrc/vgafb.c | 5 +++++ 3 files changed, 39 insertions(+), 5 deletions(-)
diff --git a/vgasrc/vgabios.c b/vgasrc/vgabios.c index 8ec6aa6..85a060e 100644 --- a/vgasrc/vgabios.c +++ b/vgasrc/vgabios.c @@ -56,9 +56,13 @@ set_cursor_shape(u8 start, u8 end) start &= 0x3f; end &= 0x1f;
+ vgafb_set_swcursor(0); + u16 curs = (start << 8) + end; SET_BDA(cursor_type, curs);
+ vgafb_set_swcursor(1); + if (!CONFIG_VGA_STDVGA_PORTS) return;
@@ -134,6 +138,8 @@ set_active_page(u8 page) if (!vmode_g) return;
+ vgafb_set_swcursor(0); + // Calculate memory address of start of page struct cursorpos cp = {0, 0, page}; int address = (int)text_address(cp); @@ -147,6 +153,8 @@ set_active_page(u8 page)
// Display the cursor, now the page is active set_cursor_pos(get_cursor_pos(page)); + + vgafb_set_swcursor(1); }
static void @@ -287,9 +295,13 @@ vga_set_mode(int mode, int flags) if (!vmode_g) return VBE_RETURN_STATUS_FAILED;
+ vgafb_set_swcursor(0); + int ret = vgahw_set_mode(vmode_g, flags); - if (ret) + if (ret) { + vgafb_set_swcursor(1); return ret; + }
// Set the BIOS mem int width = GET_GLOBAL(vmode_g->width); @@ -348,6 +360,7 @@ vga_set_mode(int mode, int flags) break; }
+ vgafb_set_swcursor(1); return 0; }
@@ -386,7 +399,9 @@ static void handle_1002(struct bregs *regs) { struct cursorpos cp = {regs->dl, regs->dh, regs->bh}; + vgafb_set_swcursor(0); set_cursor_pos(cp); + vgafb_set_swcursor(1); }
static void @@ -426,6 +441,7 @@ verify_scroll(struct bregs *regs, int dir) if (wincols <= 0 || winrows <= 0) return;
+ vgafb_set_swcursor(0); u8 page = GET_BDA(video_page); int clearlines = regs->al, movelines = winrows - clearlines; if (!clearlines || movelines <= 0) { @@ -434,10 +450,7 @@ verify_scroll(struct bregs *regs, int dir) struct carattr attr = {' ', regs->bh, 1}; struct cursorpos clrsize = {wincols, winrows}; vgafb_clear_chars(clr, attr, clrsize); - return; - } - - if (dir > 0) { + } else if (dir > 0) { // Normal scroll struct cursorpos dest = {ulx, uly, page}; struct cursorpos src = {ulx, uly + clearlines, page}; @@ -460,6 +473,7 @@ verify_scroll(struct bregs *regs, int dir) struct cursorpos clrsize = {wincols, clearlines}; vgafb_clear_chars(clr, attr, clrsize); } + vgafb_set_swcursor(1); }
static void @@ -477,9 +491,11 @@ handle_1007(struct bregs *regs) static void handle_1008(struct bregs *regs) { + vgafb_set_swcursor(0); struct carattr ca = vgafb_read_char(get_cursor_pos(regs->bh)); regs->al = ca.car; regs->ah = ca.attr; + vgafb_set_swcursor(1); }
static void noinline @@ -487,9 +503,11 @@ handle_1009(struct bregs *regs) { struct carattr ca = {regs->al, regs->bl, 1}; struct cursorpos cp = get_cursor_pos(regs->bh); + vgafb_set_swcursor(0); int count = regs->cx; while (count--) write_char(&cp, ca); + vgafb_set_swcursor(1); }
static void noinline @@ -497,9 +515,11 @@ handle_100a(struct bregs *regs) { struct carattr ca = {regs->al, regs->bl, 0}; struct cursorpos cp = get_cursor_pos(regs->bh); + vgafb_set_swcursor(0); int count = regs->cx; while (count--) write_char(&cp, ca); + vgafb_set_swcursor(1); }
@@ -540,14 +560,18 @@ static void handle_100c(struct bregs *regs) { // XXX - page (regs->bh) is unused + vgafb_set_swcursor(0); vgafb_write_pixel(regs->al, regs->cx, regs->dx); + vgafb_set_swcursor(1); }
static void handle_100d(struct bregs *regs) { // XXX - page (regs->bh) is unused + vgafb_set_swcursor(0); regs->al = vgafb_read_pixel(regs->cx, regs->dx); + vgafb_set_swcursor(1); }
static void noinline @@ -557,8 +581,10 @@ handle_100e(struct bregs *regs) // We do output only on the current page ! struct carattr ca = {regs->al, regs->bl, 0}; struct cursorpos cp = get_cursor_pos(0xff); + vgafb_set_swcursor(0); write_teletype(&cp, ca); set_cursor_pos(cp); + vgafb_set_swcursor(1); }
static void @@ -1040,6 +1066,7 @@ handle_1013(struct bregs *regs) else cp = (struct cursorpos) {regs->dl, regs->dh, regs->bh};
+ vgafb_set_swcursor(0); u16 count = regs->cx; u8 *offset_far = (void*)(regs->bp + 0); u8 attr = regs->bl; @@ -1057,6 +1084,7 @@ handle_1013(struct bregs *regs)
if (regs->al & 1) set_cursor_pos(cp); + vgafb_set_swcursor(1); }
diff --git a/vgasrc/vgabios.h b/vgasrc/vgabios.h index fc8d45a..e07309a 100644 --- a/vgasrc/vgabios.h +++ b/vgasrc/vgabios.h @@ -131,6 +131,7 @@ void vgafb_write_char(struct cursorpos cp, struct carattr ca); struct carattr vgafb_read_char(struct cursorpos cp); void vgafb_write_pixel(u8 color, u16 x, u16 y); u8 vgafb_read_pixel(u16 x, u16 y); +void vgafb_set_swcursor(int enable);
// vbe.c extern u32 VBE_total_memory; diff --git a/vgasrc/vgafb.c b/vgasrc/vgafb.c index 9652683..f179584 100644 --- a/vgasrc/vgafb.c +++ b/vgasrc/vgafb.c @@ -595,3 +595,8 @@ fail: ; struct carattr ca2 = {0, 0, 0}; return ca2; } + +void +vgafb_set_swcursor(int enable) +{ +}
Add mechanism for drawing a cursor to the framebuffer to implement a cursor in software. This can be useful for "coreboot native vga".
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- vgasrc/Kconfig | 5 +++++ vgasrc/vgabios.c | 2 +- vgasrc/vgabios.h | 1 + vgasrc/vgafb.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/vgasrc/Kconfig b/vgasrc/Kconfig index 6474c22..5f3cc9d 100644 --- a/vgasrc/Kconfig +++ b/vgasrc/Kconfig @@ -51,6 +51,7 @@ menu "VGA ROM" depends on COREBOOT bool "coreboot linear framebuffer" select VGA_EMULATE_TEXTATTR + select VGA_SOFTWARE_CURSOR help Build support for a vgabios wrapper around video devices initialized using coreboot native vga init. @@ -89,6 +90,10 @@ menu "VGA ROM" help Try to emulate text mode attributes when writing text in graphics mode. + config VGA_SOFTWARE_CURSOR + bool + help + Emulate a cursor by modifying the framebuffer.
config VGA_ALLOCATE_EXTRA_STACK depends on BUILD_VGABIOS diff --git a/vgasrc/vgabios.c b/vgasrc/vgabios.c index 85a060e..ab9f4a0 100644 --- a/vgasrc/vgabios.c +++ b/vgasrc/vgabios.c @@ -111,7 +111,7 @@ set_cursor_pos(struct cursorpos cp) stdvga_set_cursor_pos((int)text_address(cp)); }
-static struct cursorpos +struct cursorpos get_cursor_pos(u8 page) { if (page == 0xff) diff --git a/vgasrc/vgabios.h b/vgasrc/vgabios.h index e07309a..5abbe4e 100644 --- a/vgasrc/vgabios.h +++ b/vgasrc/vgabios.h @@ -115,6 +115,7 @@ struct cursorpos { }; int vga_bpp(struct vgamode_s *vmode_g); u16 calc_page_size(u8 memmodel, u16 width, u16 height); +struct cursorpos get_cursor_pos(u8 page); int bda_save_restore(int cmd, u16 seg, void *data); struct vgamode_s *get_current_mode(void); int vga_set_mode(int mode, int flags); diff --git a/vgasrc/vgafb.c b/vgasrc/vgafb.c index f179584..85ff184 100644 --- a/vgasrc/vgafb.c +++ b/vgasrc/vgafb.c @@ -454,6 +454,32 @@ gfx_write_char(struct vgamode_s *vmode_g } }
+// Draw/undraw a cursor on the framebuffer by xor'ing the cursor cell +void +gfx_set_swcursor(struct vgamode_s *vmode_g, int enable) +{ + struct cursorpos cp = get_cursor_pos(0xff); + if (cp.x >= GET_BDA(video_cols)) + return; + + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.x = cp.x * 8; + int cheight = GET_BDA(char_height); + op.y = cp.y * cheight; + + int i; + for (i = 0; i < cheight; i++, op.y++) { + op.op = GO_READ8; + handle_gfx_op(&op); + int j; + for (j = 0; j < 8; j++) + op.pixels[j] ^= 0x07; + op.op = GO_WRITE8; + handle_gfx_op(&op); + } +} + // Set the pixel at the given position. void vgafb_write_pixel(u8 color, u16 x, u16 y) @@ -599,4 +625,22 @@ fail: ; void vgafb_set_swcursor(int enable) { + if (!CONFIG_VGA_SOFTWARE_CURSOR) + return; + + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return; + + if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { + gfx_set_swcursor(vmode_g, enable); + return; + } + + // In text mode, swap foreground and background attributes for cursor + struct cursorpos cp = get_cursor_pos(0xff); + void *dest_far = text_address(cp) + 1; + u8 attr = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far); + attr = (attr >> 4) | (attr << 4); + SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, attr); }