From b60ae9eebd6226c9bf17dbba2c87d3b38b876618 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 3 Feb 2020 13:43:02 +0200 Subject: [PATCH] Backport VESA driver --- etc/make/amd64/conf.mk | 8 ++ etc/make/amd64/config.mk | 9 ++ etc/make/conf.mk | 6 +- include/sys/amd64/hw/vesa.h | 11 ++ include/sys/font/logo.h | 13 ++ include/sys/font/psf.h | 18 +++ sys/amd64/hw/con.c | 6 +- sys/amd64/hw/timer.c | 1 + sys/amd64/hw/vesa.c | 78 +++++++++++ sys/amd64/kernel.c | 6 +- sys/font/logo.c | 260 ++++++++++++++++++++++++++++++++++++ sys/font/psf.c | 52 ++++++++ 12 files changed, 463 insertions(+), 5 deletions(-) create mode 100644 include/sys/amd64/hw/vesa.h create mode 100644 include/sys/font/logo.h create mode 100644 include/sys/font/psf.h create mode 100644 sys/amd64/hw/vesa.c create mode 100644 sys/font/logo.c create mode 100644 sys/font/psf.c diff --git a/etc/make/amd64/conf.mk b/etc/make/amd64/conf.mk index b281584..cba0850 100644 --- a/etc/make/amd64/conf.mk +++ b/etc/make/amd64/conf.mk @@ -35,6 +35,14 @@ $(O)/sys/amd64/hw/ap_code.bin: sys/amd64/hw/ap_code.nasm config @printf " NASM\t%s\n" $(@:$(O)/%=%) @nasm -f bin -o $@ $< +$(O)/sys/font/%.o: etc/%.psfu config + @printf " FONT\t%s\n" $(@:$(O)/%.o=%.psfu) + @$(CC64) $(kernel_CFLAGS) -c \ + -DINCBIN_FILE='"$<"' \ + -DINCBIN_START="_psf_start" \ + -DINCBIN_END="_psf_end" \ + -o $@ sys/amd64/incbin.S + ### Kernel loader build DIRS+=$(O)/sys/amd64/loader loader_OBJS+=$(O)/sys/amd64/loader/boot.o \ diff --git a/etc/make/amd64/config.mk b/etc/make/amd64/config.mk index ac189df..dd6f477 100644 --- a/etc/make/amd64/config.mk +++ b/etc/make/amd64/config.mk @@ -7,6 +7,15 @@ AMD64_KERNEL_STACK?=65536 # Kernel stack size (when bootstrapping + task kstac QEMU_MEM?=512 QEMU_SMP?=2 +ifeq ($(VESA_ENABLE),1) +DEFINES+=-DVESA_ENABLE=1 \ + -DVESA_WIDTH=$(VESA_WIDTH) \ + -DVESA_HEIGHT=$(VESA_HEIGHT) \ + -DVESA_DEPTH=$(VESA_DEPTH) \ + -DVESA_MODE=0 +OBJS+=$(O)/sys/amd64/hw/vesa.o +endif + ### From config DEFINES+=-DAMD64_KERNEL_STACK=$(AMD64_KERNEL_STACK) \ -DAMD64_MAX_SMP=1 diff --git a/etc/make/conf.mk b/etc/make/conf.mk index 4e8f3a0..d7d8b04 100644 --- a/etc/make/conf.mk +++ b/etc/make/conf.mk @@ -63,7 +63,10 @@ OBJS+=$(O)/sys/debug.o \ $(O)/sys/snprintf.o \ $(O)/sys/random.o \ $(O)/sys/reboot.o \ - $(O)/sys/init.o + $(O)/sys/init.o \ + $(O)/sys/font/logo.o \ + $(O)/sys/font/psf.o \ + $(O)/sys/font/default8x16.o OBJS+=$(O)/sys/driver/pci/pci.o \ $(O)/sys/driver/pci/pcidb.o \ @@ -78,6 +81,7 @@ DIRS+=$(O)/sys/fs \ $(O)/sys/fs/ext2 \ $(O)/sys/char \ $(O)/sys/block \ + $(O)/sys/font \ $(O)/sys/driver/pci \ $(O)/sys/driver/ata \ $(O)/sys/driver/usb diff --git a/include/sys/amd64/hw/vesa.h b/include/sys/amd64/hw/vesa.h new file mode 100644 index 0000000..aff8ee3 --- /dev/null +++ b/include/sys/amd64/hw/vesa.h @@ -0,0 +1,11 @@ +#pragma once +#include "sys/amd64/loader/multiboot.h" +#include "sys/types.h" + +extern int vesa_available, vesa_hold; +extern uint32_t vesa_width, vesa_height, vesa_pitch, vesa_bpp; +extern uint64_t vesa_addr; + +void amd64_vesa_init(multiboot_info_t *info); +void vesa_put(uint32_t x, uint32_t y, uint32_t v); +void vesa_clear(uint32_t color); diff --git a/include/sys/font/logo.h b/include/sys/font/logo.h new file mode 100644 index 0000000..039306c --- /dev/null +++ b/include/sys/font/logo.h @@ -0,0 +1,13 @@ +#pragma once + +#define FONT_LOGO_WIDTH 64 +#define FONT_LOGO_HEIGHT 64 + +#define FONT_LOGO_HEADER_PIXEL(data,pixel) {\ +pixel[0] = (((data[0] - 33) << 2) | ((data[1] - 33) >> 4)); \ +pixel[1] = ((((data[1] - 33) & 0xF) << 4) | ((data[2] - 33) >> 2)); \ +pixel[2] = ((((data[2] - 33) & 0x3) << 6) | ((data[3] - 33))); \ +data += 4; \ +} + +extern char *font_logo_header_data; diff --git a/include/sys/font/psf.h b/include/sys/font/psf.h new file mode 100644 index 0000000..578dd07 --- /dev/null +++ b/include/sys/font/psf.h @@ -0,0 +1,18 @@ +#pragma once +#include "sys/types.h" + +#define PSF_FONT_MAGIC 0x864ab572 + +struct psf_font { + uint32_t magic; + uint32_t version; + uint32_t headersize; + uint32_t flags; + uint32_t numglyph; + uint32_t bytesperglyph; + uint32_t height; + uint32_t width; +}; + +void psf_init(uintptr_t addr, uintptr_t pitch, uint16_t *charw, uint16_t *charh); +void psf_draw(uint16_t row, uint16_t col, uint8_t c, uint32_t fg, uint32_t bg); diff --git a/sys/amd64/hw/con.c b/sys/amd64/hw/con.c index ccabaff..658cdd0 100644 --- a/sys/amd64/hw/con.c +++ b/sys/amd64/hw/con.c @@ -3,15 +3,15 @@ #include "sys/amd64/hw/io.h" #include "sys/string.h" #include "sys/types.h" -#include "sys/panic.h" #include "sys/debug.h" #include "sys/heap.h" +#include "sys/panic.h" #include "sys/mm.h" #if defined(VESA_ENABLE) -#include "sys/font/logo.h" -#include "sys/psf.h" #include "sys/amd64/hw/vesa.h" +#include "sys/font/psf.h" +#include "sys/font/logo.h" #else #define vesa_available 0 #endif diff --git a/sys/amd64/hw/timer.c b/sys/amd64/hw/timer.c index d546c8a..78e4796 100644 --- a/sys/amd64/hw/timer.c +++ b/sys/amd64/hw/timer.c @@ -1,5 +1,6 @@ #include "sys/amd64/hw/ioapic.h" #include "sys/amd64/hw/timer.h" +#include "sys/amd64/hw/vesa.h" #include "sys/amd64/hw/apic.h" #include "sys/amd64/hw/irq.h" #include "sys/amd64/hw/con.h" diff --git a/sys/amd64/hw/vesa.c b/sys/amd64/hw/vesa.c new file mode 100644 index 0000000..fb48609 --- /dev/null +++ b/sys/amd64/hw/vesa.c @@ -0,0 +1,78 @@ +#include "sys/amd64/hw/vesa.h" +#include "sys/amd64/mm/map.h" +#include "sys/block/blk.h" +#include "sys/amd64/cpu.h" +#include "sys/vmalloc.h" +#include "sys/thread.h" +#include "sys/assert.h" +#include "sys/string.h" +#include "sys/panic.h" +#include "sys/debug.h" +#include "sys/dev.h" +#include "sys/mm.h" + +//static int vesa_blk_ioctl(struct blkdev *blk, unsigned long cmd, void *arg); +//static void *vesa_blk_mmap(struct blkdev *blk, struct ofile *fd, void *hint, size_t length, int flags); + +int vesa_hold = 0; +int vesa_available = 0; +uint64_t vesa_addr; +uint32_t vesa_bpp, vesa_pitch, vesa_width, vesa_height; + +static struct blkdev _vesa_fb0 = { + .flags = 0, + .dev_data = NULL, + .read = NULL, + .write = NULL, + .ioctl = NULL, + .mmap = NULL +}; + +void vesa_clear(uint32_t color) { + if (vesa_hold) { + return; + } + switch (vesa_bpp) { + case 32: + for (size_t i = 0; i < vesa_height; ++i) { + memsetl((uint32_t *) (i * vesa_pitch + vesa_addr), color, vesa_width); + } + break; + default: + panic("NYI\n"); + } +} + +void vesa_put(uint32_t x, uint32_t y, uint32_t v) { + if (vesa_hold) { + return; + } + switch (vesa_bpp) { + case 32: + ((uint32_t *) (vesa_addr + vesa_pitch * y))[x] = v; + break; + default: + panic("NYI\n"); + } +} + +void amd64_vesa_init(multiboot_info_t *info) { + if (info->flags & (1 << 12)) { + vesa_available = 1; + vesa_addr = MM_VIRTUALIZE(info->framebuffer_addr); + vesa_pitch = info->framebuffer_pitch; + vesa_width = info->framebuffer_width; + vesa_height = info->framebuffer_height; + vesa_bpp = info->framebuffer_bpp; + + if (info->framebuffer_type == 0) { + panic("Indexed framebuffers are not supported yet\n"); + } else { + kdebug("Set up VESA video:\n"); + kdebug("BPP: %d, Width: %d, Height: %d\n", vesa_bpp, vesa_width, vesa_height); + kdebug("Addr: %p\n", vesa_addr); + } + + dev_add(DEV_CLASS_BLOCK, DEV_BLOCK_OTHER, &_vesa_fb0, "fb0"); + } +} diff --git a/sys/amd64/kernel.c b/sys/amd64/kernel.c index 5b21b17..69f8073 100644 --- a/sys/amd64/kernel.c +++ b/sys/amd64/kernel.c @@ -1,6 +1,7 @@ #include "sys/amd64/hw/rs232.h" #include "sys/driver/pci/pci.h" #include "sys/driver/usb/usb.h" +#include "sys/amd64/hw/vesa.h" #include "sys/amd64/hw/apic.h" #include "sys/amd64/hw/acpi.h" #include "sys/amd64/mm/phys.h" @@ -45,7 +46,6 @@ void kernel_main(struct amd64_loader_data *data) { kernel_set_cmdline(data->cmdline); // Reinitialize RS232 properly - amd64_con_init(); rs232_init(RS232_COM1); ps2_init(); @@ -59,6 +59,10 @@ void kernel_main(struct amd64_loader_data *data) { ps2_register_device(); amd64_acpi_init(); +#if defined(VESA_ENABLE) + amd64_vesa_init(multiboot_info); +#endif + amd64_con_init(); // Print kernel version now kinfo("yggdrasil " KERNEL_VERSION_STR "\n"); diff --git a/sys/font/logo.c b/sys/font/logo.c new file mode 100644 index 0000000..7c917d2 --- /dev/null +++ b/sys/font/logo.c @@ -0,0 +1,260 @@ +/* GIMP header image file format (RGB): /home/alnyan/Documents/logo.h */ +char *font_logo_header_data = + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + ":76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + ":76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + ":76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F" + ":76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F" + ":76F:76F:76F````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F" + ":76F:76F````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + "````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F" + ":76F:76F````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + "````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F" + ":76F````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F````" + "````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F" + "````````````````````````````:76F:76F:76F:76F:76F:76F:76F````````" + "````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!" + "!!!!!!!!`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F" + "````````````````````````````:76F:76F:76F:76F:76F:76F:76F````````" + "````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F`Q!!" + "`Q!!`Q!!`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F" + "````````!!!!!!!!````````````:76F:76F:76F:76F:76F:76F:76F````````" + "!!!!!!!!````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F`Q!!" + "`Q!!`Q!!C!!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F````" + "````````!!!!!!!!````````````````:76F:76F:76F:76F:76F````````````" + "!!!!!!!!````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + "C!!!C!!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F````````" + "````````!!!!!!!!````````````````````:76F:76F:76F````````````````" + "!!!!!!!!````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F" + "`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F````````" + "````````!!!!!!!!````````````````````:76F:76F:76F````````````````" + "!!!!!!!!````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F" + "`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F````````" + "````````!!!!!!!!````````````````````:76F:76F:76F````````````````" + "!!!!!!!!````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F" + "C!!!C!!!!!!!`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F````" + "````````````````````````````````:76F:76F:76F:76F:76F````````````" + "````````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + "`Q!!`Q!!`Q!!`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F" + "````````````````````````````:76F:76F:76F:76F:76F:76F:76F````````" + "````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + "`Q!!`Q!!C!!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F`Q!!" + "C!!!C!!!`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76FC!!!" + "`Q!!`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F`Q!!`Q!!" + "`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F`Q!!`Q!!" + "`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76FC!!!C!!!C!!!" + "C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F`Q!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F" + ":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76F:76F:76F:76F:76F`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!" + "C!!!C!!!C!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F" + ":76F:76F:76FC!!!C!!!C!!!`Q!!`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!" + "C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!" + "C!!!C!!!C!!!C!!!C!!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!`Q!!" + "`Q!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!" + "C!!!C!!!C!!!C!!!`Q!!`Q!!`Q!!`Q!!C!!!`Q!!`Q!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!`Q!!" + "`Q!!`Q!!`Q!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!" + "C!!!C!!!C!!!`Q!!`Q!!`Q!!`Q!!C!!!`Q!!`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!`Q!!C!!!" + "`Q!!`Q!!`Q!!`Q!!`Q!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!" + "C!!!`Q!!`Q!!`Q!!`Q!!`Q!!C!!!`Q!!`Q!!`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!`Q!!`Q!!" + "C!!!C!!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!" + "`Q!!`Q!!`Q!!`Q!!`Q!!C!!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!`Q!!`Q!!" + "`Q!!`Q!!C!!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!" + "`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!!!!!!!!!!!!!" + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + ""; + diff --git a/sys/font/psf.c b/sys/font/psf.c new file mode 100644 index 0000000..119261f --- /dev/null +++ b/sys/font/psf.c @@ -0,0 +1,52 @@ +#include "sys/font/psf.h" +#include "sys/panic.h" +// XXX: Only supports non-indexed linear buffers + +extern char _psf_start; +extern char _psf_end; +static struct psf_font *font; +static uintptr_t psf_fb_addr, psf_fb_pitch; + +void psf_init(uintptr_t addr, uintptr_t pitch, uint16_t *charw, uint16_t *charh) { + font = (struct psf_font *) &_psf_start; + if (font->magic != PSF_FONT_MAGIC) { + panic("Invalid PSF magic\n"); + } + + psf_fb_addr = addr; + psf_fb_pitch = pitch; + + *charw = font->width; + *charh = font->height; +} + +void psf_draw(uint16_t row, uint16_t col, uint8_t c, uint32_t fg, uint32_t bg) { + if (c >= font->numglyph) { + c = 0; + } + + int bytesperline = (font->width + 7) / 8; + + uint8_t *glyph = (uint8_t *) &_psf_start + font->headersize + c * font->bytesperglyph; + // XXX: Assuming BPP is 32 + /* calculate the upper left corner on screen where we want to display. + we only do this once, and adjust the offset later. This is faster. */ + uintptr_t offs = (row * font->height * psf_fb_pitch) + (col * font->width * 4); + /* finally display pixels according to the bitmap */ + uint32_t x, y, line, mask; + for (y = 0; y < font->height; ++y) { + /* save the starting position of the line */ + line = offs; + mask = 1 << (font->width - 1); + /* display a row */ + for (x = 0; x < font->width; ++x) { + *((uint32_t *)(psf_fb_addr + line)) = ((int) *glyph) & (mask) ? fg : bg; + /* adjust to the next pixel */ + mask >>= 1; + line += 4; + } + /* adjust to the next line */ + glyph += bytesperline; + offs += psf_fb_pitch; + } +}