Add mechanism for drawing a cursor to the framebuffer to implement a cursor in software. The timer interrupt is "hooked" so that the cursor can blink. This can be useful for "coreboot native vga".
Signed-off-by: Kevin O'Connor kevin@koconnor.net ---
This is similar to the software cursor patches I sent earlier in the week. This time I've used the timer interrupt to both improve the drawing speed and to enable cursor blinking. This version of the patch also creates a small underline cursor instead of inverting the entire character cell.
The series is also available at: https://github.com/KevinOConnor/seabios/tree/testing
--- vgasrc/Kconfig | 5 +++++ vgasrc/vgabios.c | 13 ++++++++--- vgasrc/vgabios.h | 6 +++++- vgasrc/vgaentry.S | 30 ++++++++++++++++++++++++++ vgasrc/vgafb.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ vgasrc/vgainit.c | 34 +++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+), 4 deletions(-)
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 786c009..5fdb549 100644 --- a/vgasrc/vgabios.c +++ b/vgasrc/vgabios.c @@ -51,7 +51,7 @@ calc_page_size(u8 memmodel, u16 width, u16 height) }
// Determine cursor shape (taking into account possible cursor scaling) -static u16 +u16 get_cursor_shape(void) { u16 cursor_type = GET_BDA(cursor_type); @@ -74,6 +74,7 @@ get_cursor_shape(void) static void set_cursor_shape(u16 cursor_type) { + vgafb_set_swcursor(0); SET_BDA(cursor_type, cursor_type); if (CONFIG_VGA_STDVGA_PORTS) stdvga_set_cursor_shape(get_cursor_shape()); @@ -88,6 +89,8 @@ set_cursor_pos(struct cursorpos cp) if (page > 7) return;
+ vgafb_set_swcursor(0); + // Bios cursor pos SET_BDA(cursor_pos[page], (y << 8) | x);
@@ -103,7 +106,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) @@ -129,6 +132,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); @@ -282,6 +287,8 @@ 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) return ret; @@ -305,7 +312,7 @@ vga_set_mode(int mode, int flags) int cwidth = GET_GLOBAL(vmode_g->cwidth); SET_BDA(video_cols, width / cwidth); SET_BDA(video_rows, (height / cheight) - 1); - SET_BDA(cursor_type, 0x0000); + SET_BDA(cursor_type, CONFIG_VGA_SOFTWARE_CURSOR ? 0x0607 : 0x0000); } SET_BDA(video_pagesize, calc_page_size(memmodel, width, height)); SET_BDA(crtc_address, CONFIG_VGA_STDVGA_PORTS ? stdvga_get_crtc() : 0); diff --git a/vgasrc/vgabios.h b/vgasrc/vgabios.h index 0ec4a8e..98dc3cd 100644 --- a/vgasrc/vgabios.h +++ b/vgasrc/vgabios.h @@ -84,7 +84,8 @@ struct vga_bda_s { u16 vgamode_offset; } PACKED;
-#define BF_PM_MASK 0x0f +#define BF_PM_MASK 0x0f +#define BF_SWCURSOR 0x10
#define GET_BDA_EXT(var) \ GET_FARVAR(SEG_BDA, ((struct vga_bda_s *)VGA_CUSTOM_BDA)->var) @@ -117,6 +118,8 @@ struct cursorpos { }; int vga_bpp(struct vgamode_s *vmode_g); u16 calc_page_size(u8 memmodel, u16 width, u16 height); +u16 get_cursor_shape(void); +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); @@ -133,6 +136,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/vgaentry.S b/vgasrc/vgaentry.S index 5d45380..c05502d 100644 --- a/vgasrc/vgaentry.S +++ b/vgasrc/vgaentry.S @@ -133,3 +133,33 @@ entry_10_extrastack: pushl BREGS_code(%eax) RESTOREBREGS_DSEAX iretw + + // Timer irq handling + DECLFUNC entry_timer_hook +entry_timer_hook: + ENTRY handle_timer_hook + ljmpw *%cs:Timer_Hook_Resume + + // Timer irq handling on extra stack + DECLFUNC entry_timer_hook_extrastack +entry_timer_hook_extrastack: + cli + cld + pushw %ds // Set %ds:%eax to space on ExtraStack + pushl %eax + movw %cs:ExtraStackSeg, %ds + movl $(CONFIG_VGA_EXTRA_STACK_SIZE-BREGS_size-8), %eax + SAVEBREGS_POP_DSEAX + movl %esp, BREGS_size(%eax) + movw %ss, BREGS_size+4(%eax) + + movw %ds, %dx // Setup %ss/%esp and call function + movw %dx, %ss + movl %eax, %esp + calll handle_timer_hook + + movl %esp, %eax // Restore registers and return + movw BREGS_size+4(%eax), %ss + movl BREGS_size(%eax), %esp + RESTOREBREGS_DSEAX + ljmpw *%cs:Timer_Hook_Resume diff --git a/vgasrc/vgafb.c b/vgasrc/vgafb.c index 9652683..2260253 100644 --- a/vgasrc/vgafb.c +++ b/vgasrc/vgafb.c @@ -454,6 +454,30 @@ 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, u16 cursor_type) +{ + u8 start = cursor_type >> 8, end = (cursor_type & 0xff) + 1; + 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 + start; + + int i; + for (i = start; i < cheight && i < end; 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) @@ -461,6 +485,7 @@ vgafb_write_pixel(u8 color, u16 x, u16 y) struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return; + vgafb_set_swcursor(0);
struct gfx_op op; init_gfx_op(&op, vmode_g); @@ -485,6 +510,7 @@ vgafb_read_pixel(u16 x, u16 y) struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return 0; + vgafb_set_swcursor(0);
struct gfx_op op; init_gfx_op(&op, vmode_g); @@ -518,6 +544,7 @@ vgafb_move_chars(struct cursorpos dest struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return; + vgafb_set_swcursor(0);
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { gfx_move_chars(vmode_g, dest, src, movesize); @@ -538,6 +565,7 @@ vgafb_clear_chars(struct cursorpos dest struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return; + vgafb_set_swcursor(0);
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { gfx_clear_chars(vmode_g, dest, ca, clearsize); @@ -557,6 +585,7 @@ vgafb_write_char(struct cursorpos cp, struct carattr ca) struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) return; + vgafb_set_swcursor(0);
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { gfx_write_char(vmode_g, cp, ca); @@ -579,6 +608,7 @@ vgafb_read_char(struct cursorpos cp) struct vgamode_s *vmode_g = get_current_mode(); if (!vmode_g) goto fail; + vgafb_set_swcursor(0);
if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { // FIXME gfx mode @@ -595,3 +625,37 @@ fail: ; struct carattr ca2 = {0, 0, 0}; return ca2; } + +// Draw/undraw a cursor on the screen +void +vgafb_set_swcursor(int enable) +{ + if (!CONFIG_VGA_SOFTWARE_CURSOR) + return; + u8 flags = GET_BDA_EXT(flags); + if (!!(flags & BF_SWCURSOR) == enable) + // Already in requested mode. + return; + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return; + struct cursorpos cp = get_cursor_pos(0xff); + u16 cursor_type = get_cursor_shape(); + if (cp.x >= GET_BDA(video_cols) || cp.y > GET_BDA(video_rows) + || cursor_type >= 0x2000) + // Cursor not visible + return; + + SET_BDA_EXT(flags, (flags & ~BF_SWCURSOR) | (enable ? BF_SWCURSOR : 0)); + + if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { + gfx_set_swcursor(vmode_g, enable, cp, cursor_type); + return; + } + + // In text mode, swap foreground and background attributes for cursor + 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); +} diff --git a/vgasrc/vgainit.c b/vgasrc/vgainit.c index 9d6dd1e..5c76c5b 100644 --- a/vgasrc/vgainit.c +++ b/vgasrc/vgainit.c @@ -98,6 +98,38 @@ allocate_extra_stack(void)
/**************************************************************** + * Timer hook + ****************************************************************/ + +struct segoff_s Timer_Hook_Resume VAR16 VISIBLE16; + +void VISIBLE16 +handle_timer_hook(void) +{ + if (!CONFIG_VGA_SOFTWARE_CURSOR) + return; + vgafb_set_swcursor(GET_BDA(timer_counter) % 18 < 9); +} + +static void +hook_timer_irq(void) +{ + if (!CONFIG_VGA_SOFTWARE_CURSOR) + return; + extern void entry_timer_hook(void); + extern void entry_timer_hook_extrastack(void); + struct segoff_s oldirq = GET_IVT(0x08); + struct segoff_s newirq = SEGOFF(get_global_seg(), (u32)entry_timer_hook); + if (GET_GLOBAL(ExtraStackSeg)) + newirq = SEGOFF(get_global_seg(), (u32)entry_timer_hook_extrastack); + dprintf(1, "Hooking hardware timer irq (old=%x new=%x)\n" + , oldirq.segoff, newirq.segoff); + SET_VGA(Timer_Hook_Resume, oldirq); + SET_IVT(0x08, newirq); +} + + +/**************************************************************** * VGA post ****************************************************************/
@@ -164,6 +196,8 @@ vga_post(struct bregs *regs)
allocate_extra_stack();
+ hook_timer_irq(); + SET_VGA(HaveRunInit, 1);
// Fixup checksum