From 7ecf4901b486693e525d3daafc7d6e9c3a4abcd0 Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Tue, 10 Nov 2026 02:29:20 +0800 Subject: [mubios] Complete VGA text initialization and add VBE graphics services Load the 8x16 font and VGA palette, restore text memory mapping, and synchronize BIOS cursor and page state with the hardware. Allocate QEMU standard VGA PCI memory resources and expose VBE controller information, mode queries and switching, logical scanline lengths, and display start control. Signed-off-by: Richard Qin --- mubios/drivers/vbe.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++ mubios/drivers/vga.c | 267 ++++++++++++++++++++++------ mubios/drivers/vga_font.c | 350 +++++++++++++++++++++++++++++++++++++ mubios/include/io.h | 21 +++ mubios/include/vbe.h | 75 ++++++++ mubios/include/vga.h | 60 +++++++ mubios/services/int10.c | 184 ++++++++++++-------- mubios/services/vbe.c | 320 ++++++++++++++++++++++++++++++++++ 8 files changed, 1582 insertions(+), 128 deletions(-) create mode 100644 mubios/drivers/vbe.c create mode 100644 mubios/drivers/vga_font.c create mode 100644 mubios/include/vbe.h create mode 100644 mubios/include/vga.h create mode 100644 mubios/services/vbe.c (limited to 'mubios') diff --git a/mubios/drivers/vbe.c b/mubios/drivers/vbe.c new file mode 100644 index 0000000..c85ecf5 --- /dev/null +++ b/mubios/drivers/vbe.c @@ -0,0 +1,433 @@ +/* + RCC-Project + + Copyright (C) 2026 Richard Qin + + This file is part of RCC-Project. + + RCC-Project is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your option) any + later version. + + RCC-Project is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + RCC-Project; see the file COPYING. If not see + . + */ + +#include "vbe.h" +#include "io.h" +#include "vga.h" + +#define PCI_CONFIG_ADDRESS 0x0CF8u +#define PCI_CONFIG_DATA 0x0CFCu +#define PCI_COMMAND 0x04u +#define PCI_BAR0 0x10u +#define PCI_BAR2 0x18u +#define PCI_MEMORY_MASK 0xFFFFFFF0u +#define QEMU_VGA_ID 0x11111234u +/* Allocate below the platform's high MMIO/firmware area, never in RAM. + This is an allocation ceiling, not a framebuffer physical address. */ +#define PCI_MMIO_CEILING 0xF0000000u +#define DISPI_INDEX 0x01CEu +#define DISPI_DATA 0x01CFu +#define DISPI_ID 0u +#define DISPI_MEMORY_64K 0x0Au +#define DISPI_ENABLED 0x01u +#define DISPI_GETCAPS 0x02u +#define DISPI_LINEAR 0x40u +#define DISPI_PRESERVE 0x80u + +static const struct vbe_mode modes[VBE_MODE_COUNT] + = { { 0x111u, 640u, 480u, 16u }, { 0x112u, 640u, 480u, 24u }, + { 0x142u, 640u, 480u, 32u }, { 0x114u, 800u, 600u, 16u }, + { 0x115u, 800u, 600u, 24u }, { 0x143u, 800u, 600u, 32u }, + { 0x117u, 1024u, 768u, 16u }, { 0x118u, 1024u, 768u, 24u }, + { 0x144u, 1024u, 768u, 32u }, { 0x11Au, 1280u, 1024u, 16u }, + { 0x11Bu, 1280u, 1024u, 24u }, { 0x145u, 1280u, 1024u, 32u }, + { 0x11Eu, 1600u, 1200u, 16u }, { 0x11Fu, 1600u, 1200u, 24u }, + { 0x147u, 1600u, 1200u, 32u } }; + +struct vbe_state vbe; + +static u32 +pci_read (u16 devfn, u8 offset) +{ + io_out32 (PCI_CONFIG_ADDRESS, + 0x80000000u | ((u32)devfn << 8) | (offset & 0xFCu)); + return io_in32 (PCI_CONFIG_DATA); +} + +static void +pci_write (u16 devfn, u8 offset, u32 value) +{ + io_out32 (PCI_CONFIG_ADDRESS, + 0x80000000u | ((u32)devfn << 8) | (offset & 0xFCu)); + io_out32 (PCI_CONFIG_DATA, value); +} + +static void +pci_write_command (u16 devfn, u16 command) +{ + io_out32 (PCI_CONFIG_ADDRESS, 0x80000000u | ((u32)devfn << 8) | PCI_COMMAND); + /* Do not write back the write-one-to-clear PCI status word. */ + io_out16 (PCI_CONFIG_DATA, command); +} + +u16 +vbe_read (u16 index) +{ + io_out16 (DISPI_INDEX, index); + return io_in16 (DISPI_DATA); +} + +void +vbe_write (u16 index, u16 value) +{ + io_out16 (DISPI_INDEX, index); + io_out16 (DISPI_DATA, value); +} + +static u32 +pci_ram_end (void) +{ + u16 units = (u16)cmos_read8 (0x34u) | ((u16)cmos_read8 (0x35u) << 8); + u16 kib; + + /* QEMU CMOS 34h/35h describes RAM above 16 MiB in 64 KiB units. + For smaller guests, 30h/31h describes RAM above 1 MiB in KiB. */ + if (units != 0u) + return 0x01000000u + ((u32)units << 16); + kib = (u16)cmos_read8 (0x30u) | ((u16)cmos_read8 (0x31u) << 8); + return 0x00100000u + ((u32)kib << 10); +} + +static u32 +pci_allocation_limit (u16 vga_devfn, u32 ram_end) +{ + u32 limit = PCI_MMIO_CEILING; + u32 value; + u32 base; + u16 devfn; + u8 header; + u8 offset; + + /* The default QEMU pc VGA is on root bus 0. Allocate below all existing + root-bus memory resources, leaving their full ranges untouched. */ + for (devfn = 0; devfn < 256u; ++devfn) + { + if (devfn == vga_devfn || (u16)pci_read (devfn, 0u) == 0xFFFFu) + continue; + header = (u8)(pci_read (devfn, 0x0Cu) >> 16) & 0x7Fu; + for (offset = 0x10u; offset < (header == 0u ? 0x28u : 0x18u); + offset += 4u) + { + value = pci_read (devfn, offset); + /* Bit 0 identifies I/O BARs; a 64-bit BAR has type bits 10b. */ + if ((value & 1u) != 0u) + continue; + base = value & PCI_MEMORY_MASK; + if ((value & 6u) == 4u) + { + offset += 4u; + if (pci_read (devfn, offset) != 0u) + continue; + } + if (base >= ram_end && base < limit) + limit = base; + } + /* Bridge windows and PCI expansion ROMs also occupy address space. */ + if (header == 1u) + { + base = (pci_read (devfn, 0x20u) & 0xFFF0u) << 16; + if (base >= ram_end && base < limit) + limit = base; + base = (pci_read (devfn, 0x24u) & 0xFFF0u) << 16; + if (pci_read (devfn, 0x28u) == 0u && base >= ram_end && base < limit) + limit = base; + } + value = pci_read (devfn, header == 1u ? 0x38u : 0x30u); + base = value & 0xFFFFF800u; + if (base >= ram_end && base < limit) + limit = base; + } + return limit; +} + +static u32 +pci_bar_size (u16 devfn, u8 offset) +{ + u32 saved = pci_read (devfn, offset); + u32 mask; + + /* PCI sizing is done with memory decoding disabled by the caller. */ + pci_write (devfn, offset, 0xFFFFFFFFu); + mask = pci_read (devfn, offset); + pci_write (devfn, offset, saved); + /* This driver uses QEMU standard VGA's 32-bit memory BARs only. */ + if ((mask & 7u) != 0u || (mask & PCI_MEMORY_MASK) == 0u) + return 0u; + return ~(mask & PCI_MEMORY_MASK) + 1u; +} + +static int +pci_configure_vga (u16 devfn) +{ + u16 command = (u16)pci_read (devfn, PCI_COMMAND); + u32 old_framebuffer = pci_read (devfn, PCI_BAR0); + u32 old_registers = pci_read (devfn, PCI_BAR2); + u32 ram_end = pci_ram_end (); + u32 limit = pci_allocation_limit (devfn, ram_end); + u32 framebuffer_size; + u32 registers_size; + u32 framebuffer; + u32 registers = 0u; + + pci_write_command (devfn, (u16)(command & ~3u)); + framebuffer_size = pci_bar_size (devfn, PCI_BAR0); + registers_size = pci_bar_size (devfn, PCI_BAR2); + /* PCI memory BARs must fit, aligned to their probed power-of-two size, + entirely above guest RAM and below the platform MMIO ceiling. */ + if (framebuffer_size == 0u || framebuffer_size > limit) + goto fail; + framebuffer = (limit - framebuffer_size) & ~(framebuffer_size - 1u); + if (framebuffer < ram_end) + goto fail; + if (registers_size != 0u) + { + if (registers_size > framebuffer) + goto fail; + registers = (framebuffer - registers_size) & ~(registers_size - 1u); + if (registers < ram_end) + goto fail; + } + + pci_write (devfn, PCI_BAR0, framebuffer | (old_framebuffer & 0xFu)); + if (registers_size != 0u) + pci_write (devfn, PCI_BAR2, registers | (old_registers & 0xFu)); + /* A rejected BAR assignment must not be exposed as a usable framebuffer. */ + if ((pci_read (devfn, PCI_BAR0) & PCI_MEMORY_MASK) != framebuffer + || (registers_size != 0u + && (pci_read (devfn, PCI_BAR2) & PCI_MEMORY_MASK) != registers)) + goto fail; + + pci_write_command (devfn, (u16)(command | 3u)); + vbe.framebuffer = pci_read (devfn, PCI_BAR0) & PCI_MEMORY_MASK; + vbe.memory_size = (u32)vbe_read (DISPI_MEMORY_64K) << 16; + /* The CPU mapping must cover all advertised video memory. */ + if (vbe.memory_size == 0u || vbe.memory_size > framebuffer_size) + goto fail; + return 1; + +fail: + pci_write_command (devfn, (u16)(command & ~3u)); + pci_write (devfn, PCI_BAR0, old_framebuffer); + pci_write (devfn, PCI_BAR2, old_registers); + pci_write_command (devfn, command); + vbe.framebuffer = 0u; + vbe.memory_size = 0u; + return 0; +} + +static int +mode_supported (const struct vbe_mode *mode) +{ + /* Advertise only modes within DISPI capabilities and installed VRAM. */ + return vbe.present && mode->width <= vbe.max_width + && mode->height <= vbe.max_height && mode->bpp <= vbe.max_bpp + && (u32)mode->width * mode->height * (mode->bpp / 8u) + <= vbe.memory_size; +} + +const struct vbe_mode * +vbe_find_mode (u16 number) +{ + u8 index; + + for (index = 0; index < VBE_MODE_COUNT; ++index) + if (modes[index].number == number && mode_supported (&modes[index])) + return &modes[index]; + return (const struct vbe_mode *)0; +} + +void +vbe_init (void) +{ + u16 devfn; + u16 enable; + u8 index; + u8 count = 0u; + + vbe.current_mode = 0x03u; + for (devfn = 0; devfn < 256u; ++devfn) + if (pci_read (devfn, 0u) == QEMU_VGA_ID + && (pci_read (devfn, 0x08u) >> 16) == 0x0300u) + break; + /* Absence of the supported PCI device must leave text VGA available. */ + if (devfn == 256u) + return; + vbe_write (DISPI_ID, 0xB0C5u); + if (vbe_read (DISPI_ID) != 0xB0C5u || !pci_configure_vga (devfn)) + return; + + enable = vbe_read (DISPI_ENABLE); + vbe_write (DISPI_ENABLE, (u16)(enable | DISPI_GETCAPS)); + vbe.max_width = vbe_read (DISPI_XRES); + vbe.max_height = vbe_read (DISPI_YRES); + vbe.max_bpp = vbe_read (DISPI_BPP); + vbe_write (DISPI_ENABLE, enable); + vbe.present = 1u; + for (index = 0; index < VBE_MODE_COUNT; ++index) + if (mode_supported (&modes[index])) + vbe.mode_list[count++] = modes[index].number; + vbe.mode_list[count] = 0xFFFFu; +} + +void +vbe_leave_graphics (void) +{ + if (vbe.present) + vbe_write (DISPI_ENABLE, 0u); + vbe.current_mode = 0x03u; +} + +static void +graphics_registers (void) +{ + /* DISPI sets graphics timing; enable VGA planes and attribute output too. */ + io_out8 (0x03C4u, 0x01u); + io_out8 (0x03C5u, 0x01u); + io_out8 (0x03C4u, 0x02u); + io_out8 (0x03C5u, 0x0Fu); + io_out8 (0x03C4u, 0x04u); + io_out8 (0x03C5u, 0x0Eu); + io_out8 (0x03CEu, 0x05u); + io_out8 (0x03CFu, 0x40u); + io_out8 (0x03CEu, 0x06u); + io_out8 (0x03CFu, 0x05u); + io_in8 (0x03DAu); + io_out8 (0x03C0u, 0x10u); + io_out8 (0x03C0u, 0x41u); + io_in8 (0x03DAu); + io_out8 (0x03C0u, 0x20u); +} + +u16 +vbe_set_mode (u16 request) +{ + const struct vbe_mode *mode; + volatile u8 *bda = (volatile u8 *)MUBIOS_BDA_BASE; + u16 saved[10]; + u16 index; + u16 enable; + u16 number = request & 0x3FFFu; + u8 old_video_ctl = bda[BDA_VIDEO_CTL]; + volatile u32 *pixel; + u32 words; + + if (number == 0x03u) + { + vga_text_mode_set ((u8)((request >> 8) & 0x80u)); + return VBE_OK; + } + mode = vbe_find_mode (number); + /* Only LFB access is implemented. Bit 11 requests a VBE 3.0 CRTC block; + banked modes and custom timings are not supported or advertised. */ + if (mode == (const struct vbe_mode *)0 || (request & VBE_MODE_LINEAR) == 0u) + return VBE_FAILED; + + for (index = 0; index < 10u; ++index) + saved[index] = vbe_read (index); + vbe_write (DISPI_ENABLE, 0u); + vbe_write (DISPI_BPP, mode->bpp); + vbe_write (DISPI_XRES, mode->width); + vbe_write (DISPI_YRES, mode->height); + vbe_write (DISPI_BANK, 0u); + /* Delay clearing until the actual programmed mode has been verified. */ + enable = DISPI_ENABLED | DISPI_LINEAR | DISPI_PRESERVE; + vbe_write (DISPI_ENABLE, enable); + if (vbe_read (DISPI_XRES) != mode->width + || vbe_read (DISPI_YRES) != mode->height + || vbe_read (DISPI_BPP) != mode->bpp + || vbe_read (DISPI_VIRT_WIDTH) != mode->width + || (vbe_read (DISPI_ENABLE) & (DISPI_ENABLED | DISPI_LINEAR)) + != (DISPI_ENABLED | DISPI_LINEAR)) + { + /* Restore the prior hardware state on a rejected mode request. */ + vbe_write (DISPI_ENABLE, 0u); + for (index = 1u; index <= DISPI_BPP; ++index) + vbe_write (index, saved[index]); + vbe_write (DISPI_ENABLE, saved[DISPI_ENABLE] | DISPI_PRESERVE); + vbe_write (DISPI_BANK, saved[DISPI_BANK]); + vbe_write (DISPI_VIRT_WIDTH, saved[DISPI_VIRT_WIDTH]); + vbe_write (DISPI_X_OFFSET, saved[DISPI_X_OFFSET]); + vbe_write (DISPI_Y_OFFSET, saved[DISPI_Y_OFFSET]); + vbe_write (DISPI_ENABLE, saved[DISPI_ENABLE]); + /* Disabling DISPI alone does not restore text CRTC/graphics registers. + */ + if ((saved[DISPI_ENABLE] & DISPI_ENABLED) == 0u) + { + vga_text_mode_set (0x80u); + bda[BDA_VIDEO_CTL] = old_video_ctl; + vbe.current_mode + = (u16)(0x03u | ((u16)(old_video_ctl & 0x80u) << 8)); + } + return VBE_FAILED; + } + + graphics_registers (); + if ((request & VBE_MODE_PRESERVE) == 0u) + { + pixel = (volatile u32 *)vbe.framebuffer; + words = vbe.memory_size / sizeof (*pixel); + while (words-- != 0u) + *pixel++ = 0u; + enable &= (u16)~DISPI_PRESERVE; + vbe_write (DISPI_ENABLE, enable); + } + vbe.current_mode = request; + /* FFh denotes a non-legacy video mode; no mode-03h geometry is claimed. */ + bda[BDA_VIDEO_MODE] = 0xFFu; + *(volatile u16 *)(bda + BDA_VIDEO_COLUMNS) = mode->width / 8u; + *(volatile u16 *)(bda + BDA_VIDEO_PAGE_SIZE) = 0u; + *(volatile u16 *)(bda + BDA_VIDEO_PAGE_START) = 0u; + bda[BDA_VIDEO_PAGE] = 0u; + for (index = 0u; index < VGA_PAGE_COUNT; ++index) + *(volatile u16 *)(bda + BDA_CURSOR_BASE + index * 2u) = 0u; + *(volatile u16 *)(bda + BDA_CURSOR_SHAPE) = 0u; + bda[BDA_VIDEO_ROWS] = (u8)(mode->height / VGA_FONT_HEIGHT - 1u); + *(volatile u16 *)(bda + BDA_VIDEO_HEIGHT) = VGA_FONT_HEIGHT; + bda[BDA_VIDEO_CTL] = (u8)(0x60u | ((request >> 8) & 0x80u)); + return VBE_OK; +} + +u16 +vbe_get_mode (u16 *number) +{ + u8 index; + u16 enable = vbe_read (DISPI_ENABLE); + + /* GRUB's native Bochs driver may program DISPI without calling INT 10h. + Query hardware instead of returning a stale BIOS mode number. */ + if ((enable & DISPI_ENABLED) == 0u) + { + *number = (u16)(0x03u | (vbe.current_mode & VBE_MODE_PRESERVE)); + return VBE_OK; + } + for (index = 0; index < VBE_MODE_COUNT; ++index) + if (mode_supported (&modes[index]) + && modes[index].width == vbe_read (DISPI_XRES) + && modes[index].height == vbe_read (DISPI_YRES) + && modes[index].bpp == vbe_read (DISPI_BPP)) + { + *number = modes[index].number + | ((enable & DISPI_LINEAR) != 0u ? VBE_MODE_LINEAR : 0u) + | ((enable & DISPI_PRESERVE) != 0u ? VBE_MODE_PRESERVE : 0u); + return VBE_OK; + } + return VBE_FAILED; +} diff --git a/mubios/drivers/vga.c b/mubios/drivers/vga.c index 16f88e5..4bd14b1 100644 --- a/mubios/drivers/vga.c +++ b/mubios/drivers/vga.c @@ -6,83 +6,211 @@ This file is part of RCC-Project. RCC-Project is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free Software - Foundation; either version 3 of the License, or (at your option) any later - version. - */ + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your option) any + later version. -#include "bios.h" -#include "io.h" + RCC-Project is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. -#define VGA_TEXT_BASE 0x000B8000u -#define VGA_TEXT_CELLS 2000u + You should have received a copy of the GNU General Public License along with + RCC-Project; see the file COPYING. If not see + . + */ -#define BDA_VIDEO_MODE 0x0049u -#define BDA_VIDEO_COLUMNS 0x004Au -#define BDA_VIDEO_PAGE_SIZE 0x004Cu -#define BDA_VIDEO_PAGE 0x0062u -#define BDA_CURSOR_BASE 0x0450u -#define BDA_CURSOR_SHAPE 0x0460u +#include "vga.h" +#include "io.h" +#include "vbe.h" #define VGA_MISC_OUTPUT_WRITE 0x03C2u #define VGA_SEQ_INDEX 0x03C4u -#define VGA_SEQ_DATA 0x03C5u #define VGA_CRTC_INDEX 0x03D4u -#define VGA_CRTC_DATA 0x03D5u #define VGA_GC_INDEX 0x03CEu -#define VGA_GC_DATA 0x03CFu #define VGA_INPUT_STATUS 0x03DAu #define VGA_ATTR_INDEX_DATA 0x03C0u +#define VGA_PEL_MASK 0x03C6u +#define VGA_DAC_WRITE_INDEX 0x03C8u +#define VGA_DAC_DATA 0x03C9u +#define VGA_FONT_BASE 0x000A0000u + +static void +vga_write_register (u16 port, u8 index, u8 value) +{ + io_out8 (port, index); + io_out8 ((u16)(port + 1u), value); +} static void -vga_write_registers (u16 index_port, u16 data_port, const u8 *values, u8 count) +vga_write_registers (u16 port, const u8 *values, u8 count) { u8 index; for (index = 0; index < count; ++index) + vga_write_register (port, index, values[index]); +} + +static void +vga_load_font (void) +{ + volatile u8 *plane = (volatile u8 *)VGA_FONT_BASE; + u16 character; + u8 line; + + /* Select plane 2 and disable odd/even addressing while writing glyphs. */ + vga_write_register (VGA_SEQ_INDEX, 0x00u, 0x01u); + vga_write_register (VGA_SEQ_INDEX, 0x02u, 0x04u); + vga_write_register (VGA_SEQ_INDEX, 0x04u, 0x07u); + vga_write_register (VGA_SEQ_INDEX, 0x00u, 0x03u); + vga_write_register (VGA_GC_INDEX, 0x04u, 0x02u); + vga_write_register (VGA_GC_INDEX, 0x05u, 0x00u); + vga_write_register (VGA_GC_INDEX, 0x06u, 0x04u); + + for (character = 0; character < 256u; ++character) { - io_out8 (index_port, index); - io_out8 (data_port, values[index]); + for (line = 0; line < VGA_FONT_HEIGHT; ++line) + plane[character * 32u + line] + = vga_font_8x16[character * VGA_FONT_HEIGHT + line]; + for (; line < 32u; ++line) + plane[character * 32u + line] = 0u; + } + + /* Restore the B8000 text aperture and character/attribute plane mapping. */ + vga_write_register (VGA_SEQ_INDEX, 0x00u, 0x01u); + vga_write_register (VGA_SEQ_INDEX, 0x02u, 0x03u); + vga_write_register (VGA_SEQ_INDEX, 0x03u, 0x00u); + vga_write_register (VGA_SEQ_INDEX, 0x04u, 0x03u); + vga_write_register (VGA_SEQ_INDEX, 0x00u, 0x03u); + vga_write_register (VGA_GC_INDEX, 0x06u, 0x0Eu); + vga_write_register (VGA_GC_INDEX, 0x04u, 0x00u); + vga_write_register (VGA_GC_INDEX, 0x05u, 0x10u); +} + +static void +vga_load_palette (void) +{ + u16 index; + + io_out8 (VGA_PEL_MASK, 0xFFu); + io_out8 (VGA_DAC_WRITE_INDEX, 0u); + /* Mode 03h uses the 64 EGA colors, each DAC component is six bits. */ + for (index = 0; index < 64u; ++index) + { + io_out8 (VGA_DAC_DATA, (u8)(((index >> 2) & 1u) * 0x2Au + + ((index >> 5) & 1u) * 0x15u)); + io_out8 (VGA_DAC_DATA, (u8)(((index >> 1) & 1u) * 0x2Au + + ((index >> 4) & 1u) * 0x15u)); + io_out8 (VGA_DAC_DATA, + (u8)((index & 1u) * 0x2Au + ((index >> 3) & 1u) * 0x15u)); + } + for (; index < 256u; ++index) + { + io_out8 (VGA_DAC_DATA, 0u); + io_out8 (VGA_DAC_DATA, 0u); + io_out8 (VGA_DAC_DATA, 0u); } } void -vga_text_mode_init (void) +vga_set_cursor_shape (u16 shape) +{ + volatile u8 *bda = (volatile u8 *)MUBIOS_BDA_BASE; + u8 start = (u8)((shape >> 8) & 0x3Fu); + u8 end = (u8)(shape & 0x1Fu); + u16 height = *(volatile u16 *)(bda + BDA_VIDEO_HEIGHT); + + *(volatile u16 *)(bda + BDA_CURSOR_SHAPE) = shape; + /* VGA BIOS scales an eight-scanline cursor unless emulation is disabled. + Start bit 5 disables the cursor and must not be scaled. */ + if ((bda[BDA_VIDEO_CTL] & 1u) == 0u && height > 8u && end < 8u + && start < 0x20u) + { + if (end != start + 1u) + start = (u8)(((start + 1u) * height / 8u) - 1u); + else + start = (u8)(((end + 1u) * height / 8u) - 2u); + end = (u8)(((end + 1u) * height / 8u) - 1u); + } + vga_write_register (VGA_CRTC_INDEX, 0x0Au, start); + vga_write_register (VGA_CRTC_INDEX, 0x0Bu, end); +} + +void +vga_set_cursor_position (u8 page, u16 position) +{ + volatile u8 *bda = (volatile u8 *)MUBIOS_BDA_BASE; + u16 address; + + /* INT 10h exposes eight text pages. */ + if (page >= VGA_PAGE_COUNT) + return; + + *(volatile u16 *)(bda + BDA_CURSOR_BASE + page * 2u) = position; + if (page != bda[BDA_VIDEO_PAGE]) + return; + + address = (u16)(page * (VGA_PAGE_SIZE / 2u) + (position >> 8) * VGA_COLUMNS + + (position & 0xFFu)); + vga_write_register (VGA_CRTC_INDEX, 0x0Eu, (u8)(address >> 8)); + vga_write_register (VGA_CRTC_INDEX, 0x0Fu, (u8)address); +} + +void +vga_set_active_page (u8 page) { - static const u8 sequencer[] = { 0x00u, 0x00u, 0x03u, 0x00u, 0x02u }; + volatile u8 *bda = (volatile u8 *)MUBIOS_BDA_BASE; + u16 offset; + + /* Each page occupies 4 KiB within the 32 KiB text aperture. */ + if (page >= VGA_PAGE_COUNT) + return; + + offset = (u16)(page * VGA_PAGE_SIZE); + bda[BDA_VIDEO_PAGE] = page; + *(volatile u16 *)(bda + BDA_VIDEO_PAGE_START) = offset; + vga_write_register (VGA_CRTC_INDEX, 0x0Cu, (u8)((offset / 2u) >> 8)); + vga_write_register (VGA_CRTC_INDEX, 0x0Du, (u8)(offset / 2u)); + vga_set_cursor_position ( + page, *(volatile u16 *)(bda + BDA_CURSOR_BASE + page * 2u)); +} + +void +vga_text_mode_set (u8 preserve) +{ + /* Standard VGA 80x25, 720x400 mode 03h values, as used by SeaVGABIOS. */ + static const u8 sequencer[] = { 0x01u, 0x00u, 0x03u, 0x00u, 0x02u }; static const u8 crtc[] = { 0x5Fu, 0x4Fu, 0x50u, 0x82u, 0x55u, 0x81u, 0xBFu, 0x1Fu, 0x00u, 0x4Fu, 0x0Du, 0x0Eu, 0x00u, 0x00u, 0x00u, 0x00u, 0x9Cu, 0x8Eu, 0x8Fu, 0x28u, 0x1Fu, 0x96u, 0xB9u, 0xA3u, 0xFFu }; static const u8 graphics[] - = { 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x10u, 0x0Fu, 0x00u, 0xFFu }; + = { 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x10u, 0x0Eu, 0x0Fu, 0xFFu }; static const u8 attributes[] - = { 0x00u, 0x01u, 0x02u, 0x03u, 0x04u, 0x05u, 0x06u, - 0x07u, 0x08u, 0x09u, 0x0Au, 0x0Bu, 0x0Cu, 0x0Du, - 0x0Eu, 0x0Fu, 0x0Cu, 0x00u, 0x0Fu, 0x08u, 0x00u }; - volatile u16 *video; - volatile u8 *bda; + = { 0x00u, 0x01u, 0x02u, 0x03u, 0x04u, 0x05u, 0x14u, + 0x07u, 0x38u, 0x39u, 0x3Au, 0x3Bu, 0x3Cu, 0x3Du, + 0x3Eu, 0x3Fu, 0x0Cu, 0x00u, 0x0Fu, 0x08u, 0x00u }; + volatile u16 *video = (volatile u16 *)VGA_TEXT_BASE; + volatile u8 *bda = (volatile u8 *)MUBIOS_BDA_BASE; + u32 font_address = (u32)vga_font_8x16; + volatile u16 *font_vector = (volatile u16 *)(0x43u * 4u); u16 cell; u8 index; - io_out8 (VGA_MISC_OUTPUT_WRITE, 0x67u); + vbe_leave_graphics (); - io_out8 (VGA_SEQ_INDEX, 0x00u); - io_out8 (VGA_SEQ_DATA, 0x01u); - for (index = 1; index < 5u; ++index) - { - io_out8 (VGA_SEQ_INDEX, index); - io_out8 (VGA_SEQ_DATA, sequencer[index]); - } - io_out8 (VGA_SEQ_INDEX, 0x00u); - io_out8 (VGA_SEQ_DATA, 0x03u); - - io_out8 (VGA_CRTC_INDEX, 0x11u); - io_out8 (VGA_CRTC_DATA, 0x00u); - vga_write_registers (VGA_CRTC_INDEX, VGA_CRTC_DATA, crtc, 25); - - vga_write_registers (VGA_GC_INDEX, VGA_GC_DATA, graphics, 9); + /* Keep attribute-controller output blank until registers and RAM are ready. + */ + io_in8 (VGA_INPUT_STATUS); + io_out8 (VGA_ATTR_INDEX_DATA, 0x00u); + vga_write_register (VGA_SEQ_INDEX, 0x00u, 0x01u); + io_out8 (VGA_MISC_OUTPUT_WRITE, 0x67u); + vga_write_registers (VGA_SEQ_INDEX, sequencer, 5u); + vga_write_register (VGA_SEQ_INDEX, 0x00u, 0x03u); + /* Clear CRTC write protection before programming timing registers. */ + vga_write_register (VGA_CRTC_INDEX, 0x11u, 0x00u); + vga_write_registers (VGA_CRTC_INDEX, crtc, 25u); + vga_write_registers (VGA_GC_INDEX, graphics, 9u); for (index = 0; index < 21u; ++index) { io_in8 (VGA_INPUT_STATUS); @@ -90,20 +218,47 @@ vga_text_mode_init (void) io_out8 (VGA_ATTR_INDEX_DATA, attributes[index]); } + vga_load_palette (); + vga_load_font (); + + /* AL bit 7 requests a mode change without clearing text video memory. */ + if (preserve == 0u) + for (cell = 0; cell < VGA_PAGE_COUNT * VGA_PAGE_SIZE / 2u; ++cell) + video[cell] = 0x0720u; + + bda[BDA_VIDEO_MODE] = 0x03u; + *(volatile u16 *)(bda + BDA_VIDEO_COLUMNS) = VGA_COLUMNS; + *(volatile u16 *)(bda + BDA_VIDEO_PAGE_SIZE) = VGA_PAGE_SIZE; + for (index = 0; index < VGA_PAGE_COUNT; ++index) + *(volatile u16 *)(bda + BDA_CURSOR_BASE + index * 2u) = 0u; + *(volatile u16 *)(bda + BDA_VIDEO_CRTC) = VGA_CRTC_INDEX; + bda[BDA_VIDEO_MSR] = 0x29u; + bda[BDA_VIDEO_PALETTE] = 0u; + bda[BDA_VIDEO_ROWS] = VGA_ROWS - 1u; + *(volatile u16 *)(bda + BDA_VIDEO_HEIGHT) = VGA_FONT_HEIGHT; + bda[BDA_VIDEO_CTL] = (u8)(0x60u | (preserve & 0x80u)); + vbe.current_mode = (u16)(0x03u | ((u16)(preserve & 0x80u) << 8)); + bda[BDA_VIDEO_SWITCHES] = 0xF9u; + bda[BDA_VIDEO_MODESET_CTL] &= 0x7Fu; + bda[BDA_VIDEO_DCC] = 0x08u; + + /* INT 43h identifies the ROM font through a real-mode segment:offset. */ + font_vector[0] = (u16)(font_address & 0x0Fu); + font_vector[1] = (u16)(font_address >> 4); + vga_set_cursor_shape (0x0607u); + vga_set_active_page (0u); + io_in8 (VGA_INPUT_STATUS); io_out8 (VGA_ATTR_INDEX_DATA, 0x20u); +} - video = (volatile u16 *)VGA_TEXT_BASE; - for (cell = 0; cell < VGA_TEXT_CELLS; ++cell) - video[cell] = 0x0720u; +void +vga_text_mode_init (void) +{ + volatile u8 *bda = (volatile u8 *)MUBIOS_BDA_BASE; - bda = (volatile u8 *)MUBIOS_BDA_BASE; - bda[BDA_VIDEO_MODE] = 0x03u; - bda[BDA_VIDEO_PAGE] = 0x00u; - bda[BDA_VIDEO_COLUMNS] = 80u; - bda[BDA_VIDEO_COLUMNS + 1u] = 0u; - bda[BDA_VIDEO_PAGE_SIZE] = 0x00u; - bda[BDA_VIDEO_PAGE_SIZE + 1u] = 0x10u; - *(volatile u16 *)(bda + BDA_CURSOR_BASE) = 0u; - *(volatile u16 *)(bda + BDA_CURSOR_SHAPE) = 0x0607u; + /* VGA is present, with a color display and the default 400-line timing. */ + bda[BDA_VIDEO_MODESET_CTL] = 0x51u; + vbe_init (); + vga_text_mode_set (0u); } diff --git a/mubios/drivers/vga_font.c b/mubios/drivers/vga_font.c new file mode 100644 index 0000000..09d0ae0 --- /dev/null +++ b/mubios/drivers/vga_font.c @@ -0,0 +1,350 @@ +/* + RCC-Project + + Copyright (C) 2026 Richard Qin + + This file is part of RCC-Project. + + RCC-Project is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your option) any + later version. + + RCC-Project is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + RCC-Project; see the file COPYING. If not see + . + */ + +/* + * 8x16 font from SeaBIOS vgasrc/vgafonts.c: + * https://github.com/coreboot/seabios/blob/master/vgasrc/vgafonts.c + * + * The font collection fntcol16.zip is copyright Joseph Gil. + * The individual fonts are public domain. + */ + +#include "vga.h" + +const u8 vga_font_8x16[256 * VGA_FONT_HEIGHT] = { + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, + 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, + 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, + 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, + 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, + 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, + 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, + 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, + 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb, 0xdb, + 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, + 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, + 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, + 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, + 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, + 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, + 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, + 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, + 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, + 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, + 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, + 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, + 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, + 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, + 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6c, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, + 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, + 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, + 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, + 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, + 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, + 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, + 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, + 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60, 0x60, + 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, + 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, + 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60, + 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, + 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, + 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, + 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, + 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, + 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, + 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, + 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, + 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, + 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, + 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, 0x00, 0x00, 0xcc, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, + 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, + 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, + 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, + 0x06, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, + 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, + 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, + 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, + 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c, + 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0xcc, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, + 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, + 0x06, 0x0c, 0x78, 0x00, 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, + 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, + 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x18, + 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, + 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, + 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, + 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x44, 0x11, 0x44, 0x11, + 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x55, 0xaa, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, + 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, + 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, + 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, + 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x18, 0x18, + 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, + 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, + 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, + 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, + 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, + 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, + 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 +}; diff --git a/mubios/include/io.h b/mubios/include/io.h index cedd1c1..a056c13 100644 --- a/mubios/include/io.h +++ b/mubios/include/io.h @@ -50,4 +50,25 @@ io_in16 (u16 port) return value; } +static inline void +io_out16 (u16 port, u16 value) +{ + __asm__ volatile ("outw %0, %1" : : "a"(value), "Nd"(port)); +} + +static inline u32 +io_in32 (u16 port) +{ + u32 value; + + __asm__ volatile ("inl %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static inline void +io_out32 (u16 port, u32 value) +{ + __asm__ volatile ("outl %0, %1" : : "a"(value), "Nd"(port)); +} + #endif // MUBIOS_IO_H diff --git a/mubios/include/vbe.h b/mubios/include/vbe.h new file mode 100644 index 0000000..eea2bd2 --- /dev/null +++ b/mubios/include/vbe.h @@ -0,0 +1,75 @@ +/* + RCC-Project + + Copyright (C) 2026 Richard Qin + + This file is part of RCC-Project. + + RCC-Project is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your option) any + later version. + + RCC-Project is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + RCC-Project; see the file COPYING. If not see + . + */ + +#ifndef MUBIOS_VBE_H +#define MUBIOS_VBE_H + +#include "bios.h" + +#define VBE_MODE_COUNT 15u +#define VBE_MODE_LINEAR 0x4000u +#define VBE_MODE_PRESERVE 0x8000u +#define VBE_OK 0x004Fu +#define VBE_FAILED 0x014Fu +#define VBE_UNSUPPORTED 0x024Fu + +#define DISPI_XRES 1u +#define DISPI_YRES 2u +#define DISPI_BPP 3u +#define DISPI_ENABLE 4u +#define DISPI_BANK 5u +#define DISPI_VIRT_WIDTH 6u +#define DISPI_VIRT_HEIGHT 7u +#define DISPI_X_OFFSET 8u +#define DISPI_Y_OFFSET 9u + +struct vbe_mode +{ + u16 number; + u16 width; + u16 height; + u16 bpp; +}; + +struct vbe_state +{ + u32 framebuffer; + u32 memory_size; + u16 max_width; + u16 max_height; + u16 max_bpp; + u16 current_mode; + u16 mode_list[VBE_MODE_COUNT + 1u]; + u8 present; +}; + +extern struct vbe_state vbe; + +void vbe_init (void); +void vbe_leave_graphics (void); +u16 vbe_read (u16 index); +void vbe_write (u16 index, u16 value); +const struct vbe_mode *vbe_find_mode (u16 number); +u16 vbe_set_mode (u16 request); +u16 vbe_get_mode (u16 *number); +void vbe_service (struct bios_regs *regs); + +#endif diff --git a/mubios/include/vga.h b/mubios/include/vga.h new file mode 100644 index 0000000..5b69671 --- /dev/null +++ b/mubios/include/vga.h @@ -0,0 +1,60 @@ +/* + RCC-Project + + Copyright (C) 2026 Richard Qin + + This file is part of RCC-Project. + + RCC-Project is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your option) any + later version. + + RCC-Project is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + RCC-Project; see the file COPYING. If not see + . + */ + +#ifndef MUBIOS_VGA_H +#define MUBIOS_VGA_H + +#include "bios.h" + +#define VGA_TEXT_BASE 0x000B8000u +#define VGA_COLUMNS 80u +#define VGA_ROWS 25u +#define VGA_TEXT_CELLS (VGA_COLUMNS * VGA_ROWS) +#define VGA_PAGE_SIZE 0x1000u +#define VGA_PAGE_COUNT 8u +#define VGA_FONT_HEIGHT 16u + +/* All BDA constants below are offsets relative to MUBIOS_BDA_BASE. */ +#define BDA_VIDEO_MODE 0x49u +#define BDA_VIDEO_COLUMNS 0x4Au +#define BDA_VIDEO_PAGE_SIZE 0x4Cu +#define BDA_VIDEO_PAGE_START 0x4Eu +#define BDA_CURSOR_BASE 0x50u +#define BDA_CURSOR_SHAPE 0x60u +#define BDA_VIDEO_PAGE 0x62u +#define BDA_VIDEO_CRTC 0x63u +#define BDA_VIDEO_MSR 0x65u +#define BDA_VIDEO_PALETTE 0x66u +#define BDA_VIDEO_ROWS 0x84u +#define BDA_VIDEO_HEIGHT 0x85u +#define BDA_VIDEO_CTL 0x87u +#define BDA_VIDEO_SWITCHES 0x88u +#define BDA_VIDEO_MODESET_CTL 0x89u +#define BDA_VIDEO_DCC 0x8Au + +extern const u8 vga_font_8x16[256 * VGA_FONT_HEIGHT]; + +void vga_text_mode_set (u8 preserve); +void vga_set_cursor_shape (u16 shape); +void vga_set_cursor_position (u8 page, u16 position); +void vga_set_active_page (u8 page); + +#endif diff --git a/mubios/services/int10.c b/mubios/services/int10.c index 4f6aef1..17959be 100644 --- a/mubios/services/int10.c +++ b/mubios/services/int10.c @@ -19,136 +19,176 @@ . */ -#include "bios.h" #include "regs.h" +#include "vbe.h" +#include "vga.h" -#define VGA_TEXT_BASE 0x000B8000u -#define BDA_CURSOR 0x00000450u -#define BDA_CURSOR_SHAPE 0x00000460u -#define VGA_PAGE_SIZE 0x00001000u - -static volatile u16 * +static u16 int10_cursor_for_page (u8 page) { - return (volatile u16 *)(BDA_CURSOR + ((u32)page * 2u)); + return *(volatile u16 *)(MUBIOS_BDA_BASE + BDA_CURSOR_BASE + page * 2u); } static volatile u16 * int10_video_for_page (u8 page) { - return (volatile u16 *)(VGA_TEXT_BASE + ((u32)page * VGA_PAGE_SIZE)); + return (volatile u16 *)(VGA_TEXT_BASE + (u32)page * VGA_PAGE_SIZE); } static void -int10_newline (volatile u16 *cursor) +int10_teletype (u8 character) { - u16 row; + volatile u8 *bda = (volatile u8 *)MUBIOS_BDA_BASE; + u8 page = bda[BDA_VIDEO_PAGE]; + volatile u16 *video = int10_video_for_page (page); + u16 cursor = int10_cursor_for_page (page); + u16 row = cursor >> 8; + u16 column = cursor & 0xFFu; + u16 position; + u16 cell; + u16 blank; + + /* AH=0Eh uses the active page, as in SeaVGABIOS. BEL has no text effect. */ + if (character == 0x07u) + return; + if (character == 0x0Du) + column = 0u; + else if (character == 0x0Au) + ++row; + else if (character == 0x08u) + { + /* A text-mode backspace moves left without erasing or crossing a row. */ + if (column != 0u) + --column; + } + else + { + position = (u16)(row * VGA_COLUMNS + column); + /* Mode 03h has 2000 visible cells per page. */ + if (position >= VGA_TEXT_CELLS) + return; + video[position] = (u16)((video[position] & 0xFF00u) | character); + ++column; + if (column >= VGA_COLUMNS) + { + column = 0u; + ++row; + } + } - row = (u16)(*cursor / 80u); - *cursor = (u16)((row + 1u) * 80u); + /* Passing the last row scrolls the active page, retaining text attributes. + */ + if (row >= VGA_ROWS) + { + blank = (u16)((video[(VGA_ROWS - 1u) * VGA_COLUMNS] & 0xFF00u) | 0x20u); + for (cell = 0; cell < (VGA_ROWS - 1u) * VGA_COLUMNS; ++cell) + video[cell] = video[cell + VGA_COLUMNS]; + for (; cell < VGA_TEXT_CELLS; ++cell) + video[cell] = blank; + row = VGA_ROWS - 1u; + } + vga_set_cursor_position (page, (u16)((row << 8) | column)); } void int10_service (struct bios_regs *regs) { - volatile u16 *cursor; + volatile u8 *bda = (volatile u8 *)MUBIOS_BDA_BASE; volatile u16 *video; - u8 function; - u8 page; - u8 character; - u8 attribute; + u8 function = (u8)(regs->eax >> 8); + u8 page = (u8)(regs->ebx >> 8); + u16 cursor; u16 position; u16 count; + u32 font_address; - function = (u8)(regs->eax >> 8); - page = (u8)(regs->ebx >> 8); + if (function == 0x4Fu) + { + vbe_service (regs); + return; + } if (function == 0x00u) { - if ((u8)regs->eax == 0x03u) - vga_text_mode_init (); + /* Bit 7 preserves video memory; the remaining bits select mode 03h. */ + if (((u8)regs->eax & 0x7Fu) == 0x03u) + vga_text_mode_set ((u8)regs->eax & 0x80u); return; } - if (function == 0x0Fu) + if (function == 0x01u) { - regs->eax = (regs->eax & 0xFFFF0000u) | 0x5003u; - regs->ebx &= 0xFFFF00FFu; + vga_set_cursor_shape ((u16)regs->ecx); return; } - if (function == 0x02u) + if (function == 0x05u) { - cursor = int10_cursor_for_page (page); - position = (u16)(((u8)(regs->edx >> 8) * 80u) + (u8)regs->edx); - *cursor = position; + vga_set_active_page ((u8)regs->eax); return; } - if (function == 0x03u) + if (function == 0x0Fu) { - cursor = int10_cursor_for_page (page); - position = *cursor; - regs->edx = (regs->edx & 0xFFFF0000u) | ((u32)(position / 80u) << 8) - | (u32)(position % 80u); - regs->ecx = (regs->ecx & 0xFFFF0000u) | 0x0607u; + regs->eax = (regs->eax & 0xFFFF0000u) + | ((u32)bda[BDA_VIDEO_COLUMNS] << 8) | bda[BDA_VIDEO_MODE] + | (bda[BDA_VIDEO_CTL] & 0x80u); + regs->ebx = (regs->ebx & 0xFFFF00FFu) | ((u32)bda[BDA_VIDEO_PAGE] << 8); return; } - if (function != 0x09u && function != 0x0Eu) - return; - - cursor = int10_cursor_for_page (page); - video = int10_video_for_page (page); - - character = (u8)regs->eax; - attribute = (u8)regs->ebx; - position = *cursor; - - if (function == 0x09u) + /* AH=11h/AL=30h reports the current font (BH=1) or ROM 8x16 font (BH=6). */ + if (function == 0x11u && (u8)regs->eax == 0x30u) { - count = (u16)regs->ecx; - - while (count != 0u) + if (page == 0x01u || page == 0x06u) { - if (position >= 2000u) - position = 0; - - video[position] = (u16)character | ((u16)attribute << 8); - position++; - count--; + font_address = (u32)vga_font_8x16; + regs->es = (u16)(font_address >> 4); + regs->ebp = (regs->ebp & 0xFFFF0000u) | (font_address & 0x0Fu); } - + regs->ecx = (regs->ecx & 0xFFFF0000u) + | *(volatile u16 *)(bda + BDA_VIDEO_HEIGHT); + regs->edx = (regs->edx & 0xFFFFFF00u) | bda[BDA_VIDEO_ROWS]; return; } - if (character == 0x0Du) + if (function == 0x0Eu) { - *cursor = (u16)((position / 80u) * 80u); + int10_teletype ((u8)regs->eax); return; } - if (character == 0x0Au) + /* The mode 03h INT 10h interface provides page numbers 0 through 7. */ + if (page >= VGA_PAGE_COUNT) + return; + + if (function == 0x02u) { - int10_newline (cursor); + vga_set_cursor_position (page, (u16)regs->edx); return; } - if (character == 0x08u) + if (function == 0x03u) { - if (position != 0u) - { - position--; - video[position] = (u16)(0x20u | ((u16)attribute << 8)); - *cursor = position; - } - + regs->edx = (regs->edx & 0xFFFF0000u) | int10_cursor_for_page (page); + regs->ecx = (regs->ecx & 0xFFFF0000u) + | *(volatile u16 *)(bda + BDA_CURSOR_SHAPE); return; } - if (position >= 2000u) - position = 0; + if (function != 0x09u) + return; - video[position] = (u16)character | ((u16)attribute << 8); - *cursor = (u16)(position + 1u); + cursor = int10_cursor_for_page (page); + position = (u16)((cursor >> 8) * VGA_COLUMNS + (cursor & 0xFFu)); + video = int10_video_for_page (page); + count = (u16)regs->ecx; + /* Repeated character/attribute output stays within the visible page and + does not change either the BDA or hardware cursor. */ + while (count != 0u && position < VGA_TEXT_CELLS) + { + video[position] = (u16)((u8)regs->eax | ((u16)(u8)regs->ebx << 8)); + ++position; + --count; + } } diff --git a/mubios/services/vbe.c b/mubios/services/vbe.c new file mode 100644 index 0000000..5119625 --- /dev/null +++ b/mubios/services/vbe.c @@ -0,0 +1,320 @@ +/* + RCC-Project + + Copyright (C) 2026 Richard Qin + + This file is part of RCC-Project. + + RCC-Project is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your option) any + later version. + + RCC-Project is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + RCC-Project; see the file COPYING. If not see + . + */ + +#include "vbe.h" +#include "regs.h" + +static const char oem_name[] = "mubios QEMU standard VGA"; +static const char vendor_name[] = "mubios"; +static const char product_name[] = "QEMU standard VGA VBE"; +static const char product_revision[] = "2.0"; + +static void +put16 (volatile u8 *buffer, u16 offset, u16 value) +{ + buffer[offset] = (u8)value; + buffer[offset + 1u] = (u8)(value >> 8); +} + +static void +put32 (volatile u8 *buffer, u16 offset, u32 value) +{ + put16 (buffer, offset, (u16)value); + put16 (buffer, (u16)(offset + 2u), (u16)(value >> 16)); +} + +static u32 +far_pointer (const void *pointer) +{ + u32 address = (u32)pointer; + + return ((address >> 4) << 16) | (address & 0x0Fu); +} + +static void +clear_block (volatile u8 *buffer, u16 size) +{ + u16 index; + + for (index = 0; index < size; ++index) + buffer[index] = 0u; +} + +static u16 +controller_info (volatile u8 *buffer) +{ + /* VBE 1.x callers provide 256 bytes; "VBE2" requests 512 bytes. */ + u8 extended = buffer[0] == 'V' && buffer[1] == 'B' && buffer[2] == 'E' + && buffer[3] == '2'; + + clear_block (buffer, extended ? 512u : 256u); + buffer[0] = 'V'; + buffer[1] = 'E'; + buffer[2] = 'S'; + buffer[3] = 'A'; + put16 (buffer, 0x04u, 0x0200u); + put32 (buffer, 0x06u, far_pointer (oem_name)); + /* Capabilities=0: VGA-compatible, fixed six-bit DAC, no stereo. */ + put32 (buffer, 0x0Eu, far_pointer (vbe.mode_list)); + put16 (buffer, 0x12u, (u16)(vbe.memory_size >> 16)); + if (extended) + { + put16 (buffer, 0x14u, 0x0100u); + put32 (buffer, 0x16u, far_pointer (vendor_name)); + put32 (buffer, 0x1Au, far_pointer (product_name)); + put32 (buffer, 0x1Eu, far_pointer (product_revision)); + } + return VBE_OK; +} + +static u16 +mode_info (volatile u8 *buffer, u16 number) +{ + const struct vbe_mode *mode = vbe_find_mode (number & 0x3FFFu); + u32 frame_size; + u32 pages; + + if (mode == (const struct vbe_mode *)0) + return VBE_FAILED; + clear_block (buffer, 256u); + /* Supported, extended info, color graphics, LFB available, no bank window. + BIOS character output in these graphics modes is not advertised. */ + put16 (buffer, 0x00u, 0x00DBu); + put16 (buffer, 0x10u, (u16)(mode->width * (mode->bpp / 8u))); + put16 (buffer, 0x12u, mode->width); + put16 (buffer, 0x14u, mode->height); + buffer[0x16u] = 8u; + buffer[0x17u] = 16u; + buffer[0x18u] = 1u; + buffer[0x19u] = (u8)mode->bpp; + buffer[0x1Bu] = 6u; /* Direct color, packed BGR/RGB565 pixels. */ + frame_size = (u32)mode->width * mode->height * (mode->bpp / 8u); + pages = vbe.memory_size / frame_size - 1u; + /* DISPI display-start Y is limited independently of the VRAM capacity. */ + if (pages > vbe.max_height / mode->height) + pages = vbe.max_height / mode->height; + buffer[0x1Du] = (u8)(pages > 255u ? 255u : pages); + if (mode->bpp == 16u) + { + buffer[0x1Fu] = 5u; + buffer[0x20u] = 11u; + buffer[0x21u] = 6u; + buffer[0x22u] = 5u; + buffer[0x23u] = 5u; + } + else + { + buffer[0x1Fu] = 8u; + buffer[0x20u] = 16u; + buffer[0x21u] = 8u; + buffer[0x22u] = 8u; + buffer[0x23u] = 8u; + if (mode->bpp == 32u) + { + buffer[0x25u] = 8u; + buffer[0x26u] = 24u; + } + } + put32 (buffer, 0x28u, vbe.framebuffer); + return VBE_OK; +} + +static int +graphics_active (void) +{ + return (vbe_read (DISPI_ENABLE) & 1u) != 0u; +} + +static u16 +scanline_length (struct bios_regs *regs) +{ + u8 subfunction = (u8)regs->ebx; + u16 width = vbe_read (DISPI_XRES); + u16 height = vbe_read (DISPI_YRES); + u16 bytes_per_pixel = (u16)(vbe_read (DISPI_BPP) / 8u); + u16 old_width; + u16 old_x; + u16 old_y; + u32 pixels; + u32 pitch; + u32 lines; + + /* Logical scanline operations require an active direct-color mode. */ + if (!graphics_active () + || (bytes_per_pixel != 2u && bytes_per_pixel != 3u + && bytes_per_pixel != 4u)) + return VBE_FAILED; + if (subfunction == 0u || subfunction == 2u) + { + pixels = (u16)regs->ecx; + if (subfunction == 2u) + pixels = (pixels + bytes_per_pixel - 1u) / bytes_per_pixel; + /* DISPI virtual widths are multiples of eight pixels. */ + pixels = (pixels + 7u) & ~7u; + pitch = pixels * bytes_per_pixel; + if (pixels < width || pixels > vbe.max_width || pitch > 0xFFFFu + || pitch * height > vbe.memory_size) + return VBE_FAILED; + old_width = vbe_read (DISPI_VIRT_WIDTH); + old_x = vbe_read (DISPI_X_OFFSET); + old_y = vbe_read (DISPI_Y_OFFSET); + vbe_write (DISPI_X_OFFSET, 0u); + vbe_write (DISPI_Y_OFFSET, 0u); + vbe_write (DISPI_VIRT_WIDTH, (u16)pixels); + if (vbe_read (DISPI_VIRT_WIDTH) != pixels) + { + vbe_write (DISPI_VIRT_WIDTH, old_width); + vbe_write (DISPI_X_OFFSET, old_x); + vbe_write (DISPI_Y_OFFSET, old_y); + return VBE_FAILED; + } + } + else if (subfunction == 3u) + { + /* Maximum width still has to hold one complete visible image. */ + pixels = vbe.memory_size / ((u32)bytes_per_pixel * height); + if (pixels > vbe.max_width) + pixels = vbe.max_width; + if (pixels > 0xFFFFu / bytes_per_pixel) + pixels = 0xFFFFu / bytes_per_pixel; + pixels &= ~7u; + } + else if (subfunction != 1u) + return VBE_UNSUPPORTED; + + if (subfunction != 3u) + pixels = vbe_read (DISPI_VIRT_WIDTH); + pitch = pixels * bytes_per_pixel; + lines = vbe.memory_size / pitch; + if (lines > (u32)vbe.max_height + height) + lines = (u32)vbe.max_height + height; + regs->ebx = (regs->ebx & 0xFFFF0000u) | (u16)pitch; + regs->ecx = (regs->ecx & 0xFFFF0000u) | (u16)pixels; + regs->edx + = (regs->edx & 0xFFFF0000u) | (u16)(lines > 0xFFFFu ? 0xFFFFu : lines); + return VBE_OK; +} + +static u16 +display_start (struct bios_regs *regs) +{ + u8 subfunction = (u8)regs->ebx; + u32 x = (u16)regs->ecx; + u32 y = (u16)regs->edx; + u32 width = vbe_read (DISPI_XRES); + u32 height = vbe_read (DISPI_YRES); + u32 virtual_width = vbe_read (DISPI_VIRT_WIDTH); + u32 bytes_per_pixel = vbe_read (DISPI_BPP) / 8u; + u32 pitch; + u16 old_x; + u16 old_y; + + if (!graphics_active () + || (bytes_per_pixel != 2u && bytes_per_pixel != 3u + && bytes_per_pixel != 4u)) + return VBE_FAILED; + if (subfunction == 1u) + { + regs->ebx &= 0xFFFF00FFu; + regs->ecx = (regs->ecx & 0xFFFF0000u) | vbe_read (DISPI_X_OFFSET); + regs->edx = (regs->edx & 0xFFFF0000u) | vbe_read (DISPI_Y_OFFSET); + return VBE_OK; + } + if (subfunction != 0u && subfunction != 0x80u) + return VBE_UNSUPPORTED; + + pitch = virtual_width * bytes_per_pixel; + /* The entire displayed rectangle must lie in VRAM. QEMU expresses its + start address in dwords, so the byte offset must be dword-aligned. */ + if (x + width > virtual_width || y > vbe.max_height + || (y + height) > vbe.memory_size / pitch + || x * bytes_per_pixel > vbe.memory_size - (y + height) * pitch + || ((x * bytes_per_pixel + y * pitch) & 3u) != 0u) + return VBE_FAILED; + old_x = vbe_read (DISPI_X_OFFSET); + old_y = vbe_read (DISPI_Y_OFFSET); + /* QEMU applies display changes at its display refresh. No guest-side + retrace busy wait is needed for BL=80h. */ + vbe_write (DISPI_Y_OFFSET, 0u); + vbe_write (DISPI_X_OFFSET, (u16)x); + vbe_write (DISPI_Y_OFFSET, (u16)y); + if (vbe_read (DISPI_X_OFFSET) != x || vbe_read (DISPI_Y_OFFSET) != y) + { + vbe_write (DISPI_Y_OFFSET, 0u); + vbe_write (DISPI_X_OFFSET, old_x); + vbe_write (DISPI_Y_OFFSET, old_y); + return VBE_FAILED; + } + return VBE_OK; +} + +void +vbe_service (struct bios_regs *regs) +{ + u16 status = VBE_FAILED; + u16 mode; + u8 function = (u8)regs->eax; + volatile u8 *buffer = (volatile u8 *)(((u32)regs->es << 4) + (u16)regs->edi); + + /* VBE uses AX status codes, including for unavailable hardware/functions. */ + if (!vbe.present) + goto done; + switch (function) + { + case 0x00u: + status = controller_info (buffer); + break; + case 0x01u: + status = mode_info (buffer, (u16)regs->ecx); + break; + case 0x02u: + status = vbe_set_mode ((u16)regs->ebx); + break; + case 0x03u: + status = vbe_get_mode (&mode); + if (status == VBE_OK) + regs->ebx = (regs->ebx & 0xFFFF0000u) | mode; + break; + case 0x06u: + status = scanline_length (regs); + break; + case 0x07u: + status = display_start (regs); + break; + case 0x08u: + /* Capabilities advertise a fixed six-bit DAC; accept only that format. + */ + if ((u8)regs->ebx == 1u + || ((u8)regs->ebx == 0u && (u8)(regs->ebx >> 8) == 6u)) + { + regs->ebx = (regs->ebx & 0xFFFF00FFu) | 0x0600u; + status = VBE_OK; + } + break; + default: + /* No banking, VBE PM interface, custom timings, DDC or stereo is + * claimed. */ + status = VBE_FAILED; + break; + } +done: + regs->eax = (regs->eax & 0xFFFF0000u) | status; +} -- cgit v1.2.3