Also, this changes the bochsvga mode set to call stdvga_set_linelength instead of setting the dispi virtual line length as it appears that is what the original vgabios code did.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- vgasrc/bochsvga.c | 17 ++++++++++++++++- vgasrc/bochsvga.h | 2 ++ vgasrc/clext.c | 51 ++++++++++++++++++++------------------------------- vgasrc/clext.h | 2 ++ vgasrc/stdvga.c | 31 +++++++++++++++++++++++++++++++ vgasrc/stdvga.h | 3 +++ vgasrc/vbe.c | 29 +++++++++++++++++++++++++++-- vgasrc/vgabios.c | 16 ++++++++++++++++ vgasrc/vgabios.h | 1 + vgasrc/vgahw.h | 16 ++++++++++++++++ 10 files changed, 134 insertions(+), 34 deletions(-)
diff --git a/vgasrc/bochsvga.c b/vgasrc/bochsvga.c index 74da887..3e5e907 100644 --- a/vgasrc/bochsvga.c +++ b/vgasrc/bochsvga.c @@ -217,6 +217,21 @@ bochsvga_set_window(struct vgamode_s *vmode_g, int window, int val) return 0; }
+int +bochsvga_get_linelength(struct vgamode_s *vmode_g) +{ + return dispi_read(VBE_DISPI_INDEX_VIRT_WIDTH) * vga_bpp(vmode_g) / 8; +} + +int +bochsvga_set_linelength(struct vgamode_s *vmode_g, int val) +{ + stdvga_set_linelength(vmode_g, val); + int pixels = (val * 8) / vga_bpp(vmode_g); + dispi_write(VBE_DISPI_INDEX_VIRT_WIDTH, pixels); + return 0; +} + static void bochsvga_clear_scr(void) { @@ -256,7 +271,7 @@ bochsvga_set_mode(struct vgamode_s *vmode_g, int flags) u16 crtc_addr = VGAREG_VGA_CRTC_ADDRESS; stdvga_crtc_write(crtc_addr, 0x11, 0x00); stdvga_crtc_write(crtc_addr, 0x01, width / 8 - 1); - dispi_write(VBE_DISPI_INDEX_VIRT_WIDTH, width); + stdvga_set_linelength(vmode_g, width); stdvga_crtc_write(crtc_addr, 0x12, height - 1); u8 v = 0; if ((height - 1) & 0x0100) diff --git a/vgasrc/bochsvga.h b/vgasrc/bochsvga.h index 26a82e0..b6abf0a 100644 --- a/vgasrc/bochsvga.h +++ b/vgasrc/bochsvga.h @@ -57,6 +57,8 @@ void bochsvga_list_modes(u16 seg, u16 *dest, u16 *last); struct vgamode_s *bochsvga_find_mode(int mode); int bochsvga_get_window(struct vgamode_s *vmode_g, int window); int bochsvga_set_window(struct vgamode_s *vmode_g, int window, int val); +int bochsvga_get_linelength(struct vgamode_s *vmode_g); +int bochsvga_set_linelength(struct vgamode_s *vmode_g, int val); int bochsvga_set_mode(struct vgamode_s *vmode_g, int flags);
#endif // bochsvga.h diff --git a/vgasrc/clext.c b/vgasrc/clext.c index ee39c5e..27af810 100644 --- a/vgasrc/clext.c +++ b/vgasrc/clext.c @@ -415,6 +415,26 @@ clext_set_window(struct vgamode_s *vmode_g, int window, int val) return 0; }
+int +clext_get_linelength(struct vgamode_s *vmode_g) +{ + u16 crtc_addr = stdvga_get_crtc(); + u8 reg13 = stdvga_crtc_read(crtc_addr, 0x13); + u8 reg1b = stdvga_crtc_read(crtc_addr, 0x1b); + return (((reg1b & 0x10) << 4) + reg13) * stdvga_bpp_factor(vmode_g) * 2; +} + +int +clext_set_linelength(struct vgamode_s *vmode_g, int val) +{ + u16 crtc_addr = stdvga_get_crtc(); + int factor = stdvga_bpp_factor(vmode_g) * 2; + int new_line_offset = DIV_ROUND_UP(val, factor); + stdvga_crtc_write(crtc_addr, 0x13, new_line_offset); + stdvga_crtc_mask(crtc_addr, 0x1b, 0x10, (new_line_offset & 0x100) >> 4); + return 0; +} + static void cirrus_enable_16k_granularity(void) { @@ -593,15 +613,6 @@ cirrus_get_bpp_bytes(void) return v; }
-static void -cirrus_set_line_offset(u16 new_line_offset) -{ - new_line_offset /= 8; - u16 crtc_addr = stdvga_get_crtc(); - stdvga_crtc_write(crtc_addr, 0x13, new_line_offset); - stdvga_crtc_mask(crtc_addr, 0x1b, 0x10, (new_line_offset & 0x100) >> 4); -} - static u16 cirrus_get_line_offset(void) { @@ -635,27 +646,6 @@ cirrus_get_start_addr(void) }
static void -cirrus_vesa_06h(struct bregs *regs) -{ - if (regs->bl > 2) { - regs->ax = 0x0100; - return; - } - - if (regs->bl == 0x00) { - cirrus_set_line_offset(cirrus_get_bpp_bytes() * regs->cx); - } else if (regs->bl == 0x02) { - cirrus_set_line_offset(regs->cx); - } - - u32 v = cirrus_get_line_offset(); - regs->cx = v / cirrus_get_bpp_bytes(); - regs->bx = v; - regs->dx = GET_GLOBAL(VBE_total_memory) / v; - regs->ax = 0x004f; -} - -static void cirrus_vesa_07h(struct bregs *regs) { if (regs->bl == 0x80 || regs->bl == 0x00) { @@ -707,7 +697,6 @@ void cirrus_vesa(struct bregs *regs) { switch (regs->al) { - case 0x06: cirrus_vesa_06h(regs); break; case 0x07: cirrus_vesa_07h(regs); break; case 0x10: cirrus_vesa_10h(regs); break; default: cirrus_vesa_not_handled(regs); break; diff --git a/vgasrc/clext.h b/vgasrc/clext.h index b300cf4..55476a0 100644 --- a/vgasrc/clext.h +++ b/vgasrc/clext.h @@ -6,6 +6,8 @@ struct vgamode_s *clext_find_mode(int mode); int clext_get_window(struct vgamode_s *vmode_g, int window); int clext_set_window(struct vgamode_s *vmode_g, int window, int val); +int clext_get_linelength(struct vgamode_s *vmode_g); +int clext_set_linelength(struct vgamode_s *vmode_g, int val); int clext_set_mode(struct vgamode_s *vmode_g, int flags); void clext_list_modes(u16 seg, u16 *dest, u16 *last); int clext_init(void); diff --git a/vgasrc/stdvga.c b/vgasrc/stdvga.c index 9c0cba9..a310167 100644 --- a/vgasrc/stdvga.c +++ b/vgasrc/stdvga.c @@ -229,6 +229,22 @@ stdvga_get_crtc(void) return VGAREG_MDA_CRTC_ADDRESS; }
+// Return the multiplication factor needed for the vga offset register. +int +stdvga_bpp_factor(struct vgamode_s *vmode_g) +{ + switch (GET_GLOBAL(vmode_g->memmodel)) { + case MM_TEXT: + return 2; + case MM_CGA: + return GET_GLOBAL(vmode_g->depth); + case MM_PLANAR: + return 1; + default: + return 4; + } +} + void stdvga_set_cursor_shape(u8 start, u8 end) { @@ -282,6 +298,21 @@ stdvga_set_window(struct vgamode_s *vmode_g, int window, int val) return -1; }
+int +stdvga_get_linelength(struct vgamode_s *vmode_g) +{ + u8 val = stdvga_crtc_read(stdvga_get_crtc(), 0x13); + return val * stdvga_bpp_factor(vmode_g) * 2; +} + +int +stdvga_set_linelength(struct vgamode_s *vmode_g, int val) +{ + int factor = stdvga_bpp_factor(vmode_g) * 2; + stdvga_crtc_write(stdvga_get_crtc(), 0x13, DIV_ROUND_UP(val, factor)); + return 0; +} +
/**************************************************************** * Save/Restore/Set state diff --git a/vgasrc/stdvga.h b/vgasrc/stdvga.h index 05f2bf5..ad496b5 100644 --- a/vgasrc/stdvga.h +++ b/vgasrc/stdvga.h @@ -129,6 +129,7 @@ void stdvga_planar4_plane(int plane); void stdvga_load_font(u16 seg, void *src_far, u16 count , u16 start, u8 destflags, u8 fontsize); u16 stdvga_get_crtc(void); +int stdvga_bpp_factor(struct vgamode_s *vmode_g); void stdvga_set_cursor_shape(u8 start, u8 end); void stdvga_set_active_page(u16 address); void stdvga_set_cursor_pos(u16 address); @@ -136,6 +137,8 @@ void stdvga_set_scan_lines(u8 lines); u16 stdvga_get_vde(void); int stdvga_get_window(struct vgamode_s *vmode_g, int window); int stdvga_set_window(struct vgamode_s *vmode_g, int window, int val); +int stdvga_get_linelength(struct vgamode_s *vmode_g); +int stdvga_set_linelength(struct vgamode_s *vmode_g, int val); void stdvga_save_state(u16 seg, struct saveVideoHardware *info); void stdvga_restore_state(u16 seg, struct saveVideoHardware *info); int stdvga_set_mode(struct vgamode_s *vmode_g, int flags); diff --git a/vgasrc/vbe.c b/vgasrc/vbe.c index 7e84794..68d2025 100644 --- a/vgasrc/vbe.c +++ b/vgasrc/vbe.c @@ -241,8 +241,33 @@ fail: static void vbe_104f06(struct bregs *regs) { - debug_stub(regs); - regs->ax = 0x0100; + if (regs->bl > 0x02) + goto fail; + struct vgamode_s *vmode_g = get_current_mode(); + if (! vmode_g) + goto fail; + int bpp = vga_bpp(vmode_g); + + if (regs->bl == 0x00) { + int ret = vgahw_set_linelength(vmode_g, DIV_ROUND_UP(regs->cx * bpp, 8)); + if (ret) + goto fail; + } else if (regs->bl == 0x02) { + int ret = vgahw_set_linelength(vmode_g, regs->cx); + if (ret) + goto fail; + } + int linelength = vgahw_get_linelength(vmode_g); + if (linelength < 0) + goto fail; + + regs->bx = linelength; + regs->cx = (linelength * 8) / bpp; + regs->dx = GET_GLOBAL(VBE_total_memory) / linelength; + regs->ax = 0x004f; + return; +fail: + regs->ax = 0x014f; }
static void diff --git a/vgasrc/vgabios.c b/vgasrc/vgabios.c index efc4a39..6b01b73 100644 --- a/vgasrc/vgabios.c +++ b/vgasrc/vgabios.c @@ -63,6 +63,22 @@ struct pci_data rom_pci_data VAR16VISIBLE = { * Helper functions ****************************************************************/
+// Return the bits per pixel in system memory for a given mode. +int +vga_bpp(struct vgamode_s *vmode_g) +{ + switch (GET_GLOBAL(vmode_g->memmodel)) { + case MM_TEXT: + return 16; + case MM_PLANAR: + return 1; + } + u8 depth = GET_GLOBAL(vmode_g->depth); + if (depth > 8) + return ALIGN(depth, 8); + return depth; +} + u16 calc_page_size(u8 memmodel, u16 width, u16 height) { diff --git a/vgasrc/vgabios.h b/vgasrc/vgabios.h index 23d234a..aca95be 100644 --- a/vgasrc/vgabios.h +++ b/vgasrc/vgabios.h @@ -82,6 +82,7 @@ struct carattr { struct cursorpos { u8 x, y, page; }; +int vga_bpp(struct vgamode_s *vmode_g); u16 calc_page_size(u8 memmodel, u16 width, u16 height); struct vgamode_s *get_current_mode(void); int vga_set_mode(int mode, int flags); diff --git a/vgasrc/vgahw.h b/vgasrc/vgahw.h index ca8798c..494309b 100644 --- a/vgasrc/vgahw.h +++ b/vgasrc/vgahw.h @@ -61,4 +61,20 @@ static inline int vgahw_set_window(struct vgamode_s *vmode_g, int window return stdvga_set_window(vmode_g, window, val); }
+static inline int vgahw_get_linelength(struct vgamode_s *vmode_g) { + if (CONFIG_VGA_CIRRUS) + return clext_get_linelength(vmode_g); + if (CONFIG_VGA_BOCHS) + return bochsvga_get_linelength(vmode_g); + return stdvga_get_linelength(vmode_g); +} + +static inline int vgahw_set_linelength(struct vgamode_s *vmode_g, int val) { + if (CONFIG_VGA_CIRRUS) + return clext_set_linelength(vmode_g, val); + if (CONFIG_VGA_BOCHS) + return bochsvga_set_linelength(vmode_g, val); + return stdvga_set_linelength(vmode_g, val); +} + #endif // vgahw.h