aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mubios/drivers/vbe.c433
-rw-r--r--mubios/drivers/vga.c267
-rw-r--r--mubios/drivers/vga_font.c350
-rw-r--r--mubios/include/io.h21
-rw-r--r--mubios/include/vbe.h75
-rw-r--r--mubios/include/vga.h60
-rw-r--r--mubios/services/int10.c184
-rw-r--r--mubios/services/vbe.c320
8 files changed, 1582 insertions, 128 deletions
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
+ <http://www.gnu.org/licenses/>.
+ */
+
+#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
+ <http://www.gnu.org/licenses/>.
+ */
-#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
+ <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * 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
+ <http://www.gnu.org/licenses/>.
+ */
+
+#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
+ <http://www.gnu.org/licenses/>.
+ */
+
+#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 @@
<http://www.gnu.org/licenses/>.
*/
-#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
+ <http://www.gnu.org/licenses/>.
+ */
+
+#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;
+}