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

Kevin O'Connor kevin at koconnor.net
Fri Oct 31 19:58:01 CET 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>
---
 vgasrc/cbvga.c    |  4 ++--
 vgasrc/vgabios.c  | 13 +++++++++---
 vgasrc/vgabios.h  |  4 ++++
 vgasrc/vgaentry.S | 30 ++++++++++++++++++++++++++
 vgasrc/vgafb.c    | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vgasrc/vgainit.c  | 34 ++++++++++++++++++++++++++++++
 6 files changed, 143 insertions(+), 5 deletions(-)

diff --git a/vgasrc/cbvga.c b/vgasrc/cbvga.c
index 3acc839..1cfb9d3 100644
--- a/vgasrc/cbvga.c
+++ b/vgasrc/cbvga.c
@@ -95,8 +95,8 @@ cbvga_save_restore(int cmd, u16 seg, void *data)
 int
 cbvga_set_mode(struct vgamode_s *vmode_g, int flags)
 {
-    MASK_BDA_EXT(flags, BF_EMULATE_TEXT
-                 , (vmode_g == &CBemulinfo) ? BF_EMULATE_TEXT : 0);
+    u8 emul = vmode_g == &CBemulinfo || GET_GLOBAL(CBmode) == 0x03;
+    MASK_BDA_EXT(flags, BF_EMULATE_TEXT, emul ? BF_EMULATE_TEXT : 0);
     if (!(flags & MF_NOCLEARMEM)) {
         if (GET_GLOBAL(CBmodeinfo.memmodel) == MM_TEXT) {
             memset16_far(SEG_CTEXT, (void*)0, 0x0720, 80*25*2);
diff --git a/vgasrc/vgabios.c b/vgasrc/vgabios.c
index 858f415..f5abda6 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, vga_emulate_text() ? 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 344b3d9..fd796f2 100644
--- a/vgasrc/vgabios.h
+++ b/vgasrc/vgabios.h
@@ -73,6 +73,7 @@ struct vga_bda_s {
 
 #define BF_PM_MASK      0x0f
 #define BF_EMULATE_TEXT 0x10
+#define BF_SWCURSOR     0x20
 
 #define GET_BDA_EXT(var) \
     GET_FARVAR(SEG_BDA, ((struct vga_bda_s *)VGA_CUSTOM_BDA)->var)
@@ -112,6 +113,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);
@@ -129,6 +132,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 e6a4a4c..59ddc56 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 = get_cursor_shape();
+    u8 start = cursor_type >> 8, end = cursor_type & 0xff;
+    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,36 @@ fail: ;
     struct carattr ca2 = {0, 0, 0};
     return ca2;
 }
+
+// Draw/undraw a cursor on the screen
+void
+vgafb_set_swcursor(int enable)
+{
+    if (!vga_emulate_text())
+        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);
+    if (cp.x >= GET_BDA(video_cols) || cp.y > GET_BDA(video_rows)
+        || GET_BDA(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);
+        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 6ef19e1..8d12261 100644
--- a/vgasrc/vgainit.c
+++ b/vgasrc/vgainit.c
@@ -88,6 +88,38 @@ allocate_extra_stack(void)
 
 
 /****************************************************************
+ * Timer hook
+ ****************************************************************/
+
+struct segoff_s Timer_Hook_Resume VAR16 VISIBLE16;
+
+void VISIBLE16
+handle_timer_hook(void)
+{
+    if (!vga_emulate_text())
+        return;
+    vgafb_set_swcursor(GET_BDA(timer_counter) % 18 < 9);
+}
+
+static void
+hook_timer_irq(void)
+{
+    if (!CONFIG_VGA_EMULATE_TEXT)
+        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 (CONFIG_VGA_ALLOCATE_EXTRA_STACK && 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
  ****************************************************************/
 
@@ -150,6 +182,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