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