diff options
| -rw-r--r-- | mubios/drivers/vbe.c | 433 | ||||
| -rw-r--r-- | mubios/drivers/vga.c | 267 | ||||
| -rw-r--r-- | mubios/drivers/vga_font.c | 350 | ||||
| -rw-r--r-- | mubios/include/io.h | 21 | ||||
| -rw-r--r-- | mubios/include/vbe.h | 75 | ||||
| -rw-r--r-- | mubios/include/vga.h | 60 | ||||
| -rw-r--r-- | mubios/services/int10.c | 184 | ||||
| -rw-r--r-- | mubios/services/vbe.c | 320 |
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; +} |
