[SeaBIOS] [PATCH] vgabios: Add software cursor capability

Kevin O'Connor kevin at koconnor.net
Thu Oct 23 03:57:57 CEST 2014


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 at 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
-- 
1.9.3




More information about the SeaBIOS mailing list