[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