From eb1e096bacda61a256261180f5f7b661ca2e6c65 Mon Sep 17 00:00:00 2001 From: slinky55 Date: Wed, 10 Jun 2026 18:28:33 -0500 Subject: [PATCH] rearraged files, set up to use grub bootloader --- .gitignore | 9 +- boot/boot.ld | 14 ---- boot/boot.s | 129 ----------------------------- compile_commands.json | 36 ++------ grub.cfg | 3 + kernel/boot/boot.s | 28 +++++++ kernel/drivers/vga_stream_writer.c | 72 ++++++++++++++++ kernel/gdt.c | 62 ++++++++++++++ kernel/globals.c | 3 + kernel/kernel.ld | 24 ------ kernel/kmain.c | 34 ++------ kernel/lib/print.c | 11 +++ kernel/lib/print.h | 6 ++ kernel/lib/stream.c | 3 + kernel/lib/stream.h | 9 ++ kernel/linker.ld | 28 +++++++ kernel/prekernel.s | 118 -------------------------- makefile | 67 ++++++--------- os.img | Bin 1474560 -> 0 bytes 19 files changed, 270 insertions(+), 386 deletions(-) delete mode 100644 boot/boot.ld delete mode 100644 boot/boot.s create mode 100644 grub.cfg create mode 100644 kernel/boot/boot.s create mode 100644 kernel/drivers/vga_stream_writer.c create mode 100644 kernel/gdt.c create mode 100644 kernel/globals.c delete mode 100644 kernel/kernel.ld create mode 100644 kernel/lib/print.c create mode 100644 kernel/lib/print.h create mode 100644 kernel/lib/stream.c create mode 100644 kernel/lib/stream.h create mode 100644 kernel/linker.ld delete mode 100644 kernel/prekernel.s delete mode 100644 os.img diff --git a/.gitignore b/.gitignore index e0cdc8a..6a21bed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,11 @@ *.iso -isodir -sysroot +*.img +*.o +*.bin +*.elf .idea .vscode -build \ No newline at end of file +build +isodir diff --git a/boot/boot.ld b/boot/boot.ld deleted file mode 100644 index a39690a..0000000 --- a/boot/boot.ld +++ /dev/null @@ -1,14 +0,0 @@ -ENTRY(_start) - -SECTIONS -{ - . = 0x7C00; - - .text : { - *(.text) - } - - .data : { - *(.data) - } -} diff --git a/boot/boot.s b/boot/boot.s deleted file mode 100644 index 1f41b08..0000000 --- a/boot/boot.s +++ /dev/null @@ -1,129 +0,0 @@ -.code16 -.text -.global _start -_start: - ljmp $0x07C0, $something - -something: - cli - cld - - mov %cs, %ax - mov %ax, %ds - mov %ax, %es - - # stack stuff - mov $0x0000, %ax - mov %ax, %ss - mov $0xFFF0, %sp - - # copy the first 12 bytes of our MBR table entry - mov %dl, (disk_number) - mov $mbr_entry, %di - mov $12, %cx - rep movsb - - mov $0, %ah - int $0x13 - - mov $0x08, %ah - int $0x13 - and 0b00111111, %cl - mov %cl, (total_sectors) - mov %ch, (total_cylinders) - mov %dh, (total_heads) - - mov $0x1000, %ax - mov %ax, %es - mov $0x0000, %bx - - mov (disk_number), %dl - mov $0, %ch #cylinder - mov $0, %dh #head - mov $2, %cl #sector - -load_sector: - mov $1, %al - mov $0x02, %ah - int $0x13 - - mov (kernel_sectors), %ax - cmp $0xffff, %ax - jne check_last_sector - mov %es:20, %eax - shr $9, %eax - inc %eax - -check_last_sector: - dec %ax - mov %ax, kernel_sectors - cmp $0, %ax - je done - -move: # inc our offset. If it hits 0, we've moved a segment, inc that too. - add $512, %bx - cmp $0, %bx - jnz read_next_sector - mov %es, %ax - add $1000, %ax - mov %ax, %es - -read_next_sector: # find the next valid sector, head, cylinder combo on the disk - inc %cl - mov (total_sectors), %al - cmp %al, %cl - jle load_sector - mov $1, %cl #go back to sector 1.. of the next head - - inc %dh #next head - mov (total_heads), %al - cmp %al, %dh - jle load_sector - mov $0, %dh # go back to head 0.. of the next cylinder - - inc %ch # next cylinder - mov (total_cylinders), %al - cmp %al, %ch - jle load_sector - # no more disk... fall through -done: - mov $0, %ah - int $0x13 - - mov $0x1000, %ax - mov %ax, %ds - ljmp $0x1000, $0x0000 - -disk_number: - .byte 0 -total_cylinders: - .byte 0 -total_heads: - .byte 0 -total_sectors: - .byte 0 - -kernel_sectors: - .word 0xFFFF - -mbr_entry: -partition_status: - .byte 0 -partition_start_chs: - .byte 0 - .byte 0 - .byte 0 -partition_type: - .byte 0 -partition_stop_chs: - .byte 0 - .byte 0 - .byte 0 -partition_start_lba: - .long 0 -partition_start_length: - .long 0 - -.org 510 -magic: - .word 0xAA55 diff --git a/compile_commands.json b/compile_commands.json index b3061a4..9a3b587 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -1,45 +1,19 @@ [ { - "file": "boot.s", + "file": "kernel/kmain.c", "arguments": [ "/home/slinky/opt/cross/bin/i686-elf-gcc", - "-m32", "-c", - "boot.s", + "kernel/kmain.c", "-o", - "boot.o" - ], - "directory": "/home/slinky/source/RockOS", - "output": "boot.o" - }, - { - "file": "prekernel.s", - "arguments": [ - "/home/slinky/opt/cross/bin/i686-elf-gcc", - "-m32", - "-c", - "prekernel.s", - "-o", - "prekernel.o" - ], - "directory": "/home/slinky/source/RockOS", - "output": "prekernel.o" - }, - { - "file": "kmain.c", - "arguments": [ - "/home/slinky/opt/cross/bin/i686-elf-gcc", - "-m32", + "obj/kernel/kmain.o", "-ffreestanding", "-O2", "-Wall", "-Wextra", - "-c", - "kmain.c", - "-o", - "kmain.o" + "-Ikernel" ], "directory": "/home/slinky/source/RockOS", - "output": "kmain.o" + "output": "obj/kernel/kmain.o" } ] \ No newline at end of file diff --git a/grub.cfg b/grub.cfg new file mode 100644 index 0000000..63ee132 --- /dev/null +++ b/grub.cfg @@ -0,0 +1,3 @@ +menuentry "RockOS" { + multiboot /boot/rockos +} \ No newline at end of file diff --git a/kernel/boot/boot.s b/kernel/boot/boot.s new file mode 100644 index 0000000..a4b679e --- /dev/null +++ b/kernel/boot/boot.s @@ -0,0 +1,28 @@ +.set ALIGN, 1<<0 /* align loaded modules on page boundaries */ +.set MEMINFO, 1<<1 /* provide memory map */ +.set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */ +.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */ +.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */ + +.section .multiboot +.align 4 +.long MAGIC +.long FLAGS +.long CHECKSUM + +.section .bss +.align 16 +stack_bottom: +.skip 16384 # 16 KiB +stack_top: + +.section .text +.global _start +.type _start, @function +_start: + mov $stack_top, %esp + call kernel_main + + cli +1: hlt + jmp 1b diff --git a/kernel/drivers/vga_stream_writer.c b/kernel/drivers/vga_stream_writer.c new file mode 100644 index 0000000..bd4c16a --- /dev/null +++ b/kernel/drivers/vga_stream_writer.c @@ -0,0 +1,72 @@ +#include +#include + +#include + +#define VGA_WIDTH 80 +#define VGA_HEIGHT 25 +#define VGA_MEMORY 0xB8000 + +enum vga_color { + VGA_COLOR_BLACK = 0, + VGA_COLOR_BLUE = 1, + VGA_COLOR_GREEN = 2, + VGA_COLOR_CYAN = 3, + VGA_COLOR_RED = 4, + VGA_COLOR_MAGENTA = 5, + VGA_COLOR_BROWN = 6, + VGA_COLOR_LIGHT_GREY = 7, + VGA_COLOR_DARK_GREY = 8, + VGA_COLOR_LIGHT_BLUE = 9, + VGA_COLOR_LIGHT_GREEN = 10, + VGA_COLOR_LIGHT_CYAN = 11, + VGA_COLOR_LIGHT_RED = 12, + VGA_COLOR_LIGHT_MAGENTA = 13, + VGA_COLOR_LIGHT_BROWN = 14, + VGA_COLOR_WHITE = 15, +}; + +size_t terminal_row; +size_t terminal_column; +uint8_t terminal_color; +uint16_t* terminal_buffer = (uint16_t*)VGA_MEMORY; + +static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) +{ + return fg | bg << 4; +} + +static inline uint16_t vga_entry(unsigned char uc, uint8_t color) +{ + return (uint16_t) uc | (uint16_t) color << 8; +} + +void vga_initialize() +{ + terminal_row = 0; + terminal_column = 0; + terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); + + for (size_t y = 0; y < VGA_HEIGHT; y++) { + for (size_t x = 0; x < VGA_WIDTH; x++) { + const size_t index = y * VGA_WIDTH + x; + terminal_buffer[index] = vga_entry(' ', terminal_color); + } + } +} + +void vga_putchar(char c) +{ + const size_t index = terminal_row * VGA_WIDTH + terminal_column; + terminal_buffer[index] = vga_entry(c, terminal_color); + if (++terminal_column == VGA_WIDTH) { + terminal_column = 0; + if (++terminal_row == VGA_HEIGHT) + terminal_row = 0; + } +} + +print_stream_t vga_print_stream = { + .putchar = vga_putchar, + .trunc = vga_initialize, +}; \ No newline at end of file diff --git a/kernel/gdt.c b/kernel/gdt.c new file mode 100644 index 0000000..d203b2c --- /dev/null +++ b/kernel/gdt.c @@ -0,0 +1,62 @@ +#include + +#define SEG_DESCTYPE(x) ((x) << 0x04) // Descriptor type (0 for system, 1 for code/data) +#define SEG_PRES(x) ((x) << 0x07) // Present +#define SEG_SAVL(x) ((x) << 0x0C) // Available for system use +#define SEG_LONG(x) ((x) << 0x0D) // Long mode +#define SEG_SIZE(x) ((x) << 0x0E) // Size (0 for 16-bit, 1 for 32) +#define SEG_GRAN(x) ((x) << 0x0F) // Granularity (0 for 1B - 1MB, 1 for 4KB - 4GB) +#define SEG_PRIV(x) (((x) & 0x03) << 0x05) // Set privilege level (0 - 3) + +#define SEG_DATA_RD 0x00 // Read-Only +#define SEG_DATA_RDA 0x01 // Read-Only, accessed +#define SEG_DATA_RDWR 0x02 // Read/Write +#define SEG_DATA_RDWRA 0x03 // Read/Write, accessed +#define SEG_DATA_RDEXPD 0x04 // Read-Only, expand-down +#define SEG_DATA_RDEXPDA 0x05 // Read-Only, expand-down, accessed +#define SEG_DATA_RDWREXPD 0x06 // Read/Write, expand-down +#define SEG_DATA_RDWREXPDA 0x07 // Read/Write, expand-down, accessed +#define SEG_CODE_EX 0x08 // Execute-Only +#define SEG_CODE_EXA 0x09 // Execute-Only, accessed +#define SEG_CODE_EXRD 0x0A // Execute/Read +#define SEG_CODE_EXRDA 0x0B // Execute/Read, accessed +#define SEG_CODE_EXC 0x0C // Execute-Only, conforming +#define SEG_CODE_EXCA 0x0D // Execute-Only, conforming, accessed +#define SEG_CODE_EXRDC 0x0E // Execute/Read, conforming +#define SEG_CODE_EXRDCA 0x0F // Execute/Read, conforming, accessed + +#define GDT_CODE_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \ + SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \ + SEG_PRIV(0) | SEG_CODE_EXRD + +#define GDT_DATA_PL0 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \ + SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \ + SEG_PRIV(0) | SEG_DATA_RDWR + +#define GDT_CODE_PL3 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \ + SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \ + SEG_PRIV(3) | SEG_CODE_EXRD + +#define GDT_DATA_PL3 SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \ + SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \ + SEG_PRIV(3) | SEG_DATA_RDWR + +uint64_t create_descriptor(uint32_t base, uint32_t limit, uint16_t flag) +{ + uint64_t descriptor; + + // Create the high 32 bit segment + descriptor = limit & 0x000F0000; // set limit bits 19:16 + descriptor |= (flag << 8) & 0x00F0FF00; // set type, p, dpl, s, g, d/b, l and avl fields + descriptor |= (base >> 16) & 0x000000FF; // set base bits 23:16 + descriptor |= base & 0xFF000000; // set base bits 31:24 + + // Shift by 32 to allow for low part of segment + descriptor <<= 32; + + // Create the low 32 bit segment + descriptor |= base << 16; // set base bits 15:0 + descriptor |= limit & 0x0000FFFF; // set limit bits 15:0 + + return descriptor; +} diff --git a/kernel/globals.c b/kernel/globals.c new file mode 100644 index 0000000..2ab6373 --- /dev/null +++ b/kernel/globals.c @@ -0,0 +1,3 @@ +#include + +print_stream_t* kout; \ No newline at end of file diff --git a/kernel/kernel.ld b/kernel/kernel.ld deleted file mode 100644 index 02e196e..0000000 --- a/kernel/kernel.ld +++ /dev/null @@ -1,24 +0,0 @@ -ENTRY(_start) - -SECTIONS -{ - . = 0x10000; - - .text : { - *(.text) - } - - .rodata : { - *(.rodata) - } - - .data : { - *(.data) - } - - .bss : { - *(.bss) - } - - _end = .; -} diff --git a/kernel/kmain.c b/kernel/kmain.c index 2ecaf08..d6caee1 100644 --- a/kernel/kmain.c +++ b/kernel/kmain.c @@ -1,28 +1,12 @@ -#include +#include +#include -extern void halt(); -extern void idt_load(); -extern void reboot(); +void kernel_main(void) +{ + extern print_stream_t vga_print_stream; + extern print_stream_t* kout; + kout = &vga_print_stream; + kout->trunc(); -typedef struct { - uint16_t offset_low; - uint16_t selector; - uint8_t zero; - uint8_t flags; - uint16_t offset_high; -} idt_entry_t __attribute__((packed)); - -typedef struct { - uint16_t limit; - uint32_t base; -} idt_ptr_t; - -idt_entry_t idt[256]; -idt_ptr_t idtr; - -void load_idt() {} - -void kmain() { - load_idt(); - halt(); + kprint("RockOS booting..."); } diff --git a/kernel/lib/print.c b/kernel/lib/print.c new file mode 100644 index 0000000..a499f16 --- /dev/null +++ b/kernel/lib/print.c @@ -0,0 +1,11 @@ +#include "print.h" +#include "stream.h" + +extern print_stream_t* kout; + +void kprint(const char *str) { + while (*str) { + kout->putchar(*str); + str++; + } +} \ No newline at end of file diff --git a/kernel/lib/print.h b/kernel/lib/print.h new file mode 100644 index 0000000..43c0b75 --- /dev/null +++ b/kernel/lib/print.h @@ -0,0 +1,6 @@ +#ifndef KLIBPRINT_H +#define KLIBPRINT_H + +void kprint(const char* str); + +#endif \ No newline at end of file diff --git a/kernel/lib/stream.c b/kernel/lib/stream.c new file mode 100644 index 0000000..eb7cc15 --- /dev/null +++ b/kernel/lib/stream.c @@ -0,0 +1,3 @@ +#include "stream.h" + +print_stream_t* pout; \ No newline at end of file diff --git a/kernel/lib/stream.h b/kernel/lib/stream.h new file mode 100644 index 0000000..6d88723 --- /dev/null +++ b/kernel/lib/stream.h @@ -0,0 +1,9 @@ +#ifndef KSTREAM_H +#define KSTREAM_H + +typedef struct { + void (*putchar)(char c); + void (*trunc)(); +} print_stream_t; + +#endif \ No newline at end of file diff --git a/kernel/linker.ld b/kernel/linker.ld new file mode 100644 index 0000000..c41d0da --- /dev/null +++ b/kernel/linker.ld @@ -0,0 +1,28 @@ +ENTRY(_start) + +SECTIONS +{ + . = 2M; + + .text BLOCK(4K) : ALIGN(4K) + { + *(.multiboot) + *(.text) + } + + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } +} \ No newline at end of file diff --git a/kernel/prekernel.s b/kernel/prekernel.s deleted file mode 100644 index 22b95f7..0000000 --- a/kernel/prekernel.s +++ /dev/null @@ -1,118 +0,0 @@ -.code16 -.text -.global _start -_start: - jmp everything - -.org 20 -.global kernel_size -kernel_size: - .long _end - _start - -everything: - mov $0x1000, %ax - mov %ax, %es - mov %ax, %ds - - # reset disk - mov $0, %ah - int $0x13 - - # stop the cursor - mov $1, %ah - mov $0,%cl - mov $0x20,%ch - int $0x11 - - # get the system memory, if this call fails then halt - clc - mov $0, %bx - mov $0xe801, %ax - int $0x15 - jc halt - shr $10, %ax - shr $4, %bx - add %ax, %bx - mov %bx, total_mem_mb-_start - - # vga text mode - mov $0x0003, %ax - int $0x10 - -enable_a20: - #enable the a20 gate - inb $0x92, %al - orb $2, %al - outb %al, $0x92 -prep_protected: - cli - lgdt gdt_init-_start - mov %cr0, %eax - or $0x01, %eax - mov %eax, %cr0 # protected bit set - ljmpl $8, $protected - -halt16: - jmp halt16 - -.code32 -protected: - mov $16, %ax - mov %ax, %es - mov %ax, %ds - mov %ax, %ss - mov $5*8, %ax - ltr %ax - mov $0, %ax - mov %ax, %fs - mov %ax, %gs - mov $0xFFF0, %sp - mov $0xFFF0, %bp - ljmpl $8, $kmain - -# some assembly stubs we can call from the kernel - -.global idt_load -.extern idtr -idt_load: - lidt idtr - ret - -.global reboot -reboot: - cli - lidt reboot_struct - int $1 -reboot_struct: - .word 0 - .long 0 - -.global halt -halt: - cli - hlt - jmp halt - -# kernel data structures - -.align 16 -.global gdt -gdt: - .word 0,0,0,0 - .word 0xffff, 0x0000, 0x9a00, 0x00cf - .word 0xffff, 0x0000, 0x9200, 0x00cf - .word 0xffff, 0x0000, 0xfa00, 0x00cf - .word 0xffff, 0x0000, 0xf200, 0x00cf - .word 0x0068, (tss-_start), 0x8901, 0x00cf -gdt_init: - .word gdt_init-gdt - .long gdt - -.align 16 -.global tss -tss: - .long 0 - -.global total_mem_mb -total_mem_mb: - .word 0 diff --git a/makefile b/makefile index a5dd749..cef5bee 100644 --- a/makefile +++ b/makefile @@ -1,55 +1,38 @@ CC = i686-elf-gcc LD = i686-elf-ld -OBJCOPY = i686-elf-objcopy -CFLAGS = -m32 -ffreestanding -O2 -Wall -Wextra -nostdlib -ASFLAGS = -m32 +OSNAME := rockos -BOOT_DIR := boot -KERNEL_DIR := kernel -BUILD_DIR := build +C_SOURCES := $(shell find kernel -type f -name '*.c') +S_SOURCES := $(shell find kernel -type f -name '*.s') -BOOT_BIN := $(BUILD_DIR)/boot.bin -KERNEL_BIN := $(BUILD_DIR)/kernel.bin -IMAGE := os.img +OBJECTS := $(patsubst %.c, obj/%.o, $(C_SOURCES)) $(patsubst %.s, obj/%.o, $(S_SOURCES)) -.PHONY: all clean run +CFLAGS := -ffreestanding -O2 -Wall -Wextra -Ikernel +LDFLAGS := -T kernel/linker.ld -all: $(IMAGE) +.PHONY: all run clean -$(BUILD_DIR)/%.o: $(BOOT_DIR)/%.s | $(BUILD_DIR) - $(CC) $(ASFLAGS) -c $< -o $@ +$(OSNAME).bin: $(OBJECTS) + $(LD) $(LDFLAGS) -o $@ $(OBJECTS) -$(BUILD_DIR)/%.o: $(KERNEL_DIR)/%.s | $(BUILD_DIR) - $(CC) $(ASFLAGS) -c $< -o $@ +$(OSNAME).iso: $(OSNAME).bin grub.cfg + mkdir -p isodir/boot/grub + cp $(OSNAME).bin isodir/boot/$(OSNAME) + cp grub.cfg isodir/boot/grub/grub.cfg + grub-mkrescue -o $(OSNAME).iso isodir -$(BUILD_DIR)/%.o: $(KERNEL_DIR)/%.c | $(BUILD_DIR) - $(CC) $(CFLAGS) -c $< -o $@ +obj/%.o: %.c + @mkdir -p $(dir $@) + $(CC) -c $< -o $@ $(CFLAGS) -$(BUILD_DIR)/boot.elf: $(BUILD_DIR)/boot.o $(BOOT_DIR)/boot.ld - $(LD) -T $(BOOT_DIR)/boot.ld -o $@ $(BUILD_DIR)/boot.o +obj/%.o: %.s + @mkdir -p $(dir $@) + $(CC) -c $< -o $@ $(CFLAGS) -$(BOOT_BIN): $(BUILD_DIR)/boot.elf - $(OBJCOPY) -O binary $< $@ +run: $(OSNAME).iso + qemu-system-i386 -cdrom $(OSNAME).iso -KERNEL_OBJS := $(BUILD_DIR)/prekernel.o $(BUILD_DIR)/kmain.o - -$(BUILD_DIR)/kernel.elf: $(KERNEL_OBJS) $(KERNEL_DIR)/kernel.ld - $(LD) -T $(KERNEL_DIR)/kernel.ld -o $@ $(KERNEL_OBJS) - -$(KERNEL_BIN): $(BUILD_DIR)/kernel.elf - $(OBJCOPY) -O binary $< $@ - -$(BUILD_DIR): - mkdir -p $(BUILD_DIR) - -$(IMAGE): $(BOOT_BIN) $(KERNEL_BIN) - dd if=$(BOOT_BIN) of=$@ bs=512 count=1 - dd if=$(KERNEL_BIN) of=$@ bs=512 seek=1 - truncate -s 1440k $@ - -run: $(IMAGE) - qemu-system-i386 -drive format=raw,file=$(IMAGE) - -clean: - rm -rf $(BUILD_DIR) $(IMAGE) +clean: + @echo "Cleaning up build artifacts..." + rm -rf $(OSNAME).iso $(OSNAME).bin obj isodir \ No newline at end of file diff --git a/os.img b/os.img deleted file mode 100644 index 54a213dac05e552ead54ef79626c957730ecb4ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1474560 zcmeIwJ5Ll*6ae5eC=r#gYfw52p&(XhW$2{z5BL{GTMcv*5<>$1 z-4G1T21-f_V^(O0S`Z|xv%D;9DZiP_+?)G2_vSliyEUs^+CO-{R@y98$`PfFPrHq= zuG#F%LiWBre!W_XTIW(*t+Th|UW|3T&c^!Zva!4KSviWO%EyRj`P$`p7mFSA*E zb$UJ<_&W08T^z1X&t(IvBX65WH;>uej)-FZMWfODd(KPX!R$`g|FH%yM;xWMw0V^M zWHFX|YS;GiY?qUI?Z%VG)%um{&c$S9{O0c{Ux-@q+f;kY_o@9t(zO+Zo=Q5pmK1s` z>Gs1&qAhj~({eFNmAu9_((GMV(O&bKWTri$D`PdWPCmD#6lisaEGr^Cr{s% zlkwB}A5#rQcBZUDfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N u0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5)0hrnML?6On<