From 1dc2c26a7c377cf0cf58eebd8ceb69e913e5dee5 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Tue, 14 Feb 2023 07:45:38 +0000 Subject: [PATCH 01/52] Setting up GitHub Classroom Feedback From 1af43d6a434527d8e420ab7ba4feae678067c64b Mon Sep 17 00:00:00 2001 From: NerbSerg <93074692+NerbFox@users.noreply.github.com> Date: Sat, 18 Feb 2023 09:42:35 +0700 Subject: [PATCH 02/52] Update README.md --- README.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 56af011..0b7a9c2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,55 @@ -# kit-OS-2023 -Kit untuk IF2230 - Sistem Operasi 2023 + + +# Osyikk - Operating System + +Tugas IF2230 - Sistem Operasi 2023 Tahun 2022/2023 + +## Table of Contents +* [General Info](#general-information) +* [Technologies Used](#technologies-used) +* [Features](#features) +* [Screenshots](#screenshots) +* [How to Run](#How-to-Run) +* [Project Status](#project-status) +* [Room for Improvement](#room-for-improvement) +* [Division of tasks](#division-of-tasks) + + + +## General Information +G + + +## Technologies Used +- + + + + +## Features +- Implementasi dari + +## Screenshots +![Example screenshot](./doc/OS.png) + + +## How to Run +- + +## Project Status +Project is: _not complete_ + + +## Room for Improvement + +Room for improvement: +- speed + + +## Division of tasks +1. Nigel Sahl (13521043) : +2. Razzan Daksana Yoni (13521087) : +3. Rava Maulana Azzikri (13521149) : +4. Hanif Muhammad Zhafran (13521157) : From 8064989cdb541f69e8e99740e1330cd97d42ca04 Mon Sep 17 00:00:00 2001 From: Razzan Date: Sat, 18 Feb 2023 20:28:49 +0700 Subject: [PATCH 03/52] add linker --- src/linker.ld | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/linker.ld diff --git a/src/linker.ld b/src/linker.ld new file mode 100644 index 0000000..9a19dc3 --- /dev/null +++ b/src/linker.ld @@ -0,0 +1,26 @@ +ENTRY(loader) /* the name of the entry label */ + +SECTIONS { + . = 0x00100000; /* the code should be loaded at 1 MB */ + + .text ALIGN (0x1000) : /* align at 4 KB */ + { + *(.text) /* all text sections from all files */ + } + + .rodata ALIGN (0x1000) : /* align at 4 KB */ + { + *(.rodata*) /* all read-only data sections from all files */ + } + + .data ALIGN (0x1000) : /* align at 4 KB */ + { + *(.data) /* all data sections from all files */ + } + + .bss ALIGN (0x1000) : /* align at 4 KB */ + { + *(COMMON) /* all COMMON sections from all files */ + *(.bss) /* all bss sections from all files */ + } +} From ef9ad57db86e037f6f18a667927e3da16f22f7d9 Mon Sep 17 00:00:00 2001 From: Razzan Date: Sat, 18 Feb 2023 22:15:51 +0700 Subject: [PATCH 04/52] 3.7 --- cd.iso | Bin 0 -> 372736 bytes makefile | 22 +++++++++++++++++++--- src/framebuffer.c | 31 +++++++++++++++++++++++++++++++ src/gdt.c | 32 ++++++++++++++++++++++++++++++++ src/kernel.c | 7 +++++++ src/menu.lst | 5 +++++ 6 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 cd.iso create mode 100644 src/framebuffer.c create mode 100644 src/gdt.c create mode 100644 src/menu.lst diff --git a/cd.iso b/cd.iso new file mode 100644 index 0000000000000000000000000000000000000000..5b11db49e49ef947e61f2b9be0f599789579111b GIT binary patch literal 372736 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 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+0D*4? Gf&T@vlbTQf literal 0 HcmV?d00001 diff --git a/makefile b/makefile index b0c0033..261b4d4 100644 --- a/makefile +++ b/makefile @@ -5,8 +5,9 @@ CC = gcc # Directory SOURCE_FOLDER = src +# SOURCE_FOLDER = bin/iso/boot/grub OUTPUT_FOLDER = bin -ISO_NAME = os2023 +ISO_NAME = OSyikkk # Flags WARNING_CFLAG = -Wall -Wextra -Werror @@ -18,7 +19,7 @@ LFLAGS = -T $(SOURCE_FOLDER)/linker.ld -melf_i386 run: all - @qemu-system-i386 -s -S -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso + @qemu-system-i386 -s -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso all: build build: iso clean: @@ -29,14 +30,29 @@ clean: kernel: @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/kernel_loader.s -o $(OUTPUT_FOLDER)/kernel_loader.o # TODO: Compile C file with CFLAGS + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/kernel.c -o $(OUTPUT_FOLDER)/kernel.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/framebuffer.c -o $(OUTPUT_FOLDER)/framebuffer.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/stdmem.c -o $(OUTPUT_FOLDER)/stdmem.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/portio.c -o $(OUTPUT_FOLDER)/portio.o @$(LIN) $(LFLAGS) bin/*.o -o $(OUTPUT_FOLDER)/kernel @echo Linking object files and generate elf32... @rm -f *.o iso: kernel + @rm -r $(OUTPUT_FOLDER)/iso/ @mkdir -p $(OUTPUT_FOLDER)/iso/boot/grub @cp $(OUTPUT_FOLDER)/kernel $(OUTPUT_FOLDER)/iso/boot/ @cp other/grub1 $(OUTPUT_FOLDER)/iso/boot/grub/ @cp $(SOURCE_FOLDER)/menu.lst $(OUTPUT_FOLDER)/iso/boot/grub/ # TODO: Create ISO image - @rm -r $(OUTPUT_FOLDER)/iso/ + @genisoimage -R \ + -b boot/grub/grub1 \ + -no-emul-boot \ + -boot-load-size 4 \ + -A $(ISO_NAME) \ + -input-charset utf8 \ + -quiet \ + -boot-info-table \ + -o $(OUTPUT_FOLDER)/$(ISO_NAME).iso \ + $(OUTPUT_FOLDER)/iso + diff --git a/src/framebuffer.c b/src/framebuffer.c new file mode 100644 index 0000000..c480041 --- /dev/null +++ b/src/framebuffer.c @@ -0,0 +1,31 @@ +#include "lib-header/framebuffer.h" +#include "lib-header/stdtype.h" +#include "lib-header/stdmem.h" +#include "lib-header/portio.h" + +void framebuffer_set_cursor(uint8_t r, uint8_t c) { + // TODO : Implement + out(0x3D4, 0x0A); + out(0x3D5, (in(0x3D5) & 0xC0) | r); + + out(0x3D4, 0x0B); + out(0x3D5, (in(0x3D5) & 0xE0) | c); +} + +void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg) { + // TODO : Implement + uint16_t attrib = (bg << 4) | (fg & 0x0F); + volatile uint16_t * where; + where = (volatile uint16_t *)0xB8000 + (row * 80 + col) ; + *where = c | (attrib << 8); +} + +void framebuffer_clear(void) { + // TODO : Implement + uint8_t row, col; + for (row = 0; row < 25; row++) { + for (col = 0; col < 80; col++) { + framebuffer_write(row, col, ' ', 0, 0); + } + } +} diff --git a/src/gdt.c b/src/gdt.c new file mode 100644 index 0000000..9ea0ddc --- /dev/null +++ b/src/gdt.c @@ -0,0 +1,32 @@ +#include "lib-header/stdtype.h" +#include "lib-header/gdt.h" + +/** + * global_descriptor_table, predefined GDT. + * Initial SegmentDescriptor already set properly according to GDT definition in Intel Manual & OSDev. + * Table entry : [{Null Descriptor}, {Kernel Code}, {Kernel Data (variable, etc)}, ...]. + */ +struct GlobalDescriptorTable global_descriptor_table = { + .table = { + { + // TODO : Implement + }, + { + // TODO : Implement + }, + { + // TODO : Implement + } + } +}; + +/** + * _gdt_gdtr, predefined system GDTR. + * GDT pointed by this variable is already set to point global_descriptor_table above. + * From: https://wiki.osdev.org/Global_Descriptor_Table, GDTR.size is GDT size minus 1. + */ +struct GDTR _gdt_gdtr = { + // TODO : Implement, this GDTR will point to global_descriptor_table. + // Use sizeof operator +}; + diff --git a/src/kernel.c b/src/kernel.c index 01b5a07..ce62b90 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -9,5 +9,12 @@ void kernel_setup(void) { uint32_t a; uint32_t volatile b = 0x0000BABE; __asm__("mov $0xCAFE0000, %0" : "=r"(a)); + // enter_protected_mode(&_gdt_gdtr); + framebuffer_clear(); + framebuffer_write(3, 8, 'H', 0, 0xF); + framebuffer_write(3, 9, 'a', 0, 0xF); + framebuffer_write(3, 10, 'i', 0, 0xF); + framebuffer_write(3, 11, '!', 0, 0xF); + framebuffer_set_cursor(3, 10); while (TRUE) b += 1; } \ No newline at end of file diff --git a/src/menu.lst b/src/menu.lst new file mode 100644 index 0000000..5c5d15c --- /dev/null +++ b/src/menu.lst @@ -0,0 +1,5 @@ +default 0 +timeout 0 + +title os +kernel /boot/kernel From f64e2b031630fe1ac3644907ef0ea1171a078c3f Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Sat, 18 Feb 2023 23:21:44 +0700 Subject: [PATCH 05/52] add gdt entry attributes --- src/lib-header/gdt.h | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/lib-header/gdt.h b/src/lib-header/gdt.h index 8071183..6adb0a6 100644 --- a/src/lib-header/gdt.h +++ b/src/lib-header/gdt.h @@ -18,7 +18,7 @@ extern struct GDTR _gdt_gdtr; * @param type_bit 4-bit contain type flags * @param non_system 1-bit contain system */ -struct SegmentDescriptor { +typedef struct SegmentDescriptor { // First 32-bit uint16_t segment_low; uint16_t base_low; @@ -26,10 +26,18 @@ struct SegmentDescriptor { // Next 16-bit (Bit 32 to 47) uint8_t base_mid; uint8_t type_bit : 4; - uint8_t non_system : 1; + uint8_t descriptor_type : 1; // TODO : Continue GDT definition - -} __attribute__((packed)); + uint8_t dpl : 2; + uint8_t segment_present : 1; + uint8_t segment_limit : 4; + uint8_t avl : 1; + uint8_t code_seg_64bit : 1; + uint8_t def_op_size : 1; + uint8_t granularity : 1; + uint8_t base_high : 8; + +} __attribute__((packed)) gdt_entry; /** * Global Descriptor Table containing list of segment descriptor. One GDT already defined in memory.c. From 286c14966e5cafe474db236501e20ad9ff786dd6 Mon Sep 17 00:00:00 2001 From: Razzan Date: Sat, 18 Feb 2023 23:38:44 +0700 Subject: [PATCH 06/52] dummy --- makefile | 1 + src/framebuffer.c | 46 +++++++++++++++++++++++++++----- src/gdt.c | 63 ++++++++++++++++++++++++++++++++++++++++++++ src/kernel.c | 2 +- src/lib-header/gdt.h | 6 ++--- 5 files changed, 108 insertions(+), 10 deletions(-) diff --git a/makefile b/makefile index 261b4d4..f658398 100644 --- a/makefile +++ b/makefile @@ -34,6 +34,7 @@ kernel: @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/framebuffer.c -o $(OUTPUT_FOLDER)/framebuffer.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/stdmem.c -o $(OUTPUT_FOLDER)/stdmem.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/portio.c -o $(OUTPUT_FOLDER)/portio.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/gdt.c -o $(OUTPUT_FOLDER)/gdt.o @$(LIN) $(LFLAGS) bin/*.o -o $(OUTPUT_FOLDER)/kernel @echo Linking object files and generate elf32... @rm -f *.o diff --git a/src/framebuffer.c b/src/framebuffer.c index c480041..04a1e4e 100644 --- a/src/framebuffer.c +++ b/src/framebuffer.c @@ -1,3 +1,35 @@ +// #include "lib-header/framebuffer.h" +// #include "lib-header/stdtype.h" +// #include "lib-header/stdmem.h" +// #include "lib-header/portio.h" + +// void framebuffer_set_cursor(uint8_t r, uint8_t c) { +// // TODO : Implement +// out(0x3D4, 0x0A); +// out(0x3D5, (in(0x3D5) & 0xC0) | r); + +// out(0x3D4, 0x0B); +// out(0x3D5, (in(0x3D5) & 0xE0) | c); +// } + +// void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg) { +// // TODO : Implement +// uint16_t attrib = (bg << 4) | (fg & 0x0F); +// volatile uint16_t * where; +// where = (volatile uint16_t *)0xB8000 + (row * 80 + col) ; +// *where = c | (attrib << 8); +// } + +// void framebuffer_clear(void) { +// // TODO : Implement +// uint8_t row, col; +// for (row = 0; row < 25; row++) { +// for (col = 0; col < 80; col++) { +// framebuffer_write(row, col, ' ', 0, 0); +// } +// } +// } + #include "lib-header/framebuffer.h" #include "lib-header/stdtype.h" #include "lib-header/stdmem.h" @@ -22,10 +54,12 @@ void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg) void framebuffer_clear(void) { // TODO : Implement - uint8_t row, col; - for (row = 0; row < 25; row++) { - for (col = 0; col < 80; col++) { - framebuffer_write(row, col, ' ', 0, 0); - } - } + uint16_t blank = 0x20 | (0x07 << 8); + uint16_t i; + volatile uint16_t * where; + where = (volatile uint16_t *)0xB8000; + for (i = 0; i < 80 * 25; i++) { + *where = blank; + where++; + } } diff --git a/src/gdt.c b/src/gdt.c index 9ea0ddc..3df3751 100644 --- a/src/gdt.c +++ b/src/gdt.c @@ -10,12 +10,72 @@ struct GlobalDescriptorTable global_descriptor_table = { .table = { { // TODO : Implement + + + // First 32-bit + .segment_low = 0x0000, + .base_low = 0x0000, + + // Next 16-bit (Bit 32 to 47) + .base_mid = 0x00, + .type_bit = 0x0, + + // TODO : Continue GDT definition + .descriptor_type = 0x0, + .dpl = 0x0, + .segment_present = 0x0, + .segment_limit = 0x0, + .avl = 0x0, + .code_seg_64bit = 0x0, + .def_op_size = 0x0, + .granularity = 0x0, + .base_high = 0x0 }, { // TODO : Implement + // Kernel Code Segment + + // First 32-bit + .segment_low = 0xFFFF, + .base_low = 0x0000, + + // Next 16-bit (Bit 32 to 47) + .base_mid = 0x00, + .type_bit = 0xA, + + // TODO : Continue GDT definition + .descriptor_type = 0x1, + .dpl = 0x0, + .segment_present = 0x1, + .segment_limit = 0xF, + .avl = 0x0, + .code_seg_64bit = 0x1, + .def_op_size = 0x1, + .granularity = 0x1, + .base_high = 0x00 }, { // TODO : Implement + // Kernel Data Segment + + // First 32-bit + .segment_low = 0xFFFF, + .base_low = 0x0000, + + // Next 16-bit (Bit 32 to 47) + .base_mid = 0x00, + .type_bit = 0x2, + + // TODO : Continue GDT definition + .descriptor_type = 0x1, + .dpl = 0x0, + .segment_present = 0x1, + .segment_limit = 0xF, + .avl = 0x0, + .code_seg_64bit = 0x0, + .def_op_size = 0x1, + .granularity = 0x1, + .base_high = 0x00 } } }; @@ -28,5 +88,8 @@ struct GlobalDescriptorTable global_descriptor_table = { struct GDTR _gdt_gdtr = { // TODO : Implement, this GDTR will point to global_descriptor_table. // Use sizeof operator + .size = sizeof(global_descriptor_table) - 1, + + .address = &global_descriptor_table }; diff --git a/src/kernel.c b/src/kernel.c index ce62b90..8589023 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -9,7 +9,7 @@ void kernel_setup(void) { uint32_t a; uint32_t volatile b = 0x0000BABE; __asm__("mov $0xCAFE0000, %0" : "=r"(a)); - // enter_protected_mode(&_gdt_gdtr); + enter_protected_mode(&_gdt_gdtr); framebuffer_clear(); framebuffer_write(3, 8, 'H', 0, 0xF); framebuffer_write(3, 9, 'a', 0, 0xF); diff --git a/src/lib-header/gdt.h b/src/lib-header/gdt.h index 6adb0a6..909fbc0 100644 --- a/src/lib-header/gdt.h +++ b/src/lib-header/gdt.h @@ -18,7 +18,7 @@ extern struct GDTR _gdt_gdtr; * @param type_bit 4-bit contain type flags * @param non_system 1-bit contain system */ -typedef struct SegmentDescriptor { +struct SegmentDescriptor { // First 32-bit uint16_t segment_low; uint16_t base_low; @@ -26,8 +26,8 @@ typedef struct SegmentDescriptor { // Next 16-bit (Bit 32 to 47) uint8_t base_mid; uint8_t type_bit : 4; - uint8_t descriptor_type : 1; // TODO : Continue GDT definition + uint8_t descriptor_type : 1; uint8_t dpl : 2; uint8_t segment_present : 1; uint8_t segment_limit : 4; @@ -37,7 +37,7 @@ typedef struct SegmentDescriptor { uint8_t granularity : 1; uint8_t base_high : 8; -} __attribute__((packed)) gdt_entry; +} __attribute__((packed)); /** * Global Descriptor Table containing list of segment descriptor. One GDT already defined in memory.c. From fe28a265d5ddea1543a0914706e9120759df22a4 Mon Sep 17 00:00:00 2001 From: Razzan Date: Sat, 18 Feb 2023 23:39:10 +0700 Subject: [PATCH 07/52] semoga bnr --- src/framebuffer.c | 36 ++---------------------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/src/framebuffer.c b/src/framebuffer.c index 04a1e4e..7b69e1e 100644 --- a/src/framebuffer.c +++ b/src/framebuffer.c @@ -1,35 +1,3 @@ -// #include "lib-header/framebuffer.h" -// #include "lib-header/stdtype.h" -// #include "lib-header/stdmem.h" -// #include "lib-header/portio.h" - -// void framebuffer_set_cursor(uint8_t r, uint8_t c) { -// // TODO : Implement -// out(0x3D4, 0x0A); -// out(0x3D5, (in(0x3D5) & 0xC0) | r); - -// out(0x3D4, 0x0B); -// out(0x3D5, (in(0x3D5) & 0xE0) | c); -// } - -// void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg) { -// // TODO : Implement -// uint16_t attrib = (bg << 4) | (fg & 0x0F); -// volatile uint16_t * where; -// where = (volatile uint16_t *)0xB8000 + (row * 80 + col) ; -// *where = c | (attrib << 8); -// } - -// void framebuffer_clear(void) { -// // TODO : Implement -// uint8_t row, col; -// for (row = 0; row < 25; row++) { -// for (col = 0; col < 80; col++) { -// framebuffer_write(row, col, ' ', 0, 0); -// } -// } -// } - #include "lib-header/framebuffer.h" #include "lib-header/stdtype.h" #include "lib-header/stdmem.h" @@ -54,12 +22,12 @@ void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg) void framebuffer_clear(void) { // TODO : Implement - uint16_t blank = 0x20 | (0x07 << 8); + uint16_t space = 0x20 | (0x07 << 8); // " " in ASCII with white text on black background uint16_t i; volatile uint16_t * where; where = (volatile uint16_t *)0xB8000; for (i = 0; i < 80 * 25; i++) { - *where = blank; + *where = space; where++; } } From f1347b4e87a38e56c71b3e412e1980b9fe903c14 Mon Sep 17 00:00:00 2001 From: NerbSerg <93074692+NerbFox@users.noreply.github.com> Date: Sat, 18 Feb 2023 23:57:09 +0700 Subject: [PATCH 08/52] update position --- src/gdt.c | 6 +++--- src/lib-header/gdt.h | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/gdt.c b/src/gdt.c index 3df3751..df60b11 100644 --- a/src/gdt.c +++ b/src/gdt.c @@ -19,9 +19,9 @@ struct GlobalDescriptorTable global_descriptor_table = { // Next 16-bit (Bit 32 to 47) .base_mid = 0x00, .type_bit = 0x0, + .non_system = 0x0, // TODO : Continue GDT definition - .descriptor_type = 0x0, .dpl = 0x0, .segment_present = 0x0, .segment_limit = 0x0, @@ -42,9 +42,9 @@ struct GlobalDescriptorTable global_descriptor_table = { // Next 16-bit (Bit 32 to 47) .base_mid = 0x00, .type_bit = 0xA, + .non_system = 0x1, // TODO : Continue GDT definition - .descriptor_type = 0x1, .dpl = 0x0, .segment_present = 0x1, .segment_limit = 0xF, @@ -65,9 +65,9 @@ struct GlobalDescriptorTable global_descriptor_table = { // Next 16-bit (Bit 32 to 47) .base_mid = 0x00, .type_bit = 0x2, + .non_system = 0x1, // TODO : Continue GDT definition - .descriptor_type = 0x1, .dpl = 0x0, .segment_present = 0x1, .segment_limit = 0xF, diff --git a/src/lib-header/gdt.h b/src/lib-header/gdt.h index 909fbc0..662b33f 100644 --- a/src/lib-header/gdt.h +++ b/src/lib-header/gdt.h @@ -24,10 +24,11 @@ struct SegmentDescriptor { uint16_t base_low; // Next 16-bit (Bit 32 to 47) - uint8_t base_mid; + uint8_t base_mid; uint8_t type_bit : 4; + uint8_t non_system : 1; + // TODO : Continue GDT definition - uint8_t descriptor_type : 1; uint8_t dpl : 2; uint8_t segment_present : 1; uint8_t segment_limit : 4; From ed44b698de88ecfe8b94f51fe2ed36f5b88e2080 Mon Sep 17 00:00:00 2001 From: Razzan Date: Sat, 18 Feb 2023 23:57:31 +0700 Subject: [PATCH 09/52] enter protected mode --- src/kernel.c | 10 ++++++---- src/kernel_loader.s | 9 ++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/kernel.c b/src/kernel.c index 8589023..911d599 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -6,9 +6,9 @@ #include "lib-header/kernel_loader.h" void kernel_setup(void) { - uint32_t a; - uint32_t volatile b = 0x0000BABE; - __asm__("mov $0xCAFE0000, %0" : "=r"(a)); + // uint32_t a; + // uint32_t volatile b = 0x0000BABE; + // __asm__("mov $0xCAFE0000, %0" : "=r"(a)); enter_protected_mode(&_gdt_gdtr); framebuffer_clear(); framebuffer_write(3, 8, 'H', 0, 0xF); @@ -16,5 +16,7 @@ void kernel_setup(void) { framebuffer_write(3, 10, 'i', 0, 0xF); framebuffer_write(3, 11, '!', 0, 0xF); framebuffer_set_cursor(3, 10); - while (TRUE) b += 1; + // while (TRUE) b += 1; + while (TRUE); + } \ No newline at end of file diff --git a/src/kernel_loader.s b/src/kernel_loader.s index 20445c0..40d4a61 100644 --- a/src/kernel_loader.s +++ b/src/kernel_loader.s @@ -37,11 +37,15 @@ enter_protected_mode: ; TODO: Load GDT from GDTDescriptor ; eax at this line will carry GDTR location, dont forget to use square bracket [eax] + lgdt [eax] + mov eax, cr0 ; TODO: Set bit-0 (Protection Enable bit-flag) in Control Register 0 (CR0) ; Set eax with above condition, eax will be copied to CR0 with next instruction + or al, 1 mov cr0, eax + ; Far jump to update cs register ; Warning: Invalid GDT will raise exception in any instruction below jmp 0x8:flush_cs @@ -50,5 +54,8 @@ flush_cs: ; TODO: Set all data segment register with 0x10 ; Segments register need to set with 0x10: ss, ds, es mov ss, ax - + mov ds, ax + mov es, ax + + ret From 025955c1140096c5bad7c4178d42f72cee89742c Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Tue, 21 Feb 2023 15:10:45 +0700 Subject: [PATCH 10/52] replace cursor and memory framebuffer with macro --- src/framebuffer.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/framebuffer.c b/src/framebuffer.c index 7b69e1e..a50a327 100644 --- a/src/framebuffer.c +++ b/src/framebuffer.c @@ -5,18 +5,18 @@ void framebuffer_set_cursor(uint8_t r, uint8_t c) { // TODO : Implement - out(0x3D4, 0x0A); - out(0x3D5, (in(0x3D5) & 0xC0) | r); + out(CURSOR_PORT_CMD, 0x0A); + out(CURSOR_PORT_DATA, (in(CURSOR_PORT_DATA) & 0xC0) | r); - out(0x3D4, 0x0B); - out(0x3D5, (in(0x3D5) & 0xE0) | c); + out(CURSOR_PORT_CMD, 0x0B); + out(CURSOR_PORT_DATA, (in(CURSOR_PORT_DATA) & 0xE0) | c); } void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg) { // TODO : Implement uint16_t attrib = (bg << 4) | (fg & 0x0F); volatile uint16_t * where; - where = (volatile uint16_t *)0xB8000 + (row * 80 + col) ; + where = (volatile uint16_t *)MEMORY_FRAMEBUFFER + (row * 80 + col) ; *where = c | (attrib << 8); } @@ -25,7 +25,7 @@ void framebuffer_clear(void) { uint16_t space = 0x20 | (0x07 << 8); // " " in ASCII with white text on black background uint16_t i; volatile uint16_t * where; - where = (volatile uint16_t *)0xB8000; + where = (volatile uint16_t *)MEMORY_FRAMEBUFFER; for (i = 0; i < 80 * 25; i++) { *where = space; where++; From bf9eece0c7a948730c34cb1e3fc06adc0b6ca73e Mon Sep 17 00:00:00 2001 From: NerbFox <13521043@std.stei.itb.ac.id> Date: Sun, 19 Mar 2023 13:22:23 +0700 Subject: [PATCH 11/52] Update milestone 2 --- src/filesystem/disk.h | 52 ++++++++ src/filesystem/fat32.c | 13 ++ src/filesystem/fat32.h | 249 ++++++++++++++++++++++++++++++++++++++ src/interrupt/idt.h | 80 ++++++++++++ src/interrupt/interrupt.h | 125 +++++++++++++++++++ src/interrupt/intsetup.s | 127 +++++++++++++++++++ src/keyboard/keyboard.c | 24 ++++ src/keyboard/keyboard.h | 74 +++++++++++ 8 files changed, 744 insertions(+) create mode 100644 src/filesystem/disk.h create mode 100644 src/filesystem/fat32.c create mode 100644 src/filesystem/fat32.h create mode 100644 src/interrupt/idt.h create mode 100644 src/interrupt/interrupt.h create mode 100644 src/interrupt/intsetup.s create mode 100644 src/keyboard/keyboard.c create mode 100644 src/keyboard/keyboard.h diff --git a/src/filesystem/disk.h b/src/filesystem/disk.h new file mode 100644 index 0000000..7e73d4d --- /dev/null +++ b/src/filesystem/disk.h @@ -0,0 +1,52 @@ +#ifndef _DISK_H +#define _DISK_H + +#include "stdtype.h" + +/* -- ATA PIO status codes -- */ +#define ATA_STATUS_BSY 0x80 +#define ATA_STATUS_RDY 0x40 +#define ATA_STATUS_DRQ 0x08 +#define ATA_STATUS_DF 0x20 +#define ATA_STATUS_ERR 0x01 + +#define BLOCK_SIZE 512 +#define HALF_BLOCK_SIZE (BLOCK_SIZE/2) + + + + + +// Block buffer data type - @param buf Byte buffer with size of BLOCK_SIZE +struct BlockBuffer { + uint8_t buf[BLOCK_SIZE]; +} __attribute__((packed)); + + + + + +/** + * ATA PIO logical block address read blocks. Will blocking until read is completed. + * Note: ATA PIO will use 2-bytes per read/write operation. + * Recommended to use struct BlockBuffer + * + * @param ptr Pointer for storing reading data, this pointer should point to already allocated memory location. + * With allocated size positive integer multiple of BLOCK_SIZE, ex: buf[1024] + * @param logical_block_address Block address to read data from. Use LBA addressing + * @param block_count How many block to read, starting from block logical_block_address to lba-1 + */ +void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count); + +/** + * ATA PIO logical block address write blocks. Will blocking until write is completed. + * Note: ATA PIO will use 2-bytes per read/write operation. + * Recommended to use struct BlockBuffer + * + * @param ptr Pointer to data that to be written into disk. Memory pointed should be positive integer multiple of BLOCK_SIZE + * @param logical_block_address Block address to write data into. Use LBA addressing + * @param block_count How many block to write, starting from block logical_block_address to lba-1 + */ +void write_blocks(const void *ptr, uint32_t logical_block_address, uint8_t block_count); + +#endif \ No newline at end of file diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c new file mode 100644 index 0000000..c481fcf --- /dev/null +++ b/src/filesystem/fat32.c @@ -0,0 +1,13 @@ +#include "lib-header/stdtype.h" +#include "lib-header/fat32.h" +#include "lib-header/stdmem.h" + +const uint8_t fs_signature[BLOCK_SIZE] = { + 'C', 'o', 'u', 'r', 's', 'e', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + 'D', 'e', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'b', 'y', ' ', ' ', ' ', ' ', ' ', + 'L', 'a', 'b', ' ', 'S', 'i', 's', 't', 'e', 'r', ' ', 'I', 'T', 'B', ' ', ' ', + 'M', 'a', 'd', 'e', ' ', 'w', 'i', 't', 'h', ' ', '<', '3', ' ', ' ', ' ', ' ', + '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '2', '0', '2', '3', '\n', + [BLOCK_SIZE-2] = 'O', + [BLOCK_SIZE-1] = 'k', +}; \ No newline at end of file diff --git a/src/filesystem/fat32.h b/src/filesystem/fat32.h new file mode 100644 index 0000000..ee445e3 --- /dev/null +++ b/src/filesystem/fat32.h @@ -0,0 +1,249 @@ +#ifndef _FAT32_H +#define _FAT32_H + +#include "disk.h" +#include "stdtype.h" + + +/** + * FAT32 - IF2230 edition - 2023 + * Check "IF2230 - Guidebook - Milestone 2" for more details + * https://docs.google.com/document/d/1IFyxHSYYpKgecHcS0T64oDc4bVElaq8tBcm1_mjjGGM/edit# + */ + + + +/* -- IF2230 File System constants -- */ +#define BOOT_SECTOR 0 +#define CLUSTER_BLOCK_COUNT 4 +#define CLUSTER_SIZE (BLOCK_SIZE*CLUSTER_BLOCK_COUNT) +#define CLUSTER_MAP_SIZE 512 + +/* -- FAT32 FileAllocationTable constants -- */ +// FAT reserved value for cluster 0 and 1 in FileAllocationTable +#define CLUSTER_0_VALUE 0x0FFFFFF0 +#define CLUSTER_1_VALUE 0x0FFFFFFF + +// EOF also double as valid cluster / "this is last valid cluster in the chain" +#define FAT32_FAT_END_OF_FILE 0x0FFFFFFF +#define FAT32_FAT_EMPTY_ENTRY 0x00000000 + +#define FAT_CLUSTER_NUMBER 1 +#define ROOT_CLUSTER_NUMBER 2 + +/* -- FAT32 DirectoryEntry constants -- */ +#define ATTR_SUBDIRECTORY 0b00010000 +#define UATTR_NOT_EMPTY 0b10101010 + + + +// Boot sector signature for this file system "FAT32 - IF2230 edition" +extern const uint8_t fs_signature[BLOCK_SIZE]; + +// Cluster buffer data type - @param buf Byte buffer with size of CLUSTER_SIZE +struct ClusterBuffer { + uint8_t buf[CLUSTER_SIZE]; +} __attribute__((packed)); + + + + + +/* -- FAT32 Data Structures -- */ + +/** + * FAT32 FileAllocationTable, for more information about this, check guidebook + * + * @param cluster_map Containing cluster map of FAT32 + */ +struct FAT32FileAllocationTable { + uint32_t cluster_map[CLUSTER_MAP_SIZE]; +} __attribute__((packed)); + +/** + * FAT32 standard 8.3 format - 32 bytes DirectoryEntry, Some detail can be found at: + * https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#Directory_entry, and click show table. + * + * @param name Entry name + * @param ext File extension + * @param attribute Will be used exclusively for subdirectory flag / determining this entry is file or folder + * @param user_attribute If this attribute equal with UATTR_NOT_EMPTY then entry is not empty + * + * @param undelete Unused / optional + * @param create_time Unused / optional + * @param create_date Unused / optional + * @param access_time Unused / optional + * @param cluster_high Upper 16-bit of cluster number + * + * @param modified_time Unused / optional + * @param modified_date Unused / optional + * @param cluster_low Lower 16-bit of cluster number + * @param filesize Filesize of this file, if this is directory / folder, filesize is 0 + */ +struct FAT32DirectoryEntry { + char name[8]; + char ext[3]; + uint8_t attribute; + uint8_t user_attribute; + + bool undelete; + uint16_t create_time; + uint16_t create_date; + uint16_t access_date; + uint16_t cluster_high; + + uint16_t modified_time; + uint16_t modified_date; + uint16_t cluster_low; + uint32_t filesize; +} __attribute__((packed)); + +// FAT32 DirectoryTable, containing directory entry table - @param table Table of DirectoryEntry that span within 1 cluster +struct FAT32DirectoryTable { + struct FAT32DirectoryEntry table[CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry)]; +} __attribute__((packed)); + + + + + +/* -- FAT32 Driver -- */ + +/** + * FAT32DriverState - Contain all driver states + * + * @param fat_table FAT of the system, will be loaded during initialize_filesystem_fat32() + * @param dir_table_buf Buffer for directory table + * @param cluster_buf Buffer for cluster + */ +struct FAT32DriverState { + struct FAT32FileAllocationTable fat_table; + struct FAT32DirectoryTable dir_table_buf; + struct ClusterBuffer cluster_buf; +} __attribute__((packed)); + +/** + * FAT32DriverRequest - Request for Driver CRUD operation + * + * @param buf Pointer pointing to buffer + * @param name Name for directory entry + * @param ext Extension for file + * @param parent_cluster_number Parent directory cluster number, for updating metadata + * @param buffer_size Buffer size, CRUD operation will have different behaviour with this attribute + */ +struct FAT32DriverRequest { + void *buf; + char name[8]; + char ext[3]; + uint32_t parent_cluster_number; + uint32_t buffer_size; +} __attribute__((packed)); + + + + + +/* -- Driver Interfaces -- */ + +/** + * Convert cluster number to logical block address + * + * @param cluster Cluster number to convert + * @return uint32_t Logical Block Address + */ +uint32_t cluster_to_lba(uint32_t cluster); + +/** + * Initialize DirectoryTable value with parent DirectoryEntry and directory name + * + * @param dir_table Pointer to directory table + * @param name 8-byte char for directory name + * @param parent_dir_cluster Parent directory cluster number + */ +void init_directory_table(struct FAT32DirectoryTable *dir_table, char *name, uint32_t parent_dir_cluster); + +/** + * Checking whether filesystem signature is missing or not in boot sector + * + * @return True if memcmp(boot_sector, fs_signature) returning inequality + */ +bool is_empty_storage(void); + +/** + * Create new FAT32 file system. Will write fs_signature into boot sector and + * proper FileAllocationTable (contain CLUSTER_0_VALUE, CLUSTER_1_VALUE, + * and initialized root directory) into cluster number 1 + */ +void create_fat32(void); + +/** + * Initialize file system driver state, if is_empty_storage() then create_fat32() + * Else, read and cache entire FileAllocationTable (located at cluster number 1) into driver state + */ +void initialize_filesystem_fat32(void); + +/** + * Write cluster operation, wrapper for write_blocks(). + * Recommended to use struct ClusterBuffer + * + * @param ptr Pointer to source data + * @param cluster_number Cluster number to write + * @param cluster_count Cluster count to write, due limitation of write_blocks block_count 255 => max cluster_count = 63 + */ +void write_clusters(const void *ptr, uint32_t cluster_number, uint8_t cluster_count); + +/** + * Read cluster operation, wrapper for read_blocks(). + * Recommended to use struct ClusterBuffer + * + * @param ptr Pointer to buffer for reading + * @param cluster_number Cluster number to read + * @param cluster_count Cluster count to read, due limitation of read_blocks block_count 255 => max cluster_count = 63 + */ +void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count); + + + + + +/* -- CRUD Operation -- */ + +/** + * FAT32 Folder / Directory read + * + * @param request buf point to struct FAT32DirectoryTable, + * name is directory name, + * ext is unused, + * parent_cluster_number is target directory table to read, + * buffer_size must be exactly sizeof(struct FAT32DirectoryTable) + * @return Error code: 0 success - 1 not a folder - 2 not found - -1 unknown + */ +int8_t read_directory(struct FAT32DriverRequest request); + + +/** + * FAT32 read, read a file from file system. + * + * @param request All attribute will be used for read, buffer_size will limit reading count + * @return Error code: 0 success - 1 not a file - 2 not enough buffer - 3 not found - -1 unknown + */ +int8_t read(struct FAT32DriverRequest request); + +/** + * FAT32 write, write a file or folder to file system. + * + * @param request All attribute will be used for write, buffer_size == 0 then create a folder / directory + * @return Error code: 0 success - 1 file/folder already exist - 2 invalid parent cluster - -1 unknown + */ +int8_t write(struct FAT32DriverRequest request); + + +/** + * FAT32 delete, delete a file or empty directory (only 1 DirectoryEntry) in file system. + * + * @param request buf and buffer_size is unused + * @return Error code: 0 success - 1 not found - 2 folder is not empty - -1 unknown + */ +int8_t delete(struct FAT32DriverRequest request); + +#endif \ No newline at end of file diff --git a/src/interrupt/idt.h b/src/interrupt/idt.h new file mode 100644 index 0000000..90c91e5 --- /dev/null +++ b/src/interrupt/idt.h @@ -0,0 +1,80 @@ +#ifndef _IDT_H +#define _IDT_H + +#include "stdtype.h" + +// IDT hard limit, see Intel x86 manual 3a - 6.10 Interrupt Descriptor Table +#define IDT_MAX_ENTRY_COUNT 256 +#define ISR_STUB_TABLE_LIMIT 64 +#define INTERRUPT_GATE_R_BIT_1 0b000 +#define INTERRUPT_GATE_R_BIT_2 0b110 +#define INTERRUPT_GATE_R_BIT_3 0b0 + +// Some GDT Constant +#define GDT_KERNEL_CODE_SEGMENT_SELECTOR 0x8 +#define GDT_KERNEL_DATA_SEGMENT_SELECTOR 0x10 + + +// Interrupt Handler / ISR stub for reducing code duplication, this array can be iterated in initialize_idt() +extern void *isr_stub_table[ISR_STUB_TABLE_LIMIT]; + +extern struct IDTR _idt_idtr; + +/** + * IDTGate, IDT entry that point into interrupt handler + * Struct defined exactly in Intel x86 Vol 3a - Figure 6-2. IDT Gate Descriptors + * + * @param offset_low Lower 16-bit offset + * @param segment Memory segment + * @param _reserved Reserved bit, bit length: 5 + * @param _r_bit_1 Reserved for idtgate type, bit length: 3 + * @param _r_bit_2 Reserved for idtgate type, bit length: 3 + * @param gate_32 Is this gate size 32-bit? If not then its 16-bit gate + * @param _r_bit_3 Reserved for idtgate type, bit length: 1 + * ... + */ +struct IDTGate { + // First 32-bit (Bit 0 to 31) + uint16_t offset_low; + + // TODO : Implement +} __attribute__((packed)); + +/** + * Interrupt Descriptor Table, containing lists of IDTGate. + * One IDT already defined in idt.c + * + * ... + */ +// TODO : Implement +// ... + +/** + * IDTR, carrying information where's the IDT located and size. + * Global kernel variable defined at idt.c. + * + * ... + */ +// TODO : Implement +// ... + + + +/** + * Set IDTGate with proper interrupt handler values. + * Will directly edit global IDT variable and set values properly + * + * @param int_vector Interrupt vector to handle + * @param handler_address Interrupt handler address + * @param gdt_seg_selector GDT segment selector, for kernel use GDT_KERNEL_CODE_SEGMENT_SELECTOR + * @param privilege Descriptor privilege level + */ +void set_interrupt_gate(uint8_t int_vector, void *handler_address, uint16_t gdt_seg_selector, uint8_t privilege); + + +/** + * Set IDT with proper values and load with lidt + */ +void initialize_idt(void); + +#endif \ No newline at end of file diff --git a/src/interrupt/interrupt.h b/src/interrupt/interrupt.h new file mode 100644 index 0000000..6a84bbc --- /dev/null +++ b/src/interrupt/interrupt.h @@ -0,0 +1,125 @@ +#ifndef _INTERRUPT_H +#define _INTERRUPT_H + +#include "stdtype.h" + +/* -- PIC constants -- */ + +// PIC interrupt offset +#define PIC1_OFFSET 0x20 +#define PIC2_OFFSET 0x28 + +// PIC ports +#define PIC1 0x20 +#define PIC2 0xA0 +#define PIC1_COMMAND PIC1 +#define PIC1_DATA (PIC1 + 1) +#define PIC2_COMMAND PIC2 +#define PIC2_DATA (PIC2 + 1) + +// PIC ACK & mask constant +#define PIC_ACK 0x20 +#define PIC_DISABLE_ALL_MASK 0xFF + +// PIC remap constants +#define ICW1_ICW4 0x01 /* ICW4 (not) needed */ +#define ICW1_SINGLE 0x02 /* Single (cascade) mode */ +#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ +#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ +#define ICW1_INIT 0x10 /* Initialization - required! */ + +#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ +#define ICW4_AUTO 0x02 /* Auto (normal) EOI */ +#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ +#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ +#define ICW4_SFNM 0x10 /* Special fully nested (not) */ + + +/* -- PICs IRQ list -- */ + +// PIC Master +#define IRQ_TIMER 0 +#define IRQ_KEYBOARD 1 +#define IRQ_CASCADE 2 +#define IRQ_COM2 3 +#define IRQ_COM1 4 +#define IRQ_LPT2 5 +#define IRQ_FLOPPY_DISK 6 +#define IRQ_LPT1_SPUR 7 + +// PIC Slave +#define IRQ_CMOS 8 +#define IRQ_PERIPHERAL_1 9 +#define IRQ_PERIPHERAL_2 10 +#define IRQ_PERIPHERAL_3 11 +#define IRQ_MOUSE 12 +#define IRQ_FPU 13 +#define IRQ_PRIMARY_ATA 14 +#define IRQ_SECOND_ATA 15 + + +/** + * CPURegister, store CPU registers that can be used for interrupt handler / ISRs + * + * @param gp_register CPU general purpose register (a, b, c, d) + * @param stack_register CPU stack register (bp, sp) + */ +struct CPURegister { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t ebp; + uint32_t esp; +} __attribute__((packed)); + +/** + * InterruptInfo, data pushed by CPU when interrupt / exception is raised. + * Refer to Intel x86 Vol 3a: Figure 6-4 Stack usage on transfer to Interrupt. + * + * Note, when returning from interrupt handler with iret, esp must be pointing to eip pushed before + * or in other words, CPURegister, int_number and error_code should be pop-ed from stack. + * + * @param error_code Error code that pushed with the exception + * @param eip Instruction pointer where interrupt is raised + * @param cs Code segment selector where interrupt is raised + * @param eflags CPU eflags register when interrupt is raised + */ +struct InterruptStack { + uint32_t error_code; + uint32_t eip; + uint32_t cs; + uint32_t eflags; +} __attribute__((packed)); + + + + + +// Activate PIC mask for keyboard only +void activate_keyboard_interrupt(void); + +// I/O port wait, around 1-4 microsecond, for I/O synchronization purpose +void io_wait(void); + +// Send ACK to PIC - @param irq Interrupt request number destination, note: this function already include PIC1_OFFSET +void pic_ack(uint8_t irq); + +// Shift PIC interrupt number to PIC1_OFFSET and PIC2_OFFSET (master and slave) +void pic_remap(void); + +/** + * Main interrupt handler when any interrupt / exception is raised. + * Do not call this function normally. + * + * This function will be called first if any INT 0x00 - 0x40 is raised, + * and will call proper ISR for respective interrupt / exception. + * + * Again, this function is not for normal function call, all parameter will be automatically set when interrupt is called. + * @param cpu CPU register when interrupt is raised + * @param int_number Interrupt number that trigger interrupt exception + * @param info Information about interrupt that pushed automatically by CPU + */ +void main_interrupt_handler(struct CPURegister cpu, uint32_t int_number, struct InterruptStack info); + +#endif \ No newline at end of file diff --git a/src/interrupt/intsetup.s b/src/interrupt/intsetup.s new file mode 100644 index 0000000..ba9eae6 --- /dev/null +++ b/src/interrupt/intsetup.s @@ -0,0 +1,127 @@ +extern main_interrupt_handler +global isr_stub_table + +; Generic handler section for interrupt +call_generic_handler: + ; Before interrupt_handler_n is called (caller of this generic handler section), + ; stack will have these value that pushed automatically by CPU + ; [esp + 12] eflags + ; [esp + 8 ] cs + ; [esp + 4 ] eip + ; [esp + 0 ] error code + + ; CPURegister + push esp + push ebp + push edx + push ecx + push ebx + push eax + + + ; call the C function + call main_interrupt_handler + + ; restore the registers + pop eax + pop ebx + pop ecx + pop edx + pop ebp + pop esp + + ; restore the esp (interrupt number & error code) + add esp, 8 + + ; return to the code that got interrupted + ; at this point, stack should be structured like this + ; [esp], [esp+4], [esp+8] + ; eip, cs, eflags + ; improper value will cause invalid return address & register + sti + iret + + + +; Macro for creating interrupt handler that only push interrupt number +%macro no_error_code_interrupt_handler 1 +interrupt_handler_%1: + push dword 0 ; push 0 as error code + push dword %1 ; push the interrupt number + jmp call_generic_handler ; jump to the common handler +%endmacro + +%macro error_code_interrupt_handler 1 +interrupt_handler_%1: + push dword %1 + jmp call_generic_handler +%endmacro + +; CPU exception handlers +no_error_code_interrupt_handler 0 ; 0x0 - Division by zero +no_error_code_interrupt_handler 1 ; 0x1 - Debug Exception +no_error_code_interrupt_handler 2 ; 0x2 - NMI, Non-Maskable Interrupt +no_error_code_interrupt_handler 3 ; 0x3 - Breakpoint Exception +no_error_code_interrupt_handler 4 ; 0x4 - INTO Overflow +no_error_code_interrupt_handler 5 ; 0x5 - Out of Bounds +no_error_code_interrupt_handler 6 ; 0x6 - Invalid Opcode +no_error_code_interrupt_handler 7 ; 0x7 - Device Not Available +error_code_interrupt_handler 8 ; 0x8 - Double Fault +no_error_code_interrupt_handler 9 ; 0x9 - Deprecated +error_code_interrupt_handler 10 ; 0xA - Invalid TSS +error_code_interrupt_handler 11 ; 0xB - Segment Not Present +error_code_interrupt_handler 12 ; 0xC - Stack-Segment Fault +error_code_interrupt_handler 13 ; 0xD - General Protection Fault +error_code_interrupt_handler 14 ; 0xE - Page Fault +no_error_code_interrupt_handler 15 ; 0xF - Reserved +no_error_code_interrupt_handler 16 ; 0x10 - x87 Floating-Point Exception +error_code_interrupt_handler 17 ; 0x11 - Alignment Check Exception +no_error_code_interrupt_handler 18 ; 0x12 - Machine Check Exception +no_error_code_interrupt_handler 19 ; 0x13 - SIMD Floating-Point Exception +no_error_code_interrupt_handler 20 ; 0x14 - Virtualization Exception +no_error_code_interrupt_handler 21 ; 0x15 - Control Protection Exception +no_error_code_interrupt_handler 22 ; 0x16 - Reserved +no_error_code_interrupt_handler 23 ; 0x17 - Reserved +no_error_code_interrupt_handler 24 ; 0x18 - Reserved +no_error_code_interrupt_handler 25 ; 0x19 - Reserved +no_error_code_interrupt_handler 26 ; 0x1A - Reserved +no_error_code_interrupt_handler 27 ; 0x1B - Reserved +no_error_code_interrupt_handler 28 ; 0x1C - Hypervisor Injection Exception +no_error_code_interrupt_handler 29 ; 0x1D - VMM Communication Exception +error_code_interrupt_handler 30 ; 0x1E - Security Exception +no_error_code_interrupt_handler 31 ; 0x1F - Reserved + +; User defined interrupt handler +; Assuming PIC1 & PIC2 offset is 0x20 and 0x28 +; 32 - 0x20 - IRQ0: Programmable Interval Timer +; 33 - 0x21 - IRQ1: Keyboard +; 34 - 0x22 - IRQ2: PIC Cascade, used internally +; 35 - 0x23 - IRQ3: COM2, if enabled +; 36 - 0x24 - IRQ4: COM1, if enabled +; 37 - 0x25 - IRQ5: LPT2, if enabled +; 38 - 0x26 - IRQ6: Floppy Disk +; 39 - 0x27 - IRQ7: LPT1 + +; 40 - 0x28 - IRQ8: CMOS real-time clock +; 41 - 0x29 - IRQ9: Free +; 42 - 0x2A - IRQ10: Free +; 43 - 0x2B - IRQ11: Free +; 44 - 0x2C - IRQ12: PS2 Mouse +; 45 - 0x2D - IRQ13: Coprocessor +; 46 - 0x2E - IRQ14: Primary ATA Hard Disk +; 47 - 0x2F - IRQ15: Secondary ATA Hard Disk +%assign i 32 +%rep 32 +no_error_code_interrupt_handler i +%assign i i+1 +%endrep + + + +; ISR stub table, useful for reducing code repetition +isr_stub_table: + %assign i 0 + %rep 64 + dd interrupt_handler_%+i + %assign i i+1 + %endrep \ No newline at end of file diff --git a/src/keyboard/keyboard.c b/src/keyboard/keyboard.c new file mode 100644 index 0000000..7ff5b82 --- /dev/null +++ b/src/keyboard/keyboard.c @@ -0,0 +1,24 @@ +#include "lib-header/keyboard.h" +#include "lib-header/portio.h" +#include "lib-header/framebuffer.h" +#include "lib-header/stdmem.h" + +const char keyboard_scancode_1_to_ascii_map[256] = { + 0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; diff --git a/src/keyboard/keyboard.h b/src/keyboard/keyboard.h new file mode 100644 index 0000000..f29c4a8 --- /dev/null +++ b/src/keyboard/keyboard.h @@ -0,0 +1,74 @@ +#ifndef _USER_ISR_H +#define _USER_ISR_H + +#include "interrupt.h" +#include "stdtype.h" + +#define EXT_SCANCODE_UP 0x48 +#define EXT_SCANCODE_DOWN 0x50 +#define EXT_SCANCODE_LEFT 0x4B +#define EXT_SCANCODE_RIGHT 0x4D + +#define KEYBOARD_DATA_PORT 0x60 +#define EXTENDED_SCANCODE_BYTE 0xE0 + +#define KEYBOARD_BUFFER_SIZE 256 + +/** + * keyboard_scancode_1_to_ascii_map[256], Convert scancode values that correspond to ASCII printables + * How to use this array: ascii_char = k[scancode] + * + * By default, QEMU using scancode set 1 (from empirical testing) + */ +extern const char keyboard_scancode_1_to_ascii_map[256]; + +/** + * KeyboardDriverState - Contain all driver states + * + * @param read_extended_mode Optional, can be used for signaling next read is extended scancode (ex. arrow keys) + * @param keyboard_input_on Indicate whether keyboard ISR is activated or not + * @param buffer_index Used for keyboard_buffer index + * @param keyboard_buffer Storing keyboard input values in ASCII + */ +struct KeyboardDriverState { + bool read_extended_mode; + bool keyboard_input_on; + uint8_t buffer_index; + char keyboard_buffer[KEYBOARD_BUFFER_SIZE]; +} __attribute((packed)); + + + + + +/* -- Driver Interfaces -- */ + +// Activate keyboard ISR / start listen keyboard & save to buffer +void keyboard_state_activate(void); + +// Deactivate keyboard ISR / stop listening keyboard interrupt +void keyboard_state_deactivate(void); + +// Get keyboard buffer values - @param buf Pointer to char buffer, recommended size at least KEYBOARD_BUFFER_SIZE +void get_keyboard_buffer(char *buf); + +// Check whether keyboard ISR is active or not - @return Equal with keyboard_input_on value +bool is_keyboard_blocking(void); + + +/* -- Keyboard Interrupt Service Routine -- */ + +/** + * Handling keyboard interrupt & process scancodes into ASCII character. + * Will start listen and process keyboard scancode if keyboard_input_on. + * + * Will only print printable character into framebuffer. + * Stop processing when enter key (line feed) is pressed. + * + * Note that, with keyboard interrupt & ISR, keyboard reading is non-blocking. + * This can be made into blocking input with `while (is_keyboard_blocking());` + * after calling `keyboard_state_activate();` + */ +void keyboard_isr(void); + +#endif \ No newline at end of file From 2a334a86fddbb35bccd5cb58262c2bba5cbe6c57 Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Sun, 19 Mar 2023 16:56:40 +0700 Subject: [PATCH 12/52] implement idt, interrupt, and organizing repo files Co-authored-by: Rava Maulana Co-authored-by: Nigel Sahl Co-authored-by: razzanYoni --- makefile | 9 ++++ src/filesystem/fat32.c | 6 +-- src/interrupt/idt.c | 50 ++++++++++++++++++++++ src/interrupt/interrupt.c | 52 +++++++++++++++++++++++ src/kernel.c | 5 +++ src/keyboard/keyboard.c | 8 ++-- src/{filesystem => lib-header}/disk.h | 0 src/{filesystem => lib-header}/fat32.h | 0 src/{interrupt => lib-header}/idt.h | 43 ++++++++++++++----- src/{interrupt => lib-header}/interrupt.h | 1 + src/{keyboard => lib-header}/keyboard.h | 0 11 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 src/interrupt/idt.c create mode 100644 src/interrupt/interrupt.c rename src/{filesystem => lib-header}/disk.h (100%) rename src/{filesystem => lib-header}/fat32.h (100%) rename src/{interrupt => lib-header}/idt.h (58%) rename src/{interrupt => lib-header}/interrupt.h (99%) rename src/{keyboard => lib-header}/keyboard.h (100%) diff --git a/makefile b/makefile index f658398..1052b4b 100644 --- a/makefile +++ b/makefile @@ -5,6 +5,9 @@ CC = gcc # Directory SOURCE_FOLDER = src +INTERRUPT_FOLDER = interrupt +KEYBOARD_FOLDER = keyboard +FILESYSTEM_FOLDER = filesystem # SOURCE_FOLDER = bin/iso/boot/grub OUTPUT_FOLDER = bin ISO_NAME = OSyikkk @@ -29,12 +32,18 @@ clean: kernel: @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/kernel_loader.s -o $(OUTPUT_FOLDER)/kernel_loader.o + @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/intsetup.s -o $(OUTPUT_FOLDER)/intsetup.o # TODO: Compile C file with CFLAGS @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/kernel.c -o $(OUTPUT_FOLDER)/kernel.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/framebuffer.c -o $(OUTPUT_FOLDER)/framebuffer.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/stdmem.c -o $(OUTPUT_FOLDER)/stdmem.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/portio.c -o $(OUTPUT_FOLDER)/portio.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/gdt.c -o $(OUTPUT_FOLDER)/gdt.o + + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/idt.c -o $(OUTPUT_FOLDER)/idt.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/interrupt.c -o $(OUTPUT_FOLDER)/interrupt.o + + @$(LIN) $(LFLAGS) bin/*.o -o $(OUTPUT_FOLDER)/kernel @echo Linking object files and generate elf32... @rm -f *.o diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index c481fcf..b56e965 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -1,6 +1,6 @@ -#include "lib-header/stdtype.h" -#include "lib-header/fat32.h" -#include "lib-header/stdmem.h" +#include "../lib-header/stdtype.h" +#include "../lib-header/fat32.h" +#include "../lib-header/stdmem.h" const uint8_t fs_signature[BLOCK_SIZE] = { 'C', 'o', 'u', 'r', 's', 'e', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', diff --git a/src/interrupt/idt.c b/src/interrupt/idt.c new file mode 100644 index 0000000..a1cc543 --- /dev/null +++ b/src/interrupt/idt.c @@ -0,0 +1,50 @@ +#include "../lib-header/idt.h" + +void *isr_stub_table[ISR_STUB_TABLE_LIMIT]; +struct IDTR _idt_idtr; + +// global initialization of IDT +// = { +// .size = sizeof(interrupt_descriptor_table) - 1, +// .address = (void *) & interrupt_descriptor_table.table[0] +//}; + +void initialize_idt(void){ + /* TODO : + * Iterate all isr_stub_table, + * Set all IDT entry with set_interrupt_gate() + * with following values: + * Vector: i + * Handler Address: isr_stub_table[i] + * Segment: GDT_KERNEL_CODE_SEGMENT_SELECTOR + * Privilege: 0 + */ + // Local initialization of IDT + _idt_idtr.size = sizeof(interrupt_descriptor_table) - 1; + _idt_idtr.address = (void *) & interrupt_descriptor_table.table[0]; + + + for (uint8_t i = 0; i < ISR_STUB_TABLE_LIMIT; i++) { + set_interrupt_gate(i, isr_stub_table[i], GDT_KERNEL_CODE_SEGMENT_SELECTOR, 0); + } + __asm__ volatile("lidt %0" : : "m"(_idt_idtr)); + __asm__ volatile("sti"); + +} + +void set_interrupt_gate(uint8_t int_vector, void *handler_address, uint16_t gdt_seg_selector, uint8_t privilege) { + struct IDTGate *idt_int_gate = &interrupt_descriptor_table.table[int_vector]; + // TODO : Set handler offset, privilege & segment + idt_int_gate->offset_low = (uint32_t)handler_address & 0xFFFF; + idt_int_gate->segment = gdt_seg_selector; + idt_int_gate->_reserved = 0b0; + idt_int_gate->dpl = privilege; + idt_int_gate->offset_high = (uint32_t)handler_address >> 16; + + // Target system 32-bit and flag this as valid interrupt gate + idt_int_gate->_r_bit_1 = INTERRUPT_GATE_R_BIT_1; + idt_int_gate->_r_bit_2 = INTERRUPT_GATE_R_BIT_2; + idt_int_gate->_r_bit_3 = INTERRUPT_GATE_R_BIT_3; + idt_int_gate->gate_32 = 1; + idt_int_gate->valid_bit = 1; +} diff --git a/src/interrupt/interrupt.c b/src/interrupt/interrupt.c new file mode 100644 index 0000000..d32346b --- /dev/null +++ b/src/interrupt/interrupt.c @@ -0,0 +1,52 @@ +#include "../lib-header/interrupt.h" + +void io_wait(void) { + out(0x80, 0); +} + +void pic_ack(uint8_t irq) { + if (irq >= 8) + out(PIC2_COMMAND, PIC_ACK); + out(PIC1_COMMAND, PIC_ACK); +} + +void pic_remap(void) { + uint8_t a1, a2; + + // Save masks + a1 = in(PIC1_DATA); + a2 = in(PIC2_DATA); + + // Starts the initialization sequence in cascade mode + out(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); + io_wait(); + out(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); + io_wait(); + out(PIC1_DATA, PIC1_OFFSET); // ICW2: Master PIC vector offset + io_wait(); + out(PIC2_DATA, PIC2_OFFSET); // ICW2: Slave PIC vector offset + io_wait(); + out(PIC1_DATA, 0b0100); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) + io_wait(); + out(PIC2_DATA, 0b0010); // ICW3: tell Slave PIC its cascade identity (0000 0010) + io_wait(); + + out(PIC1_DATA, ICW4_8086); + io_wait(); + out(PIC2_DATA, ICW4_8086); + io_wait(); + + // Restore masks + out(PIC1_DATA, a1); + out(PIC2_DATA, a2); +} + +void main_interrupt_handler ( + __attribute__((unused)) struct CPURegister cpu, + uint32_t int_number, + __attribute__((unused)) struct InterruptStack info +) { + switch (int_number) { + + } +} \ No newline at end of file diff --git a/src/kernel.c b/src/kernel.c index 911d599..e3859c6 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -4,12 +4,16 @@ #include "lib-header/gdt.h" #include "lib-header/framebuffer.h" #include "lib-header/kernel_loader.h" +#include "lib-header/idt.h" +#include "lib-header/interrupt.h" void kernel_setup(void) { // uint32_t a; // uint32_t volatile b = 0x0000BABE; // __asm__("mov $0xCAFE0000, %0" : "=r"(a)); enter_protected_mode(&_gdt_gdtr); + pic_remap(); + initialize_idt(); framebuffer_clear(); framebuffer_write(3, 8, 'H', 0, 0xF); framebuffer_write(3, 9, 'a', 0, 0xF); @@ -17,6 +21,7 @@ void kernel_setup(void) { framebuffer_write(3, 11, '!', 0, 0xF); framebuffer_set_cursor(3, 10); // while (TRUE) b += 1; + __asm__("int $0x4"); while (TRUE); } \ No newline at end of file diff --git a/src/keyboard/keyboard.c b/src/keyboard/keyboard.c index 7ff5b82..c88db52 100644 --- a/src/keyboard/keyboard.c +++ b/src/keyboard/keyboard.c @@ -1,7 +1,7 @@ -#include "lib-header/keyboard.h" -#include "lib-header/portio.h" -#include "lib-header/framebuffer.h" -#include "lib-header/stdmem.h" +#include "../lib-header/keyboard.h" +#include "../lib-header/portio.h" +#include "../lib-header/framebuffer.h" +#include "../lib-header/stdmem.h" const char keyboard_scancode_1_to_ascii_map[256] = { 0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', diff --git a/src/filesystem/disk.h b/src/lib-header/disk.h similarity index 100% rename from src/filesystem/disk.h rename to src/lib-header/disk.h diff --git a/src/filesystem/fat32.h b/src/lib-header/fat32.h similarity index 100% rename from src/filesystem/fat32.h rename to src/lib-header/fat32.h diff --git a/src/interrupt/idt.h b/src/lib-header/idt.h similarity index 58% rename from src/interrupt/idt.h rename to src/lib-header/idt.h index 90c91e5..107b39e 100644 --- a/src/interrupt/idt.h +++ b/src/lib-header/idt.h @@ -2,6 +2,7 @@ #define _IDT_H #include "stdtype.h" +// #include // IDT hard limit, see Intel x86 manual 3a - 6.10 Interrupt Descriptor Table #define IDT_MAX_ENTRY_COUNT 256 @@ -24,40 +25,62 @@ extern struct IDTR _idt_idtr; * IDTGate, IDT entry that point into interrupt handler * Struct defined exactly in Intel x86 Vol 3a - Figure 6-2. IDT Gate Descriptors * - * @param offset_low Lower 16-bit offset - * @param segment Memory segment - * @param _reserved Reserved bit, bit length: 5 - * @param _r_bit_1 Reserved for idtgate type, bit length: 3 - * @param _r_bit_2 Reserved for idtgate type, bit length: 3 - * @param gate_32 Is this gate size 32-bit? If not then its 16-bit gate - * @param _r_bit_3 Reserved for idtgate type, bit length: 1 + * @param offset_low Lower 16-bit offset + * @param segment Memory segment + * @param _reserved Reserved bit, bit length: 5 + * @param _r_bit_1 Reserved for idtgate type, bit length: 3 + * @param _r_bit_2 Reserved for idtgate type, bit length: 3 + * @param gate_32 Is this gate size 32-bit? If not then its 16-bit gate + * @param _r_bit_3 Reserved for idtgate type, bit length: 1 + * @param dpl Descriptor Privilege Level, bit length: 2 + * @param valid_bit Segment present flag, bit length: 1 + * @param offset_high Higher 16-bit offset * ... + */ struct IDTGate { // First 32-bit (Bit 0 to 31) uint16_t offset_low; + uint16_t segment; // TODO : Implement + uint8_t _reserved : 5; + uint8_t _r_bit_1 : 3; + uint8_t _r_bit_2 : 3; + uint8_t gate_32 : 1; + uint8_t _r_bit_3 : 1; + uint8_t dpl : 2; + uint8_t valid_bit : 1; + uint16_t offset_high; + } __attribute__((packed)); /** * Interrupt Descriptor Table, containing lists of IDTGate. * One IDT already defined in idt.c * - * ... + * @param table Fixed-width array of IDTGate with size IDT_MAX_ENTRY_COUNT */ // TODO : Implement // ... +struct InterruptDescriptorTable { + struct IDTGate table [IDT_MAX_ENTRY_COUNT]; +} __attribute__((packed)) interrupt_descriptor_table; + /** * IDTR, carrying information where's the IDT located and size. * Global kernel variable defined at idt.c. * - * ... + * @param size Interrupt Descriptor Table size + * @param address IDT address */ // TODO : Implement // ... - +struct IDTR { + uint16_t size; + struct InterruptDescriptorTable *address; +} __attribute__((packed)); /** diff --git a/src/interrupt/interrupt.h b/src/lib-header/interrupt.h similarity index 99% rename from src/interrupt/interrupt.h rename to src/lib-header/interrupt.h index 6a84bbc..e746673 100644 --- a/src/interrupt/interrupt.h +++ b/src/lib-header/interrupt.h @@ -2,6 +2,7 @@ #define _INTERRUPT_H #include "stdtype.h" +#include "portio.h" /* -- PIC constants -- */ diff --git a/src/keyboard/keyboard.h b/src/lib-header/keyboard.h similarity index 100% rename from src/keyboard/keyboard.h rename to src/lib-header/keyboard.h From b9738ec03200549c7616435693d36d47537bee7f Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Mon, 20 Mar 2023 00:03:56 +0700 Subject: [PATCH 13/52] Add keyboard implementation Co-authored-by: razzanYoni Co-authored-by: Nigel Sahl Co-authored-by: Rava Maulana --- .gitignore | 2 + README.md | 23 ++--- makefile | 1 + src/filesystem/disk.c | 52 ++++++++++ src/filesystem/fat32.c | 134 ++++++++++++++++++++++++- src/framebuffer.c | 87 ++++++++++++++-- src/interrupt/idt.c | 2 +- src/interrupt/interrupt.c | 22 ++++- src/kernel.c | 15 +-- src/keyboard/keyboard.c | 186 +++++++++++++++++++++++++++++++++++ src/lib-header/framebuffer.h | 32 ++++++ src/lib-header/interrupt.h | 2 + src/lib-header/keyboard.h | 38 ++++++- src/lib-header/portio.h | 2 +- src/lib-header/stdmem.h | 2 + 15 files changed, 567 insertions(+), 33 deletions(-) create mode 100644 .gitignore create mode 100644 src/filesystem/disk.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad213f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/test +*.json \ No newline at end of file diff --git a/README.md b/README.md index 0b7a9c2..5448be9 100644 --- a/README.md +++ b/README.md @@ -18,25 +18,25 @@ Tugas IF2230 - Sistem Operasi 2023 Tahun 2022/2023 ## General Information -G +Operating System ## Technologies Used -- +- Qemu +- NASM +- C - - ## Features -- Implementasi dari +- Implementasi dari mata kuliah Sistem Operasi ## Screenshots ![Example screenshot](./doc/OS.png) ## How to Run -- +- make run di terminal ## Project Status Project is: _not complete_ @@ -45,11 +45,12 @@ Project is: _not complete_ ## Room for Improvement Room for improvement: -- speed +- speed up the code +- add more features ## Division of tasks -1. Nigel Sahl (13521043) : -2. Razzan Daksana Yoni (13521087) : -3. Rava Maulana Azzikri (13521149) : -4. Hanif Muhammad Zhafran (13521157) : +1. Nigel Sahl (13521043) +2. Razzan Daksana Yoni (13521087) +3. Rava Maulana Azzikri (13521149) +4. Hanif Muhammad Zhafran (13521157) diff --git a/makefile b/makefile index 1052b4b..e24b4a1 100644 --- a/makefile +++ b/makefile @@ -42,6 +42,7 @@ kernel: @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/idt.c -o $(OUTPUT_FOLDER)/idt.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/interrupt.c -o $(OUTPUT_FOLDER)/interrupt.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(KEYBOARD_FOLDER)/keyboard.c -o $(OUTPUT_FOLDER)/keyboard.o @$(LIN) $(LFLAGS) bin/*.o -o $(OUTPUT_FOLDER)/kernel diff --git a/src/filesystem/disk.c b/src/filesystem/disk.c new file mode 100644 index 0000000..cbbd52f --- /dev/null +++ b/src/filesystem/disk.c @@ -0,0 +1,52 @@ +#include "../lib-header/disk.h" +#include "../lib-header/portio.h" + +static void ATA_busy_wait() { + while (in(0x1F7) & ATA_STATUS_BSY); +} + +static void ATA_DRQ_wait() { + while (!(in(0x1F7) & ATA_STATUS_RDY)); +} + +void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count) { + ATA_busy_wait(); + out(0x1F6, 0xE0 | ((logical_block_address >> 24) & 0xF)); + out(0x1F2, block_count); + out(0x1F3, (uint8_t) logical_block_address); + out(0x1F4, (uint8_t) (logical_block_address >> 8)); + out(0x1F5, (uint8_t) (logical_block_address >> 16)); + out(0x1F7, 0x20); + + uint16_t *target = (uint16_t*) ptr; + + for (uint32_t i = 0; i < block_count; i++) { + ATA_busy_wait(); + ATA_DRQ_wait(); + for (uint32_t j = 0; j < HALF_BLOCK_SIZE; j++) + target[j] = in16(0x1F0); + // Note : uint16_t => 2 bytes, HALF_BLOCK_SIZE*2 = BLOCK_SIZE with pointer arithmetic + target += HALF_BLOCK_SIZE; + } +} + +void write_blocks(const void *ptr, uint32_t logical_block_address, uint8_t block_count) { + ATA_busy_wait(); + out(0x1F6, 0xE0 | ((logical_block_address >> 24) & 0xF)); + out(0x1F2, block_count); + out(0x1F3, (uint8_t) logical_block_address); + out(0x1F4, (uint8_t) (logical_block_address >> 8)); + out(0x1F5, (uint8_t) (logical_block_address >> 16)); + out(0x1F7, 0x30); + + for (uint32_t i = 0; i < block_count; i++) { + ATA_busy_wait(); + ATA_DRQ_wait(); + /* Note : uint16_t => 2 bytes, i is current block number to write + HALF_BLOCK_SIZE*i = block_offset with pointer arithmetic + */ + for (uint32_t j = 0; j < HALF_BLOCK_SIZE; j++) + out16(0x1F0, ((uint16_t*) ptr)[HALF_BLOCK_SIZE*i + j]); + } +} + diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index b56e965..8b36c04 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -10,4 +10,136 @@ const uint8_t fs_signature[BLOCK_SIZE] = { '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '2', '0', '2', '3', '\n', [BLOCK_SIZE-2] = 'O', [BLOCK_SIZE-1] = 'k', -}; \ No newline at end of file +}; + +/* -- Driver Interfaces -- */ + +/** + * Convert cluster number to logical block address + * + * @param cluster Cluster number to convert + * @return uint32_t Logical Block Address + */ +uint32_t cluster_to_lba(uint32_t cluster){ + // dr nanobyte -> lba = data_region_begin + (cluster-2)*sector_per_cluster + return (cluster-2) * cluster_size; // ? +} + +/** + * Initialize DirectoryTable value with parent DirectoryEntry and directory name + * + * @param dir_table Pointer to directory table + * @param name 8-byte char for directory name + * @param parent_dir_cluster Parent directory cluster number + */ +void init_directory_table(struct FAT32DirectoryTable *dir_table, char *name, uint32_t parent_dir_cluster){ + +} + +/** + * Checking whether filesystem signature is missing or not in boot sector + * + * @return True if memcmp(boot_sector, fs_signature) returning inequality + */ +bool is_empty_storage(void){ + +} + +/** + * Create new FAT32 file system. Will write fs_signature into boot sector and + * proper FileAllocationTable (contain CLUSTER_0_VALUE, CLUSTER_1_VALUE, + * and initialized root directory) into cluster number 1 + */ +void create_fat32(void){ + +} + +/** + * Initialize file system driver state, if is_empty_storage() then create_fat32() + * Else, read and cache entire FileAllocationTable (located at cluster number 1) into driver state + */ +void initialize_filesystem_fat32(void){ + if (is_empty_storage()){ + create_fat32(); + } else { + // read cluster 1 + // cache FAT + + } +} + +/** + * Write cluster operation, wrapper for write_blocks(). + * Recommended to use struct ClusterBuffer + * + * @param ptr Pointer to source data + * @param cluster_number Cluster number to write + * @param cluster_count Cluster count to write, due limitation of write_blocks block_count 255 => max cluster_count = 63 + */ +void write_clusters(const void *ptr, uint32_t cluster_number, uint8_t cluster_count){ + +} + +/** + * Read cluster operation, wrapper for read_blocks(). + * Recommended to use struct ClusterBuffer + * + * @param ptr Pointer to buffer for reading + * @param cluster_number Cluster number to read + * @param cluster_count Cluster count to read, due limitation of read_blocks block_count 255 => max cluster_count = 63 + */ +void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count){ + +} + + + + + +/* -- CRUD Operation -- */ + +/** + * FAT32 Folder / Directory read + * + * @param request buf point to struct FAT32DirectoryTable, + * name is directory name, + * ext is unused, + * parent_cluster_number is target directory table to read, + * buffer_size must be exactly sizeof(struct FAT32DirectoryTable) + * @return Error code: 0 success - 1 not a folder - 2 not found - -1 unknown + */ +int8_t read_directory(struct FAT32DriverRequest request){ + +} + + +/** + * FAT32 read, read a file from file system. + * + * @param request All attribute will be used for read, buffer_size will limit reading count + * @return Error code: 0 success - 1 not a file - 2 not enough buffer - 3 not found - -1 unknown + */ +int8_t read(struct FAT32DriverRequest request){ + +} + +/** + * FAT32 write, write a file or folder to file system. + * + * @param request All attribute will be used for write, buffer_size == 0 then create a folder / directory + * @return Error code: 0 success - 1 file/folder already exist - 2 invalid parent cluster - -1 unknown + */ +int8_t write(struct FAT32DriverRequest request){ + +} + + +/** + * FAT32 delete, delete a file or empty directory (only 1 DirectoryEntry) in file system. + * + * @param request buf and buffer_size is unused + * @return Error code: 0 success - 1 not found - 2 folder is not empty - -1 unknown + */ +int8_t delete(struct FAT32DriverRequest request){ + +} \ No newline at end of file diff --git a/src/framebuffer.c b/src/framebuffer.c index a50a327..73c23e7 100644 --- a/src/framebuffer.c +++ b/src/framebuffer.c @@ -3,20 +3,47 @@ #include "lib-header/stdmem.h" #include "lib-header/portio.h" + void framebuffer_set_cursor(uint8_t r, uint8_t c) { // TODO : Implement - out(CURSOR_PORT_CMD, 0x0A); - out(CURSOR_PORT_DATA, (in(CURSOR_PORT_DATA) & 0xC0) | r); - - out(CURSOR_PORT_CMD, 0x0B); - out(CURSOR_PORT_DATA, (in(CURSOR_PORT_DATA) & 0xE0) | c); + uint16_t pos = r * MAX_COLS + c; + // out(0x3D4, 0x0F); + // out(0x3D5, (uint8_t)(pos & 0xFF)); + // out(0x3D4, 0x0E); + // out(0x3D5, (uint8_t)((pos >> 8) & 0xFF)); + + out(CURSOR_PORT_CMD, VGA_CURSOR_HIGH); + out(CURSOR_PORT_DATA, (uint8_t)((pos >> 8) & 0xFF)); + out(CURSOR_PORT_CMD, VGA_CURSOR_LOW); + out(CURSOR_PORT_DATA, (uint8_t)(pos & 0xFF)); +} + +struct Cursor framebuffer_get_cursor() { + out(CURSOR_PORT_CMD, VGA_CURSOR_HIGH); + int offset = in(CURSOR_PORT_DATA) << 8; + out(CURSOR_PORT_CMD, VGA_CURSOR_LOW); + offset += in(CURSOR_PORT_DATA); + struct Cursor c = + { + .row = offset / MAX_COLS, + .col = offset % MAX_COLS + }; + return c; +}; + +int framebuffer_get_col(struct Cursor c) { + return c.col; +} + +int framebuffer_get_row(struct Cursor c) { + return c.row; } void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg) { // TODO : Implement - uint16_t attrib = (bg << 4) | (fg & 0x0F); + uint16_t attrib = (bg << 4) | (fg & WHITE); volatile uint16_t * where; - where = (volatile uint16_t *)MEMORY_FRAMEBUFFER + (row * 80 + col) ; + where = (volatile uint16_t *)MEMORY_FRAMEBUFFER + (row * MAX_COLS + col) ; *where = c | (attrib << 8); } @@ -26,8 +53,52 @@ void framebuffer_clear(void) { uint16_t i; volatile uint16_t * where; where = (volatile uint16_t *)MEMORY_FRAMEBUFFER; - for (i = 0; i < 80 * 25; i++) { + for (i = 0; i < MAX_COLS * MAX_ROWS; i++) { *where = space; where++; } } + +void framebuffer_write_string(char * str) { + struct Cursor c = framebuffer_get_cursor(); + int offset = c.row * MAX_COLS + c.col; + int i = 0; + while (str[i] != '\0') { + if (offset >= MAX_COLS * MAX_ROWS) { + offset = framebuffer_scroll_ln(offset); + } + if (str[i] == '\n') { + // offset = (offset / 160 + 1) * 160; + offset = (offset / MAX_COLS + 1) * MAX_COLS; + } else { + // framebuffer_write(offset / 160, offset % 160, str[i], WHITE, BLACK); + framebuffer_write(offset / MAX_COLS, offset % MAX_COLS, str[i], WHITE, BLACK); + offset += 1; + } + i++; + } + framebuffer_set_cursor(offset / MAX_COLS, offset % MAX_COLS); +} + +int framebuffer_scroll_ln(int offset) { + // di enter dulu baru scroll + memcpy( + (void *)MEMORY_FRAMEBUFFER, + (void *)(MEMORY_FRAMEBUFFER + MAX_COLS * 2), + // (void *)(MEMORY_FRAMEBUFFER + MAX_COLS * 2), + // 160 * 2 * 24); + // 2 * MAX_COLS * 2 * (MAX_ROWS - 1)); + MAX_COLS * 2 * (MAX_ROWS + 1)); + + for (int i = 0; i < MAX_COLS; i++) { + framebuffer_write(MAX_ROWS - 1, i, ' ', WHITE, BLACK); + framebuffer_write(MAX_ROWS - 2, i, ' ', WHITE, BLACK); + // framebuffer_write(MAX_ROWS - 3, i, ' ', WHITE, BLACK); + } + + + // return (c.row * MAX_COLS + c.col) - (MAX_ROWS - 1) * MAX_COLS; + // return (c.row * MAX_COLS + c.col) - 2 * MAX_COLS; + return (offset) - 3 * (MAX_COLS); + // return offset - (MAX_ROWS - 1) * MAX_COLS; +} \ No newline at end of file diff --git a/src/interrupt/idt.c b/src/interrupt/idt.c index a1cc543..5dc8635 100644 --- a/src/interrupt/idt.c +++ b/src/interrupt/idt.c @@ -1,6 +1,6 @@ #include "../lib-header/idt.h" -void *isr_stub_table[ISR_STUB_TABLE_LIMIT]; +// void *isr_stub_table[ISR_STUB_TABLE_LIMIT]; struct IDTR _idt_idtr; // global initialization of IDT diff --git a/src/interrupt/interrupt.c b/src/interrupt/interrupt.c index d32346b..8bcdd5b 100644 --- a/src/interrupt/interrupt.c +++ b/src/interrupt/interrupt.c @@ -1,5 +1,7 @@ #include "../lib-header/interrupt.h" +#define IRQ1 33 + void io_wait(void) { out(0x80, 0); } @@ -46,7 +48,19 @@ void main_interrupt_handler ( uint32_t int_number, __attribute__((unused)) struct InterruptStack info ) { - switch (int_number) { - - } -} \ No newline at end of file + switch (int_number+1) { + case IRQ1: + keyboard_isr(); + break; + // case 32: + // framebuffer_write(3, 11, 'b', 0, 0xF); + // break; + + // default : + }; +} + +void activate_keyboard_interrupt(void) { + out(PIC1_DATA, PIC_DISABLE_ALL_MASK ^ (1 << IRQ_KEYBOARD)); + out(PIC2_DATA, PIC_DISABLE_ALL_MASK); +} diff --git a/src/kernel.c b/src/kernel.c index e3859c6..949d55f 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -15,13 +15,16 @@ void kernel_setup(void) { pic_remap(); initialize_idt(); framebuffer_clear(); - framebuffer_write(3, 8, 'H', 0, 0xF); - framebuffer_write(3, 9, 'a', 0, 0xF); - framebuffer_write(3, 10, 'i', 0, 0xF); - framebuffer_write(3, 11, '!', 0, 0xF); - framebuffer_set_cursor(3, 10); + // framebuffer_write(3, 8, 'H', 0, 0xF); + // framebuffer_write(3, 9, 'a', 0, 0xF); + // framebuffer_write(3, 10, 'i', 0, 0xF); + // framebuffer_write(3, 11, '!', 0, 0xF); + framebuffer_set_cursor(0, 0); + framebuffer_write_string("> "); // while (TRUE) b += 1; __asm__("int $0x4"); - while (TRUE); + while (TRUE) { + keyboard_state_activate(); + } } \ No newline at end of file diff --git a/src/keyboard/keyboard.c b/src/keyboard/keyboard.c index c88db52..74f8025 100644 --- a/src/keyboard/keyboard.c +++ b/src/keyboard/keyboard.c @@ -3,6 +3,7 @@ #include "../lib-header/framebuffer.h" #include "../lib-header/stdmem.h" +#define SC_MAX 57 const char keyboard_scancode_1_to_ascii_map[256] = { 0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', @@ -22,3 +23,188 @@ const char keyboard_scancode_1_to_ascii_map[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +// CTRL 29 +// LSHIFT 42 +const char scancode_to_char[] = { + 0, 0x1B, '!', '@', '#', '$', '%', + '^', '&', '*', '(', ')', '_', '+', + 0, 0, 'Q', 'W', 'E', 'R', 'T', + 'Y', 'U', 'I', 'O', 'P', '[', ']', + '?', '?', 'A', 'S', 'D', 'F', 'G', + 'H', 'J', 'K', 'L', ':', '\"', '~', + 0, '\\', 'Z', 'X', 'C', 'V', 'B', + 'N', 'M', '<', '>', '?', 0, 0, + 0, 0 +}; + + +static struct KeyboardDriverState keyboard_state = {.caps_cond = 0} ; +// static int writing = 1; +static int tempCode = 0; +// static int countCode = 0; + +void keyboard_state_activate(void) { + keyboard_state.keyboard_input_on = 1; +} + +void keyboard_state_deactivate(void) { + keyboard_state.keyboard_input_on = 0; +} + +void keyboard_isr(void) { + struct Cursor cursor = framebuffer_get_cursor(); + int row = cursor.row, col = cursor.col; + if (!keyboard_state.keyboard_input_on) { + keyboard_state.buffer_index = 0; + } else { + uint8_t scancode = in(KEYBOARD_DATA_PORT); + if (scancode == 42) { + keyboard_state.shift_pressed = 1; + } + if (scancode == 42 + 0x80) { + keyboard_state.shift_pressed = 0; + } + + if (scancode == 58) { + keyboard_state.caps_cond = !keyboard_state.caps_cond; + } + + char mapped_char; + if (!(keyboard_state.shift_pressed ^ keyboard_state.caps_cond)) { + mapped_char = keyboard_scancode_1_to_ascii_map[scancode]; + } else { + // if () + mapped_char = scancode_to_char[scancode]; + } + // TODO : Implement scancode processing + // if(scancode < 0x80) { + // if(writing == 1) { + // framebuffer_write(row, col, mapped_char, 0xF, 0); + // col++; + // writing = 0; + // } + // } else { + // if(writing == 0) { + // writing = 1; + // } + // } + + if(scancode < 0x80 && scancode != 42 && scancode != 58) { + // countCode++; + // if(scancode != tempCode || countCode > 10) { + if(scancode != tempCode) { + if(mapped_char == '\b' && col != 2) { + framebuffer_set_cursor(row, col - 1); + framebuffer_write(row, col - 1, ' ', 0xF, 0); + keyboard_state.buffer_index--; + keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = ' '; + } else if (mapped_char == '\n') { + execute_cmd(keyboard_state.keyboard_buffer); + memset(keyboard_state.keyboard_buffer, 0, 256); + keyboard_state.buffer_index = 0; + } else if (mapped_char != '\b' && scancode != 75) { + keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = mapped_char; + keyboard_state.buffer_index++; + framebuffer_write(row, col, mapped_char, 0xF, 0); + framebuffer_set_cursor(row, col + 1); + } else if (scancode == 75 && col != 2) { + framebuffer_set_cursor(row, col - 1); + keyboard_state.buffer_index--; + } + // if(scancode != tempCode) { + // countCode = 0; + // } + tempCode = scancode; + if (mapped_char == 27) { + clear_screen(); + } + // framebuffer_set_cursor(row, col); + } + } else { + tempCode = scancode; + // countCode = 0; + } + // if (scancode > 0x80) { + // if (scancode <= (0x39 + 0x80)){ + // keyboard_state.shift_pressed = 0; + // } else if (scancode == (0x1D + 0x80)) { + // keyboard_state.ctrl_pressed = 0; + // } else if (scancode == (0x38 + 0x80)) { + // keyboard_state.alt_pressed = 0; + // } + // framebuffer_write(row, col, keyboard_state.keyboard_buffer[keyboard_state.buffer_index-1], 0xF, 0); + // keyboard_state.buffer_index = 0; + // // col++; + // } + // else if (mapped_char == '\b') { + // if (col == 0) { + // row--; + // col = 15; + // } else { + // keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = ' '; + // keyboard_state.buffer_index--; + // // framebuffer_write(row, col, ' ', 0xF, 0); + // col--; + // } + // } else if (mapped_char != 0 && mapped_char != '\n') { + // keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = mapped_char; + // keyboard_state.buffer_index++; + // // col++; + // } else if (mapped_char == '\n') { + // keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = '\n'; + // row++; + // col = 0; + // } + // if (mapped_char == 'c'){ + // framebuffer_clear(); + // row = 0; col = 0; + // } + // } + // col++; + } + // framebuffer_set_cursor(row, col); + pic_ack(IRQ_KEYBOARD); +} + +// Get keyboard buffer values - @param buf Pointer to char buffer, recommended size at least KEYBOARD_BUFFER_SIZE +void get_keyboard_buffer(char *buf){ + memcpy(buf, keyboard_state.keyboard_buffer, KEYBOARD_BUFFER_SIZE); + // buf = keyboard_state.keyboard_buffer; +} + +// Check whether keyboard ISR is active or not - @return Equal with keyboard_input_on value +bool is_keyboard_blocking(void){ + return keyboard_state.keyboard_input_on; +} + +void clear_screen(){ + framebuffer_clear(); + framebuffer_set_cursor(0, 0); + framebuffer_write_string("> "); + memset(keyboard_state.keyboard_buffer, 0, 256); +} + +int strcmp(char *s1, char *s2) { + int i = 0; + while (s1[i] == s2[i]) { + if (s1[i] == '\0') { + return 0; + } + i++; + } + return s1[i] - s2[i]; +} + +void execute_cmd(char *input) { + if (strcmp(input, "clear") == 0){ + clear_screen(); + framebuffer_set_cursor(0, 0); + framebuffer_write_string("> "); + } else { + framebuffer_write_string("\nCommand not found: "); + framebuffer_write_string(input); + struct Cursor c = framebuffer_get_cursor(); + framebuffer_set_cursor(c.row + 1, 0); + framebuffer_write_string("> "); + } +} \ No newline at end of file diff --git a/src/lib-header/framebuffer.h b/src/lib-header/framebuffer.h index 1e6786e..5369030 100644 --- a/src/lib-header/framebuffer.h +++ b/src/lib-header/framebuffer.h @@ -6,6 +6,19 @@ #define MEMORY_FRAMEBUFFER (uint8_t *) 0xB8000 #define CURSOR_PORT_CMD 0x03D4 #define CURSOR_PORT_DATA 0x03D5 +#define VGA_CURSOR_HIGH 0x0E +#define VGA_CURSOR_LOW 0x0F + +#define MAX_ROWS 25 +#define MAX_COLS 80 +#define WHITE 0x0F +#define BLACK 0x00 + +struct Cursor { + uint8_t row; + uint8_t col; +} __attribute((packed)); + /** * Terminal framebuffer @@ -35,6 +48,14 @@ void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg) */ void framebuffer_set_cursor(uint8_t r, uint8_t c); +/** + * Get cursor location + * + * @param r row + * @param c column +*/ +struct Cursor framebuffer_get_cursor(); + /** * Set all cell in framebuffer character to 0x00 (empty character) * and color to 0x07 (gray character & black background) @@ -42,4 +63,15 @@ void framebuffer_set_cursor(uint8_t r, uint8_t c); */ void framebuffer_clear(void); +/** + * @param char* str + * Write string to framebuffer + */ +void framebuffer_write_string(char *str); + +/** + * @param int offset + * Scroll framebuffer by offset lines + */ +int framebuffer_scroll_ln(int offset); #endif \ No newline at end of file diff --git a/src/lib-header/interrupt.h b/src/lib-header/interrupt.h index e746673..e253bae 100644 --- a/src/lib-header/interrupt.h +++ b/src/lib-header/interrupt.h @@ -3,6 +3,8 @@ #include "stdtype.h" #include "portio.h" +#include "keyboard.h" +#include "framebuffer.h" /* -- PIC constants -- */ diff --git a/src/lib-header/keyboard.h b/src/lib-header/keyboard.h index f29c4a8..4976f30 100644 --- a/src/lib-header/keyboard.h +++ b/src/lib-header/keyboard.h @@ -1,8 +1,9 @@ #ifndef _USER_ISR_H #define _USER_ISR_H -#include "interrupt.h" #include "stdtype.h" +#include "interrupt.h" +#include "portio.h" #define EXT_SCANCODE_UP 0x48 #define EXT_SCANCODE_DOWN 0x50 @@ -33,6 +34,10 @@ extern const char keyboard_scancode_1_to_ascii_map[256]; struct KeyboardDriverState { bool read_extended_mode; bool keyboard_input_on; + bool shift_pressed; + bool ctrl_pressed; + bool alt_pressed; + bool caps_cond; uint8_t buffer_index; char keyboard_buffer[KEYBOARD_BUFFER_SIZE]; } __attribute((packed)); @@ -71,4 +76,35 @@ bool is_keyboard_blocking(void); */ void keyboard_isr(void); +/** + * @brief Clear screen + * + * + */ +void clear_screen(); + +/** + * @brief Print string to screen + * + * @param char* s + * @param int n + */ +void append(char s[], char n); + +/** + * @brief Compare two string + * + * @param s1 + * @param s2 + * @return int + */ +int strcmp(char s1[], char s2[]); + +/** + * @brief Execute command from input + * + * @param char* input + */ +void execute_cmd(char *input); + #endif \ No newline at end of file diff --git a/src/lib-header/portio.h b/src/lib-header/portio.h index c4080b9..ed32fb3 100644 --- a/src/lib-header/portio.h +++ b/src/lib-header/portio.h @@ -1,7 +1,7 @@ #ifndef _PORTIO_H #define _PORTIO_H -#include "lib-header/stdtype.h" +#include "stdtype.h" /** out: * Sends the given data to the given I/O port diff --git a/src/lib-header/stdmem.h b/src/lib-header/stdmem.h index a95fce1..70b0f01 100644 --- a/src/lib-header/stdmem.h +++ b/src/lib-header/stdmem.h @@ -3,6 +3,8 @@ #include "lib-header/stdtype.h" +#define VIDEO_ADDR 0xB8000 + /** * C standard memset, check man memset or * https://man7.org/linux/man-pages/man3/memset.3.html for more details From aa8f85fcf03148577faeff12797e3842752e6c13 Mon Sep 17 00:00:00 2001 From: Rava Maulana Date: Mon, 27 Mar 2023 11:19:07 +0700 Subject: [PATCH 14/52] update hanif --- src/filesystem/fat32.c | 9 +- src/interrupt/idt.c | 61 ++++++------- src/keyboard/keyboard.c | 194 ++++++++++++++++++++-------------------- src/lib-header/idt.h | 58 ++++++------ 4 files changed, 164 insertions(+), 158 deletions(-) diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index 8b36c04..5907a3d 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -22,7 +22,7 @@ const uint8_t fs_signature[BLOCK_SIZE] = { */ uint32_t cluster_to_lba(uint32_t cluster){ // dr nanobyte -> lba = data_region_begin + (cluster-2)*sector_per_cluster - return (cluster-2) * cluster_size; // ? + // return (cluster-2) * cluster_size; // ? } /** @@ -98,8 +98,10 @@ void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count){ /* -- CRUD Operation -- */ -/** - * FAT32 Folder / Directory read +/* + * @brief + * + * * * @param request buf point to struct FAT32DirectoryTable, * name is directory name, @@ -108,6 +110,7 @@ void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count){ * buffer_size must be exactly sizeof(struct FAT32DirectoryTable) * @return Error code: 0 success - 1 not a folder - 2 not found - -1 unknown */ + int8_t read_directory(struct FAT32DriverRequest request){ } diff --git a/src/interrupt/idt.c b/src/interrupt/idt.c index 5dc8635..41fb089 100644 --- a/src/interrupt/idt.c +++ b/src/interrupt/idt.c @@ -2,6 +2,7 @@ // void *isr_stub_table[ISR_STUB_TABLE_LIMIT]; struct IDTR _idt_idtr; +struct InterruptDescriptorTable interrupt_descriptor_table; // global initialization of IDT // = { @@ -9,42 +10,42 @@ struct IDTR _idt_idtr; // .address = (void *) & interrupt_descriptor_table.table[0] //}; -void initialize_idt(void){ - /* TODO : +void initialize_idt(void) { + /* TODO : * Iterate all isr_stub_table, * Set all IDT entry with set_interrupt_gate() * with following values: * Vector: i - * Handler Address: isr_stub_table[i] + * Handler Address: isr_stub_table[i] * Segment: GDT_KERNEL_CODE_SEGMENT_SELECTOR * Privilege: 0 - */ - // Local initialization of IDT - _idt_idtr.size = sizeof(interrupt_descriptor_table) - 1; - _idt_idtr.address = (void *) & interrupt_descriptor_table.table[0]; - - - for (uint8_t i = 0; i < ISR_STUB_TABLE_LIMIT; i++) { - set_interrupt_gate(i, isr_stub_table[i], GDT_KERNEL_CODE_SEGMENT_SELECTOR, 0); - } - __asm__ volatile("lidt %0" : : "m"(_idt_idtr)); - __asm__ volatile("sti"); - + */ + // Local initialization of IDT + _idt_idtr.size = sizeof(interrupt_descriptor_table) - 1; + _idt_idtr.address = (void *)&interrupt_descriptor_table.table[0]; + + for (uint8_t i = 0; i < ISR_STUB_TABLE_LIMIT; i++) { + set_interrupt_gate(i, isr_stub_table[i], GDT_KERNEL_CODE_SEGMENT_SELECTOR, + 0); + } + __asm__ volatile("lidt %0" : : "m"(_idt_idtr)); + __asm__ volatile("sti"); } -void set_interrupt_gate(uint8_t int_vector, void *handler_address, uint16_t gdt_seg_selector, uint8_t privilege) { - struct IDTGate *idt_int_gate = &interrupt_descriptor_table.table[int_vector]; - // TODO : Set handler offset, privilege & segment - idt_int_gate->offset_low = (uint32_t)handler_address & 0xFFFF; - idt_int_gate->segment = gdt_seg_selector; - idt_int_gate->_reserved = 0b0; - idt_int_gate->dpl = privilege; - idt_int_gate->offset_high = (uint32_t)handler_address >> 16; - - // Target system 32-bit and flag this as valid interrupt gate - idt_int_gate->_r_bit_1 = INTERRUPT_GATE_R_BIT_1; - idt_int_gate->_r_bit_2 = INTERRUPT_GATE_R_BIT_2; - idt_int_gate->_r_bit_3 = INTERRUPT_GATE_R_BIT_3; - idt_int_gate->gate_32 = 1; - idt_int_gate->valid_bit = 1; +void set_interrupt_gate(uint8_t int_vector, void *handler_address, + uint16_t gdt_seg_selector, uint8_t privilege) { + struct IDTGate *idt_int_gate = &interrupt_descriptor_table.table[int_vector]; + // TODO : Set handler offset, privilege & segment + idt_int_gate->offset_low = (uint32_t)handler_address & 0xFFFF; + idt_int_gate->segment = gdt_seg_selector; + idt_int_gate->_reserved = 0b0; + idt_int_gate->dpl = privilege; + idt_int_gate->offset_high = (uint32_t)handler_address >> 16; + + // Target system 32-bit and flag this as valid interrupt gate + idt_int_gate->_r_bit_1 = INTERRUPT_GATE_R_BIT_1; + idt_int_gate->_r_bit_2 = INTERRUPT_GATE_R_BIT_2; + idt_int_gate->_r_bit_3 = INTERRUPT_GATE_R_BIT_3; + idt_int_gate->gate_32 = 1; + idt_int_gate->valid_bit = 1; } diff --git a/src/keyboard/keyboard.c b/src/keyboard/keyboard.c index 74f8025..adafe52 100644 --- a/src/keyboard/keyboard.c +++ b/src/keyboard/keyboard.c @@ -1,55 +1,50 @@ #include "../lib-header/keyboard.h" -#include "../lib-header/portio.h" + #include "../lib-header/framebuffer.h" +#include "../lib-header/portio.h" #include "../lib-header/stdmem.h" #define SC_MAX 57 const char keyboard_scancode_1_to_ascii_map[256] = { - 0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', - 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', - 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', - 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', + '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', + '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', + ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', + '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, + '+', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; // CTRL 29 // LSHIFT 42 const char scancode_to_char[] = { - 0, 0x1B, '!', '@', '#', '$', '%', - '^', '&', '*', '(', ')', '_', '+', - 0, 0, 'Q', 'W', 'E', 'R', 'T', - 'Y', 'U', 'I', 'O', 'P', '[', ']', - '?', '?', 'A', 'S', 'D', 'F', 'G', - 'H', 'J', 'K', 'L', ':', '\"', '~', - 0, '\\', 'Z', 'X', 'C', 'V', 'B', - 'N', 'M', '<', '>', '?', 0, 0, - 0, 0 -}; - + 0, 0x1B, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', + '_', '+', 0, 0, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', 'H', + 'J', 'K', 'L', ':', '\"', '~', 0, '\\', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', 0, 0, 0, 0}; -static struct KeyboardDriverState keyboard_state = {.caps_cond = 0} ; +static struct KeyboardDriverState keyboard_state = {.caps_cond = 0}; // static int writing = 1; static int tempCode = 0; // static int countCode = 0; -void keyboard_state_activate(void) { - keyboard_state.keyboard_input_on = 1; -} +void keyboard_state_activate(void) { keyboard_state.keyboard_input_on = 1; } -void keyboard_state_deactivate(void) { - keyboard_state.keyboard_input_on = 0; -} +void keyboard_state_deactivate(void) { keyboard_state.keyboard_input_on = 0; } void keyboard_isr(void) { struct Cursor cursor = framebuffer_get_cursor(); @@ -89,13 +84,18 @@ void keyboard_isr(void) { // } // } - if(scancode < 0x80 && scancode != 42 && scancode != 58) { + if (scancode < 0x80 && scancode != 42 && scancode != 58) { // countCode++; // if(scancode != tempCode || countCode > 10) { - if(scancode != tempCode) { - if(mapped_char == '\b' && col != 2) { - framebuffer_set_cursor(row, col - 1); - framebuffer_write(row, col - 1, ' ', 0xF, 0); + if (scancode != tempCode) { + if (mapped_char == '\b' && keyboard_state.buffer_index > 0) { + if (col == 0) { + framebuffer_set_cursor(row - 1, MAX_COLS - 1); + framebuffer_write(row - 1, MAX_COLS - 1, ' ', WHITE, BLACK); + } else { + framebuffer_set_cursor(row, col - 1); + framebuffer_write(row, col - 1, ' ', WHITE, BLACK); + } keyboard_state.buffer_index--; keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = ' '; } else if (mapped_char == '\n') { @@ -103,7 +103,8 @@ void keyboard_isr(void) { memset(keyboard_state.keyboard_buffer, 0, 256); keyboard_state.buffer_index = 0; } else if (mapped_char != '\b' && scancode != 75) { - keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = mapped_char; + keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = + mapped_char; keyboard_state.buffer_index++; framebuffer_write(row, col, mapped_char, 0xF, 0); framebuffer_set_cursor(row, col + 1); @@ -116,7 +117,7 @@ void keyboard_isr(void) { // } tempCode = scancode; if (mapped_char == 27) { - clear_screen(); + clear_screen(); } // framebuffer_set_cursor(row, col); } @@ -132,52 +133,53 @@ void keyboard_isr(void) { // } else if (scancode == (0x38 + 0x80)) { // keyboard_state.alt_pressed = 0; // } - // framebuffer_write(row, col, keyboard_state.keyboard_buffer[keyboard_state.buffer_index-1], 0xF, 0); - // keyboard_state.buffer_index = 0; - // // col++; - // } - // else if (mapped_char == '\b') { - // if (col == 0) { - // row--; - // col = 15; - // } else { - // keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = ' '; - // keyboard_state.buffer_index--; - // // framebuffer_write(row, col, ' ', 0xF, 0); - // col--; - // } - // } else if (mapped_char != 0 && mapped_char != '\n') { - // keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = mapped_char; - // keyboard_state.buffer_index++; - // // col++; - // } else if (mapped_char == '\n') { - // keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = '\n'; - // row++; - // col = 0; - // } - // if (mapped_char == 'c'){ - // framebuffer_clear(); - // row = 0; col = 0; - // } - // } - // col++; + // framebuffer_write(row, col, + // keyboard_state.keyboard_buffer[keyboard_state.buffer_index-1], 0xF, 0); + // keyboard_state.buffer_index = 0; + // // col++; + // } + // else if (mapped_char == '\b') { + // if (col == 0) { + // row--; + // col = 15; + // } else { + // keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = ' '; + // keyboard_state.buffer_index--; + // // framebuffer_write(row, col, ' ', 0xF, 0); + // col--; + // } + // } else if (mapped_char != 0 && mapped_char != '\n') { + // keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = + // mapped_char; keyboard_state.buffer_index++; + // // col++; + // } else if (mapped_char == '\n') { + // keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = '\n'; + // row++; + // col = 0; + // } + // if (mapped_char == 'c'){ + // framebuffer_clear(); + // row = 0; col = 0; + // } + // } + // col++; } // framebuffer_set_cursor(row, col); pic_ack(IRQ_KEYBOARD); } -// Get keyboard buffer values - @param buf Pointer to char buffer, recommended size at least KEYBOARD_BUFFER_SIZE -void get_keyboard_buffer(char *buf){ +// Get keyboard buffer values - @param buf Pointer to char buffer, recommended +// size at least KEYBOARD_BUFFER_SIZE +void get_keyboard_buffer(char *buf) { memcpy(buf, keyboard_state.keyboard_buffer, KEYBOARD_BUFFER_SIZE); // buf = keyboard_state.keyboard_buffer; } -// Check whether keyboard ISR is active or not - @return Equal with keyboard_input_on value -bool is_keyboard_blocking(void){ - return keyboard_state.keyboard_input_on; -} +// Check whether keyboard ISR is active or not - @return Equal with +// keyboard_input_on value +bool is_keyboard_blocking(void) { return keyboard_state.keyboard_input_on; } -void clear_screen(){ +void clear_screen() { framebuffer_clear(); framebuffer_set_cursor(0, 0); framebuffer_write_string("> "); @@ -185,26 +187,26 @@ void clear_screen(){ } int strcmp(char *s1, char *s2) { - int i = 0; - while (s1[i] == s2[i]) { - if (s1[i] == '\0') { - return 0; - } - i++; + int i = 0; + while (s1[i] == s2[i]) { + if (s1[i] == '\0') { + return 0; } - return s1[i] - s2[i]; + i++; + } + return s1[i] - s2[i]; } void execute_cmd(char *input) { - if (strcmp(input, "clear") == 0){ - clear_screen(); - framebuffer_set_cursor(0, 0); - framebuffer_write_string("> "); - } else { - framebuffer_write_string("\nCommand not found: "); - framebuffer_write_string(input); - struct Cursor c = framebuffer_get_cursor(); - framebuffer_set_cursor(c.row + 1, 0); - framebuffer_write_string("> "); - } + if (strcmp(input, "clear") == 0) { + clear_screen(); + framebuffer_set_cursor(0, 0); + framebuffer_write_string("> "); + } else { + framebuffer_write_string("\nCommand not found: "); + framebuffer_write_string(input); + struct Cursor c = framebuffer_get_cursor(); + framebuffer_set_cursor(c.row + 1, 0); + framebuffer_write_string("> "); + } } \ No newline at end of file diff --git a/src/lib-header/idt.h b/src/lib-header/idt.h index 107b39e..c76d251 100644 --- a/src/lib-header/idt.h +++ b/src/lib-header/idt.h @@ -5,8 +5,8 @@ // #include // IDT hard limit, see Intel x86 manual 3a - 6.10 Interrupt Descriptor Table -#define IDT_MAX_ENTRY_COUNT 256 -#define ISR_STUB_TABLE_LIMIT 64 +#define IDT_MAX_ENTRY_COUNT 256 +#define ISR_STUB_TABLE_LIMIT 64 #define INTERRUPT_GATE_R_BIT_1 0b000 #define INTERRUPT_GATE_R_BIT_2 0b110 #define INTERRUPT_GATE_R_BIT_3 0b0 @@ -15,8 +15,8 @@ #define GDT_KERNEL_CODE_SEGMENT_SELECTOR 0x8 #define GDT_KERNEL_DATA_SEGMENT_SELECTOR 0x10 - -// Interrupt Handler / ISR stub for reducing code duplication, this array can be iterated in initialize_idt() +// Interrupt Handler / ISR stub for reducing code duplication, this array can be +// iterated in initialize_idt() extern void *isr_stub_table[ISR_STUB_TABLE_LIMIT]; extern struct IDTR _idt_idtr; @@ -30,28 +30,29 @@ extern struct IDTR _idt_idtr; * @param _reserved Reserved bit, bit length: 5 * @param _r_bit_1 Reserved for idtgate type, bit length: 3 * @param _r_bit_2 Reserved for idtgate type, bit length: 3 - * @param gate_32 Is this gate size 32-bit? If not then its 16-bit gate + * @param gate_32 Is this gate size 32-bit? If not then its 16-bit + gate * @param _r_bit_3 Reserved for idtgate type, bit length: 1 * @param dpl Descriptor Privilege Level, bit length: 2 * @param valid_bit Segment present flag, bit length: 1 * @param offset_high Higher 16-bit offset * ... - + */ struct IDTGate { - // First 32-bit (Bit 0 to 31) - uint16_t offset_low; - uint16_t segment; - - // TODO : Implement - uint8_t _reserved : 5; - uint8_t _r_bit_1 : 3; - uint8_t _r_bit_2 : 3; - uint8_t gate_32 : 1; - uint8_t _r_bit_3 : 1; - uint8_t dpl : 2; - uint8_t valid_bit : 1; - uint16_t offset_high; + // First 32-bit (Bit 0 to 31) + uint16_t offset_low; + uint16_t segment; + + // TODO : Implement + uint8_t _reserved : 5; + uint8_t _r_bit_1 : 3; + uint8_t _r_bit_2 : 3; + uint8_t gate_32 : 1; + uint8_t _r_bit_3 : 1; + uint8_t dpl : 2; + uint8_t valid_bit : 1; + uint16_t offset_high; } __attribute__((packed)); @@ -64,9 +65,8 @@ struct IDTGate { // TODO : Implement // ... struct InterruptDescriptorTable { - struct IDTGate table [IDT_MAX_ENTRY_COUNT]; -} __attribute__((packed)) interrupt_descriptor_table; - + struct IDTGate table[IDT_MAX_ENTRY_COUNT]; +} __attribute__((packed)); /** * IDTR, carrying information where's the IDT located and size. @@ -78,22 +78,22 @@ struct InterruptDescriptorTable { // TODO : Implement // ... struct IDTR { - uint16_t size; - struct InterruptDescriptorTable *address; + uint16_t size; + struct InterruptDescriptorTable *address; } __attribute__((packed)); - /** * Set IDTGate with proper interrupt handler values. * Will directly edit global IDT variable and set values properly - * + * * @param int_vector Interrupt vector to handle * @param handler_address Interrupt handler address - * @param gdt_seg_selector GDT segment selector, for kernel use GDT_KERNEL_CODE_SEGMENT_SELECTOR + * @param gdt_seg_selector GDT segment selector, for kernel use + * GDT_KERNEL_CODE_SEGMENT_SELECTOR * @param privilege Descriptor privilege level */ -void set_interrupt_gate(uint8_t int_vector, void *handler_address, uint16_t gdt_seg_selector, uint8_t privilege); - +void set_interrupt_gate(uint8_t int_vector, void *handler_address, + uint16_t gdt_seg_selector, uint8_t privilege); /** * Set IDT with proper values and load with lidt From 3d2a7dbb1004334419c531aa7bd1f778af48eef0 Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Mon, 27 Mar 2023 14:21:15 +0700 Subject: [PATCH 15/52] Update FAT32 Co-authored-by: razzanYoni Co-authored-by: Nigel Sahl Co-authored-by: Rava Maulana --- src/filesystem/disk.c | 4 +-- src/filesystem/fat32.c | 62 ++++++++++++++++++++++++++++++++++++------ src/lib-header/fat32.h | 2 +- 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/filesystem/disk.c b/src/filesystem/disk.c index cbbd52f..c7112ad 100644 --- a/src/filesystem/disk.c +++ b/src/filesystem/disk.c @@ -24,7 +24,7 @@ void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count) ATA_busy_wait(); ATA_DRQ_wait(); for (uint32_t j = 0; j < HALF_BLOCK_SIZE; j++) - target[j] = in16(0x1F0); + target[j] = in(0x1F0); // Note : uint16_t => 2 bytes, HALF_BLOCK_SIZE*2 = BLOCK_SIZE with pointer arithmetic target += HALF_BLOCK_SIZE; } @@ -46,7 +46,7 @@ void write_blocks(const void *ptr, uint32_t logical_block_address, uint8_t block HALF_BLOCK_SIZE*i = block_offset with pointer arithmetic */ for (uint32_t j = 0; j < HALF_BLOCK_SIZE; j++) - out16(0x1F0, ((uint16_t*) ptr)[HALF_BLOCK_SIZE*i + j]); + out(0x1F0, ((uint16_t*) ptr)[HALF_BLOCK_SIZE*i + j]); } } diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index 5907a3d..b482df4 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -22,8 +22,8 @@ const uint8_t fs_signature[BLOCK_SIZE] = { */ uint32_t cluster_to_lba(uint32_t cluster){ // dr nanobyte -> lba = data_region_begin + (cluster-2)*sector_per_cluster - // return (cluster-2) * cluster_size; // ? -} + return cluster * CLUSTER_BLOCK_COUNT; +} /** * Initialize DirectoryTable value with parent DirectoryEntry and directory name @@ -33,7 +33,25 @@ uint32_t cluster_to_lba(uint32_t cluster){ * @param parent_dir_cluster Parent directory cluster number */ void init_directory_table(struct FAT32DirectoryTable *dir_table, char *name, uint32_t parent_dir_cluster){ + struct FAT32DirectoryEntry *new_entry = &(dir_table->table[0]); + memcpy(&(new_entry->name), name, 8); + new_entry->attribute = ATTR_SUBDIRECTORY; + new_entry->user_attribute = UATTR_NOT_EMPTY; + + new_entry->undelete = 0; + new_entry->create_time = 0; + new_entry->create_date = 0; + new_entry->access_date = 0; + + new_entry->cluster_high = (uint16_t)(parent_dir_cluster >> 16); + + new_entry->modified_time = 0; + new_entry->modified_date = 0; + new_entry->cluster_low = (uint16_t)(parent_dir_cluster & 0xFFFF); + new_entry->filesize = 0; + + dir_table->table[0]; } /** @@ -42,7 +60,8 @@ void init_directory_table(struct FAT32DirectoryTable *dir_table, char *name, uin * @return True if memcmp(boot_sector, fs_signature) returning inequality */ bool is_empty_storage(void){ - + // read boot sector + return memsmp(BOOT_SECTOR, fs_signature, BLOCK_SIZE) != 0; } /** @@ -51,11 +70,19 @@ bool is_empty_storage(void){ * and initialized root directory) into cluster number 1 */ void create_fat32(void){ - + // write fs_signature into boot sector + // write_blocks(fs_signature, BOOT_SECTOR, 1); + // write FAT into cluster number 1 + // write_blocks(); + // write root directory into cluster number 2 } /** - * Initialize file system driver state, if is_empty_storage() then create_fat32() + * @brief + * + * + * + * * Else, read and cache entire FileAllocationTable (located at cluster number 1) into driver state */ void initialize_filesystem_fat32(void){ @@ -63,8 +90,9 @@ void initialize_filesystem_fat32(void){ create_fat32(); } else { // read cluster 1 - // cache FAT - + read_clusters(BOOT_SECTOR, fs_signature, 1); + // read FAT + // read root directory } } @@ -123,7 +151,23 @@ int8_t read_directory(struct FAT32DriverRequest request){ * @return Error code: 0 success - 1 not a file - 2 not enough buffer - 3 not found - -1 unknown */ int8_t read(struct FAT32DriverRequest request){ - + // for (uint32_t i = 0; i < request.buffer_size; i++){ + // if (memcmp(request.name, request.buf[i].name, 8) == 0){ + // // found + // if (request.buf[i].attributes & FAT32_ATTR_DIRECTORY){ + // // not a file + // return 1; + // } else { + // // read file + // read_clusters(request.buf, request.buf[i].first_cluster, request.buffer_size); + // return 0; + // } + // } + // else { + // // not found + // return 3; + // } + // } } /** @@ -144,5 +188,5 @@ int8_t write(struct FAT32DriverRequest request){ * @return Error code: 0 success - 1 not found - 2 folder is not empty - -1 unknown */ int8_t delete(struct FAT32DriverRequest request){ - + } \ No newline at end of file diff --git a/src/lib-header/fat32.h b/src/lib-header/fat32.h index ee445e3..227db4c 100644 --- a/src/lib-header/fat32.h +++ b/src/lib-header/fat32.h @@ -26,7 +26,7 @@ // EOF also double as valid cluster / "this is last valid cluster in the chain" #define FAT32_FAT_END_OF_FILE 0x0FFFFFFF -#define FAT32_FAT_EMPTY_ENTRY 0x00000000 +#define FAT32_FAT_EMPTY_ENTRY 0x00000000 #define FAT_CLUSTER_NUMBER 1 #define ROOT_CLUSTER_NUMBER 2 From 6a4d34a737cc757da59babd83a03964cf4771c9d Mon Sep 17 00:00:00 2001 From: Razzan <13521087@mahasiswa.itb.ac.id> Date: Mon, 27 Mar 2023 23:44:39 +0700 Subject: [PATCH 16/52] fix makefile --- .vscode/tasks.json | 4 ++-- makefile | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 2e633d6..a4cba81 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -19,11 +19,11 @@ { "type": "shell", "label": "Launch QEMU", - "command": "echo Starting QEMU&qemu-system-i386 -s -S -cdrom os2023.iso", + "command": "make run", "isBackground": true, "dependsOn": "Build OS", "options": { - "cwd": "${workspaceFolder}/bin" + "cwd": "${workspaceFolder}" }, "problemMatcher": { "pattern": [ diff --git a/makefile b/makefile index e24b4a1..4d26c63 100644 --- a/makefile +++ b/makefile @@ -11,6 +11,8 @@ FILESYSTEM_FOLDER = filesystem # SOURCE_FOLDER = bin/iso/boot/grub OUTPUT_FOLDER = bin ISO_NAME = OSyikkk +# DISK +DISK_NAME = storage # Flags WARNING_CFLAG = -Wall -Wextra -Werror @@ -22,11 +24,13 @@ LFLAGS = -T $(SOURCE_FOLDER)/linker.ld -melf_i386 run: all - @qemu-system-i386 -s -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso + @qemu-system-i386 -s -S -drive file=$(OUTPUT_FOLDER)/$(DISK_NAME).bin,format=raw,if=ide,index=0,media=disk -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso all: build build: iso clean: rm -rf *.o *.iso $(OUTPUT_FOLDER)/kernel +disk: + @qemu-img create -f raw $(OUTPUT_FOLDER)/$(DISK_NAME).bin 4M From 6d8818965a9d7ef5ccf5d0de469094e9865fda8e Mon Sep 17 00:00:00 2001 From: Razzan <13521087@mahasiswa.itb.ac.id> Date: Tue, 28 Mar 2023 00:47:18 +0700 Subject: [PATCH 17/52] fix : makefile --- makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/makefile b/makefile index 4d26c63..f3953a4 100644 --- a/makefile +++ b/makefile @@ -24,7 +24,13 @@ LFLAGS = -T $(SOURCE_FOLDER)/linker.ld -melf_i386 run: all + # @qemu-system-i386 -s -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso + + # debug mode @qemu-system-i386 -s -S -drive file=$(OUTPUT_FOLDER)/$(DISK_NAME).bin,format=raw,if=ide,index=0,media=disk -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso + + # normal mode + # @qemu-system-i386 -s -drive file=$(OUTPUT_FOLDER)/$(DISK_NAME).bin,format=raw,if=ide,index=0,media=disk -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso all: build build: iso clean: @@ -33,7 +39,6 @@ disk: @qemu-img create -f raw $(OUTPUT_FOLDER)/$(DISK_NAME).bin 4M - kernel: @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/kernel_loader.s -o $(OUTPUT_FOLDER)/kernel_loader.o @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/intsetup.s -o $(OUTPUT_FOLDER)/intsetup.o From f9ed853012235e36488f9d6111cd605a59211520 Mon Sep 17 00:00:00 2001 From: Razzan <13521087@mahasiswa.itb.ac.id> Date: Tue, 28 Mar 2023 05:03:48 +0700 Subject: [PATCH 18/52] balikin disk ke uint_16 --- .vscode/tasks.json | 2 +- makefile | 11 +++++------ src/filesystem/disk.c | 4 ++-- src/kernel.c | 3 +++ src/lib-header/portio.h | 3 +++ src/portio.c | 18 ++++++++++++++++++ 6 files changed, 32 insertions(+), 9 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a4cba81..9617140 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -19,7 +19,7 @@ { "type": "shell", "label": "Launch QEMU", - "command": "make run", + "command": "make debug", "isBackground": true, "dependsOn": "Build OS", "options": { diff --git a/makefile b/makefile index f3953a4..75bb501 100644 --- a/makefile +++ b/makefile @@ -25,12 +25,9 @@ LFLAGS = -T $(SOURCE_FOLDER)/linker.ld -melf_i386 run: all # @qemu-system-i386 -s -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso - - # debug mode + @qemu-system-i386 -s -drive file=$(OUTPUT_FOLDER)/$(DISK_NAME).bin,format=raw,if=ide,index=0,media=disk -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso +debug: all @qemu-system-i386 -s -S -drive file=$(OUTPUT_FOLDER)/$(DISK_NAME).bin,format=raw,if=ide,index=0,media=disk -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso - - # normal mode - # @qemu-system-i386 -s -drive file=$(OUTPUT_FOLDER)/$(DISK_NAME).bin,format=raw,if=ide,index=0,media=disk -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso all: build build: iso clean: @@ -52,6 +49,8 @@ kernel: @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/idt.c -o $(OUTPUT_FOLDER)/idt.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/interrupt.c -o $(OUTPUT_FOLDER)/interrupt.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(KEYBOARD_FOLDER)/keyboard.c -o $(OUTPUT_FOLDER)/keyboard.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/disk.c -o $(OUTPUT_FOLDER)/disk.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/fat32.c -o $(OUTPUT_FOLDER)/fat32.o @$(LIN) $(LFLAGS) bin/*.o -o $(OUTPUT_FOLDER)/kernel @@ -59,7 +58,7 @@ kernel: @rm -f *.o iso: kernel - @rm -r $(OUTPUT_FOLDER)/iso/ + # @rm -r $(OUTPUT_FOLDER)/iso/ @mkdir -p $(OUTPUT_FOLDER)/iso/boot/grub @cp $(OUTPUT_FOLDER)/kernel $(OUTPUT_FOLDER)/iso/boot/ @cp other/grub1 $(OUTPUT_FOLDER)/iso/boot/grub/ diff --git a/src/filesystem/disk.c b/src/filesystem/disk.c index c7112ad..cbbd52f 100644 --- a/src/filesystem/disk.c +++ b/src/filesystem/disk.c @@ -24,7 +24,7 @@ void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count) ATA_busy_wait(); ATA_DRQ_wait(); for (uint32_t j = 0; j < HALF_BLOCK_SIZE; j++) - target[j] = in(0x1F0); + target[j] = in16(0x1F0); // Note : uint16_t => 2 bytes, HALF_BLOCK_SIZE*2 = BLOCK_SIZE with pointer arithmetic target += HALF_BLOCK_SIZE; } @@ -46,7 +46,7 @@ void write_blocks(const void *ptr, uint32_t logical_block_address, uint8_t block HALF_BLOCK_SIZE*i = block_offset with pointer arithmetic */ for (uint32_t j = 0; j < HALF_BLOCK_SIZE; j++) - out(0x1F0, ((uint16_t*) ptr)[HALF_BLOCK_SIZE*i + j]); + out16(0x1F0, ((uint16_t*) ptr)[HALF_BLOCK_SIZE*i + j]); } } diff --git a/src/kernel.c b/src/kernel.c index 949d55f..7ee5efa 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -6,6 +6,9 @@ #include "lib-header/kernel_loader.h" #include "lib-header/idt.h" #include "lib-header/interrupt.h" +#include "lib-header/keyboard.h" +#include "lib-header/disk.h" +#include "lib-header/fat32.h" void kernel_setup(void) { // uint32_t a; diff --git a/src/lib-header/portio.h b/src/lib-header/portio.h index ed32fb3..dc3ed3f 100644 --- a/src/lib-header/portio.h +++ b/src/lib-header/portio.h @@ -19,4 +19,7 @@ void out(uint16_t port, uint8_t data); */ uint8_t in(uint16_t port); +uint16_t in16(uint16_t port); + +void out16(uint16_t port, uint16_t data); #endif \ No newline at end of file diff --git a/src/portio.c b/src/portio.c index 5f51edd..c281831 100644 --- a/src/portio.c +++ b/src/portio.c @@ -22,4 +22,22 @@ uint8_t in(uint16_t port) { : "Nd"(port) ); return result; +} + +uint16_t in16(uint16_t port) { + uint16_t result; + __asm__ volatile( + "inw %1, %0" + : "=a" (result) + : "Nd" (port) + ); + return result; +} + +void out16(uint16_t port, uint16_t data) { + __asm__ volatile( + "outw %0, %1" + : // + : "a"(data), "Nd"(port) + ); } \ No newline at end of file From 164a2f782c8267aa8545437bfbb7235c7688d977 Mon Sep 17 00:00:00 2001 From: Razzan <13521087@mahasiswa.itb.ac.id> Date: Tue, 28 Mar 2023 13:00:52 +0700 Subject: [PATCH 19/52] fix : biar bisa ke-load --- src/filesystem/fat32.c | 64 +++++++++++++++++++++++++++--------------- src/kernel.c | 1 + src/kernel_loader.s | 8 ++++-- src/linker.ld | 5 ++++ 4 files changed, 52 insertions(+), 26 deletions(-) diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index b482df4..81e7fa1 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -2,6 +2,8 @@ #include "../lib-header/fat32.h" #include "../lib-header/stdmem.h" +struct FAT32DriverState driver_state; + const uint8_t fs_signature[BLOCK_SIZE] = { 'C', 'o', 'u', 'r', 's', 'e', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'D', 'e', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'b', 'y', ' ', ' ', ' ', ' ', ' ', @@ -51,7 +53,7 @@ void init_directory_table(struct FAT32DirectoryTable *dir_table, char *name, uin new_entry->cluster_low = (uint16_t)(parent_dir_cluster & 0xFFFF); new_entry->filesize = 0; - dir_table->table[0]; + dir_table->table[0] = *new_entry; } /** @@ -61,7 +63,9 @@ void init_directory_table(struct FAT32DirectoryTable *dir_table, char *name, uin */ bool is_empty_storage(void){ // read boot sector - return memsmp(BOOT_SECTOR, fs_signature, BLOCK_SIZE) != 0; + uint8_t boot_sector[BLOCK_SIZE]; + read_blocks(boot_sector, BOOT_SECTOR, 1); + return memcmp(BOOT_SECTOR, fs_signature, BLOCK_SIZE) != 0; } /** @@ -70,11 +74,20 @@ bool is_empty_storage(void){ * and initialized root directory) into cluster number 1 */ void create_fat32(void){ - // write fs_signature into boot sector - // write_blocks(fs_signature, BOOT_SECTOR, 1); - // write FAT into cluster number 1 - // write_blocks(); - // write root directory into cluster number 2 + write_blocks(fs_signature, BOOT_SECTOR, 1); + + struct FAT32FileAllocationTable file_alloc_table = {0}; + + struct FAT32DirectoryTable dir_table; + + file_alloc_table.cluster_map[0] = CLUSTER_0_VALUE; + file_alloc_table.cluster_map[1] = CLUSTER_1_VALUE; + file_alloc_table.cluster_map[2] = FAT32_FAT_END_OF_FILE; + + write_blocks(&file_alloc_table, cluster_to_lba(FAT_CLUSTER_NUMBER), CLUSTER_BLOCK_COUNT); + + init_directory_table(&dir_table, "ROOT\0\0\0\0", ROOT_CLUSTER_NUMBER); + write_blocks(&dir_table, cluster_to_lba(ROOT_CLUSTER_NUMBER), CLUSTER_BLOCK_COUNT); } /** @@ -89,10 +102,15 @@ void initialize_filesystem_fat32(void){ if (is_empty_storage()){ create_fat32(); } else { - // read cluster 1 - read_clusters(BOOT_SECTOR, fs_signature, 1); - // read FAT - // read root directory + struct FAT32FileAllocationTable file_alloc_table; + read_blocks(&file_alloc_table, cluster_to_lba(FAT_CLUSTER_NUMBER), CLUSTER_BLOCK_COUNT); + + struct FAT32DirectoryTable dir_table; + read_blocks(&dir_table, cluster_to_lba(ROOT_CLUSTER_NUMBER), CLUSTER_BLOCK_COUNT); + + // struct FAT32DriverState driver_state; + // driver_state.fat_table = file_alloc_table; + // driver_state.dir_table_buf = dir_table; } } @@ -104,9 +122,9 @@ void initialize_filesystem_fat32(void){ * @param cluster_number Cluster number to write * @param cluster_count Cluster count to write, due limitation of write_blocks block_count 255 => max cluster_count = 63 */ -void write_clusters(const void *ptr, uint32_t cluster_number, uint8_t cluster_count){ +// void write_clusters(const void *ptr, uint32_t cluster_number, uint8_t cluster_count){ -} +// } /** * Read cluster operation, wrapper for read_blocks(). @@ -116,9 +134,9 @@ void write_clusters(const void *ptr, uint32_t cluster_number, uint8_t cluster_co * @param cluster_number Cluster number to read * @param cluster_count Cluster count to read, due limitation of read_blocks block_count 255 => max cluster_count = 63 */ -void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count){ +// void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count){ -} +// } @@ -139,9 +157,9 @@ void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count){ * @return Error code: 0 success - 1 not a folder - 2 not found - -1 unknown */ -int8_t read_directory(struct FAT32DriverRequest request){ +// int8_t read_directory(struct FAT32DriverRequest request){ -} +// } /** @@ -150,7 +168,7 @@ int8_t read_directory(struct FAT32DriverRequest request){ * @param request All attribute will be used for read, buffer_size will limit reading count * @return Error code: 0 success - 1 not a file - 2 not enough buffer - 3 not found - -1 unknown */ -int8_t read(struct FAT32DriverRequest request){ +// int8_t read(struct FAT32DriverRequest request){ // for (uint32_t i = 0; i < request.buffer_size; i++){ // if (memcmp(request.name, request.buf[i].name, 8) == 0){ // // found @@ -168,7 +186,7 @@ int8_t read(struct FAT32DriverRequest request){ // return 3; // } // } -} +// } /** * FAT32 write, write a file or folder to file system. @@ -176,9 +194,9 @@ int8_t read(struct FAT32DriverRequest request){ * @param request All attribute will be used for write, buffer_size == 0 then create a folder / directory * @return Error code: 0 success - 1 file/folder already exist - 2 invalid parent cluster - -1 unknown */ -int8_t write(struct FAT32DriverRequest request){ +// int8_t write(struct FAT32DriverRequest request){ -} +// } /** @@ -187,6 +205,6 @@ int8_t write(struct FAT32DriverRequest request){ * @param request buf and buffer_size is unused * @return Error code: 0 success - 1 not found - 2 folder is not empty - -1 unknown */ -int8_t delete(struct FAT32DriverRequest request){ +// int8_t delete(struct FAT32DriverRequest request){ -} \ No newline at end of file +// } \ No newline at end of file diff --git a/src/kernel.c b/src/kernel.c index 7ee5efa..b3d8c4c 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -24,6 +24,7 @@ void kernel_setup(void) { // framebuffer_write(3, 11, '!', 0, 0xF); framebuffer_set_cursor(0, 0); framebuffer_write_string("> "); + initialize_filesystem_fat32(); // while (TRUE) b += 1; __asm__("int $0x4"); while (TRUE) { diff --git a/src/kernel_loader.s b/src/kernel_loader.s index 40d4a61..36c7f48 100644 --- a/src/kernel_loader.s +++ b/src/kernel_loader.s @@ -4,7 +4,7 @@ extern kernel_setup ; kernel -KERNEL_STACK_SIZE equ 4096 ; size of stack in bytes +KERNEL_STACK_SIZE equ 2097152 ; size of stack in bytes MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant FLAGS equ 0x0 ; multiboot flags CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum @@ -15,12 +15,14 @@ align 4 ; align at 4 bytes kernel_stack: ; label points to beginning of memory resb KERNEL_STACK_SIZE ; reserve stack for the kernel -section .text ; start of the text (code) section -align 4 ; the code must be 4 byte aligned +section .__mbHeader +align 4 dd MAGIC_NUMBER ; write the magic number to the machine code, dd FLAGS ; the flags, dd CHECKSUM ; and the checksum +section .text ; start of the text (code) section + loader: ; the loader label (defined as entry point in linker script) diff --git a/src/linker.ld b/src/linker.ld index 9a19dc3..d5c80f7 100644 --- a/src/linker.ld +++ b/src/linker.ld @@ -3,6 +3,11 @@ ENTRY(loader) /* the name of the entry label */ SECTIONS { . = 0x00100000; /* the code should be loaded at 1 MB */ + .__mbHeader : /* align at 4 KB */ + { + *(.__mbHeader) /* all text sections from all files */ + } + .text ALIGN (0x1000) : /* align at 4 KB */ { *(.text) /* all text sections from all files */ From c0cf8314e25a51e97c87a654bea6d8d5881decc4 Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Tue, 28 Mar 2023 16:51:07 +0700 Subject: [PATCH 20/52] Update read and read_directory implementation Co-authored-by: razzanYoni Co-authored-by: Nigel Sahl Co-authored-by: Rava Maulana --- .vscode/settings.json | 5 ++ src/filesystem/fat32.c | 175 +++++++++++++++++++++++++++++------------ src/kernel.c | 4 + src/lib-header/fat32.h | 2 +- 4 files changed, 135 insertions(+), 51 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index dec631f..63a56e2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,8 @@ { "debug.onTaskErrors": "debugAnyway", + "files.associations": { + "stdmem.h": "c", + "fat32.h": "c", + "stdtype.h": "c" + }, } \ No newline at end of file diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index 81e7fa1..fbe0431 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -2,7 +2,7 @@ #include "../lib-header/fat32.h" #include "../lib-header/stdmem.h" -struct FAT32DriverState driver_state; +static struct FAT32DriverState driver_state; const uint8_t fs_signature[BLOCK_SIZE] = { 'C', 'o', 'u', 'r', 's', 'e', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', @@ -65,7 +65,7 @@ bool is_empty_storage(void){ // read boot sector uint8_t boot_sector[BLOCK_SIZE]; read_blocks(boot_sector, BOOT_SECTOR, 1); - return memcmp(BOOT_SECTOR, fs_signature, BLOCK_SIZE) != 0; + return memcmp(boot_sector, fs_signature, BLOCK_SIZE) != 0; } /** @@ -76,18 +76,14 @@ bool is_empty_storage(void){ void create_fat32(void){ write_blocks(fs_signature, BOOT_SECTOR, 1); - struct FAT32FileAllocationTable file_alloc_table = {0}; + driver_state.fat_table.cluster_map[0] = CLUSTER_0_VALUE; + driver_state.fat_table.cluster_map[1] = CLUSTER_1_VALUE; + driver_state.fat_table.cluster_map[2] = FAT32_FAT_END_OF_FILE; - struct FAT32DirectoryTable dir_table; - - file_alloc_table.cluster_map[0] = CLUSTER_0_VALUE; - file_alloc_table.cluster_map[1] = CLUSTER_1_VALUE; - file_alloc_table.cluster_map[2] = FAT32_FAT_END_OF_FILE; - - write_blocks(&file_alloc_table, cluster_to_lba(FAT_CLUSTER_NUMBER), CLUSTER_BLOCK_COUNT); + write_clusters(&driver_state.fat_table, FAT_CLUSTER_NUMBER, 1); - init_directory_table(&dir_table, "ROOT\0\0\0\0", ROOT_CLUSTER_NUMBER); - write_blocks(&dir_table, cluster_to_lba(ROOT_CLUSTER_NUMBER), CLUSTER_BLOCK_COUNT); + init_directory_table(&(driver_state.dir_table_buf), "ROOT\0\0\0\0", ROOT_CLUSTER_NUMBER); + write_clusters(&(driver_state.dir_table_buf), ROOT_CLUSTER_NUMBER, 1); } /** @@ -102,15 +98,8 @@ void initialize_filesystem_fat32(void){ if (is_empty_storage()){ create_fat32(); } else { - struct FAT32FileAllocationTable file_alloc_table; - read_blocks(&file_alloc_table, cluster_to_lba(FAT_CLUSTER_NUMBER), CLUSTER_BLOCK_COUNT); - - struct FAT32DirectoryTable dir_table; - read_blocks(&dir_table, cluster_to_lba(ROOT_CLUSTER_NUMBER), CLUSTER_BLOCK_COUNT); - - // struct FAT32DriverState driver_state; - // driver_state.fat_table = file_alloc_table; - // driver_state.dir_table_buf = dir_table; + read_clusters(&(driver_state.fat_table), FAT_CLUSTER_NUMBER, 1); + read_clusters(&(driver_state.dir_table_buf), ROOT_CLUSTER_NUMBER, 1); } } @@ -122,9 +111,9 @@ void initialize_filesystem_fat32(void){ * @param cluster_number Cluster number to write * @param cluster_count Cluster count to write, due limitation of write_blocks block_count 255 => max cluster_count = 63 */ -// void write_clusters(const void *ptr, uint32_t cluster_number, uint8_t cluster_count){ - -// } +void write_clusters(const void *ptr, uint32_t cluster_number, uint8_t cluster_count){ + write_blocks(ptr, cluster_to_lba(cluster_number), cluster_count * CLUSTER_BLOCK_COUNT); +} /** * Read cluster operation, wrapper for read_blocks(). @@ -134,17 +123,13 @@ void initialize_filesystem_fat32(void){ * @param cluster_number Cluster number to read * @param cluster_count Cluster count to read, due limitation of read_blocks block_count 255 => max cluster_count = 63 */ -// void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count){ - -// } - - - - +void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count){ + read_blocks(ptr, cluster_to_lba(cluster_number), cluster_count * CLUSTER_BLOCK_COUNT); +} /* -- CRUD Operation -- */ -/* +/** * @brief * * @@ -156,10 +141,33 @@ void initialize_filesystem_fat32(void){ * buffer_size must be exactly sizeof(struct FAT32DirectoryTable) * @return Error code: 0 success - 1 not a folder - 2 not found - -1 unknown */ - -// int8_t read_directory(struct FAT32DriverRequest request){ - -// } +int8_t read_directory(struct FAT32DriverRequest request){ + int8_t error_code = -1; + + struct FAT32DirectoryTable parent_table; + read_clusters(&parent_table, request.parent_cluster_number, 1); + + for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + if(memcmp(request.name, parent_table.table[i].name, 8) == 0) { + if (parent_table.table[i].attribute == ATTR_SUBDIRECTORY) { + // terbaca sebagai folder + error_code = 0; + uint32_t curr_cluster = parent_table.table[i].cluster_high << 16 & parent_table.table[i].cluster_low; + + struct FAT32DirectoryTable curr_dir; + read_clusters(&curr_dir, curr_cluster, 1); + memcpy(request.buf, &curr_dir, sizeof(struct FAT32DirectoryTable)); + break; + } else { + error_code = 1; + } + } else { + // TODO: implement unknown + error_code = 2; + } + } + return error_code; +} /** @@ -167,10 +175,16 @@ void initialize_filesystem_fat32(void){ * * @param request All attribute will be used for read, buffer_size will limit reading count * @return Error code: 0 success - 1 not a file - 2 not enough buffer - 3 not found - -1 unknown - */ -// int8_t read(struct FAT32DriverRequest request){ - // for (uint32_t i = 0; i < request.buffer_size; i++){ - // if (memcmp(request.name, request.buf[i].name, 8) == 0){ + */ +int8_t read(struct FAT32DriverRequest request){ + struct FAT32DirectoryTable parent_table; + read_clusters(&parent_table, request.parent_cluster_number, 1); + + for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + + } + // for (uint32_t i = 0; i < .buffer_size; i++){ + // if (memcmp(request.name, , 8) == 0){ // // found // if (request.buf[i].attributes & FAT32_ATTR_DIRECTORY){ // // not a file @@ -181,12 +195,10 @@ void initialize_filesystem_fat32(void){ // return 0; // } // } - // else { - // // not found - // return 3; - // } // } -// } + // return 3; + +} /** * FAT32 write, write a file or folder to file system. @@ -194,9 +206,62 @@ void initialize_filesystem_fat32(void){ * @param request All attribute will be used for write, buffer_size == 0 then create a folder / directory * @return Error code: 0 success - 1 file/folder already exist - 2 invalid parent cluster - -1 unknown */ -// int8_t write(struct FAT32DriverRequest request){ - -// } +int8_t write(struct FAT32DriverRequest request){ + struct FAT32DirectoryTable parent_table; + read_clusters(&parent_table, request.parent_cluster_number, 1); + + uint32_t idx_empty = 0; + for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + if(parent_table.table[i].attribute == 0x0) { + idx_empty = i; + } + // TODO: Implement full directory table exception + } + + if(request.buffer_size == 0) { + // Create a directory + memcpy(parent_table.table[idx_empty].name, request.name, 8); + parent_table.table[idx_empty].attribute = ATTR_SUBDIRECTORY; + parent_table.table[idx_empty].user_attribute = UATTR_NOT_EMPTY; + + parent_table.table[idx_empty].undelete = 0; + parent_table.table[idx_empty].create_time = 0; + parent_table.table[idx_empty].create_date = 0; + parent_table.table[idx_empty].access_date = 0; + + parent_table.table[idx_empty].cluster_high = (uint16_t)(request.parent_cluster_number >> 16); + + parent_table.table[idx_empty].modified_time = 0; + parent_table.table[idx_empty].modified_date = 0; + + parent_table.table[idx_empty].cluster_low = (uint16_t)(request.parent_cluster_number & 0xFFFF); + parent_table.table[idx_empty].filesize = 0; + + // TODO: Implement write to FAT + + } else { + memcpy(parent_table.table[idx_empty].name, request.name, 8); + memcpy(parent_table.table[idx_empty].ext, request.ext, 3); + + parent_table.table[idx_empty].attribute = ATTR_ARCHIVE; + parent_table.table[idx_empty].user_attribute = UATTR_NOT_EMPTY; + + parent_table.table[idx_empty].undelete = 0; + parent_table.table[idx_empty].create_time = 0; + parent_table.table[idx_empty].create_date = 0; + parent_table.table[idx_empty].access_date = 0; + + parent_table.table[idx_empty].cluster_high = (uint16_t)(request.parent_cluster_number >> 16); + + parent_table.table[idx_empty].modified_time = 0; + parent_table.table[idx_empty].modified_date = 0; + + parent_table.table[idx_empty].cluster_low = (uint16_t)(request.parent_cluster_number & 0xFFFF); + parent_table.table[idx_empty].filesize = 0; + + // TODO: Implement write to FAT + } +} /** @@ -205,6 +270,16 @@ void initialize_filesystem_fat32(void){ * @param request buf and buffer_size is unused * @return Error code: 0 success - 1 not found - 2 folder is not empty - -1 unknown */ -// int8_t delete(struct FAT32DriverRequest request){ +int8_t delete(struct FAT32DriverRequest request){ + struct FAT32DirectoryTable parent_table; + read_clusters(&parent_table, request.parent_cluster_number, 1); + + uint8_t found = 0; + uint32_t idx_find = 0; + // if (request.) + for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + // if () + // TODO: Implement full directory table exception + } -// } \ No newline at end of file +} \ No newline at end of file diff --git a/src/kernel.c b/src/kernel.c index b3d8c4c..8560bcd 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -25,6 +25,10 @@ void kernel_setup(void) { framebuffer_set_cursor(0, 0); framebuffer_write_string("> "); initialize_filesystem_fat32(); + struct FAT32DriverRequest request; + memcpy(request.name, "ROO\0\0\0\0\0", 8); + request.parent_cluster_number = ROOT_CLUSTER_NUMBER; + read_directory(request); // while (TRUE) b += 1; __asm__("int $0x4"); while (TRUE) { diff --git a/src/lib-header/fat32.h b/src/lib-header/fat32.h index 227db4c..87f1551 100644 --- a/src/lib-header/fat32.h +++ b/src/lib-header/fat32.h @@ -34,7 +34,7 @@ /* -- FAT32 DirectoryEntry constants -- */ #define ATTR_SUBDIRECTORY 0b00010000 #define UATTR_NOT_EMPTY 0b10101010 - +#define ATTR_ARCHIVE 0b00100000 // Boot sector signature for this file system "FAT32 - IF2230 edition" From 1f3aecd5d57b89efeeb2e164fad6d20228264811 Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Wed, 29 Mar 2023 15:34:48 +0700 Subject: [PATCH 21/52] update CRUD Co-authored-by: razzanYoni Co-authored-by: Rava Maulana Co-authored-by: Nigel Sahl --- src/filesystem/fat32.c | 296 ++++++++++++++++++++++++++++++++--------- src/kernel.c | 91 ++++++++++--- src/lib-header/fat32.h | 10 ++ 3 files changed, 314 insertions(+), 83 deletions(-) diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index fbe0431..10b5666 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -37,6 +37,8 @@ uint32_t cluster_to_lba(uint32_t cluster){ void init_directory_table(struct FAT32DirectoryTable *dir_table, char *name, uint32_t parent_dir_cluster){ struct FAT32DirectoryEntry *new_entry = &(dir_table->table[0]); memcpy(&(new_entry->name), name, 8); + memcpy(&(new_entry->ext), "\0\0\0", 3); + new_entry->attribute = ATTR_SUBDIRECTORY; new_entry->user_attribute = UATTR_NOT_EMPTY; @@ -143,6 +145,7 @@ void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count){ */ int8_t read_directory(struct FAT32DriverRequest request){ int8_t error_code = -1; + bool not_found = 0; struct FAT32DirectoryTable parent_table; read_clusters(&parent_table, request.parent_cluster_number, 1); @@ -152,18 +155,21 @@ int8_t read_directory(struct FAT32DriverRequest request){ if (parent_table.table[i].attribute == ATTR_SUBDIRECTORY) { // terbaca sebagai folder error_code = 0; - uint32_t curr_cluster = parent_table.table[i].cluster_high << 16 & parent_table.table[i].cluster_low; + uint32_t curr_cluster = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; struct FAT32DirectoryTable curr_dir; read_clusters(&curr_dir, curr_cluster, 1); memcpy(request.buf, &curr_dir, sizeof(struct FAT32DirectoryTable)); break; } else { + // Not a folder error_code = 1; + not_found = 1; } } else { - // TODO: implement unknown - error_code = 2; + if (!not_found){ + error_code = 2; + } } } return error_code; @@ -177,90 +183,155 @@ int8_t read_directory(struct FAT32DriverRequest request){ * @return Error code: 0 success - 1 not a file - 2 not enough buffer - 3 not found - -1 unknown */ int8_t read(struct FAT32DriverRequest request){ + uint8_t error_code = -1; + + if(memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { + // Empty request name error + return error_code; + } + struct FAT32DirectoryTable parent_table; read_clusters(&parent_table, request.parent_cluster_number, 1); - for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - + for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + if (memcmp(request.name, parent_table.table[i].name, 8) == 0 && memcmp(request.ext, parent_table.table[i].ext, 3) == 0) { + uint32_t buffer_count = request.buffer_size/CLUSTER_SIZE; + uint32_t cluster_number = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; + + // Check for not enough buffer + uint32_t count = 0; + while(cluster_number != FAT32_FAT_END_OF_FILE) { + count++; + cluster_number = driver_state.fat_table.cluster_map[cluster_number]; + } + + if(count > buffer_count) { + // Not enough buffer error + return error_code = 2; + } + + uint32_t j = 0; + cluster_number = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; + while(cluster_number != FAT32_FAT_END_OF_FILE) { + read_clusters((struct ClusterBuffer*) (request.buf) + j, cluster_number, 1); + cluster_number = driver_state.fat_table.cluster_map[cluster_number]; + j++; + } + read_clusters((struct ClusterBuffer*) (request.buf) + j, cluster_number, 1); + return error_code = 0; + } } - // for (uint32_t i = 0; i < .buffer_size; i++){ - // if (memcmp(request.name, , 8) == 0){ - // // found - // if (request.buf[i].attributes & FAT32_ATTR_DIRECTORY){ - // // not a file - // return 1; - // } else { - // // read file - // read_clusters(request.buf, request.buf[i].first_cluster, request.buffer_size); - // return 0; - // } - // } - // } - // return 3; - + + return error_code; } /** - * FAT32 write, write a file or folder to file system. + * @brief + * + * * * @param request All attribute will be used for write, buffer_size == 0 then create a folder / directory * @return Error code: 0 success - 1 file/folder already exist - 2 invalid parent cluster - -1 unknown */ int8_t write(struct FAT32DriverRequest request){ + int8_t error_code = -1; + + if(memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { + // Empty request name error + return error_code; + } + + // reserved cluster number + if (request.parent_cluster_number < 2){ + return error_code = 2; + } + struct FAT32DirectoryTable parent_table; read_clusters(&parent_table, request.parent_cluster_number, 1); - uint32_t idx_empty = 0; - for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - if(parent_table.table[i].attribute == 0x0) { + uint32_t idx_empty = 0, i = 0; + while(i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry)) { + if(parent_table.table[i].user_attribute != UATTR_NOT_EMPTY) { idx_empty = i; + break; } - // TODO: Implement full directory table exception + i++; } - if(request.buffer_size == 0) { - // Create a directory - memcpy(parent_table.table[idx_empty].name, request.name, 8); - parent_table.table[idx_empty].attribute = ATTR_SUBDIRECTORY; - parent_table.table[idx_empty].user_attribute = UATTR_NOT_EMPTY; + // Writes to parent directory table + memcpy(parent_table.table[idx_empty].name, request.name, 8); + parent_table.table[idx_empty].attribute = request.buffer_size == 0 ? ATTR_SUBDIRECTORY : ATTR_ARCHIVE; + parent_table.table[idx_empty].user_attribute = UATTR_NOT_EMPTY; + parent_table.table[idx_empty].undelete = 0; + parent_table.table[idx_empty].create_time = 0; + parent_table.table[idx_empty].create_date = 0; + parent_table.table[idx_empty].access_date = 0; + parent_table.table[idx_empty].modified_time = 0; + parent_table.table[idx_empty].modified_date = 0; + parent_table.table[idx_empty].filesize = request.buffer_size; - parent_table.table[idx_empty].undelete = 0; - parent_table.table[idx_empty].create_time = 0; - parent_table.table[idx_empty].create_date = 0; - parent_table.table[idx_empty].access_date = 0; - - parent_table.table[idx_empty].cluster_high = (uint16_t)(request.parent_cluster_number >> 16); - - parent_table.table[idx_empty].modified_time = 0; - parent_table.table[idx_empty].modified_date = 0; + if(request.buffer_size == 0) { + // Create a new directory + uint32_t i = ROOT_CLUSTER_NUMBER; + uint8_t found = 0; + while(i < CLUSTER_MAP_SIZE && !found) { + if(driver_state.fat_table.cluster_map[i] == FAT32_FAT_EMPTY_ENTRY) { + driver_state.fat_table.cluster_map[i] = FAT32_FAT_END_OF_FILE; + found = 1; + } + i++; + } - parent_table.table[idx_empty].cluster_low = (uint16_t)(request.parent_cluster_number & 0xFFFF); - parent_table.table[idx_empty].filesize = 0; + if(i == CLUSTER_MAP_SIZE) { + // Full file allocation table error + return error_code = 1; + } - // TODO: Implement write to FAT - + parent_table.table[idx_empty].cluster_high = (uint16_t) (((i-1) >> 16) & 0xFFFF); + parent_table.table[idx_empty].cluster_low = (uint16_t) ((i-1) & 0xFFFF); } else { - memcpy(parent_table.table[idx_empty].name, request.name, 8); - memcpy(parent_table.table[idx_empty].ext, request.ext, 3); + // Create a new file + + // Search for empty cells in file allocation table + uint32_t i = 0, j = ROOT_CLUSTER_NUMBER; + uint32_t empty_fat_clusters[request.buffer_size / CLUSTER_SIZE]; + while(i < request.buffer_size/CLUSTER_SIZE && j < CLUSTER_MAP_SIZE) { + if(driver_state.fat_table.cluster_map[j] == FAT32_FAT_EMPTY_ENTRY) { + empty_fat_clusters[i] = j; + i++; + } + j++; + } - parent_table.table[idx_empty].attribute = ATTR_ARCHIVE; - parent_table.table[idx_empty].user_attribute = UATTR_NOT_EMPTY; + if(i < request.buffer_size/CLUSTER_SIZE) { + // Full file allocation table error + return error_code = 1; + } - parent_table.table[idx_empty].undelete = 0; - parent_table.table[idx_empty].create_time = 0; - parent_table.table[idx_empty].create_date = 0; - parent_table.table[idx_empty].access_date = 0; + // Create a linked list in file allocation table + for(i = 0; i < (request.buffer_size/CLUSTER_SIZE)-1; i++) { + driver_state.fat_table.cluster_map[empty_fat_clusters[i]] = empty_fat_clusters[i+1]; + } + driver_state.fat_table.cluster_map[empty_fat_clusters[(request.buffer_size/CLUSTER_SIZE)-1]] = FAT32_FAT_END_OF_FILE; - parent_table.table[idx_empty].cluster_high = (uint16_t)(request.parent_cluster_number >> 16); + // Set parent directory table entry + memcpy(parent_table.table[idx_empty].ext, request.ext, 3); + parent_table.table[idx_empty].cluster_high = (uint16_t) ((empty_fat_clusters[0] >> 16) & 0xFFFF); + parent_table.table[idx_empty].cluster_low = (uint16_t) (empty_fat_clusters[0] & 0xFFFF); - parent_table.table[idx_empty].modified_time = 0; - parent_table.table[idx_empty].modified_date = 0; + // Write file content to storage + if(request.buffer_size > 0) { + for(i = 0; i < request.buffer_size/CLUSTER_SIZE; i++) { + write_clusters(((struct ClusterBuffer*) (request.buf) + i), empty_fat_clusters[i], 1); + } + } + } - parent_table.table[idx_empty].cluster_low = (uint16_t)(request.parent_cluster_number & 0xFFFF); - parent_table.table[idx_empty].filesize = 0; + // Write updated file allocation table and parent directory table to storage + write_clusters(&(driver_state.fat_table), FAT_CLUSTER_NUMBER, 1); + write_clusters(&(parent_table), request.parent_cluster_number, 1); - // TODO: Implement write to FAT - } + return error_code = 0; } @@ -271,15 +342,110 @@ int8_t write(struct FAT32DriverRequest request){ * @return Error code: 0 success - 1 not found - 2 folder is not empty - -1 unknown */ int8_t delete(struct FAT32DriverRequest request){ + // struct FAT32DirectoryTable parent_table; + // read_clusters(&parent_table, request.parent_cluster_number, 1); + // int8_t error_code = -1; + // uint8_t found = 0; + // uint32_t idx_find = 0; + // uint32_t curr_cluster = request. + // if (request.) + // for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + // if (memcmp(request.name, parent_table.table[i].name, 8) == 0 && + // memcmp(request.ext, parent_table.table[i].ext, 3) == 0 && + // ) + // } + + // CEKK (bikinan rava) + uint8_t error_code = -1; + + if(memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { + // Empty request name error + return error_code; + } + // TODO: Implement invalid parent cluster number exception + struct FAT32DirectoryTable parent_table; read_clusters(&parent_table, request.parent_cluster_number, 1); - uint8_t found = 0; - uint32_t idx_find = 0; - // if (request.) - for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - // if () - // TODO: Implement full directory table exception - } + for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + if (memcmp(request.name, parent_table.table[i].name, 8) == 0 && memcmp(request.ext, parent_table.table[i].ext, 3) == 0) { + uint32_t cluster_number = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; + + if (parent_table.table[i].attribute == ATTR_SUBDIRECTORY) { + // Delete directory + + // Get current directory table + struct FAT32DirectoryTable curr_table; + read_clusters(&curr_table, cluster_number, 1); + + // Check if the directory is empty + uint8_t empty = 1; + for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + if(curr_table.table[i].user_attribute == UATTR_NOT_EMPTY) { + empty = 0; + break; + } + } + + if(!empty) { + // Directory not empty error + error_code = 2; + return error_code; + } + + // Remove directory from storage + struct ClusterBuffer cbuf; + for(uint32_t i = 0; i < CLUSTER_SIZE; i++) { + cbuf.buf[i] = '\0'; + } + write_clusters(&cbuf, cluster_number, 1); + + // Remove directory from file allocation table + driver_state.fat_table.cluster_map[cluster_number] = FAT32_FAT_EMPTY_ENTRY; + error_code = 0; + } else if (parent_table.table[i].attribute == ATTR_ARCHIVE) { + + // Delete file + // Remove file from file allocation table and storage + while (driver_state.fat_table.cluster_map[cluster_number] != FAT32_FAT_END_OF_FILE) { + // Remove file from storage + struct ClusterBuffer cbuf; + for(uint32_t i = 0; i < CLUSTER_SIZE; i++) { + cbuf.buf[i] = '\0'; + } + write_clusters(&cbuf, cluster_number, 1); + + // Remove file from file allocation table + cluster_number = driver_state.fat_table.cluster_map[cluster_number]; + driver_state.fat_table.cluster_map[cluster_number] = FAT32_FAT_EMPTY_ENTRY; + } + driver_state.fat_table.cluster_map[cluster_number] = FAT32_FAT_EMPTY_ENTRY; + + error_code = 0; + } + + // Remove parent directory entry + memcpy(parent_table.table[i].name, "\0\0\0\0\0\0\0\0", 8); + memcpy(parent_table.table[i].ext, "\0\0\0", 3); + parent_table.table[i].attribute = 0; + parent_table.table[i].user_attribute = 0; + parent_table.table[i].undelete = 0; + parent_table.table[i].create_time = 0; + parent_table.table[i].create_date = 0; + parent_table.table[i].access_date = 0; + parent_table.table[i].modified_time = 0; + parent_table.table[i].modified_date = 0; + parent_table.table[i].filesize = 0; + parent_table.table[i].cluster_low = 0; + parent_table.table[i].cluster_high = 0; + + // Write updated file allocation table and parent directory table to storage + write_clusters(&(driver_state.fat_table), FAT_CLUSTER_NUMBER, 1); + write_clusters(&(parent_table), request.parent_cluster_number, 1); + + return error_code; + } + } + return error_code; } \ No newline at end of file diff --git a/src/kernel.c b/src/kernel.c index 8560bcd..da6a5a5 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -11,28 +11,83 @@ #include "lib-header/fat32.h" void kernel_setup(void) { - // uint32_t a; - // uint32_t volatile b = 0x0000BABE; - // __asm__("mov $0xCAFE0000, %0" : "=r"(a)); - enter_protected_mode(&_gdt_gdtr); + // enter_protected_mode(&_gdt_gdtr); + // pic_remap(); + // initialize_idt(); + // framebuffer_clear(); + + // framebuffer_set_cursor(0, 0); + // framebuffer_write_string("> "); + + // initialize_filesystem_fat32(); + + // struct ClusterBuffer cbuf[5]; + // for(uint32_t i = 0; i < 5; i++) { + // for(uint32_t j = 0; j < CLUSTER_SIZE; j++) { + // cbuf[i].buf[j] = i + 'a'; + // } + // } + + // struct FAT32DriverRequest request = { + // .buf = cbuf, + // .name = "DIR\0\0\0\0\0", + // .ext = "pdf", + // .parent_cluster_number = ROOT_CLUSTER_NUMBER, + // .buffer_size = 0, + // }; + + // write(request); + // memcpy(request.name, "HELLO\0\0\0", 8); + // request.buffer_size = 5*CLUSTER_SIZE; + // write(request); + + // __asm__("int $0x4"); + // while (TRUE) { + // keyboard_state_activate(); + // } + + enter_protected_mode(&_gdt_gdtr); pic_remap(); initialize_idt(); + activate_keyboard_interrupt(); framebuffer_clear(); - // framebuffer_write(3, 8, 'H', 0, 0xF); - // framebuffer_write(3, 9, 'a', 0, 0xF); - // framebuffer_write(3, 10, 'i', 0, 0xF); - // framebuffer_write(3, 11, '!', 0, 0xF); framebuffer_set_cursor(0, 0); - framebuffer_write_string("> "); initialize_filesystem_fat32(); - struct FAT32DriverRequest request; - memcpy(request.name, "ROO\0\0\0\0\0", 8); - request.parent_cluster_number = ROOT_CLUSTER_NUMBER; - read_directory(request); - // while (TRUE) b += 1; - __asm__("int $0x4"); - while (TRUE) { - keyboard_state_activate(); - } + keyboard_state_activate(); + + struct ClusterBuffer cbuf[5]; + for (uint32_t i = 0; i < 5; i++) + for (uint32_t j = 0; j < CLUSTER_SIZE; j++) + cbuf[i].buf[j] = i + 'a'; + + struct FAT32DriverRequest request = { + .buf = cbuf, + .name = "ikanaide", + .ext = "uwu", + .parent_cluster_number = ROOT_CLUSTER_NUMBER, + .buffer_size = 0, + } ; + + write(request); // Create folder "ikanaide" + memcpy(request.name, "kano1\0\0\0", 8); + write(request); // Create folder "kano1" + memcpy(request.name, "ikanaide", 8); + memcpy(request.ext, "\0\0\0", 3); + delete(request); // Delete first folder, thus creating hole in FS + + memcpy(request.name, "daijoubu", 8); + memcpy(request.ext, "owo", 3); + request.buffer_size = 5*CLUSTER_SIZE; + write(request); // Create fragmented file "daijoubu" + + struct ClusterBuffer readcbuf; + read_clusters(&readcbuf, ROOT_CLUSTER_NUMBER+1, 1); + // If read properly, readcbuf should filled with 'a' + + request.buffer_size = CLUSTER_SIZE; + read(request); // Failed read due not enough buffer size + request.buffer_size = 5*CLUSTER_SIZE; + read(request); // Success read on file "daijoubu" + while (TRUE); } \ No newline at end of file diff --git a/src/lib-header/fat32.h b/src/lib-header/fat32.h index 87f1551..cd7b1e0 100644 --- a/src/lib-header/fat32.h +++ b/src/lib-header/fat32.h @@ -246,4 +246,14 @@ int8_t write(struct FAT32DriverRequest request); */ int8_t delete(struct FAT32DriverRequest request); +/** + * FAT32 find empty cluster in file allocation table. + * @brief Find empty cluster in file allocation table. + * + * @param size size of empty_fat_clusters + * + * @return struct EmptyCluster +*/ +struct EmptyCluster find_empty_cluster(uint32_t size); + #endif \ No newline at end of file From 7b9c010f477a12a4d092b3064802e85ab62fb279 Mon Sep 17 00:00:00 2001 From: razzanYoni <97822527+razzanYoni@users.noreply.github.com> Date: Wed, 29 Mar 2023 17:07:50 +0700 Subject: [PATCH 22/52] Update README.md --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5448be9..8ebc9f4 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,16 @@ Operating System ## Screenshots ![Example screenshot](./doc/OS.png) - -## How to Run -- make run di terminal +## Installation +Clone the repo +```sh +git clone https://github.com/Sister20/if2230-2023-osyikkk.git +``` + +## Usage +```sh +make run +``` ## Project Status Project is: _not complete_ From 2e02feb94455af8b2056e6a2957566cc4b592950 Mon Sep 17 00:00:00 2001 From: razzanYoni <97822527+razzanYoni@users.noreply.github.com> Date: Wed, 29 Mar 2023 17:11:29 +0700 Subject: [PATCH 23/52] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8ebc9f4..99eca83 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Room for improvement: ## Division of tasks -1. Nigel Sahl (13521043) -2. Razzan Daksana Yoni (13521087) -3. Rava Maulana Azzikri (13521149) -4. Hanif Muhammad Zhafran (13521157) +[13521043 Nigel Sahl](https://github.com/NerbFox) +[13521087 Razzan Daksana Yoni](https://github.com/razzanYoni) +[13521149 Rava Maulana Azzikri](https://github.com/RMA1403) +[13521157 Hanif Muhammad Zhafran](https://github.com/hanifmz07) From 9bccff10641c147f3d5571a8cd0bc655713c0b5e Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Wed, 29 Mar 2023 23:00:18 +0700 Subject: [PATCH 24/52] CRUD finished Co-authored-by: Nigel Sahl Co-authored-by: razzanYoni Co-authored-by: Rava Maulana --- src/filesystem/fat32.c | 51 +++++++++++++++++++++++++----------------- src/kernel.c | 23 +++++++++++++++++++ 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index 10b5666..99b5c6f 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -144,8 +144,17 @@ void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count){ * @return Error code: 0 success - 1 not a folder - 2 not found - -1 unknown */ int8_t read_directory(struct FAT32DriverRequest request){ - int8_t error_code = -1; - bool not_found = 0; + int8_t error_code = 2; + + if (memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { + // Folder name empty + return error_code = -1; + } + + if (request.buffer_size != 0) { + // Requested data is not a folder + return error_code = 1; + } struct FAT32DirectoryTable parent_table; read_clusters(&parent_table, request.parent_cluster_number, 1); @@ -154,21 +163,12 @@ int8_t read_directory(struct FAT32DriverRequest request){ if(memcmp(request.name, parent_table.table[i].name, 8) == 0) { if (parent_table.table[i].attribute == ATTR_SUBDIRECTORY) { // terbaca sebagai folder - error_code = 0; uint32_t curr_cluster = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; struct FAT32DirectoryTable curr_dir; read_clusters(&curr_dir, curr_cluster, 1); memcpy(request.buf, &curr_dir, sizeof(struct FAT32DirectoryTable)); - break; - } else { - // Not a folder - error_code = 1; - not_found = 1; - } - } else { - if (!not_found){ - error_code = 2; + return error_code = 0; } } } @@ -183,11 +183,16 @@ int8_t read_directory(struct FAT32DriverRequest request){ * @return Error code: 0 success - 1 not a file - 2 not enough buffer - 3 not found - -1 unknown */ int8_t read(struct FAT32DriverRequest request){ - uint8_t error_code = -1; + uint8_t error_code = 3; if(memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { // Empty request name error - return error_code; + return error_code = -1; + } + + if (request.buffer_size == 0) { + // Requested data is not a folder + return error_code = 1; } struct FAT32DirectoryTable parent_table; @@ -408,19 +413,23 @@ int8_t delete(struct FAT32DriverRequest request){ // Delete file // Remove file from file allocation table and storage - while (driver_state.fat_table.cluster_map[cluster_number] != FAT32_FAT_END_OF_FILE) { + + struct ClusterBuffer cbuf; + for(uint32_t i = 0; i < CLUSTER_SIZE; i++) { + cbuf.buf[i] = '\0'; + } + do { // Remove file from storage - struct ClusterBuffer cbuf; - for(uint32_t i = 0; i < CLUSTER_SIZE; i++) { - cbuf.buf[i] = '\0'; - } write_clusters(&cbuf, cluster_number, 1); // Remove file from file allocation table + uint32_t temp_cluster_number = cluster_number; cluster_number = driver_state.fat_table.cluster_map[cluster_number]; - driver_state.fat_table.cluster_map[cluster_number] = FAT32_FAT_EMPTY_ENTRY; - } + driver_state.fat_table.cluster_map[temp_cluster_number] = FAT32_FAT_EMPTY_ENTRY; + } while (driver_state.fat_table.cluster_map[cluster_number] != FAT32_FAT_END_OF_FILE); + // Handle EOF cluster driver_state.fat_table.cluster_map[cluster_number] = FAT32_FAT_EMPTY_ENTRY; + write_clusters(&cbuf, cluster_number, 1); error_code = 0; } diff --git a/src/kernel.c b/src/kernel.c index da6a5a5..1fcd62d 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -67,6 +67,13 @@ void kernel_setup(void) { .parent_cluster_number = ROOT_CLUSTER_NUMBER, .buffer_size = 0, } ; + struct FAT32DriverRequest request_dir = { + .buf = cbuf, + .name = "nbuna1\0\0", + .ext = "\0\0\0", + .parent_cluster_number = ROOT_CLUSTER_NUMBER, + .buffer_size = 0, + } ; write(request); // Create folder "ikanaide" memcpy(request.name, "kano1\0\0\0", 8); @@ -79,15 +86,31 @@ void kernel_setup(void) { memcpy(request.ext, "owo", 3); request.buffer_size = 5*CLUSTER_SIZE; write(request); // Create fragmented file "daijoubu" + // delete(request); struct ClusterBuffer readcbuf; read_clusters(&readcbuf, ROOT_CLUSTER_NUMBER+1, 1); // If read properly, readcbuf should filled with 'a' + request.buffer_size = 0; + read(request); request.buffer_size = CLUSTER_SIZE; read(request); // Failed read due not enough buffer size request.buffer_size = 5*CLUSTER_SIZE; read(request); // Success read on file "daijoubu" + memcpy(request.name, "test\0\0\0\0", 8); + read(request); + memcpy(request.name, "\0\0\0\0\0\0\0\0", 8); + read(request); + memcpy(request.name, "daijoubu", 8); + + write(request_dir); + read_directory(request); + read_directory(request_dir); + memcpy(request_dir.name, "nbunan\0\0", 8); + read_directory(request_dir); + memcpy(request_dir.name, "\0\0\0\0\0\0\0\0", 8); + read_directory(request_dir); while (TRUE); } \ No newline at end of file From 98f898c6c8989db45c4c043ecd03a7c5e30b3724 Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Thu, 30 Mar 2023 00:03:08 +0700 Subject: [PATCH 25/52] update cmos, todo --- .vscode/settings.json | 4 ++- src/filesystem/cmos.c | 22 ++++++++++++ src/{ => framebuffer}/framebuffer.c | 21 +++++++----- src/keyboard/keyboard.c | 17 ++++++++-- src/lib-header/cmos.h | 52 +++++++++++++++++++++++++++++ 5 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 src/filesystem/cmos.c rename src/{ => framebuffer}/framebuffer.c (86%) create mode 100644 src/lib-header/cmos.h diff --git a/.vscode/settings.json b/.vscode/settings.json index 63a56e2..70f0d91 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,8 @@ "files.associations": { "stdmem.h": "c", "fat32.h": "c", - "stdtype.h": "c" + "stdtype.h": "c", + "portio.h": "c", + "cmos.h": "c" }, } \ No newline at end of file diff --git a/src/filesystem/cmos.c b/src/filesystem/cmos.c new file mode 100644 index 0000000..a545db4 --- /dev/null +++ b/src/filesystem/cmos.c @@ -0,0 +1,22 @@ +#include "../lib-header/cmos.h" + +int8_t get_update_in_progress_flag() { + out(CMOS_ADDRESS, 0x0A); + return (in(CMOS_DATA) & 0x80); +} + +uint8_t get_RTC_register(int reg) { + out(CMOS_ADDRESS, reg); + return in(CMOS_DATA); +} + +void read_cmos(){ + while (!get_update_in_progress_flag()){ + // second = get_RTC_register(CMOS_SECONDS); + // minute = get_RTC_register(CMOS_MINUTES); + // hour = get_RTC_register(CMOS_HOURS); + // day = get_RTC_register(CMOS_DAY_OF_MONTH); + // month = get_RTC_register(CMOS_MONTH); + // year = get_RTC_register(CMOS_YEAR); + } +} \ No newline at end of file diff --git a/src/framebuffer.c b/src/framebuffer/framebuffer.c similarity index 86% rename from src/framebuffer.c rename to src/framebuffer/framebuffer.c index 73c23e7..28d31d1 100644 --- a/src/framebuffer.c +++ b/src/framebuffer/framebuffer.c @@ -1,7 +1,7 @@ -#include "lib-header/framebuffer.h" -#include "lib-header/stdtype.h" -#include "lib-header/stdmem.h" -#include "lib-header/portio.h" +#include "../lib-header/framebuffer.h" +#include "../lib-header/stdtype.h" +#include "../lib-header/stdmem.h" +#include "../lib-header/portio.h" void framebuffer_set_cursor(uint8_t r, uint8_t c) { @@ -40,7 +40,6 @@ int framebuffer_get_row(struct Cursor c) { } void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg) { - // TODO : Implement uint16_t attrib = (bg << 4) | (fg & WHITE); volatile uint16_t * where; where = (volatile uint16_t *)MEMORY_FRAMEBUFFER + (row * MAX_COLS + col) ; @@ -48,8 +47,7 @@ void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg) } void framebuffer_clear(void) { - // TODO : Implement - uint16_t space = 0x20 | (0x07 << 8); // " " in ASCII with white text on black background + uint16_t space = 0x20 | (0x07 << 8); uint16_t i; volatile uint16_t * where; where = (volatile uint16_t *)MEMORY_FRAMEBUFFER; @@ -63,9 +61,11 @@ void framebuffer_write_string(char * str) { struct Cursor c = framebuffer_get_cursor(); int offset = c.row * MAX_COLS + c.col; int i = 0; + bool cursor_bottom_row = 0; while (str[i] != '\0') { if (offset >= MAX_COLS * MAX_ROWS) { offset = framebuffer_scroll_ln(offset); + cursor_bottom_row = 1; } if (str[i] == '\n') { // offset = (offset / 160 + 1) * 160; @@ -77,9 +77,14 @@ void framebuffer_write_string(char * str) { } i++; } - framebuffer_set_cursor(offset / MAX_COLS, offset % MAX_COLS); + if (cursor_bottom_row) { + framebuffer_set_cursor(MAX_ROWS - 1, 1); + } else { + framebuffer_set_cursor(offset / MAX_COLS, offset % MAX_COLS); + } } +// TODO: scrolling mechanism still not finished int framebuffer_scroll_ln(int offset) { // di enter dulu baru scroll memcpy( diff --git a/src/keyboard/keyboard.c b/src/keyboard/keyboard.c index adafe52..386036d 100644 --- a/src/keyboard/keyboard.c +++ b/src/keyboard/keyboard.c @@ -4,6 +4,8 @@ #include "../lib-header/portio.h" #include "../lib-header/stdmem.h" +// TODO : Add Feature + #define SC_MAX 57 const char keyboard_scancode_1_to_ascii_map[256] = { 0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', @@ -203,10 +205,19 @@ void execute_cmd(char *input) { framebuffer_set_cursor(0, 0); framebuffer_write_string("> "); } else { - framebuffer_write_string("\nCommand not found: "); - framebuffer_write_string(input); + if (strcmp(input, "help") == 0) { + framebuffer_write_string("\nAvaible commands:\n"); + framebuffer_write_string("clear - Clear the screen\n"); + framebuffer_write_string("help - List of available commands\n"); + } else if (strcmp(&input[3], "del") == 0) { + framebuffer_write_string("\nCommand not found: "); + framebuffer_write_string(input); + } else { + framebuffer_write_string("\nCommand not found: "); + framebuffer_write_string(input); + } struct Cursor c = framebuffer_get_cursor(); framebuffer_set_cursor(c.row + 1, 0); framebuffer_write_string("> "); - } + } } \ No newline at end of file diff --git a/src/lib-header/cmos.h b/src/lib-header/cmos.h new file mode 100644 index 0000000..0ce5080 --- /dev/null +++ b/src/lib-header/cmos.h @@ -0,0 +1,52 @@ +#ifndef _CMOS_H +#define _CMOS_H + +// TODO : Implement the CMOS driver + +#include "portio.h" +// CMOS +#define CURRENT_YEAR 2023 +#define CMOS_ADDRESS 0x70 +#define CMOS_DATA 0x71 + +#define REG_SECONDS 0x00 +#define REG_MINUTES 0x02 +#define REG_HOURS 0x04 +#define REG_DAY_OF_MONTH 0x07 +#define REG_MONTH 0x08 +#define REG_YEAR 0x09 + +typedef struct dateNtime{ + struct date date; + struct time time; +} __attribute__((packed)); + +typedef struct date +{ + uint8_t day; + uint8_t month; + uint16_t year; +} __attribute__((packed)); + +typedef struct time +{ + uint8_t second; + uint8_t minute; + uint8_t hour; +} __attribute__((packed)); + +void init_cmos(); + +int8_t get_update_in_progress_flag(); + +uint8_t get_cmos_reg(int reg); + +void read_cmos(); + +int8_t is_leap_year(uint8_t year, uint8_t month); + +struct date get_date(struct dateNtime dateNtime); + +struct time get_time(struct dateNtime dateNtime); + +#endif \ No newline at end of file From 457db49a2626bb90478d3c324c5c89f1901fcba5 Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Thu, 30 Mar 2023 00:03:20 +0700 Subject: [PATCH 26/52] update makefile --- makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/makefile b/makefile index 75bb501..0623425 100644 --- a/makefile +++ b/makefile @@ -8,6 +8,7 @@ SOURCE_FOLDER = src INTERRUPT_FOLDER = interrupt KEYBOARD_FOLDER = keyboard FILESYSTEM_FOLDER = filesystem +FRAMEBUFFER_FOLDER = framebuffer # SOURCE_FOLDER = bin/iso/boot/grub OUTPUT_FOLDER = bin ISO_NAME = OSyikkk @@ -41,11 +42,11 @@ kernel: @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/intsetup.s -o $(OUTPUT_FOLDER)/intsetup.o # TODO: Compile C file with CFLAGS @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/kernel.c -o $(OUTPUT_FOLDER)/kernel.o - @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/framebuffer.c -o $(OUTPUT_FOLDER)/framebuffer.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/stdmem.c -o $(OUTPUT_FOLDER)/stdmem.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/portio.c -o $(OUTPUT_FOLDER)/portio.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/gdt.c -o $(OUTPUT_FOLDER)/gdt.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(FRAMEBUFFER_FOLDER)/framebuffer.c -o $(OUTPUT_FOLDER)/framebuffer.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/idt.c -o $(OUTPUT_FOLDER)/idt.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/interrupt.c -o $(OUTPUT_FOLDER)/interrupt.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(KEYBOARD_FOLDER)/keyboard.c -o $(OUTPUT_FOLDER)/keyboard.o From 44953f000eb0087dc425818e502f6c78a75e9b0f Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Thu, 30 Mar 2023 17:44:22 +0700 Subject: [PATCH 27/52] Update cmos (not yet finished) Co-authored-by: razzanYoni Co-authored-by: Nigel Sahl Co-authored-by: Rava Maulana --- .vscode/settings.json | 3 +- src/filesystem/cmos.c | 64 +++++++++++++++++++++++++++----- src/filesystem/fat32.c | 19 ++-------- src/interrupt/interrupt.c | 11 ++---- src/kernel.c | 47 ++++------------------- src/keyboard/keyboard.c | 78 +++++++++------------------------------ src/lib-header/cmos.h | 16 +++++--- 7 files changed, 99 insertions(+), 139 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 70f0d91..d7f5ca5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,7 @@ "fat32.h": "c", "stdtype.h": "c", "portio.h": "c", - "cmos.h": "c" + "cmos.h": "c", + "interrupt.h": "c" }, } \ No newline at end of file diff --git a/src/filesystem/cmos.c b/src/filesystem/cmos.c index a545db4..1391fa6 100644 --- a/src/filesystem/cmos.c +++ b/src/filesystem/cmos.c @@ -1,22 +1,68 @@ #include "../lib-header/cmos.h" -int8_t get_update_in_progress_flag() { +static struct dateNtime currentdatentime; + +void init_cmos(){ + read_cmos(); +} + +int8_t is_update_cmos() { out(CMOS_ADDRESS, 0x0A); return (in(CMOS_DATA) & 0x80); } -uint8_t get_RTC_register(int reg) { +uint8_t get_cmos_reg(int reg) { out(CMOS_ADDRESS, reg); return in(CMOS_DATA); } +void set_cmos_reg(int reg, uint8_t value){ + out(CMOS_ADDRESS, reg); + out(CMOS_DATA, value); +} + void read_cmos(){ - while (!get_update_in_progress_flag()){ - // second = get_RTC_register(CMOS_SECONDS); - // minute = get_RTC_register(CMOS_MINUTES); - // hour = get_RTC_register(CMOS_HOURS); - // day = get_RTC_register(CMOS_DAY_OF_MONTH); - // month = get_RTC_register(CMOS_MONTH); - // year = get_RTC_register(CMOS_YEAR); + while(is_update_cmos()); + + currentdatentime.time.second = get_cmos_reg(0x00); + currentdatentime.time.minute = get_cmos_reg(0x02); + currentdatentime.time.hour = get_cmos_reg(0x04); + currentdatentime.date.day = get_cmos_reg(0x07); + currentdatentime.date.month = get_cmos_reg(0x08); + currentdatentime.date.year = get_cmos_reg(0x09); + + uint8_t regB = get_cmos_reg(0x0B); + + if(!(regB & 0x04)){ + currentdatentime.time.second = (currentdatentime.time.second & 0x0F) + ((currentdatentime.time.second / 16) * 10); + currentdatentime.time.minute = (currentdatentime.time.minute & 0x0F) + ((currentdatentime.time.minute / 16) * 10); + currentdatentime.time.hour = ((currentdatentime.time.hour & 0x0F) + (((currentdatentime.time.hour & 0x70) / 16) * 10)) | (currentdatentime.time.hour & 0x80); + currentdatentime.date.day = (currentdatentime.date.day & 0x0F) + ((currentdatentime.date.day / 16) * 10); + currentdatentime.date.month = (currentdatentime.date.month & 0x0F) + ((currentdatentime.date.month / 16) * 10); + currentdatentime.date.year = (currentdatentime.date.year & 0x0F) + ((currentdatentime.date.year / 16) * 10); } +} + +void write_cmos(struct dateNtime * dnt){ + while (is_update_cmos()); + + set_cmos_reg(0x00, dnt->time.second); + set_cmos_reg(0x02, dnt->time.minute); + set_cmos_reg(0x04, dnt->time.hour); + set_cmos_reg(0x07, dnt->date.day); + set_cmos_reg(0x08, dnt->date.month); + set_cmos_reg(0x09, dnt->date.year); +} + +int8_t is_leap_year(uint16_t year){ + if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) return 1; + return 0; +} + +struct date get_currdate(){ + return currentdatentime.date; +} + +struct time get_currtime(){ + return currentdatentime.time; } \ No newline at end of file diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index 99b5c6f..975e3e4 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -1,6 +1,7 @@ #include "../lib-header/stdtype.h" #include "../lib-header/fat32.h" #include "../lib-header/stdmem.h" +// #include "../lib-header/cmos.h" static struct FAT32DriverState driver_state; @@ -183,7 +184,7 @@ int8_t read_directory(struct FAT32DriverRequest request){ * @return Error code: 0 success - 1 not a file - 2 not enough buffer - 3 not found - -1 unknown */ int8_t read(struct FAT32DriverRequest request){ - uint8_t error_code = 3; + int8_t error_code = 3; if(memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { // Empty request name error @@ -347,21 +348,7 @@ int8_t write(struct FAT32DriverRequest request){ * @return Error code: 0 success - 1 not found - 2 folder is not empty - -1 unknown */ int8_t delete(struct FAT32DriverRequest request){ - // struct FAT32DirectoryTable parent_table; - // read_clusters(&parent_table, request.parent_cluster_number, 1); - // int8_t error_code = -1; - // uint8_t found = 0; - // uint32_t idx_find = 0; - // uint32_t curr_cluster = request. - // if (request.) - // for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - // if (memcmp(request.name, parent_table.table[i].name, 8) == 0 && - // memcmp(request.ext, parent_table.table[i].ext, 3) == 0 && - // ) - // } - - // CEKK (bikinan rava) - uint8_t error_code = -1; + int8_t error_code = -1; if(memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { // Empty request name error diff --git a/src/interrupt/interrupt.c b/src/interrupt/interrupt.c index 8bcdd5b..0eab274 100644 --- a/src/interrupt/interrupt.c +++ b/src/interrupt/interrupt.c @@ -1,6 +1,5 @@ #include "../lib-header/interrupt.h" -#define IRQ1 33 void io_wait(void) { out(0x80, 0); @@ -49,18 +48,14 @@ void main_interrupt_handler ( __attribute__((unused)) struct InterruptStack info ) { switch (int_number+1) { - case IRQ1: + case (PIC1_OFFSET + IRQ_KEYBOARD) : keyboard_isr(); break; - // case 32: - // framebuffer_write(3, 11, 'b', 0, 0xF); - // break; - - // default : + // ? : CMOS Masuk sini juga kah? }; } void activate_keyboard_interrupt(void) { out(PIC1_DATA, PIC_DISABLE_ALL_MASK ^ (1 << IRQ_KEYBOARD)); out(PIC2_DATA, PIC_DISABLE_ALL_MASK); -} +} \ No newline at end of file diff --git a/src/kernel.c b/src/kernel.c index 1fcd62d..581a14d 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -11,49 +11,14 @@ #include "lib-header/fat32.h" void kernel_setup(void) { - // enter_protected_mode(&_gdt_gdtr); - // pic_remap(); - // initialize_idt(); - // framebuffer_clear(); - - // framebuffer_set_cursor(0, 0); - // framebuffer_write_string("> "); - - // initialize_filesystem_fat32(); - - // struct ClusterBuffer cbuf[5]; - // for(uint32_t i = 0; i < 5; i++) { - // for(uint32_t j = 0; j < CLUSTER_SIZE; j++) { - // cbuf[i].buf[j] = i + 'a'; - // } - // } - - // struct FAT32DriverRequest request = { - // .buf = cbuf, - // .name = "DIR\0\0\0\0\0", - // .ext = "pdf", - // .parent_cluster_number = ROOT_CLUSTER_NUMBER, - // .buffer_size = 0, - // }; - - // write(request); - // memcpy(request.name, "HELLO\0\0\0", 8); - // request.buffer_size = 5*CLUSTER_SIZE; - // write(request); - - // __asm__("int $0x4"); - // while (TRUE) { - // keyboard_state_activate(); - // } - - enter_protected_mode(&_gdt_gdtr); + enter_protected_mode(&_gdt_gdtr); pic_remap(); initialize_idt(); - activate_keyboard_interrupt(); + // activate_cmos_interrupt(); framebuffer_clear(); framebuffer_set_cursor(0, 0); + framebuffer_write_string("> "); initialize_filesystem_fat32(); - keyboard_state_activate(); struct ClusterBuffer cbuf[5]; for (uint32_t i = 0; i < 5; i++) @@ -103,6 +68,7 @@ void kernel_setup(void) { memcpy(request.name, "\0\0\0\0\0\0\0\0", 8); read(request); memcpy(request.name, "daijoubu", 8); + // delete(request); write(request_dir); read_directory(request); @@ -112,5 +78,8 @@ void kernel_setup(void) { memcpy(request_dir.name, "\0\0\0\0\0\0\0\0", 8); read_directory(request_dir); - while (TRUE); + __asm__("int $0x4"); + while (TRUE) { + keyboard_state_activate(); + } } \ No newline at end of file diff --git a/src/keyboard/keyboard.c b/src/keyboard/keyboard.c index 386036d..4fb289b 100644 --- a/src/keyboard/keyboard.c +++ b/src/keyboard/keyboard.c @@ -32,7 +32,7 @@ const char keyboard_scancode_1_to_ascii_map[256] = { }; // CTRL 29 // LSHIFT 42 -const char scancode_to_char[] = { +const char scancode_capital_letters[] = { 0, 0x1B, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, 0, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', 'H', @@ -49,46 +49,41 @@ void keyboard_state_activate(void) { keyboard_state.keyboard_input_on = 1; } void keyboard_state_deactivate(void) { keyboard_state.keyboard_input_on = 0; } void keyboard_isr(void) { + // Get cursor index struct Cursor cursor = framebuffer_get_cursor(); int row = cursor.row, col = cursor.col; + if (!keyboard_state.keyboard_input_on) { + // Set buffer index to zero if not receiving input keyboard_state.buffer_index = 0; } else { + // Get scancode uint8_t scancode = in(KEYBOARD_DATA_PORT); if (scancode == 42) { + // Left shift scancode key down keyboard_state.shift_pressed = 1; } if (scancode == 42 + 0x80) { + // Left shift scancode key up keyboard_state.shift_pressed = 0; } if (scancode == 58) { + // Caps lock scancode keyboard_state.caps_cond = !keyboard_state.caps_cond; } char mapped_char; if (!(keyboard_state.shift_pressed ^ keyboard_state.caps_cond)) { + // Handle lowercase letters mapped_char = keyboard_scancode_1_to_ascii_map[scancode]; } else { - // if () - mapped_char = scancode_to_char[scancode]; + // Handle uppercase letters + mapped_char = scancode_capital_letters[scancode]; } // TODO : Implement scancode processing - // if(scancode < 0x80) { - // if(writing == 1) { - // framebuffer_write(row, col, mapped_char, 0xF, 0); - // col++; - // writing = 0; - // } - // } else { - // if(writing == 0) { - // writing = 1; - // } - // } if (scancode < 0x80 && scancode != 42 && scancode != 58) { - // countCode++; - // if(scancode != tempCode || countCode > 10) { if (scancode != tempCode) { if (mapped_char == '\b' && keyboard_state.buffer_index > 0) { if (col == 0) { @@ -99,7 +94,7 @@ void keyboard_isr(void) { framebuffer_write(row, col - 1, ' ', WHITE, BLACK); } keyboard_state.buffer_index--; - keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = ' '; + keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = '\0'; } else if (mapped_char == '\n') { execute_cmd(keyboard_state.keyboard_buffer); memset(keyboard_state.keyboard_buffer, 0, 256); @@ -112,11 +107,7 @@ void keyboard_isr(void) { framebuffer_set_cursor(row, col + 1); } else if (scancode == 75 && col != 2) { framebuffer_set_cursor(row, col - 1); - keyboard_state.buffer_index--; } - // if(scancode != tempCode) { - // countCode = 0; - // } tempCode = scancode; if (mapped_char == 27) { clear_screen(); @@ -127,44 +118,6 @@ void keyboard_isr(void) { tempCode = scancode; // countCode = 0; } - // if (scancode > 0x80) { - // if (scancode <= (0x39 + 0x80)){ - // keyboard_state.shift_pressed = 0; - // } else if (scancode == (0x1D + 0x80)) { - // keyboard_state.ctrl_pressed = 0; - // } else if (scancode == (0x38 + 0x80)) { - // keyboard_state.alt_pressed = 0; - // } - // framebuffer_write(row, col, - // keyboard_state.keyboard_buffer[keyboard_state.buffer_index-1], 0xF, 0); - // keyboard_state.buffer_index = 0; - // // col++; - // } - // else if (mapped_char == '\b') { - // if (col == 0) { - // row--; - // col = 15; - // } else { - // keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = ' '; - // keyboard_state.buffer_index--; - // // framebuffer_write(row, col, ' ', 0xF, 0); - // col--; - // } - // } else if (mapped_char != 0 && mapped_char != '\n') { - // keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = - // mapped_char; keyboard_state.buffer_index++; - // // col++; - // } else if (mapped_char == '\n') { - // keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = '\n'; - // row++; - // col = 0; - // } - // if (mapped_char == 'c'){ - // framebuffer_clear(); - // row = 0; col = 0; - // } - // } - // col++; } // framebuffer_set_cursor(row, col); pic_ack(IRQ_KEYBOARD); @@ -206,11 +159,14 @@ void execute_cmd(char *input) { framebuffer_write_string("> "); } else { if (strcmp(input, "help") == 0) { - framebuffer_write_string("\nAvaible commands:\n"); + framebuffer_write_string("\nAvailable commands:\n"); framebuffer_write_string("clear - Clear the screen\n"); framebuffer_write_string("help - List of available commands\n"); } else if (strcmp(&input[3], "del") == 0) { - framebuffer_write_string("\nCommand not found: "); + // get filename from input + // char filename[256]; + // for + framebuffer_write_string("\nDeleting file: "); framebuffer_write_string(input); } else { framebuffer_write_string("\nCommand not found: "); diff --git a/src/lib-header/cmos.h b/src/lib-header/cmos.h index 0ce5080..52815fb 100644 --- a/src/lib-header/cmos.h +++ b/src/lib-header/cmos.h @@ -4,6 +4,8 @@ // TODO : Implement the CMOS driver #include "portio.h" +#include "interrupt.h" + // CMOS #define CURRENT_YEAR 2023 #define CMOS_ADDRESS 0x70 @@ -17,8 +19,8 @@ #define REG_YEAR 0x09 typedef struct dateNtime{ - struct date date; struct time time; + struct date date; } __attribute__((packed)); typedef struct date @@ -37,16 +39,20 @@ typedef struct time void init_cmos(); -int8_t get_update_in_progress_flag(); +int8_t is_update_cmos(); uint8_t get_cmos_reg(int reg); + +void set_cmos_reg(int reg, uint8_t value); void read_cmos(); -int8_t is_leap_year(uint8_t year, uint8_t month); +void write_cmos(struct dateNtime * dnt); + +int8_t is_leap_year(uint16_t year); -struct date get_date(struct dateNtime dateNtime); +struct date get_currdate(); -struct time get_time(struct dateNtime dateNtime); +struct time get_currtime(); #endif \ No newline at end of file From 6ba6645f36ba89b42c5281ace8ee383b7b22078b Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Thu, 30 Mar 2023 22:54:56 +0700 Subject: [PATCH 28/52] Fix scrolling keyboard, delete todo Co-authored-by: razzanYoni Co-authored-by: Nigel Sahl Co-authored-by: Rava Maulana --- makefile | 2 -- src/filesystem/fat32.c | 2 -- src/framebuffer/framebuffer.c | 38 ++++++----------------------- src/gdt.c | 9 ------- src/interrupt/idt.c | 10 -------- src/kernel_loader.s | 3 --- src/keyboard/keyboard.c | 45 +++++++++++++++++++---------------- src/lib-header/cmos.h | 2 +- src/lib-header/framebuffer.h | 7 ++++-- src/lib-header/gdt.h | 1 - src/lib-header/idt.h | 7 ++---- src/lib-header/keyboard.h | 7 ++++++ src/lib-header/portio.h | 12 ++++++++++ 13 files changed, 58 insertions(+), 87 deletions(-) diff --git a/makefile b/makefile index 0623425..dc2f8d5 100644 --- a/makefile +++ b/makefile @@ -40,7 +40,6 @@ disk: kernel: @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/kernel_loader.s -o $(OUTPUT_FOLDER)/kernel_loader.o @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/intsetup.s -o $(OUTPUT_FOLDER)/intsetup.o -# TODO: Compile C file with CFLAGS @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/kernel.c -o $(OUTPUT_FOLDER)/kernel.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/stdmem.c -o $(OUTPUT_FOLDER)/stdmem.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/portio.c -o $(OUTPUT_FOLDER)/portio.o @@ -64,7 +63,6 @@ iso: kernel @cp $(OUTPUT_FOLDER)/kernel $(OUTPUT_FOLDER)/iso/boot/ @cp other/grub1 $(OUTPUT_FOLDER)/iso/boot/grub/ @cp $(SOURCE_FOLDER)/menu.lst $(OUTPUT_FOLDER)/iso/boot/grub/ -# TODO: Create ISO image @genisoimage -R \ -b boot/grub/grub1 \ -no-emul-boot \ diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index 975e3e4..18c8566 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -354,8 +354,6 @@ int8_t delete(struct FAT32DriverRequest request){ // Empty request name error return error_code; } - // TODO: Implement invalid parent cluster number exception - struct FAT32DirectoryTable parent_table; read_clusters(&parent_table, request.parent_cluster_number, 1); diff --git a/src/framebuffer/framebuffer.c b/src/framebuffer/framebuffer.c index 28d31d1..252d16d 100644 --- a/src/framebuffer/framebuffer.c +++ b/src/framebuffer/framebuffer.c @@ -5,13 +5,7 @@ void framebuffer_set_cursor(uint8_t r, uint8_t c) { - // TODO : Implement - uint16_t pos = r * MAX_COLS + c; - // out(0x3D4, 0x0F); - // out(0x3D5, (uint8_t)(pos & 0xFF)); - // out(0x3D4, 0x0E); - // out(0x3D5, (uint8_t)((pos >> 8) & 0xFF)); - + uint16_t pos = r * MAX_COLS + c; out(CURSOR_PORT_CMD, VGA_CURSOR_HIGH); out(CURSOR_PORT_DATA, (uint8_t)((pos >> 8) & 0xFF)); out(CURSOR_PORT_CMD, VGA_CURSOR_LOW); @@ -61,49 +55,31 @@ void framebuffer_write_string(char * str) { struct Cursor c = framebuffer_get_cursor(); int offset = c.row * MAX_COLS + c.col; int i = 0; - bool cursor_bottom_row = 0; while (str[i] != '\0') { - if (offset >= MAX_COLS * MAX_ROWS) { - offset = framebuffer_scroll_ln(offset); - cursor_bottom_row = 1; + if (offset >= MAX_COLS * (MAX_ROWS)) { + offset = framebuffer_scroll_ln(offset); } if (str[i] == '\n') { - // offset = (offset / 160 + 1) * 160; offset = (offset / MAX_COLS + 1) * MAX_COLS; } else { - // framebuffer_write(offset / 160, offset % 160, str[i], WHITE, BLACK); framebuffer_write(offset / MAX_COLS, offset % MAX_COLS, str[i], WHITE, BLACK); offset += 1; } i++; } - if (cursor_bottom_row) { - framebuffer_set_cursor(MAX_ROWS - 1, 1); - } else { - framebuffer_set_cursor(offset / MAX_COLS, offset % MAX_COLS); - } + + framebuffer_set_cursor(offset / MAX_COLS, offset % MAX_COLS); } -// TODO: scrolling mechanism still not finished int framebuffer_scroll_ln(int offset) { - // di enter dulu baru scroll memcpy( (void *)MEMORY_FRAMEBUFFER, (void *)(MEMORY_FRAMEBUFFER + MAX_COLS * 2), - // (void *)(MEMORY_FRAMEBUFFER + MAX_COLS * 2), - // 160 * 2 * 24); - // 2 * MAX_COLS * 2 * (MAX_ROWS - 1)); - MAX_COLS * 2 * (MAX_ROWS + 1)); + MAX_COLS * 2 * (MAX_ROWS)); for (int i = 0; i < MAX_COLS; i++) { framebuffer_write(MAX_ROWS - 1, i, ' ', WHITE, BLACK); - framebuffer_write(MAX_ROWS - 2, i, ' ', WHITE, BLACK); - // framebuffer_write(MAX_ROWS - 3, i, ' ', WHITE, BLACK); } - - // return (c.row * MAX_COLS + c.col) - (MAX_ROWS - 1) * MAX_COLS; - // return (c.row * MAX_COLS + c.col) - 2 * MAX_COLS; - return (offset) - 3 * (MAX_COLS); - // return offset - (MAX_ROWS - 1) * MAX_COLS; + return (offset) - (1 * (MAX_COLS)); } \ No newline at end of file diff --git a/src/gdt.c b/src/gdt.c index df60b11..241a88c 100644 --- a/src/gdt.c +++ b/src/gdt.c @@ -9,9 +9,6 @@ struct GlobalDescriptorTable global_descriptor_table = { .table = { { - // TODO : Implement - - // First 32-bit .segment_low = 0x0000, .base_low = 0x0000, @@ -21,7 +18,6 @@ struct GlobalDescriptorTable global_descriptor_table = { .type_bit = 0x0, .non_system = 0x0, - // TODO : Continue GDT definition .dpl = 0x0, .segment_present = 0x0, .segment_limit = 0x0, @@ -32,7 +28,6 @@ struct GlobalDescriptorTable global_descriptor_table = { .base_high = 0x0 }, { - // TODO : Implement // Kernel Code Segment // First 32-bit @@ -44,7 +39,6 @@ struct GlobalDescriptorTable global_descriptor_table = { .type_bit = 0xA, .non_system = 0x1, - // TODO : Continue GDT definition .dpl = 0x0, .segment_present = 0x1, .segment_limit = 0xF, @@ -55,7 +49,6 @@ struct GlobalDescriptorTable global_descriptor_table = { .base_high = 0x00 }, { - // TODO : Implement // Kernel Data Segment // First 32-bit @@ -67,7 +60,6 @@ struct GlobalDescriptorTable global_descriptor_table = { .type_bit = 0x2, .non_system = 0x1, - // TODO : Continue GDT definition .dpl = 0x0, .segment_present = 0x1, .segment_limit = 0xF, @@ -86,7 +78,6 @@ struct GlobalDescriptorTable global_descriptor_table = { * From: https://wiki.osdev.org/Global_Descriptor_Table, GDTR.size is GDT size minus 1. */ struct GDTR _gdt_gdtr = { - // TODO : Implement, this GDTR will point to global_descriptor_table. // Use sizeof operator .size = sizeof(global_descriptor_table) - 1, diff --git a/src/interrupt/idt.c b/src/interrupt/idt.c index 41fb089..532b9b9 100644 --- a/src/interrupt/idt.c +++ b/src/interrupt/idt.c @@ -11,15 +11,6 @@ struct InterruptDescriptorTable interrupt_descriptor_table; //}; void initialize_idt(void) { - /* TODO : - * Iterate all isr_stub_table, - * Set all IDT entry with set_interrupt_gate() - * with following values: - * Vector: i - * Handler Address: isr_stub_table[i] - * Segment: GDT_KERNEL_CODE_SEGMENT_SELECTOR - * Privilege: 0 - */ // Local initialization of IDT _idt_idtr.size = sizeof(interrupt_descriptor_table) - 1; _idt_idtr.address = (void *)&interrupt_descriptor_table.table[0]; @@ -35,7 +26,6 @@ void initialize_idt(void) { void set_interrupt_gate(uint8_t int_vector, void *handler_address, uint16_t gdt_seg_selector, uint8_t privilege) { struct IDTGate *idt_int_gate = &interrupt_descriptor_table.table[int_vector]; - // TODO : Set handler offset, privilege & segment idt_int_gate->offset_low = (uint32_t)handler_address & 0xFFFF; idt_int_gate->segment = gdt_seg_selector; idt_int_gate->_reserved = 0b0; diff --git a/src/kernel_loader.s b/src/kernel_loader.s index 36c7f48..738278d 100644 --- a/src/kernel_loader.s +++ b/src/kernel_loader.s @@ -36,13 +36,11 @@ loader: ; the loader label (defined as en enter_protected_mode: cli mov eax, [esp+4] - ; TODO: Load GDT from GDTDescriptor ; eax at this line will carry GDTR location, dont forget to use square bracket [eax] lgdt [eax] mov eax, cr0 - ; TODO: Set bit-0 (Protection Enable bit-flag) in Control Register 0 (CR0) ; Set eax with above condition, eax will be copied to CR0 with next instruction or al, 1 mov cr0, eax @@ -53,7 +51,6 @@ enter_protected_mode: jmp 0x8:flush_cs flush_cs: mov ax, 10h - ; TODO: Set all data segment register with 0x10 ; Segments register need to set with 0x10: ss, ds, es mov ss, ax mov ds, ax diff --git a/src/keyboard/keyboard.c b/src/keyboard/keyboard.c index 4fb289b..4e8ebff 100644 --- a/src/keyboard/keyboard.c +++ b/src/keyboard/keyboard.c @@ -4,9 +4,6 @@ #include "../lib-header/portio.h" #include "../lib-header/stdmem.h" -// TODO : Add Feature - -#define SC_MAX 57 const char keyboard_scancode_1_to_ascii_map[256] = { 0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', @@ -31,7 +28,7 @@ const char keyboard_scancode_1_to_ascii_map[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; // CTRL 29 -// LSHIFT 42 +// LSHIFT SCANCODE_LSHIFT const char scancode_capital_letters[] = { 0, 0x1B, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, 0, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', @@ -59,16 +56,16 @@ void keyboard_isr(void) { } else { // Get scancode uint8_t scancode = in(KEYBOARD_DATA_PORT); - if (scancode == 42) { + if (scancode == SCANCODE_LSHIFT) { // Left shift scancode key down keyboard_state.shift_pressed = 1; } - if (scancode == 42 + 0x80) { + if (scancode == SCANCODE_LSHIFT + SCANCODE_KEYUP_THRESHOLD) { // Left shift scancode key up keyboard_state.shift_pressed = 0; } - if (scancode == 58) { + if (scancode == SCANCODE_CAPS) { // Caps lock scancode keyboard_state.caps_cond = !keyboard_state.caps_cond; } @@ -81,17 +78,17 @@ void keyboard_isr(void) { // Handle uppercase letters mapped_char = scancode_capital_letters[scancode]; } - // TODO : Implement scancode processing - if (scancode < 0x80 && scancode != 42 && scancode != 58) { + // Handle if scancode is key down and not capslock + if (scancode < SCANCODE_KEYUP_THRESHOLD && scancode != SCANCODE_LSHIFT && scancode != SCANCODE_CAPS) { if (scancode != tempCode) { if (mapped_char == '\b' && keyboard_state.buffer_index > 0) { if (col == 0) { framebuffer_set_cursor(row - 1, MAX_COLS - 1); - framebuffer_write(row - 1, MAX_COLS - 1, ' ', WHITE, BLACK); + framebuffer_write(row - 1, MAX_COLS - 1, '\0', WHITE, BLACK); } else { framebuffer_set_cursor(row, col - 1); - framebuffer_write(row, col - 1, ' ', WHITE, BLACK); + framebuffer_write(row, col - 1, '\0', WHITE, BLACK); } keyboard_state.buffer_index--; keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = '\0'; @@ -99,17 +96,23 @@ void keyboard_isr(void) { execute_cmd(keyboard_state.keyboard_buffer); memset(keyboard_state.keyboard_buffer, 0, 256); keyboard_state.buffer_index = 0; - } else if (mapped_char != '\b' && scancode != 75) { + } else if (mapped_char != '\b' && scancode != SCANCODE_LEFTARROW) { keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = mapped_char; keyboard_state.buffer_index++; - framebuffer_write(row, col, mapped_char, 0xF, 0); - framebuffer_set_cursor(row, col + 1); - } else if (scancode == 75 && col != 2) { + + int offset = row * MAX_COLS + col; + if (row == (MAX_ROWS - 1) && col == (MAX_COLS - 1)) { + offset = framebuffer_scroll_ln(offset); + } + + framebuffer_write(offset / MAX_COLS, offset % MAX_COLS, mapped_char, WHITE, BLACK); + framebuffer_set_cursor(offset / MAX_COLS, offset % MAX_COLS + 1); + } else if (scancode == SCANCODE_LEFTARROW && col != 2) { framebuffer_set_cursor(row, col - 1); } tempCode = scancode; - if (mapped_char == 27) { + if (mapped_char == SCANCODE_ESC) { clear_screen(); } // framebuffer_set_cursor(row, col); @@ -153,19 +156,19 @@ int strcmp(char *s1, char *s2) { } void execute_cmd(char *input) { - if (strcmp(input, "clear") == 0) { + // execute command from input + if (strcmp(input, "clear") == 0) { + // clear screen clear_screen(); framebuffer_set_cursor(0, 0); framebuffer_write_string("> "); } else { if (strcmp(input, "help") == 0) { + // list of available commands framebuffer_write_string("\nAvailable commands:\n"); framebuffer_write_string("clear - Clear the screen\n"); - framebuffer_write_string("help - List of available commands\n"); + framebuffer_write_string("help - List of available commands"); } else if (strcmp(&input[3], "del") == 0) { - // get filename from input - // char filename[256]; - // for framebuffer_write_string("\nDeleting file: "); framebuffer_write_string(input); } else { diff --git a/src/lib-header/cmos.h b/src/lib-header/cmos.h index 52815fb..2f2bc40 100644 --- a/src/lib-header/cmos.h +++ b/src/lib-header/cmos.h @@ -1,7 +1,7 @@ #ifndef _CMOS_H #define _CMOS_H -// TODO : Implement the CMOS driver +// TODO : Belum diintegrasiin ke fat32 #include "portio.h" #include "interrupt.h" diff --git a/src/lib-header/framebuffer.h b/src/lib-header/framebuffer.h index 5369030..5185330 100644 --- a/src/lib-header/framebuffer.h +++ b/src/lib-header/framebuffer.h @@ -64,12 +64,15 @@ struct Cursor framebuffer_get_cursor(); void framebuffer_clear(void); /** - * @param char* str - * Write string to framebuffer + * @brief Write string to framebuffer + * + * @param char* str */ void framebuffer_write_string(char *str); /** + * @brief scroll framebuffer by offset lines + * * @param int offset * Scroll framebuffer by offset lines */ diff --git a/src/lib-header/gdt.h b/src/lib-header/gdt.h index 662b33f..afce048 100644 --- a/src/lib-header/gdt.h +++ b/src/lib-header/gdt.h @@ -28,7 +28,6 @@ struct SegmentDescriptor { uint8_t type_bit : 4; uint8_t non_system : 1; - // TODO : Continue GDT definition uint8_t dpl : 2; uint8_t segment_present : 1; uint8_t segment_limit : 4; diff --git a/src/lib-header/idt.h b/src/lib-header/idt.h index c76d251..0d92fc6 100644 --- a/src/lib-header/idt.h +++ b/src/lib-header/idt.h @@ -44,7 +44,6 @@ struct IDTGate { uint16_t offset_low; uint16_t segment; - // TODO : Implement uint8_t _reserved : 5; uint8_t _r_bit_1 : 3; uint8_t _r_bit_2 : 3; @@ -62,8 +61,7 @@ struct IDTGate { * * @param table Fixed-width array of IDTGate with size IDT_MAX_ENTRY_COUNT */ -// TODO : Implement -// ... + struct InterruptDescriptorTable { struct IDTGate table[IDT_MAX_ENTRY_COUNT]; } __attribute__((packed)); @@ -75,8 +73,7 @@ struct InterruptDescriptorTable { * @param size Interrupt Descriptor Table size * @param address IDT address */ -// TODO : Implement -// ... + struct IDTR { uint16_t size; struct InterruptDescriptorTable *address; diff --git a/src/lib-header/keyboard.h b/src/lib-header/keyboard.h index 4976f30..64632eb 100644 --- a/src/lib-header/keyboard.h +++ b/src/lib-header/keyboard.h @@ -15,6 +15,13 @@ #define KEYBOARD_BUFFER_SIZE 256 +#define SC_MAX 57 +#define SCANCODE_KEYUP_THRESHOLD 0x80 +#define SCANCODE_LSHIFT 42 +#define SCANCODE_CAPS 58 +#define SCANCODE_LEFTARROW 75 +#define SCANCODE_ESC 27 + /** * keyboard_scancode_1_to_ascii_map[256], Convert scancode values that correspond to ASCII printables * How to use this array: ascii_char = k[scancode] diff --git a/src/lib-header/portio.h b/src/lib-header/portio.h index dc3ed3f..ca3721a 100644 --- a/src/lib-header/portio.h +++ b/src/lib-header/portio.h @@ -19,7 +19,19 @@ void out(uint16_t port, uint8_t data); */ uint8_t in(uint16_t port); +/** in: + * Read data from the given I/O port + * + * @param port The I/O port to request the data + * @return Recieved data from the corresponding I/O port in 16 bit + */ uint16_t in16(uint16_t port); +/** out: + * Sends the given data to the given I/O port + * + * @param port The I/O port to send the data to + * @param data The data to send to the I/O port in 16 bit + */ void out16(uint16_t port, uint16_t data); #endif \ No newline at end of file From 011a52df72e4a66f5b525bc71c6464ff3f484894 Mon Sep 17 00:00:00 2001 From: NerbFox <13521043@std.stei.itb.ac.id> Date: Mon, 3 Apr 2023 16:39:29 +0700 Subject: [PATCH 29/52] Update Milestone 3 --- src/inserter/external-inserter.c | 98 ++++++++++++++++++++++++++++++++ src/paging/paging.c | 42 ++++++++++++++ src/paging/paging.h | 96 +++++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+) create mode 100644 src/inserter/external-inserter.c create mode 100644 src/paging/paging.c create mode 100644 src/paging/paging.h diff --git a/src/inserter/external-inserter.c b/src/inserter/external-inserter.c new file mode 100644 index 0000000..db6cbfc --- /dev/null +++ b/src/inserter/external-inserter.c @@ -0,0 +1,98 @@ +#include +#include + +// Usual gcc fixed width integer type +typedef u_int32_t uint32_t; +typedef u_int8_t uint8_t; + +// Manual import from fat32.h, disk.h, & stdmem.h due some issue with size_t +#define BLOCK_SIZE 512 + +struct FAT32DriverRequest { + void *buf; + char name[8]; + char ext[3]; + uint32_t parent_cluster_number; + uint32_t buffer_size; +} __attribute__((packed)); + +void* memcpy(void* restrict dest, const void* restrict src, size_t n); + +void initialize_filesystem_fat32(void); +int8_t read(struct FAT32DriverRequest request); +int8_t read_directory(struct FAT32DriverRequest request); +int8_t write(struct FAT32DriverRequest request); +int8_t delete(struct FAT32DriverRequest request); + + + + +// Global variable +uint8_t *image_storage; +uint8_t *file_buffer; + +void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count) { + for (int i = 0; i < block_count; i++) + memcpy((uint8_t*) ptr + BLOCK_SIZE*i, image_storage + BLOCK_SIZE*(logical_block_address+i), BLOCK_SIZE); +} + +void write_blocks(const void *ptr, uint32_t logical_block_address, uint8_t block_count) { + for (int i = 0; i < block_count; i++) + memcpy(image_storage + BLOCK_SIZE*(logical_block_address+i), (uint8_t*) ptr + BLOCK_SIZE*i, BLOCK_SIZE); +} + + +int main(int argc, char *argv[]) { + if (argc < 4) { + fprintf(stderr, "inserter: ./inserter \n"); + exit(1); + } + + // Read storage into memory, requiring 4 MB memory + image_storage = malloc(4*1024*1024); + file_buffer = malloc(4*1024*1024); + FILE *fptr = fopen(argv[3], "r"); + fread(image_storage, 4*1024*1024, 1, fptr); + fclose(fptr); + + // Read target file, assuming file is less than 4 MiB + FILE *fptr_target = fopen(argv[1], "r"); + size_t filesize = 0; + if (fptr_target == NULL) + filesize = 0; + else { + fread(file_buffer, 4*1024*1024, 1, fptr_target); + fseek(fptr_target, 0, SEEK_END); + filesize = ftell(fptr_target); + fclose(fptr_target); + } + + printf("Filename : %s\n", argv[1]); + printf("Filesize : %ld bytes\n", filesize); + + // FAT32 operations + initialize_filesystem_fat32(); + struct FAT32DriverRequest request = { + .buf = file_buffer, + .ext = "\0\0\0", + .buffer_size = filesize, + }; + sscanf(argv[2], "%u", &request.parent_cluster_number); + sscanf(argv[1], "%8s", request.name); + int retcode = write(request); + if (retcode == 0) + puts("Write success"); + else if (retcode == 1) + puts("Error: File/folder name already exist"); + else if (retcode == 2) + puts("Error: Invalid parent cluster"); + else + puts("Error: Unknown error"); + + // Write image in memory into original, overwrite them + fptr = fopen(argv[3], "w"); + fwrite(image_storage, 4*1024*1024, 1, fptr); + fclose(fptr); + + return 0; +} \ No newline at end of file diff --git a/src/paging/paging.c b/src/paging/paging.c new file mode 100644 index 0000000..926466c --- /dev/null +++ b/src/paging/paging.c @@ -0,0 +1,42 @@ +#include "lib-header/paging.h" + +__attribute__((aligned(0x1000))) struct PageDirectory _paging_kernel_page_directory = { + .table = { + [0] = { + .flag.present_bit = 1, + .flag.write_bit = 1, + .lower_address = 0, + .flag.use_pagesize_4_mb = 1, + }, + [0x300] = { + .flag.present_bit = 1, + .flag.write_bit = 1, + .lower_address = 0, + .flag.use_pagesize_4_mb = 1, + }, + } +}; + +static struct PageDriverState page_driver_state = { + .last_available_physical_addr = (uint8_t*) 0 + PAGE_FRAME_SIZE, +}; + +void update_page_directory_entry(void *physical_addr, void *virtual_addr, struct PageDirectoryEntryFlag flag) { + uint32_t page_index = ((uint32_t) virtual_addr >> 22) & 0x3FF; + + _paging_kernel_page_directory.table[page_index].flag = flag; + _paging_kernel_page_directory.table[page_index].lower_address = ((uint32_t)physical_addr >> 22) & 0x3FF; + flush_single_tlb(virtual_addr); +} + +int8_t allocate_single_user_page_frame(void *virtual_addr) { + // Using default QEMU config (128 MiB max memory) + uint32_t last_physical_addr = (uint32_t) page_driver_state.last_available_physical_addr; + + // TODO : Allocate Page Directory Entry with user privilege + return -1; +} + +void flush_single_tlb(void *virtual_addr) { + asm volatile("invlpg (%0)" : /* */ : "b"(virtual_addr): "memory"); +} diff --git a/src/paging/paging.h b/src/paging/paging.h new file mode 100644 index 0000000..7e998b2 --- /dev/null +++ b/src/paging/paging.h @@ -0,0 +1,96 @@ +#ifndef _PAGING_H +#define _PAGING_H + +#include "stdtype.h" + +#define PAGE_ENTRY_COUNT 1024 +#define PAGE_FRAME_SIZE (4*1024*1024) + +// Operating system page directory, using page size PAGE_FRAME_SIZE (4 MiB) +extern struct PageDirectory _paging_kernel_page_directory; + + + + +/** + * Page Directory Entry Flag, only first 8 bit + * + * @param present_bit Indicate whether this entry is exist or not + * ... + */ +struct PageDirectoryEntryFlag { + uint8_t present_bit : 1; + // TODO : Continue. Note: Only first 8 bit flags +} __attribute__((packed)); + +/** + * Page Directory Entry, for page size 4 MB. + * Check Intel Manual 3a - Ch 4 Paging - Figure 4-4 PDE: 4MB page + * + * @param flag Contain 8-bit page directory entry flag + * @param global_page Is this page translation global (also cannot be flushed) + * ... + * Note: + * - Assume "Bits 39:32 of address" (higher_address) is 8-bit and Reserved is 1 + * - "Bits 31:22 of address" is called lower_address in kit + */ +struct PageDirectoryEntry { + struct PageDirectoryEntryFlag flag; + uint16_t global_page : 1; + // TODO : Continue, Use uint16_t + bitfield here, Do not use uint8_t +} __attribute__((packed)); + +/** + * Page Directory, contain array of PageDirectoryEntry. + * Note: This data structure not only can be manipulated by kernel, + * MMU operation, TLB hit & miss also affecting this data structure (dirty, accessed bit, etc). + * Warning: Address must be aligned in 4 KB (listed on Intel Manual), use __attribute__((aligned(0x1000))), + * unaligned definition of PageDirectory will cause triple fault + * + * @param table Fixed-width array of PageDirectoryEntry with size PAGE_ENTRY_COUNT + */ +struct PageDirectory { + // TODO : Implement +} __attribute__((packed)); + +/** + * Containing page driver states + * + * @param last_available_physical_addr Pointer to last empty physical addr (multiple of 4 MiB) + */ +struct PageDriverState { + uint8_t *last_available_physical_addr; +} __attribute__((packed)); + + + + + +/** + * update_page_directory, + * Edit _paging_kernel_page_directory with respective parameter + * + * @param physical_addr Physical address to map + * @param virtual_addr Virtual address to map + * @param flag Page entry flags + */ +void update_page_directory(void *physical_addr, void *virtual_addr, struct PageDirectoryEntryFlag flag); + +/** + * flush_single_tlb, + * invalidate page that contain virtual address in parameter + * + * @param virtual_addr Virtual address to flush + */ +void flush_single_tlb(void *virtual_addr); + +/** + * Allocate user memory into specified virtual memory address. + * Multiple call on same virtual address will unmap previous physical address and change it into new one. + * + * @param virtual_addr Virtual address to be mapped + * @return int8_t 0 success, -1 for failed allocation + */ +int8_t allocate_single_user_page_frame(void *virtual_addr); + +#endif \ No newline at end of file From 21d00e79a4147184461d4dc9e78df175c5cde1ec Mon Sep 17 00:00:00 2001 From: NerbFox <13521043@std.stei.itb.ac.id> Date: Mon, 3 Apr 2023 16:40:13 +0700 Subject: [PATCH 30/52] Create kernel_loader.s --- src/paging/kernel_loader.s | 90 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/paging/kernel_loader.s diff --git a/src/paging/kernel_loader.s b/src/paging/kernel_loader.s new file mode 100644 index 0000000..a0ddd3b --- /dev/null +++ b/src/paging/kernel_loader.s @@ -0,0 +1,90 @@ +global loader ; the entry symbol for ELF +global enter_protected_mode ; go to protected mode +global set_tss_register ; set tss register to GDT entry +extern kernel_setup ; kernel C entrypoint +extern _paging_kernel_page_directory ; kernel page directory + +KERNEL_VIRTUAL_BASE equ 0xC0000000 ; kernel virtual memory +KERNEL_STACK_SIZE equ 2097152 ; size of stack in bytes +MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant +FLAGS equ 0x0 ; multiboot flags +CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum + ; (magic number + checksum + flags should equal 0) + + +section .bss +align 4 ; align at 4 bytes +kernel_stack: ; label points to beginning of memory + resb KERNEL_STACK_SIZE ; reserve stack for the kernel + + +section .multiboot ; GRUB multiboot header +align 4 ; the code must be 4 byte aligned + dd MAGIC_NUMBER ; write the magic number to the machine code, + dd FLAGS ; the flags, + dd CHECKSUM ; and the checksum + + + +section .setup.text ; start of the text (code) section +loader equ (loader_entrypoint - KERNEL_VIRTUAL_BASE) +loader_entrypoint: ; the loader label (defined as entry point in linker script) + ; Set CR3 (CPU page register) + mov eax, _paging_kernel_page_directory - KERNEL_VIRTUAL_BASE + mov cr3, eax + + ; Use 4 MB paging + mov eax, cr4 + or eax, 0x00000010 ; PSE (4 MB paging) + mov cr4, eax + + ; Enable paging + mov eax, cr0 + or eax, 0x80000000 ; PG flag + mov cr0, eax + + ; Jump into higher half first, cannot use C because call stack is still not working + lea eax, [loader_virtual] + jmp eax + +loader_virtual: + mov dword [_paging_kernel_page_directory], 0 + invlpg [0] ; Delete identity mapping and invalidate TLB cache for first page + mov esp, kernel_stack + KERNEL_STACK_SIZE ; Setup stack register to proper location + call kernel_setup +.loop: + jmp .loop ; loop forever + + + +section .text +; More details: https://en.wikibooks.org/wiki/X86_Assembly/Protected_Mode +enter_protected_mode: + ; Load GDT from GDTDescriptor + cli + mov eax, [esp+4] + lgdt [eax] + + ; Set Protection Enable bit-flag in Control Register 0 (CR0) + ; Or in other words: Switch to protected mode + mov eax, cr0 + or eax, 1 + mov cr0, eax + + ; Far jump to update cs register + ; Warning: Invalid GDT will raise exception in any instruction below + jmp 0x8:flush_cs +flush_cs: + ; Update all segment register + mov ax, 10h + mov ss, ax + mov ds, ax + mov es, ax + + ret + +set_tss_register: + mov ax, 0x28 | 0 ; GDT TSS Selector, ring 0 + ltr ax + ret + From 2ea441535d105a7013b22a7895de9884747af264 Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Tue, 4 Apr 2023 00:03:21 +0700 Subject: [PATCH 31/52] Update 3.1 and 3.2 Co-authored-by: razzanYoni Co-authored-by: Rava Maulana Co-authored-by: Nigel Sahl --- makefile | 14 +++++++-- src/gdt.c | 31 ++++++++++++++++++++ src/interrupt/interrupt.c | 10 ++++++- src/kernel_loader.s | 45 ++++++++++++++++++++++------- src/lib-header/framebuffer.h | 2 +- src/lib-header/gdt.h | 7 +++++ src/lib-header/interrupt.h | 15 ++++++++++ src/{paging => lib-header}/paging.h | 34 ++++++++++++++++------ src/linker.ld | 28 +++++++++++++----- src/paging/paging.c | 22 +++++++++----- src/paging/paging.s | 0 11 files changed, 170 insertions(+), 38 deletions(-) rename src/{paging => lib-header}/paging.h (75%) create mode 100644 src/paging/paging.s diff --git a/makefile b/makefile index dc2f8d5..53770bc 100644 --- a/makefile +++ b/makefile @@ -9,6 +9,8 @@ INTERRUPT_FOLDER = interrupt KEYBOARD_FOLDER = keyboard FILESYSTEM_FOLDER = filesystem FRAMEBUFFER_FOLDER = framebuffer +INSERTER_FOLDER = inserter + # SOURCE_FOLDER = bin/iso/boot/grub OUTPUT_FOLDER = bin ISO_NAME = OSyikkk @@ -18,8 +20,9 @@ DISK_NAME = storage # Flags WARNING_CFLAG = -Wall -Wextra -Werror DEBUG_CFLAG = -ffreestanding -fshort-wchar -g -STRIP_CFLAG = -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -CFLAGS = $(DEBUG_CFLAG) $(WARNING_CFLAG) $(STRIP_CFLAG) -m32 -c -I$(SOURCE_FOLDER) +# STRIP_CFLAG = -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs +# CFLAGS = $(DEBUG_CFLAG) $(WARNING_CFLAG) $(STRIP_CFLAG) -m32 -c -I$(SOURCE_FOLDER) +CFLAGS = $(DEBUG_CFLAG) $(WARNING_CFLAG) -m32 -c -I$(SOURCE_FOLDER) AFLAGS = -f elf32 -g -F dwarf LFLAGS = -T $(SOURCE_FOLDER)/linker.ld -melf_i386 @@ -37,7 +40,7 @@ disk: @qemu-img create -f raw $(OUTPUT_FOLDER)/$(DISK_NAME).bin 4M -kernel: +kernel: inserter @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/kernel_loader.s -o $(OUTPUT_FOLDER)/kernel_loader.o @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/intsetup.s -o $(OUTPUT_FOLDER)/intsetup.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/kernel.c -o $(OUTPUT_FOLDER)/kernel.o @@ -74,3 +77,8 @@ iso: kernel -o $(OUTPUT_FOLDER)/$(ISO_NAME).iso \ $(OUTPUT_FOLDER)/iso +inserter: + $(CC) -Wno-builtin-declaration-mismatch \ + $(SOURCE_FOLDER)/stdmem.c $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/fat32.c \ + $(SOURCE_FOLDER)/$(INSERTER_FOLDER)/external-inserter.c \ + -o $(OUTPUT_FOLDER)/inserter \ No newline at end of file diff --git a/src/gdt.c b/src/gdt.c index 241a88c..a826345 100644 --- a/src/gdt.c +++ b/src/gdt.c @@ -84,3 +84,34 @@ struct GDTR _gdt_gdtr = { .address = &global_descriptor_table }; +// static struct GlobalDescriptorTable global_descriptor_table = { +// .table = { +// {/* TODO: Null Descriptor */}, +// {/* TODO: Kernel Code Descriptor */}, +// {/* TODO: Kernel Data Descriptor */}, +// {/* TODO: User Code Descriptor */}, +// {/* TODO: User Data Descriptor */}, +// { +// .segment_high = (sizeof(struct TSSEntry) & (0xF << 16)) >> 16, +// .segment_low = sizeof(struct TSSEntry), +// .base_high = 0, +// .base_mid = 0, +// .base_low = 0, +// .non_system = 0, // S bit +// .type_bit = 0x9, +// .privilege = 0, // DPL +// .valid_bit = 1, // P bit +// .opr_32_bit = 1, // D/B bit +// .long_mode = 0, // L bit +// .granularity = 0, // G bit +// }, +// {0} +// } +// }; + +// void gdt_install_tss(void) { +// uint32_t base = (uint32_t) &_interrupt_tss_entry; +// global_descriptor_table.table[5].base_high = (base & (0xFF << 24)) >> 24; +// global_descriptor_table.table[5].base_mid = (base & (0xFF << 16)) >> 16; +// global_descriptor_table.table[5].base_low = base & 0xFFFF; +// } diff --git a/src/interrupt/interrupt.c b/src/interrupt/interrupt.c index 0eab274..c9514d1 100644 --- a/src/interrupt/interrupt.c +++ b/src/interrupt/interrupt.c @@ -58,4 +58,12 @@ void main_interrupt_handler ( void activate_keyboard_interrupt(void) { out(PIC1_DATA, PIC_DISABLE_ALL_MASK ^ (1 << IRQ_KEYBOARD)); out(PIC2_DATA, PIC_DISABLE_ALL_MASK); -} \ No newline at end of file +} + +void set_tss_kernel_current_stack(void) { + uint32_t stack_ptr; + // Reading base stack frame instead esp + __asm__ volatile ("mov %%ebp, %0": "=r"(stack_ptr) : /* */); + // Add 8 because 4 for ret address and other 4 is for stack_ptr variable + _interrupt_tss_entry.esp0 = stack_ptr + 8; +} diff --git a/src/kernel_loader.s b/src/kernel_loader.s index 738278d..a53eaf6 100644 --- a/src/kernel_loader.s +++ b/src/kernel_loader.s @@ -1,9 +1,11 @@ -global loader ; the entry symbol for ELF -global enter_protected_mode ; go to protected mode -extern kernel_setup ; kernel - +global loader ; the entry symbol for ELF +global enter_protected_mode ; go to protected mode +global set_tss_register ; set tss register to GDT entry +extern kernel_setup ; kernel +extern _paging_kernel_page_directory ; kernel page directory +KERNEL_VIRTUAL_BASE equ 0xC0000000 ; kernel virtual memory KERNEL_STACK_SIZE equ 2097152 ; size of stack in bytes MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant FLAGS equ 0x0 ; multiboot flags @@ -21,17 +23,36 @@ align 4 dd FLAGS ; the flags, dd CHECKSUM ; and the checksum -section .text ; start of the text (code) section +section .setup.text ; start of the text (code) section +loader equ (loader_entrypoint - KERNEL_VIRTUAL_BASE) +loader_entrypoint: ; the loader label (defined as entry point in linker script) + ; Set CR3 (CPU page register) + mov eax, _paging_kernel_page_directory - KERNEL_VIRTUAL_BASE + mov cr3, eax + ; Use 4 MB paging + mov eax, cr4 + or eax, 0x00000010 ; PSE (4 MB paging) + mov cr4, eax + ; Enable paging + mov eax, cr0 + or eax, 0x80000000 ; PG flag + mov cr0, eax -loader: ; the loader label (defined as entry point in linker script) - mov esp, kernel_stack + KERNEL_STACK_SIZE ; setup stack register to proper location - call kernel_setup + ; Jump into higher half first, cannot use C because call stack is not working + lea eax, [loader_virtual] + jmp eax + +loader_virtual: + mov dword [_paging_kernel_page_directory], 0 + invlpg [0] ; Delete identity mapping and invalidate TLB cache for first page + mov esp, kernel_stack + KERNEL_STACK_SIZE ; Setup stack register to proper location + call kernel_setup ; Call kernel setup .loop: jmp .loop ; loop forever - +section .text ; start of the text (code) section ; More details: https://en.wikibooks.org/wiki/X86_Assembly/Protected_Mode enter_protected_mode: cli @@ -56,5 +77,9 @@ flush_cs: mov ds, ax mov es, ax - ret + +set_tss_register: + mov ax, 0x28 | 0 ; GDT TSS Selector, ring 0 + ltr ax + ret \ No newline at end of file diff --git a/src/lib-header/framebuffer.h b/src/lib-header/framebuffer.h index 5185330..b298bb6 100644 --- a/src/lib-header/framebuffer.h +++ b/src/lib-header/framebuffer.h @@ -3,7 +3,7 @@ #include "lib-header/stdtype.h" -#define MEMORY_FRAMEBUFFER (uint8_t *) 0xB8000 +#define MEMORY_FRAMEBUFFER (uint8_t *) 0xC00B8000 #define CURSOR_PORT_CMD 0x03D4 #define CURSOR_PORT_DATA 0x03D5 #define VGA_CURSOR_HIGH 0x0E diff --git a/src/lib-header/gdt.h b/src/lib-header/gdt.h index afce048..57c7572 100644 --- a/src/lib-header/gdt.h +++ b/src/lib-header/gdt.h @@ -1,5 +1,8 @@ #ifndef _GDT_H #define _GDT_H +#define GDT_USER_CODE_SEGMENT_SELECTOR 0x18 +#define GDT_USER_DATA_SEGMENT_SELECTOR 0x20 +#define GDT_TSS_SELECTOR 0x28 #include "lib-header/stdtype.h" @@ -60,4 +63,8 @@ struct GDTR { struct GlobalDescriptorTable *address; } __attribute__((packed)); + +// Set GDT_TSS_SELECTOR with proper TSS values, accessing _interrupt_tss_entry +void gdt_install_tss(void); + #endif \ No newline at end of file diff --git a/src/lib-header/interrupt.h b/src/lib-header/interrupt.h index e253bae..b92c54b 100644 --- a/src/lib-header/interrupt.h +++ b/src/lib-header/interrupt.h @@ -5,6 +5,21 @@ #include "portio.h" #include "keyboard.h" #include "framebuffer.h" +extern struct TSSEntry _interrupt_tss_entry; + +/** + * TSSEntry, Task State Segment. Used when jumping back to ring 0 / kernel + */ +struct TSSEntry { + uint32_t prev_tss; // Previous TSS + uint32_t esp0; // Stack pointer to load when changing to kernel mode + uint32_t ss0; // Stack segment to load when changing to kernel mode + // Unused variables + uint32_t unused_register[23]; +} __attribute__((packed)); + +// Set kernel stack in TSS +void set_tss_kernel_current_stack(void); /* -- PIC constants -- */ diff --git a/src/paging/paging.h b/src/lib-header/paging.h similarity index 75% rename from src/paging/paging.h rename to src/lib-header/paging.h index 7e998b2..574b8f0 100644 --- a/src/paging/paging.h +++ b/src/lib-header/paging.h @@ -1,7 +1,7 @@ #ifndef _PAGING_H #define _PAGING_H -#include "stdtype.h" +#include "./stdtype.h" #define PAGE_ENTRY_COUNT 1024 #define PAGE_FRAME_SIZE (4*1024*1024) @@ -9,18 +9,31 @@ // Operating system page directory, using page size PAGE_FRAME_SIZE (4 MiB) extern struct PageDirectory _paging_kernel_page_directory; - +// ? : Activating paging /** * Page Directory Entry Flag, only first 8 bit * - * @param present_bit Indicate whether this entry is exist or not - * ... + * @param present_bit Indicate whether this entry is exist or not + * @param read_write_bit + * @param user_supervisor_bit + * @param write_through_bit + * @param cache_disable_bit + * @param accessed_bit + * @param available_bit + * @param page_size_bit */ struct PageDirectoryEntryFlag { - uint8_t present_bit : 1; + uint8_t present_bit : 1; // TODO : Continue. Note: Only first 8 bit flags + uint8_t write_bit : 1; + uint8_t user_bit : 1; + uint8_t write_through_bit : 1; + uint8_t cache_disable_bit : 1; + uint8_t accessed_bit : 1; + uint8_t available_bit : 1; + uint8_t use_pagesize_4_mb : 1; } __attribute__((packed)); /** @@ -38,6 +51,11 @@ struct PageDirectoryEntry { struct PageDirectoryEntryFlag flag; uint16_t global_page : 1; // TODO : Continue, Use uint16_t + bitfield here, Do not use uint8_t + uint16_t available : 3; + uint16_t pat_bit : 1; + uint16_t higher_address: 8; + uint16_t reserved : 1; + uint16_t lower_address : 10; } __attribute__((packed)); /** @@ -51,7 +69,8 @@ struct PageDirectoryEntry { */ struct PageDirectory { // TODO : Implement -} __attribute__((packed)); + struct PageDirectoryEntry table[PAGE_ENTRY_COUNT]; +} __attribute__((aligned(0x1000))); /** * Containing page driver states @@ -63,9 +82,6 @@ struct PageDriverState { } __attribute__((packed)); - - - /** * update_page_directory, * Edit _paging_kernel_page_directory with respective parameter diff --git a/src/linker.ld b/src/linker.ld index d5c80f7..4bb0d99 100644 --- a/src/linker.ld +++ b/src/linker.ld @@ -1,31 +1,45 @@ ENTRY(loader) /* the name of the entry label */ SECTIONS { - . = 0x00100000; /* the code should be loaded at 1 MB */ + . = 0xC0100000; /* use relocation address (memory references) at 0xC010 0000 */ - .__mbHeader : /* align at 4 KB */ + /* Optional variable that can be used in kernel, starting address of kernel */ + _linker_kernel_virtual_addr_start = .; + _linker_kernel_physical_addr_start = . - 0xC0000000; + + .__mbHeader ALIGN (0x1000) : AT (ADDR (.__mbHeader) - 0xC0000000) + { + *(.__mbHeader) /* put GRUB multiboot header at front */ + } + + .setup.text ALIGN (0x1000) : AT (ADDR (.setup.text) - 0xC0000000) { - *(.__mbHeader) /* all text sections from all files */ + *(.setup.text) /* initial setup code */ } - .text ALIGN (0x1000) : /* align at 4 KB */ + .text ALIGN (0x1000) : AT (ADDR (.text) - 0xC0000000) { *(.text) /* all text sections from all files */ } - .rodata ALIGN (0x1000) : /* align at 4 KB */ + .rodata ALIGN (0x1000) : AT (ADDR (.rodata) - 0xC0000000) { *(.rodata*) /* all read-only data sections from all files */ } - .data ALIGN (0x1000) : /* align at 4 KB */ + .data ALIGN (0x1000) : AT (ADDR (.data) - 0xC0000000) { *(.data) /* all data sections from all files */ } - .bss ALIGN (0x1000) : /* align at 4 KB */ + .bss ALIGN (0x1000) : AT (ADDR (.bss) - 0xC0000000) { *(COMMON) /* all COMMON sections from all files */ *(.bss) /* all bss sections from all files */ + kernel_loader.o(.bss) + _linker_kernel_stack_top = .; } + /* Optional variable that can be used in kernel, show end address of kernel */ + _linker_kernel_virtual_addr_end = .; + _linker_kernel_physical_addr_end = . - 0xC0000000; } diff --git a/src/paging/paging.c b/src/paging/paging.c index 926466c..21f5358 100644 --- a/src/paging/paging.c +++ b/src/paging/paging.c @@ -1,4 +1,4 @@ -#include "lib-header/paging.h" +#include "../lib-header/paging.h" __attribute__((aligned(0x1000))) struct PageDirectory _paging_kernel_page_directory = { .table = { @@ -30,13 +30,21 @@ void update_page_directory_entry(void *physical_addr, void *virtual_addr, struct } int8_t allocate_single_user_page_frame(void *virtual_addr) { - // Using default QEMU config (128 MiB max memory) - uint32_t last_physical_addr = (uint32_t) page_driver_state.last_available_physical_addr; - - // TODO : Allocate Page Directory Entry with user privilege - return -1; + uint32_t start = (uint32_t) page_driver_state.last_available_physical_addr; + if (start == 0x100000) { + return -1; + } else { + update_page_directory_entry((void*) start, virtual_addr, (struct PageDirectoryEntryFlag) { + .present_bit = 1, + .write_bit = 1, + .user_bit = 1, + .use_pagesize_4_mb = 1, + }); + page_driver_state.last_available_physical_addr = (uint8_t*) start + PAGE_FRAME_SIZE; + return 0; + } } void flush_single_tlb(void *virtual_addr) { asm volatile("invlpg (%0)" : /* */ : "b"(virtual_addr): "memory"); -} +} \ No newline at end of file diff --git a/src/paging/paging.s b/src/paging/paging.s new file mode 100644 index 0000000..e69de29 From 7bb8c31e7b91280fb24e5a6d2f87147f5debc0cc Mon Sep 17 00:00:00 2001 From: razzanYoni <13521087@mahasiswa.itb.ac.id> Date: Fri, 21 Apr 2023 15:14:10 +0700 Subject: [PATCH 32/52] implement 3.2 Co-authored-by: Nigel Sahl --- .gitignore | 2 +- .vscode/launch.json | 111 ++-- .vscode/settings.json | 20 +- .vscode/tasks.json | 113 ++-- README.md | 126 ++--- bin/.gitignore | 6 +- cd.iso | Bin 372736 -> 0 bytes makefile | 180 ++++--- src/filesystem/cmos.c | 134 ++--- src/filesystem/disk.c | 104 ++-- src/filesystem/fat32.c | 888 +++++++++++++++---------------- src/framebuffer/framebuffer.c | 168 +++--- src/gdt.c | 362 +++++++++---- src/inserter/external-inserter.c | 197 +++---- src/interrupt/idt.c | 82 +-- src/interrupt/interrupt.c | 139 ++--- src/interrupt/intsetup.s | 252 ++++----- src/kernel.c | 184 ++++--- src/kernel_loader.s | 190 ++++--- src/keyboard/keyboard.c | 362 ++++++------- src/lib-header/cmos.h | 114 ++-- src/lib-header/disk.h | 102 ++-- src/lib-header/fat32.h | 516 +++++++++--------- src/lib-header/framebuffer.h | 158 +++--- src/lib-header/gdt.h | 139 ++--- src/lib-header/idt.h | 198 +++---- src/lib-header/interrupt.h | 284 +++++----- src/lib-header/kernel_loader.h | 44 +- src/lib-header/keyboard.h | 232 ++++---- src/lib-header/paging.h | 222 ++++---- src/lib-header/portio.h | 72 +-- src/lib-header/stdmem.h | 110 ++-- src/lib-header/stdtype.h | 92 ++-- src/linker.ld | 90 ++-- src/menu.lst | 10 +- src/paging/kernel_loader.s | 180 +++---- src/paging/paging.c | 98 ++-- src/portio.c | 84 +-- src/stdmem.c | 86 +-- src/user-entry.s | 7 + src/user-linker.ld | 24 + src/user-shell.c | 7 + src/user-shell.s | 7 + 43 files changed, 3396 insertions(+), 3100 deletions(-) delete mode 100644 cd.iso create mode 100644 src/user-entry.s create mode 100644 src/user-linker.ld create mode 100644 src/user-shell.c create mode 100644 src/user-shell.s diff --git a/.gitignore b/.gitignore index ad213f1..b40125b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/test +/test *.json \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index ca6c8fc..de62a7c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,42 +1,71 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Kernel", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceRoot}/bin/kernel", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ], - "preLaunchTask": "Launch QEMU", - "postDebugTask": "Exit QEMU", - "targetArchitecture": "x86", - "customLaunchSetupCommands": [ - { - "text": "target remote localhost:1234", - "description": "Connect to QEMU remote debugger" - }, - { - "text": "symbol-file kernel", - "description": "Get kernel symbols" - }, - { - "text": "set output-radix 16", - "description": "Use hexadecimal output" - } - ], - "avoidWindowsConsoleRedirection": true - }, - ] +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Kernel", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceRoot}/bin/kernel", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "Launch QEMU", + "postDebugTask": "Exit QEMU", + "targetArchitecture": "x86", + "customLaunchSetupCommands": [ + { + "text": "target remote localhost:1234", + "description": "Connect to QEMU remote debugger" + }, + { + "text": "symbol-file kernel", + "description": "Get kernel symbols" + }, + { + "text": "set output-radix 16", + "description": "Use hexadecimal output" + } + ], + "avoidWindowsConsoleRedirection": true + }, + { + "name": "Inserter", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceRoot}/bin/inserter", + "args": ["shell", "2", "storage.bin"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "preLaunchTask": "Build Inserter", + "externalConsole": false, + "MIMode": "gdb", + "setupCommands" : [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassemby Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + }, + { + "text": "set output-radix 16", + "description": "Use hexadecimal output" + } + ] + } + ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index d7f5ca5..4e910ac 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,11 @@ -{ - "debug.onTaskErrors": "debugAnyway", - "files.associations": { - "stdmem.h": "c", - "fat32.h": "c", - "stdtype.h": "c", - "portio.h": "c", - "cmos.h": "c", - "interrupt.h": "c" - }, +{ + "debug.onTaskErrors": "debugAnyway", + "files.associations": { + "stdmem.h": "c", + "fat32.h": "c", + "stdtype.h": "c", + "portio.h": "c", + "cmos.h": "c", + "interrupt.h": "c" + }, } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9617140..adbe69c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,50 +1,65 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "type": "cppbuild", - "label": "Build OS", - "command": "make", - "args": [ - "build", - ], - "options": { - "cwd": "${workspaceFolder}" - }, - "group": { - "kind": "build", - "isDefault": true - } - }, - { - "type": "shell", - "label": "Launch QEMU", - "command": "make debug", - "isBackground": true, - "dependsOn": "Build OS", - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": { - "pattern": [ - { - "regexp": ".", - "file": 1, - "location": 2, - "message": 3 - } - ], - "background": { - "activeOnStart": true, - "beginsPattern": ".", - "endsPattern": ".", - } - } - }, - { - "type": "shell", - "label": "Exit QEMU", - "command": "pkill -f qemu || test $? -eq 1" - } - ], +{ + "version": "2.0.0", + "tasks": [ + { + "type": "cppbuild", + "label": "Build OS", + "command": "make", + "args": [ + "build", + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "shell", + "label": "Launch QEMU", + "command": "make debug", + "isBackground": true, + "dependsOn": "Build OS", + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": { + "pattern": [ + { + "regexp": ".", + "file": 1, + "location": 2, + "message": 3 + } + ], + "background": { + "activeOnStart": true, + "beginsPattern": ".", + "endsPattern": ".", + } + } + }, + { + "type": "cppbuild", + "label": "Build Inserter", + "command": "make", + "args": [ + "inserter", + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "shell", + "label": "Exit QEMU", + "command": "pkill -f qemu || test $? -eq 1" + } + ], } \ No newline at end of file diff --git a/README.md b/README.md index 99eca83..9cbb2fa 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,63 @@ - - -# Osyikk - Operating System - -Tugas IF2230 - Sistem Operasi 2023 Tahun 2022/2023 - -## Table of Contents -* [General Info](#general-information) -* [Technologies Used](#technologies-used) -* [Features](#features) -* [Screenshots](#screenshots) -* [How to Run](#How-to-Run) -* [Project Status](#project-status) -* [Room for Improvement](#room-for-improvement) -* [Division of tasks](#division-of-tasks) - - - -## General Information -Operating System - - -## Technologies Used -- Qemu -- NASM -- C - - -## Features -- Implementasi dari mata kuliah Sistem Operasi - -## Screenshots -![Example screenshot](./doc/OS.png) - -## Installation -Clone the repo -```sh -git clone https://github.com/Sister20/if2230-2023-osyikkk.git -``` - -## Usage -```sh -make run -``` - -## Project Status -Project is: _not complete_ - - -## Room for Improvement - -Room for improvement: -- speed up the code -- add more features - - -## Division of tasks -[13521043 Nigel Sahl](https://github.com/NerbFox) -[13521087 Razzan Daksana Yoni](https://github.com/razzanYoni) -[13521149 Rava Maulana Azzikri](https://github.com/RMA1403) -[13521157 Hanif Muhammad Zhafran](https://github.com/hanifmz07) + + +# Osyikk - Operating System + +Tugas IF2230 - Sistem Operasi 2023 Tahun 2022/2023 + +## Table of Contents +* [General Info](#general-information) +* [Technologies Used](#technologies-used) +* [Features](#features) +* [Screenshots](#screenshots) +* [How to Run](#How-to-Run) +* [Project Status](#project-status) +* [Room for Improvement](#room-for-improvement) +* [Division of tasks](#division-of-tasks) + + + +## General Information +Operating System + + +## Technologies Used +- Qemu +- NASM +- C + + +## Features +- Implementasi dari mata kuliah Sistem Operasi + +## Screenshots +![Example screenshot](./doc/OS.png) + +## Installation +Clone the repo +```sh +git clone https://github.com/Sister20/if2230-2023-osyikkk.git +``` + +## Usage +```sh +make run +``` + +## Project Status +Project is: _not complete_ + + +## Room for Improvement + +Room for improvement: +- speed up the code +- add more features + + +## Division of tasks +[13521043 Nigel Sahl](https://github.com/NerbFox) +[13521087 Razzan Daksana Yoni](https://github.com/razzanYoni) +[13521149 Rava Maulana Azzikri](https://github.com/RMA1403) +[13521157 Hanif Muhammad Zhafran](https://github.com/hanifmz07) diff --git a/bin/.gitignore b/bin/.gitignore index 40b71b3..6da7fa5 100644 --- a/bin/.gitignore +++ b/bin/.gitignore @@ -1,4 +1,4 @@ -*.o -*.iso -* +*.o +*.iso +* !.gitignore \ No newline at end of file diff --git a/cd.iso b/cd.iso deleted file mode 100644 index 5b11db49e49ef947e61f2b9be0f599789579111b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 372736 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 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+0D*4? Gf&T@vlbTQf diff --git a/makefile b/makefile index 53770bc..0884b24 100644 --- a/makefile +++ b/makefile @@ -1,84 +1,96 @@ -# Compiler & linker -ASM = nasm -LIN = ld -CC = gcc - -# Directory -SOURCE_FOLDER = src -INTERRUPT_FOLDER = interrupt -KEYBOARD_FOLDER = keyboard -FILESYSTEM_FOLDER = filesystem -FRAMEBUFFER_FOLDER = framebuffer -INSERTER_FOLDER = inserter - -# SOURCE_FOLDER = bin/iso/boot/grub -OUTPUT_FOLDER = bin -ISO_NAME = OSyikkk -# DISK -DISK_NAME = storage - -# Flags -WARNING_CFLAG = -Wall -Wextra -Werror -DEBUG_CFLAG = -ffreestanding -fshort-wchar -g -# STRIP_CFLAG = -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -# CFLAGS = $(DEBUG_CFLAG) $(WARNING_CFLAG) $(STRIP_CFLAG) -m32 -c -I$(SOURCE_FOLDER) -CFLAGS = $(DEBUG_CFLAG) $(WARNING_CFLAG) -m32 -c -I$(SOURCE_FOLDER) -AFLAGS = -f elf32 -g -F dwarf -LFLAGS = -T $(SOURCE_FOLDER)/linker.ld -melf_i386 - - -run: all - # @qemu-system-i386 -s -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso - @qemu-system-i386 -s -drive file=$(OUTPUT_FOLDER)/$(DISK_NAME).bin,format=raw,if=ide,index=0,media=disk -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso -debug: all - @qemu-system-i386 -s -S -drive file=$(OUTPUT_FOLDER)/$(DISK_NAME).bin,format=raw,if=ide,index=0,media=disk -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso -all: build -build: iso -clean: - rm -rf *.o *.iso $(OUTPUT_FOLDER)/kernel -disk: - @qemu-img create -f raw $(OUTPUT_FOLDER)/$(DISK_NAME).bin 4M - - -kernel: inserter - @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/kernel_loader.s -o $(OUTPUT_FOLDER)/kernel_loader.o - @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/intsetup.s -o $(OUTPUT_FOLDER)/intsetup.o - @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/kernel.c -o $(OUTPUT_FOLDER)/kernel.o - @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/stdmem.c -o $(OUTPUT_FOLDER)/stdmem.o - @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/portio.c -o $(OUTPUT_FOLDER)/portio.o - @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/gdt.c -o $(OUTPUT_FOLDER)/gdt.o - - @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(FRAMEBUFFER_FOLDER)/framebuffer.c -o $(OUTPUT_FOLDER)/framebuffer.o - @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/idt.c -o $(OUTPUT_FOLDER)/idt.o - @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/interrupt.c -o $(OUTPUT_FOLDER)/interrupt.o - @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(KEYBOARD_FOLDER)/keyboard.c -o $(OUTPUT_FOLDER)/keyboard.o - @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/disk.c -o $(OUTPUT_FOLDER)/disk.o - @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/fat32.c -o $(OUTPUT_FOLDER)/fat32.o - - - @$(LIN) $(LFLAGS) bin/*.o -o $(OUTPUT_FOLDER)/kernel - @echo Linking object files and generate elf32... - @rm -f *.o - -iso: kernel - # @rm -r $(OUTPUT_FOLDER)/iso/ - @mkdir -p $(OUTPUT_FOLDER)/iso/boot/grub - @cp $(OUTPUT_FOLDER)/kernel $(OUTPUT_FOLDER)/iso/boot/ - @cp other/grub1 $(OUTPUT_FOLDER)/iso/boot/grub/ - @cp $(SOURCE_FOLDER)/menu.lst $(OUTPUT_FOLDER)/iso/boot/grub/ - @genisoimage -R \ - -b boot/grub/grub1 \ - -no-emul-boot \ - -boot-load-size 4 \ - -A $(ISO_NAME) \ - -input-charset utf8 \ - -quiet \ - -boot-info-table \ - -o $(OUTPUT_FOLDER)/$(ISO_NAME).iso \ - $(OUTPUT_FOLDER)/iso - -inserter: - $(CC) -Wno-builtin-declaration-mismatch \ - $(SOURCE_FOLDER)/stdmem.c $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/fat32.c \ - $(SOURCE_FOLDER)/$(INSERTER_FOLDER)/external-inserter.c \ - -o $(OUTPUT_FOLDER)/inserter \ No newline at end of file +# Compiler & linker +ASM = nasm +LIN = ld +CC = gcc + +# Directory +SOURCE_FOLDER = src +INTERRUPT_FOLDER = interrupt +KEYBOARD_FOLDER = keyboard +FILESYSTEM_FOLDER = filesystem +FRAMEBUFFER_FOLDER = framebuffer +INSERTER_FOLDER = inserter + +# SOURCE_FOLDER = bin/iso/boot/grub +OUTPUT_FOLDER = bin +ISO_NAME = OSyikkk +# DISK +DISK_NAME = storage + +# Flags +WARNING_CFLAG = -Wall -Wextra -Werror +DEBUG_CFLAG = -ffreestanding -fshort-wchar -g +# STRIP_CFLAG = -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs +# CFLAGS = $(DEBUG_CFLAG) $(WARNING_CFLAG) $(STRIP_CFLAG) -m32 -c -I$(SOURCE_FOLDER) +CFLAGS = $(DEBUG_CFLAG) $(WARNING_CFLAG) -m32 -c -I$(SOURCE_FOLDER) +AFLAGS = -f elf32 -g -F dwarf +LFLAGS = -T $(SOURCE_FOLDER)/linker.ld -melf_i386 + + +run: all + # @qemu-system-i386 -s -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso + @qemu-system-i386 -s -drive file=$(OUTPUT_FOLDER)/$(DISK_NAME).bin,format=raw,if=ide,index=0,media=disk -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso +debug: all + @qemu-system-i386 -s -S -drive file=$(OUTPUT_FOLDER)/$(DISK_NAME).bin,format=raw,if=ide,index=0,media=disk -cdrom $(OUTPUT_FOLDER)/$(ISO_NAME).iso +all: build +build: iso +clean: + rm -rf *.o *.iso $(OUTPUT_FOLDER)/kernel +disk: + @qemu-img create -f raw $(OUTPUT_FOLDER)/$(DISK_NAME).bin 4M + + +kernel: inserter + @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/kernel_loader.s -o $(OUTPUT_FOLDER)/kernel_loader.o + @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/intsetup.s -o $(OUTPUT_FOLDER)/intsetup.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/kernel.c -o $(OUTPUT_FOLDER)/kernel.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/stdmem.c -o $(OUTPUT_FOLDER)/stdmem.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/portio.c -o $(OUTPUT_FOLDER)/portio.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/gdt.c -o $(OUTPUT_FOLDER)/gdt.o + + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(FRAMEBUFFER_FOLDER)/framebuffer.c -o $(OUTPUT_FOLDER)/framebuffer.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/idt.c -o $(OUTPUT_FOLDER)/idt.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(INTERRUPT_FOLDER)/interrupt.c -o $(OUTPUT_FOLDER)/interrupt.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(KEYBOARD_FOLDER)/keyboard.c -o $(OUTPUT_FOLDER)/keyboard.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/disk.c -o $(OUTPUT_FOLDER)/disk.o + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/fat32.c -o $(OUTPUT_FOLDER)/fat32.o + + @$(LIN) $(LFLAGS) bin/*.o -o $(OUTPUT_FOLDER)/kernel + @echo Linking object files and generate elf32... + @rm -f *.o + +iso: kernel + # @rm -r $(OUTPUT_FOLDER)/iso/ + @mkdir -p $(OUTPUT_FOLDER)/iso/boot/grub + @cp $(OUTPUT_FOLDER)/kernel $(OUTPUT_FOLDER)/iso/boot/ + @cp other/grub1 $(OUTPUT_FOLDER)/iso/boot/grub/ + @cp $(SOURCE_FOLDER)/menu.lst $(OUTPUT_FOLDER)/iso/boot/grub/ + @genisoimage -R \ + -b boot/grub/grub1 \ + -no-emul-boot \ + -boot-load-size 4 \ + -A $(ISO_NAME) \ + -input-charset utf8 \ + -quiet \ + -boot-info-table \ + -o $(OUTPUT_FOLDER)/$(ISO_NAME).iso \ + $(OUTPUT_FOLDER)/iso + +inserter: + $(CC) -Wno-builtin-declaration-mismatch \ + $(SOURCE_FOLDER)/stdmem.c $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/fat32.c \ + $(SOURCE_FOLDER)/$(INSERTER_FOLDER)/external-inserter.c \ + -o $(OUTPUT_FOLDER)/inserter + +user-shell: + @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/user-entry.s -o user-entry.o + @$(CC) $(CFLAGS) -fno-pie $(SOURCE_FOLDER)/user-shell.c -o user-shell.o + @$(LIN) -T $(SOURCE_FOLDER)/user-linker.ld -melf_i386 \ + user-entry.o user-shell.o -o $(OUTPUT_FOLDER)/shell + @echo Linking object shell object files and generate flat binary... + @size --target=binary bin/shell + @rm -f *.o + +insert-shell: inserter user-shell + @echo Inserting shell into root directory... + @cd $(OUTPUT_FOLDER); ./inserter shell 2 $(DISK_NAME).bin \ No newline at end of file diff --git a/src/filesystem/cmos.c b/src/filesystem/cmos.c index 1391fa6..b540984 100644 --- a/src/filesystem/cmos.c +++ b/src/filesystem/cmos.c @@ -1,68 +1,68 @@ -#include "../lib-header/cmos.h" - -static struct dateNtime currentdatentime; - -void init_cmos(){ - read_cmos(); -} - -int8_t is_update_cmos() { - out(CMOS_ADDRESS, 0x0A); - return (in(CMOS_DATA) & 0x80); -} - -uint8_t get_cmos_reg(int reg) { - out(CMOS_ADDRESS, reg); - return in(CMOS_DATA); -} - -void set_cmos_reg(int reg, uint8_t value){ - out(CMOS_ADDRESS, reg); - out(CMOS_DATA, value); -} - -void read_cmos(){ - while(is_update_cmos()); - - currentdatentime.time.second = get_cmos_reg(0x00); - currentdatentime.time.minute = get_cmos_reg(0x02); - currentdatentime.time.hour = get_cmos_reg(0x04); - currentdatentime.date.day = get_cmos_reg(0x07); - currentdatentime.date.month = get_cmos_reg(0x08); - currentdatentime.date.year = get_cmos_reg(0x09); - - uint8_t regB = get_cmos_reg(0x0B); - - if(!(regB & 0x04)){ - currentdatentime.time.second = (currentdatentime.time.second & 0x0F) + ((currentdatentime.time.second / 16) * 10); - currentdatentime.time.minute = (currentdatentime.time.minute & 0x0F) + ((currentdatentime.time.minute / 16) * 10); - currentdatentime.time.hour = ((currentdatentime.time.hour & 0x0F) + (((currentdatentime.time.hour & 0x70) / 16) * 10)) | (currentdatentime.time.hour & 0x80); - currentdatentime.date.day = (currentdatentime.date.day & 0x0F) + ((currentdatentime.date.day / 16) * 10); - currentdatentime.date.month = (currentdatentime.date.month & 0x0F) + ((currentdatentime.date.month / 16) * 10); - currentdatentime.date.year = (currentdatentime.date.year & 0x0F) + ((currentdatentime.date.year / 16) * 10); - } -} - -void write_cmos(struct dateNtime * dnt){ - while (is_update_cmos()); - - set_cmos_reg(0x00, dnt->time.second); - set_cmos_reg(0x02, dnt->time.minute); - set_cmos_reg(0x04, dnt->time.hour); - set_cmos_reg(0x07, dnt->date.day); - set_cmos_reg(0x08, dnt->date.month); - set_cmos_reg(0x09, dnt->date.year); -} - -int8_t is_leap_year(uint16_t year){ - if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) return 1; - return 0; -} - -struct date get_currdate(){ - return currentdatentime.date; -} - -struct time get_currtime(){ - return currentdatentime.time; +#include "../lib-header/cmos.h" + +static struct dateNtime currentdatentime; + +void init_cmos(){ + read_cmos(); +} + +int8_t is_update_cmos() { + out(CMOS_ADDRESS, 0x0A); + return (in(CMOS_DATA) & 0x80); +} + +uint8_t get_cmos_reg(int reg) { + out(CMOS_ADDRESS, reg); + return in(CMOS_DATA); +} + +void set_cmos_reg(int reg, uint8_t value){ + out(CMOS_ADDRESS, reg); + out(CMOS_DATA, value); +} + +void read_cmos(){ + while(is_update_cmos()); + + currentdatentime.time.second = get_cmos_reg(0x00); + currentdatentime.time.minute = get_cmos_reg(0x02); + currentdatentime.time.hour = get_cmos_reg(0x04); + currentdatentime.date.day = get_cmos_reg(0x07); + currentdatentime.date.month = get_cmos_reg(0x08); + currentdatentime.date.year = get_cmos_reg(0x09); + + uint8_t regB = get_cmos_reg(0x0B); + + if(!(regB & 0x04)){ + currentdatentime.time.second = (currentdatentime.time.second & 0x0F) + ((currentdatentime.time.second / 16) * 10); + currentdatentime.time.minute = (currentdatentime.time.minute & 0x0F) + ((currentdatentime.time.minute / 16) * 10); + currentdatentime.time.hour = ((currentdatentime.time.hour & 0x0F) + (((currentdatentime.time.hour & 0x70) / 16) * 10)) | (currentdatentime.time.hour & 0x80); + currentdatentime.date.day = (currentdatentime.date.day & 0x0F) + ((currentdatentime.date.day / 16) * 10); + currentdatentime.date.month = (currentdatentime.date.month & 0x0F) + ((currentdatentime.date.month / 16) * 10); + currentdatentime.date.year = (currentdatentime.date.year & 0x0F) + ((currentdatentime.date.year / 16) * 10); + } +} + +void write_cmos(struct dateNtime * dnt){ + while (is_update_cmos()); + + set_cmos_reg(0x00, dnt->time.second); + set_cmos_reg(0x02, dnt->time.minute); + set_cmos_reg(0x04, dnt->time.hour); + set_cmos_reg(0x07, dnt->date.day); + set_cmos_reg(0x08, dnt->date.month); + set_cmos_reg(0x09, dnt->date.year); +} + +int8_t is_leap_year(uint16_t year){ + if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) return 1; + return 0; +} + +struct date get_currdate(){ + return currentdatentime.date; +} + +struct time get_currtime(){ + return currentdatentime.time; } \ No newline at end of file diff --git a/src/filesystem/disk.c b/src/filesystem/disk.c index cbbd52f..2aca705 100644 --- a/src/filesystem/disk.c +++ b/src/filesystem/disk.c @@ -1,52 +1,52 @@ -#include "../lib-header/disk.h" -#include "../lib-header/portio.h" - -static void ATA_busy_wait() { - while (in(0x1F7) & ATA_STATUS_BSY); -} - -static void ATA_DRQ_wait() { - while (!(in(0x1F7) & ATA_STATUS_RDY)); -} - -void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count) { - ATA_busy_wait(); - out(0x1F6, 0xE0 | ((logical_block_address >> 24) & 0xF)); - out(0x1F2, block_count); - out(0x1F3, (uint8_t) logical_block_address); - out(0x1F4, (uint8_t) (logical_block_address >> 8)); - out(0x1F5, (uint8_t) (logical_block_address >> 16)); - out(0x1F7, 0x20); - - uint16_t *target = (uint16_t*) ptr; - - for (uint32_t i = 0; i < block_count; i++) { - ATA_busy_wait(); - ATA_DRQ_wait(); - for (uint32_t j = 0; j < HALF_BLOCK_SIZE; j++) - target[j] = in16(0x1F0); - // Note : uint16_t => 2 bytes, HALF_BLOCK_SIZE*2 = BLOCK_SIZE with pointer arithmetic - target += HALF_BLOCK_SIZE; - } -} - -void write_blocks(const void *ptr, uint32_t logical_block_address, uint8_t block_count) { - ATA_busy_wait(); - out(0x1F6, 0xE0 | ((logical_block_address >> 24) & 0xF)); - out(0x1F2, block_count); - out(0x1F3, (uint8_t) logical_block_address); - out(0x1F4, (uint8_t) (logical_block_address >> 8)); - out(0x1F5, (uint8_t) (logical_block_address >> 16)); - out(0x1F7, 0x30); - - for (uint32_t i = 0; i < block_count; i++) { - ATA_busy_wait(); - ATA_DRQ_wait(); - /* Note : uint16_t => 2 bytes, i is current block number to write - HALF_BLOCK_SIZE*i = block_offset with pointer arithmetic - */ - for (uint32_t j = 0; j < HALF_BLOCK_SIZE; j++) - out16(0x1F0, ((uint16_t*) ptr)[HALF_BLOCK_SIZE*i + j]); - } -} - +#include "../lib-header/disk.h" +#include "../lib-header/portio.h" + +static void ATA_busy_wait() { + while (in(0x1F7) & ATA_STATUS_BSY); +} + +static void ATA_DRQ_wait() { + while (!(in(0x1F7) & ATA_STATUS_RDY)); +} + +void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count) { + ATA_busy_wait(); + out(0x1F6, 0xE0 | ((logical_block_address >> 24) & 0xF)); + out(0x1F2, block_count); + out(0x1F3, (uint8_t) logical_block_address); + out(0x1F4, (uint8_t) (logical_block_address >> 8)); + out(0x1F5, (uint8_t) (logical_block_address >> 16)); + out(0x1F7, 0x20); + + uint16_t *target = (uint16_t*) ptr; + + for (uint32_t i = 0; i < block_count; i++) { + ATA_busy_wait(); + ATA_DRQ_wait(); + for (uint32_t j = 0; j < HALF_BLOCK_SIZE; j++) + target[j] = in16(0x1F0); + // Note : uint16_t => 2 bytes, HALF_BLOCK_SIZE*2 = BLOCK_SIZE with pointer arithmetic + target += HALF_BLOCK_SIZE; + } +} + +void write_blocks(const void *ptr, uint32_t logical_block_address, uint8_t block_count) { + ATA_busy_wait(); + out(0x1F6, 0xE0 | ((logical_block_address >> 24) & 0xF)); + out(0x1F2, block_count); + out(0x1F3, (uint8_t) logical_block_address); + out(0x1F4, (uint8_t) (logical_block_address >> 8)); + out(0x1F5, (uint8_t) (logical_block_address >> 16)); + out(0x1F7, 0x30); + + for (uint32_t i = 0; i < block_count; i++) { + ATA_busy_wait(); + ATA_DRQ_wait(); + /* Note : uint16_t => 2 bytes, i is current block number to write + HALF_BLOCK_SIZE*i = block_offset with pointer arithmetic + */ + for (uint32_t j = 0; j < HALF_BLOCK_SIZE; j++) + out16(0x1F0, ((uint16_t*) ptr)[HALF_BLOCK_SIZE*i + j]); + } +} + diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index 18c8566..0e807d9 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -1,445 +1,445 @@ -#include "../lib-header/stdtype.h" -#include "../lib-header/fat32.h" -#include "../lib-header/stdmem.h" -// #include "../lib-header/cmos.h" - -static struct FAT32DriverState driver_state; - -const uint8_t fs_signature[BLOCK_SIZE] = { - 'C', 'o', 'u', 'r', 's', 'e', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', - 'D', 'e', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'b', 'y', ' ', ' ', ' ', ' ', ' ', - 'L', 'a', 'b', ' ', 'S', 'i', 's', 't', 'e', 'r', ' ', 'I', 'T', 'B', ' ', ' ', - 'M', 'a', 'd', 'e', ' ', 'w', 'i', 't', 'h', ' ', '<', '3', ' ', ' ', ' ', ' ', - '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '2', '0', '2', '3', '\n', - [BLOCK_SIZE-2] = 'O', - [BLOCK_SIZE-1] = 'k', -}; - -/* -- Driver Interfaces -- */ - -/** - * Convert cluster number to logical block address - * - * @param cluster Cluster number to convert - * @return uint32_t Logical Block Address - */ -uint32_t cluster_to_lba(uint32_t cluster){ - // dr nanobyte -> lba = data_region_begin + (cluster-2)*sector_per_cluster - return cluster * CLUSTER_BLOCK_COUNT; -} - -/** - * Initialize DirectoryTable value with parent DirectoryEntry and directory name - * - * @param dir_table Pointer to directory table - * @param name 8-byte char for directory name - * @param parent_dir_cluster Parent directory cluster number - */ -void init_directory_table(struct FAT32DirectoryTable *dir_table, char *name, uint32_t parent_dir_cluster){ - struct FAT32DirectoryEntry *new_entry = &(dir_table->table[0]); - memcpy(&(new_entry->name), name, 8); - memcpy(&(new_entry->ext), "\0\0\0", 3); - - new_entry->attribute = ATTR_SUBDIRECTORY; - new_entry->user_attribute = UATTR_NOT_EMPTY; - - new_entry->undelete = 0; - new_entry->create_time = 0; - new_entry->create_date = 0; - new_entry->access_date = 0; - - new_entry->cluster_high = (uint16_t)(parent_dir_cluster >> 16); - - new_entry->modified_time = 0; - new_entry->modified_date = 0; - - new_entry->cluster_low = (uint16_t)(parent_dir_cluster & 0xFFFF); - new_entry->filesize = 0; - - dir_table->table[0] = *new_entry; -} - -/** - * Checking whether filesystem signature is missing or not in boot sector - * - * @return True if memcmp(boot_sector, fs_signature) returning inequality - */ -bool is_empty_storage(void){ - // read boot sector - uint8_t boot_sector[BLOCK_SIZE]; - read_blocks(boot_sector, BOOT_SECTOR, 1); - return memcmp(boot_sector, fs_signature, BLOCK_SIZE) != 0; -} - -/** - * Create new FAT32 file system. Will write fs_signature into boot sector and - * proper FileAllocationTable (contain CLUSTER_0_VALUE, CLUSTER_1_VALUE, - * and initialized root directory) into cluster number 1 - */ -void create_fat32(void){ - write_blocks(fs_signature, BOOT_SECTOR, 1); - - driver_state.fat_table.cluster_map[0] = CLUSTER_0_VALUE; - driver_state.fat_table.cluster_map[1] = CLUSTER_1_VALUE; - driver_state.fat_table.cluster_map[2] = FAT32_FAT_END_OF_FILE; - - write_clusters(&driver_state.fat_table, FAT_CLUSTER_NUMBER, 1); - - init_directory_table(&(driver_state.dir_table_buf), "ROOT\0\0\0\0", ROOT_CLUSTER_NUMBER); - write_clusters(&(driver_state.dir_table_buf), ROOT_CLUSTER_NUMBER, 1); -} - -/** - * @brief - * - * - * - * - * Else, read and cache entire FileAllocationTable (located at cluster number 1) into driver state - */ -void initialize_filesystem_fat32(void){ - if (is_empty_storage()){ - create_fat32(); - } else { - read_clusters(&(driver_state.fat_table), FAT_CLUSTER_NUMBER, 1); - read_clusters(&(driver_state.dir_table_buf), ROOT_CLUSTER_NUMBER, 1); - } -} - -/** - * Write cluster operation, wrapper for write_blocks(). - * Recommended to use struct ClusterBuffer - * - * @param ptr Pointer to source data - * @param cluster_number Cluster number to write - * @param cluster_count Cluster count to write, due limitation of write_blocks block_count 255 => max cluster_count = 63 - */ -void write_clusters(const void *ptr, uint32_t cluster_number, uint8_t cluster_count){ - write_blocks(ptr, cluster_to_lba(cluster_number), cluster_count * CLUSTER_BLOCK_COUNT); -} - -/** - * Read cluster operation, wrapper for read_blocks(). - * Recommended to use struct ClusterBuffer - * - * @param ptr Pointer to buffer for reading - * @param cluster_number Cluster number to read - * @param cluster_count Cluster count to read, due limitation of read_blocks block_count 255 => max cluster_count = 63 - */ -void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count){ - read_blocks(ptr, cluster_to_lba(cluster_number), cluster_count * CLUSTER_BLOCK_COUNT); -} - -/* -- CRUD Operation -- */ - -/** - * @brief - * - * - * - * @param request buf point to struct FAT32DirectoryTable, - * name is directory name, - * ext is unused, - * parent_cluster_number is target directory table to read, - * buffer_size must be exactly sizeof(struct FAT32DirectoryTable) - * @return Error code: 0 success - 1 not a folder - 2 not found - -1 unknown - */ -int8_t read_directory(struct FAT32DriverRequest request){ - int8_t error_code = 2; - - if (memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { - // Folder name empty - return error_code = -1; - } - - if (request.buffer_size != 0) { - // Requested data is not a folder - return error_code = 1; - } - - struct FAT32DirectoryTable parent_table; - read_clusters(&parent_table, request.parent_cluster_number, 1); - - for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - if(memcmp(request.name, parent_table.table[i].name, 8) == 0) { - if (parent_table.table[i].attribute == ATTR_SUBDIRECTORY) { - // terbaca sebagai folder - uint32_t curr_cluster = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; - - struct FAT32DirectoryTable curr_dir; - read_clusters(&curr_dir, curr_cluster, 1); - memcpy(request.buf, &curr_dir, sizeof(struct FAT32DirectoryTable)); - return error_code = 0; - } - } - } - return error_code; -} - - -/** - * FAT32 read, read a file from file system. - * - * @param request All attribute will be used for read, buffer_size will limit reading count - * @return Error code: 0 success - 1 not a file - 2 not enough buffer - 3 not found - -1 unknown - */ -int8_t read(struct FAT32DriverRequest request){ - int8_t error_code = 3; - - if(memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { - // Empty request name error - return error_code = -1; - } - - if (request.buffer_size == 0) { - // Requested data is not a folder - return error_code = 1; - } - - struct FAT32DirectoryTable parent_table; - read_clusters(&parent_table, request.parent_cluster_number, 1); - - for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - if (memcmp(request.name, parent_table.table[i].name, 8) == 0 && memcmp(request.ext, parent_table.table[i].ext, 3) == 0) { - uint32_t buffer_count = request.buffer_size/CLUSTER_SIZE; - uint32_t cluster_number = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; - - // Check for not enough buffer - uint32_t count = 0; - while(cluster_number != FAT32_FAT_END_OF_FILE) { - count++; - cluster_number = driver_state.fat_table.cluster_map[cluster_number]; - } - - if(count > buffer_count) { - // Not enough buffer error - return error_code = 2; - } - - uint32_t j = 0; - cluster_number = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; - while(cluster_number != FAT32_FAT_END_OF_FILE) { - read_clusters((struct ClusterBuffer*) (request.buf) + j, cluster_number, 1); - cluster_number = driver_state.fat_table.cluster_map[cluster_number]; - j++; - } - read_clusters((struct ClusterBuffer*) (request.buf) + j, cluster_number, 1); - return error_code = 0; - } - } - - return error_code; -} - -/** - * @brief - * - * - * - * @param request All attribute will be used for write, buffer_size == 0 then create a folder / directory - * @return Error code: 0 success - 1 file/folder already exist - 2 invalid parent cluster - -1 unknown - */ -int8_t write(struct FAT32DriverRequest request){ - int8_t error_code = -1; - - if(memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { - // Empty request name error - return error_code; - } - - // reserved cluster number - if (request.parent_cluster_number < 2){ - return error_code = 2; - } - - struct FAT32DirectoryTable parent_table; - read_clusters(&parent_table, request.parent_cluster_number, 1); - - uint32_t idx_empty = 0, i = 0; - while(i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry)) { - if(parent_table.table[i].user_attribute != UATTR_NOT_EMPTY) { - idx_empty = i; - break; - } - i++; - } - - // Writes to parent directory table - memcpy(parent_table.table[idx_empty].name, request.name, 8); - parent_table.table[idx_empty].attribute = request.buffer_size == 0 ? ATTR_SUBDIRECTORY : ATTR_ARCHIVE; - parent_table.table[idx_empty].user_attribute = UATTR_NOT_EMPTY; - parent_table.table[idx_empty].undelete = 0; - parent_table.table[idx_empty].create_time = 0; - parent_table.table[idx_empty].create_date = 0; - parent_table.table[idx_empty].access_date = 0; - parent_table.table[idx_empty].modified_time = 0; - parent_table.table[idx_empty].modified_date = 0; - parent_table.table[idx_empty].filesize = request.buffer_size; - - if(request.buffer_size == 0) { - // Create a new directory - uint32_t i = ROOT_CLUSTER_NUMBER; - uint8_t found = 0; - while(i < CLUSTER_MAP_SIZE && !found) { - if(driver_state.fat_table.cluster_map[i] == FAT32_FAT_EMPTY_ENTRY) { - driver_state.fat_table.cluster_map[i] = FAT32_FAT_END_OF_FILE; - found = 1; - } - i++; - } - - if(i == CLUSTER_MAP_SIZE) { - // Full file allocation table error - return error_code = 1; - } - - parent_table.table[idx_empty].cluster_high = (uint16_t) (((i-1) >> 16) & 0xFFFF); - parent_table.table[idx_empty].cluster_low = (uint16_t) ((i-1) & 0xFFFF); - } else { - // Create a new file - - // Search for empty cells in file allocation table - uint32_t i = 0, j = ROOT_CLUSTER_NUMBER; - uint32_t empty_fat_clusters[request.buffer_size / CLUSTER_SIZE]; - while(i < request.buffer_size/CLUSTER_SIZE && j < CLUSTER_MAP_SIZE) { - if(driver_state.fat_table.cluster_map[j] == FAT32_FAT_EMPTY_ENTRY) { - empty_fat_clusters[i] = j; - i++; - } - j++; - } - - if(i < request.buffer_size/CLUSTER_SIZE) { - // Full file allocation table error - return error_code = 1; - } - - // Create a linked list in file allocation table - for(i = 0; i < (request.buffer_size/CLUSTER_SIZE)-1; i++) { - driver_state.fat_table.cluster_map[empty_fat_clusters[i]] = empty_fat_clusters[i+1]; - } - driver_state.fat_table.cluster_map[empty_fat_clusters[(request.buffer_size/CLUSTER_SIZE)-1]] = FAT32_FAT_END_OF_FILE; - - // Set parent directory table entry - memcpy(parent_table.table[idx_empty].ext, request.ext, 3); - parent_table.table[idx_empty].cluster_high = (uint16_t) ((empty_fat_clusters[0] >> 16) & 0xFFFF); - parent_table.table[idx_empty].cluster_low = (uint16_t) (empty_fat_clusters[0] & 0xFFFF); - - // Write file content to storage - if(request.buffer_size > 0) { - for(i = 0; i < request.buffer_size/CLUSTER_SIZE; i++) { - write_clusters(((struct ClusterBuffer*) (request.buf) + i), empty_fat_clusters[i], 1); - } - } - } - - // Write updated file allocation table and parent directory table to storage - write_clusters(&(driver_state.fat_table), FAT_CLUSTER_NUMBER, 1); - write_clusters(&(parent_table), request.parent_cluster_number, 1); - - return error_code = 0; -} - - -/** - * FAT32 delete, delete a file or empty directory (only 1 DirectoryEntry) in file system. - * - * @param request buf and buffer_size is unused - * @return Error code: 0 success - 1 not found - 2 folder is not empty - -1 unknown - */ -int8_t delete(struct FAT32DriverRequest request){ - int8_t error_code = -1; - - if(memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { - // Empty request name error - return error_code; - } - struct FAT32DirectoryTable parent_table; - read_clusters(&parent_table, request.parent_cluster_number, 1); - - for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - if (memcmp(request.name, parent_table.table[i].name, 8) == 0 && memcmp(request.ext, parent_table.table[i].ext, 3) == 0) { - uint32_t cluster_number = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; - - if (parent_table.table[i].attribute == ATTR_SUBDIRECTORY) { - // Delete directory - - // Get current directory table - struct FAT32DirectoryTable curr_table; - read_clusters(&curr_table, cluster_number, 1); - - // Check if the directory is empty - uint8_t empty = 1; - for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - if(curr_table.table[i].user_attribute == UATTR_NOT_EMPTY) { - empty = 0; - break; - } - } - - if(!empty) { - // Directory not empty error - error_code = 2; - return error_code; - } - - // Remove directory from storage - struct ClusterBuffer cbuf; - for(uint32_t i = 0; i < CLUSTER_SIZE; i++) { - cbuf.buf[i] = '\0'; - } - write_clusters(&cbuf, cluster_number, 1); - - // Remove directory from file allocation table - driver_state.fat_table.cluster_map[cluster_number] = FAT32_FAT_EMPTY_ENTRY; - - error_code = 0; - } else if (parent_table.table[i].attribute == ATTR_ARCHIVE) { - - // Delete file - // Remove file from file allocation table and storage - - struct ClusterBuffer cbuf; - for(uint32_t i = 0; i < CLUSTER_SIZE; i++) { - cbuf.buf[i] = '\0'; - } - do { - // Remove file from storage - write_clusters(&cbuf, cluster_number, 1); - - // Remove file from file allocation table - uint32_t temp_cluster_number = cluster_number; - cluster_number = driver_state.fat_table.cluster_map[cluster_number]; - driver_state.fat_table.cluster_map[temp_cluster_number] = FAT32_FAT_EMPTY_ENTRY; - } while (driver_state.fat_table.cluster_map[cluster_number] != FAT32_FAT_END_OF_FILE); - // Handle EOF cluster - driver_state.fat_table.cluster_map[cluster_number] = FAT32_FAT_EMPTY_ENTRY; - write_clusters(&cbuf, cluster_number, 1); - - error_code = 0; - } - - // Remove parent directory entry - memcpy(parent_table.table[i].name, "\0\0\0\0\0\0\0\0", 8); - memcpy(parent_table.table[i].ext, "\0\0\0", 3); - parent_table.table[i].attribute = 0; - parent_table.table[i].user_attribute = 0; - parent_table.table[i].undelete = 0; - parent_table.table[i].create_time = 0; - parent_table.table[i].create_date = 0; - parent_table.table[i].access_date = 0; - parent_table.table[i].modified_time = 0; - parent_table.table[i].modified_date = 0; - parent_table.table[i].filesize = 0; - parent_table.table[i].cluster_low = 0; - parent_table.table[i].cluster_high = 0; - - // Write updated file allocation table and parent directory table to storage - write_clusters(&(driver_state.fat_table), FAT_CLUSTER_NUMBER, 1); - write_clusters(&(parent_table), request.parent_cluster_number, 1); - - return error_code; - } - } - return error_code; +#include "../lib-header/stdtype.h" +#include "../lib-header/fat32.h" +#include "../lib-header/stdmem.h" +// #include "../lib-header/cmos.h" + +static struct FAT32DriverState driver_state; + +const uint8_t fs_signature[BLOCK_SIZE] = { + 'C', 'o', 'u', 'r', 's', 'e', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + 'D', 'e', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'b', 'y', ' ', ' ', ' ', ' ', ' ', + 'L', 'a', 'b', ' ', 'S', 'i', 's', 't', 'e', 'r', ' ', 'I', 'T', 'B', ' ', ' ', + 'M', 'a', 'd', 'e', ' ', 'w', 'i', 't', 'h', ' ', '<', '3', ' ', ' ', ' ', ' ', + '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '2', '0', '2', '3', '\n', + [BLOCK_SIZE-2] = 'O', + [BLOCK_SIZE-1] = 'k', +}; + +/* -- Driver Interfaces -- */ + +/** + * Convert cluster number to logical block address + * + * @param cluster Cluster number to convert + * @return uint32_t Logical Block Address + */ +uint32_t cluster_to_lba(uint32_t cluster){ + // dr nanobyte -> lba = data_region_begin + (cluster-2)*sector_per_cluster + return cluster * CLUSTER_BLOCK_COUNT; +} + +/** + * Initialize DirectoryTable value with parent DirectoryEntry and directory name + * + * @param dir_table Pointer to directory table + * @param name 8-byte char for directory name + * @param parent_dir_cluster Parent directory cluster number + */ +void init_directory_table(struct FAT32DirectoryTable *dir_table, char *name, uint32_t parent_dir_cluster){ + struct FAT32DirectoryEntry *new_entry = &(dir_table->table[0]); + memcpy(&(new_entry->name), name, 8); + memcpy(&(new_entry->ext), "\0\0\0", 3); + + new_entry->attribute = ATTR_SUBDIRECTORY; + new_entry->user_attribute = UATTR_NOT_EMPTY; + + new_entry->undelete = 0; + new_entry->create_time = 0; + new_entry->create_date = 0; + new_entry->access_date = 0; + + new_entry->cluster_high = (uint16_t)(parent_dir_cluster >> 16); + + new_entry->modified_time = 0; + new_entry->modified_date = 0; + + new_entry->cluster_low = (uint16_t)(parent_dir_cluster & 0xFFFF); + new_entry->filesize = 0; + + dir_table->table[0] = *new_entry; +} + +/** + * Checking whether filesystem signature is missing or not in boot sector + * + * @return True if memcmp(boot_sector, fs_signature) returning inequality + */ +bool is_empty_storage(void){ + // read boot sector + uint8_t boot_sector[BLOCK_SIZE]; + read_blocks(boot_sector, BOOT_SECTOR, 1); + return memcmp(boot_sector, fs_signature, BLOCK_SIZE) != 0; +} + +/** + * Create new FAT32 file system. Will write fs_signature into boot sector and + * proper FileAllocationTable (contain CLUSTER_0_VALUE, CLUSTER_1_VALUE, + * and initialized root directory) into cluster number 1 + */ +void create_fat32(void){ + write_blocks(fs_signature, BOOT_SECTOR, 1); + + driver_state.fat_table.cluster_map[0] = CLUSTER_0_VALUE; + driver_state.fat_table.cluster_map[1] = CLUSTER_1_VALUE; + driver_state.fat_table.cluster_map[2] = FAT32_FAT_END_OF_FILE; + + write_clusters(&driver_state.fat_table, FAT_CLUSTER_NUMBER, 1); + + init_directory_table(&(driver_state.dir_table_buf), "ROOT\0\0\0\0", ROOT_CLUSTER_NUMBER); + write_clusters(&(driver_state.dir_table_buf), ROOT_CLUSTER_NUMBER, 1); +} + +/** + * @brief + * + * + * + * + * Else, read and cache entire FileAllocationTable (located at cluster number 1) into driver state + */ +void initialize_filesystem_fat32(void){ + if (is_empty_storage()){ + create_fat32(); + } else { + read_clusters(&(driver_state.fat_table), FAT_CLUSTER_NUMBER, 1); + read_clusters(&(driver_state.dir_table_buf), ROOT_CLUSTER_NUMBER, 1); + } +} + +/** + * Write cluster operation, wrapper for write_blocks(). + * Recommended to use struct ClusterBuffer + * + * @param ptr Pointer to source data + * @param cluster_number Cluster number to write + * @param cluster_count Cluster count to write, due limitation of write_blocks block_count 255 => max cluster_count = 63 + */ +void write_clusters(const void *ptr, uint32_t cluster_number, uint8_t cluster_count){ + write_blocks(ptr, cluster_to_lba(cluster_number), cluster_count * CLUSTER_BLOCK_COUNT); +} + +/** + * Read cluster operation, wrapper for read_blocks(). + * Recommended to use struct ClusterBuffer + * + * @param ptr Pointer to buffer for reading + * @param cluster_number Cluster number to read + * @param cluster_count Cluster count to read, due limitation of read_blocks block_count 255 => max cluster_count = 63 + */ +void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count){ + read_blocks(ptr, cluster_to_lba(cluster_number), cluster_count * CLUSTER_BLOCK_COUNT); +} + +/* -- CRUD Operation -- */ + +/** + * @brief + * + * + * + * @param request buf point to struct FAT32DirectoryTable, + * name is directory name, + * ext is unused, + * parent_cluster_number is target directory table to read, + * buffer_size must be exactly sizeof(struct FAT32DirectoryTable) + * @return Error code: 0 success - 1 not a folder - 2 not found - -1 unknown + */ +int8_t read_directory(struct FAT32DriverRequest request){ + int8_t error_code = 2; + + if (memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { + // Folder name empty + return error_code = -1; + } + + if (request.buffer_size != 0) { + // Requested data is not a folder + return error_code = 1; + } + + struct FAT32DirectoryTable parent_table; + read_clusters(&parent_table, request.parent_cluster_number, 1); + + for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + if(memcmp(request.name, parent_table.table[i].name, 8) == 0) { + if (parent_table.table[i].attribute == ATTR_SUBDIRECTORY) { + // terbaca sebagai folder + uint32_t curr_cluster = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; + + struct FAT32DirectoryTable curr_dir; + read_clusters(&curr_dir, curr_cluster, 1); + memcpy(request.buf, &curr_dir, sizeof(struct FAT32DirectoryTable)); + return error_code = 0; + } + } + } + return error_code; +} + + +/** + * FAT32 read, read a file from file system. + * + * @param request All attribute will be used for read, buffer_size will limit reading count + * @return Error code: 0 success - 1 not a file - 2 not enough buffer - 3 not found - -1 unknown + */ +int8_t read(struct FAT32DriverRequest request){ + int8_t error_code = 3; + + if(memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { + // Empty request name error + return error_code = -1; + } + + if (request.buffer_size == 0) { + // Requested data is not a folder + return error_code = 1; + } + + struct FAT32DirectoryTable parent_table; + read_clusters(&parent_table, request.parent_cluster_number, 1); + + for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + if (memcmp(request.name, parent_table.table[i].name, 8) == 0 && memcmp(request.ext, parent_table.table[i].ext, 3) == 0) { + uint32_t buffer_count = request.buffer_size/CLUSTER_SIZE; + uint32_t cluster_number = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; + + // Check for not enough buffer + uint32_t count = 0; + while(cluster_number != FAT32_FAT_END_OF_FILE) { + count++; + cluster_number = driver_state.fat_table.cluster_map[cluster_number]; + } + + if(count > buffer_count) { + // Not enough buffer error + return error_code = 2; + } + + uint32_t j = 0; + cluster_number = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; + while(cluster_number != FAT32_FAT_END_OF_FILE) { + read_clusters((struct ClusterBuffer*) (request.buf) + j, cluster_number, 1); + cluster_number = driver_state.fat_table.cluster_map[cluster_number]; + j++; + } + read_clusters((struct ClusterBuffer*) (request.buf) + j, cluster_number, 1); + return error_code = 0; + } + } + + return error_code; +} + +/** + * @brief + * + * + * + * @param request All attribute will be used for write, buffer_size == 0 then create a folder / directory + * @return Error code: 0 success - 1 file/folder already exist - 2 invalid parent cluster - -1 unknown + */ +int8_t write(struct FAT32DriverRequest request){ + int8_t error_code = -1; + + if(memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { + // Empty request name error + return error_code; + } + + // reserved cluster number + if (request.parent_cluster_number < 2){ + return error_code = 2; + } + + struct FAT32DirectoryTable parent_table; + read_clusters(&parent_table, request.parent_cluster_number, 1); + + uint32_t idx_empty = 0, i = 0; + while(i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry)) { + if(parent_table.table[i].user_attribute != UATTR_NOT_EMPTY) { + idx_empty = i; + break; + } + i++; + } + + // Writes to parent directory table + memcpy(parent_table.table[idx_empty].name, request.name, 8); + parent_table.table[idx_empty].attribute = request.buffer_size == 0 ? ATTR_SUBDIRECTORY : ATTR_ARCHIVE; + parent_table.table[idx_empty].user_attribute = UATTR_NOT_EMPTY; + parent_table.table[idx_empty].undelete = 0; + parent_table.table[idx_empty].create_time = 0; + parent_table.table[idx_empty].create_date = 0; + parent_table.table[idx_empty].access_date = 0; + parent_table.table[idx_empty].modified_time = 0; + parent_table.table[idx_empty].modified_date = 0; + parent_table.table[idx_empty].filesize = request.buffer_size; + + if(request.buffer_size == 0) { + // Create a new directory + uint32_t i = ROOT_CLUSTER_NUMBER; + uint8_t found = 0; + while(i < CLUSTER_MAP_SIZE && !found) { + if(driver_state.fat_table.cluster_map[i] == FAT32_FAT_EMPTY_ENTRY) { + driver_state.fat_table.cluster_map[i] = FAT32_FAT_END_OF_FILE; + found = 1; + } + i++; + } + + if(i == CLUSTER_MAP_SIZE) { + // Full file allocation table error + return error_code = 1; + } + + parent_table.table[idx_empty].cluster_high = (uint16_t) (((i-1) >> 16) & 0xFFFF); + parent_table.table[idx_empty].cluster_low = (uint16_t) ((i-1) & 0xFFFF); + } else { + // Create a new file + + // Search for empty cells in file allocation table + uint32_t i = 0, j = ROOT_CLUSTER_NUMBER; + uint32_t empty_fat_clusters[request.buffer_size / CLUSTER_SIZE]; + while(i < request.buffer_size/CLUSTER_SIZE && j < CLUSTER_MAP_SIZE) { + if(driver_state.fat_table.cluster_map[j] == FAT32_FAT_EMPTY_ENTRY) { + empty_fat_clusters[i] = j; + i++; + } + j++; + } + + if(i < request.buffer_size/CLUSTER_SIZE) { + // Full file allocation table error + return error_code = 1; + } + + // Create a linked list in file allocation table + for(i = 0; i < (request.buffer_size/CLUSTER_SIZE)-1; i++) { + driver_state.fat_table.cluster_map[empty_fat_clusters[i]] = empty_fat_clusters[i+1]; + } + driver_state.fat_table.cluster_map[empty_fat_clusters[(request.buffer_size/CLUSTER_SIZE)-1]] = FAT32_FAT_END_OF_FILE; + + // Set parent directory table entry + memcpy(parent_table.table[idx_empty].ext, request.ext, 3); + parent_table.table[idx_empty].cluster_high = (uint16_t) ((empty_fat_clusters[0] >> 16) & 0xFFFF); + parent_table.table[idx_empty].cluster_low = (uint16_t) (empty_fat_clusters[0] & 0xFFFF); + + // Write file content to storage + if(request.buffer_size > 0) { + for(i = 0; i < request.buffer_size/CLUSTER_SIZE; i++) { + write_clusters(((struct ClusterBuffer*) (request.buf) + i), empty_fat_clusters[i], 1); + } + } + } + + // Write updated file allocation table and parent directory table to storage + write_clusters(&(driver_state.fat_table), FAT_CLUSTER_NUMBER, 1); + write_clusters(&(parent_table), request.parent_cluster_number, 1); + + return error_code = 0; +} + + +/** + * FAT32 delete, delete a file or empty directory (only 1 DirectoryEntry) in file system. + * + * @param request buf and buffer_size is unused + * @return Error code: 0 success - 1 not found - 2 folder is not empty - -1 unknown + */ +int8_t delete(struct FAT32DriverRequest request){ + int8_t error_code = -1; + + if(memcmp(request.name, "\0\0\0\0\0\0\0\0", 8) == 0) { + // Empty request name error + return error_code; + } + struct FAT32DirectoryTable parent_table; + read_clusters(&parent_table, request.parent_cluster_number, 1); + + for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + if (memcmp(request.name, parent_table.table[i].name, 8) == 0 && memcmp(request.ext, parent_table.table[i].ext, 3) == 0) { + uint32_t cluster_number = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; + + if (parent_table.table[i].attribute == ATTR_SUBDIRECTORY) { + // Delete directory + + // Get current directory table + struct FAT32DirectoryTable curr_table; + read_clusters(&curr_table, cluster_number, 1); + + // Check if the directory is empty + uint8_t empty = 1; + for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + if(curr_table.table[i].user_attribute == UATTR_NOT_EMPTY) { + empty = 0; + break; + } + } + + if(!empty) { + // Directory not empty error + error_code = 2; + return error_code; + } + + // Remove directory from storage + struct ClusterBuffer cbuf; + for(uint32_t i = 0; i < CLUSTER_SIZE; i++) { + cbuf.buf[i] = '\0'; + } + write_clusters(&cbuf, cluster_number, 1); + + // Remove directory from file allocation table + driver_state.fat_table.cluster_map[cluster_number] = FAT32_FAT_EMPTY_ENTRY; + + error_code = 0; + } else if (parent_table.table[i].attribute == ATTR_ARCHIVE) { + + // Delete file + // Remove file from file allocation table and storage + + struct ClusterBuffer cbuf; + for(uint32_t i = 0; i < CLUSTER_SIZE; i++) { + cbuf.buf[i] = '\0'; + } + do { + // Remove file from storage + write_clusters(&cbuf, cluster_number, 1); + + // Remove file from file allocation table + uint32_t temp_cluster_number = cluster_number; + cluster_number = driver_state.fat_table.cluster_map[cluster_number]; + driver_state.fat_table.cluster_map[temp_cluster_number] = FAT32_FAT_EMPTY_ENTRY; + } while (driver_state.fat_table.cluster_map[cluster_number] != FAT32_FAT_END_OF_FILE); + // Handle EOF cluster + driver_state.fat_table.cluster_map[cluster_number] = FAT32_FAT_EMPTY_ENTRY; + write_clusters(&cbuf, cluster_number, 1); + + error_code = 0; + } + + // Remove parent directory entry + memcpy(parent_table.table[i].name, "\0\0\0\0\0\0\0\0", 8); + memcpy(parent_table.table[i].ext, "\0\0\0", 3); + parent_table.table[i].attribute = 0; + parent_table.table[i].user_attribute = 0; + parent_table.table[i].undelete = 0; + parent_table.table[i].create_time = 0; + parent_table.table[i].create_date = 0; + parent_table.table[i].access_date = 0; + parent_table.table[i].modified_time = 0; + parent_table.table[i].modified_date = 0; + parent_table.table[i].filesize = 0; + parent_table.table[i].cluster_low = 0; + parent_table.table[i].cluster_high = 0; + + // Write updated file allocation table and parent directory table to storage + write_clusters(&(driver_state.fat_table), FAT_CLUSTER_NUMBER, 1); + write_clusters(&(parent_table), request.parent_cluster_number, 1); + + return error_code; + } + } + return error_code; } \ No newline at end of file diff --git a/src/framebuffer/framebuffer.c b/src/framebuffer/framebuffer.c index 252d16d..5208fce 100644 --- a/src/framebuffer/framebuffer.c +++ b/src/framebuffer/framebuffer.c @@ -1,85 +1,85 @@ -#include "../lib-header/framebuffer.h" -#include "../lib-header/stdtype.h" -#include "../lib-header/stdmem.h" -#include "../lib-header/portio.h" - - -void framebuffer_set_cursor(uint8_t r, uint8_t c) { - uint16_t pos = r * MAX_COLS + c; - out(CURSOR_PORT_CMD, VGA_CURSOR_HIGH); - out(CURSOR_PORT_DATA, (uint8_t)((pos >> 8) & 0xFF)); - out(CURSOR_PORT_CMD, VGA_CURSOR_LOW); - out(CURSOR_PORT_DATA, (uint8_t)(pos & 0xFF)); -} - -struct Cursor framebuffer_get_cursor() { - out(CURSOR_PORT_CMD, VGA_CURSOR_HIGH); - int offset = in(CURSOR_PORT_DATA) << 8; - out(CURSOR_PORT_CMD, VGA_CURSOR_LOW); - offset += in(CURSOR_PORT_DATA); - struct Cursor c = - { - .row = offset / MAX_COLS, - .col = offset % MAX_COLS - }; - return c; -}; - -int framebuffer_get_col(struct Cursor c) { - return c.col; -} - -int framebuffer_get_row(struct Cursor c) { - return c.row; -} - -void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg) { - uint16_t attrib = (bg << 4) | (fg & WHITE); - volatile uint16_t * where; - where = (volatile uint16_t *)MEMORY_FRAMEBUFFER + (row * MAX_COLS + col) ; - *where = c | (attrib << 8); -} - -void framebuffer_clear(void) { - uint16_t space = 0x20 | (0x07 << 8); - uint16_t i; - volatile uint16_t * where; - where = (volatile uint16_t *)MEMORY_FRAMEBUFFER; - for (i = 0; i < MAX_COLS * MAX_ROWS; i++) { - *where = space; - where++; - } -} - -void framebuffer_write_string(char * str) { - struct Cursor c = framebuffer_get_cursor(); - int offset = c.row * MAX_COLS + c.col; - int i = 0; - while (str[i] != '\0') { - if (offset >= MAX_COLS * (MAX_ROWS)) { - offset = framebuffer_scroll_ln(offset); - } - if (str[i] == '\n') { - offset = (offset / MAX_COLS + 1) * MAX_COLS; - } else { - framebuffer_write(offset / MAX_COLS, offset % MAX_COLS, str[i], WHITE, BLACK); - offset += 1; - } - i++; - } - - framebuffer_set_cursor(offset / MAX_COLS, offset % MAX_COLS); -} - -int framebuffer_scroll_ln(int offset) { - memcpy( - (void *)MEMORY_FRAMEBUFFER, - (void *)(MEMORY_FRAMEBUFFER + MAX_COLS * 2), - MAX_COLS * 2 * (MAX_ROWS)); - - for (int i = 0; i < MAX_COLS; i++) { - framebuffer_write(MAX_ROWS - 1, i, ' ', WHITE, BLACK); - } - - return (offset) - (1 * (MAX_COLS)); +#include "../lib-header/framebuffer.h" +#include "../lib-header/stdtype.h" +#include "../lib-header/stdmem.h" +#include "../lib-header/portio.h" + + +void framebuffer_set_cursor(uint8_t r, uint8_t c) { + uint16_t pos = r * MAX_COLS + c; + out(CURSOR_PORT_CMD, VGA_CURSOR_HIGH); + out(CURSOR_PORT_DATA, (uint8_t)((pos >> 8) & 0xFF)); + out(CURSOR_PORT_CMD, VGA_CURSOR_LOW); + out(CURSOR_PORT_DATA, (uint8_t)(pos & 0xFF)); +} + +struct Cursor framebuffer_get_cursor() { + out(CURSOR_PORT_CMD, VGA_CURSOR_HIGH); + int offset = in(CURSOR_PORT_DATA) << 8; + out(CURSOR_PORT_CMD, VGA_CURSOR_LOW); + offset += in(CURSOR_PORT_DATA); + struct Cursor c = + { + .row = offset / MAX_COLS, + .col = offset % MAX_COLS + }; + return c; +}; + +int framebuffer_get_col(struct Cursor c) { + return c.col; +} + +int framebuffer_get_row(struct Cursor c) { + return c.row; +} + +void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg) { + uint16_t attrib = (bg << 4) | (fg & WHITE); + volatile uint16_t * where; + where = (volatile uint16_t *)MEMORY_FRAMEBUFFER + (row * MAX_COLS + col) ; + *where = c | (attrib << 8); +} + +void framebuffer_clear(void) { + uint16_t space = 0x20 | (0x07 << 8); + uint16_t i; + volatile uint16_t * where; + where = (volatile uint16_t *)MEMORY_FRAMEBUFFER; + for (i = 0; i < MAX_COLS * MAX_ROWS; i++) { + *where = space; + where++; + } +} + +void framebuffer_write_string(char * str) { + struct Cursor c = framebuffer_get_cursor(); + int offset = c.row * MAX_COLS + c.col; + int i = 0; + while (str[i] != '\0') { + if (offset >= MAX_COLS * (MAX_ROWS)) { + offset = framebuffer_scroll_ln(offset); + } + if (str[i] == '\n') { + offset = (offset / MAX_COLS + 1) * MAX_COLS; + } else { + framebuffer_write(offset / MAX_COLS, offset % MAX_COLS, str[i], WHITE, BLACK); + offset += 1; + } + i++; + } + + framebuffer_set_cursor(offset / MAX_COLS, offset % MAX_COLS); +} + +int framebuffer_scroll_ln(int offset) { + memcpy( + (void *)MEMORY_FRAMEBUFFER, + (void *)(MEMORY_FRAMEBUFFER + MAX_COLS * 2), + MAX_COLS * 2 * (MAX_ROWS)); + + for (int i = 0; i < MAX_COLS; i++) { + framebuffer_write(MAX_ROWS - 1, i, ' ', WHITE, BLACK); + } + + return (offset) - (1 * (MAX_COLS)); } \ No newline at end of file diff --git a/src/gdt.c b/src/gdt.c index a826345..956bb49 100644 --- a/src/gdt.c +++ b/src/gdt.c @@ -1,117 +1,245 @@ -#include "lib-header/stdtype.h" -#include "lib-header/gdt.h" - -/** - * global_descriptor_table, predefined GDT. - * Initial SegmentDescriptor already set properly according to GDT definition in Intel Manual & OSDev. - * Table entry : [{Null Descriptor}, {Kernel Code}, {Kernel Data (variable, etc)}, ...]. - */ -struct GlobalDescriptorTable global_descriptor_table = { - .table = { - { - // First 32-bit - .segment_low = 0x0000, - .base_low = 0x0000, - - // Next 16-bit (Bit 32 to 47) - .base_mid = 0x00, - .type_bit = 0x0, - .non_system = 0x0, - - .dpl = 0x0, - .segment_present = 0x0, - .segment_limit = 0x0, - .avl = 0x0, - .code_seg_64bit = 0x0, - .def_op_size = 0x0, - .granularity = 0x0, - .base_high = 0x0 - }, - { - // Kernel Code Segment - - // First 32-bit - .segment_low = 0xFFFF, - .base_low = 0x0000, - - // Next 16-bit (Bit 32 to 47) - .base_mid = 0x00, - .type_bit = 0xA, - .non_system = 0x1, - - .dpl = 0x0, - .segment_present = 0x1, - .segment_limit = 0xF, - .avl = 0x0, - .code_seg_64bit = 0x1, - .def_op_size = 0x1, - .granularity = 0x1, - .base_high = 0x00 - }, - { - // Kernel Data Segment - - // First 32-bit - .segment_low = 0xFFFF, - .base_low = 0x0000, - - // Next 16-bit (Bit 32 to 47) - .base_mid = 0x00, - .type_bit = 0x2, - .non_system = 0x1, - - .dpl = 0x0, - .segment_present = 0x1, - .segment_limit = 0xF, - .avl = 0x0, - .code_seg_64bit = 0x0, - .def_op_size = 0x1, - .granularity = 0x1, - .base_high = 0x00 - } - } -}; - -/** - * _gdt_gdtr, predefined system GDTR. - * GDT pointed by this variable is already set to point global_descriptor_table above. - * From: https://wiki.osdev.org/Global_Descriptor_Table, GDTR.size is GDT size minus 1. - */ -struct GDTR _gdt_gdtr = { - // Use sizeof operator - .size = sizeof(global_descriptor_table) - 1, - - .address = &global_descriptor_table -}; - -// static struct GlobalDescriptorTable global_descriptor_table = { -// .table = { -// {/* TODO: Null Descriptor */}, -// {/* TODO: Kernel Code Descriptor */}, -// {/* TODO: Kernel Data Descriptor */}, -// {/* TODO: User Code Descriptor */}, -// {/* TODO: User Data Descriptor */}, -// { -// .segment_high = (sizeof(struct TSSEntry) & (0xF << 16)) >> 16, -// .segment_low = sizeof(struct TSSEntry), -// .base_high = 0, -// .base_mid = 0, -// .base_low = 0, -// .non_system = 0, // S bit -// .type_bit = 0x9, -// .privilege = 0, // DPL -// .valid_bit = 1, // P bit -// .opr_32_bit = 1, // D/B bit -// .long_mode = 0, // L bit -// .granularity = 0, // G bit -// }, -// {0} -// } -// }; - -// void gdt_install_tss(void) { -// uint32_t base = (uint32_t) &_interrupt_tss_entry; -// global_descriptor_table.table[5].base_high = (base & (0xFF << 24)) >> 24; -// global_descriptor_table.table[5].base_mid = (base & (0xFF << 16)) >> 16; -// global_descriptor_table.table[5].base_low = base & 0xFFFF; -// } +#include "lib-header/gdt.h" + +/** + * global_descriptor_table, predefined GDT. + * Initial SegmentDescriptor already set properly according to GDT definition in Intel Manual & OSDev. + * Table entry : [{Null Descriptor}, {Kernel Code}, {Kernel Data (variable, etc)}, ...]. + */ +static struct GlobalDescriptorTable global_descriptor_table = { + .table = { + { + // First 32-bit + .segment_low = 0x0000, + .base_low = 0x0000, + + // Next 16-bit (Bit 32 to 47) + .base_mid = 0x00, + .type_bit = 0x0, + .non_system = 0x0, + + .dpl = 0x0, + .segment_present = 0x0, + .segment_limit = 0x0, + .avl = 0x0, + .code_seg_64bit = 0x0, + .def_op_size = 0x0, + .granularity = 0x0, + .base_high = 0x0 + }, + { + // Kernel Code Segment + + // First 32-bit + .segment_low = 0xFFFF, + .base_low = 0x0000, + + // Next 16-bit (Bit 32 to 47) + .base_mid = 0x00, + .type_bit = 0xA, + .non_system = 0x1, + + .dpl = 0x0, + .segment_present = 0x1, + .segment_limit = 0xF, + .avl = 0x0, + .code_seg_64bit = 0x1, + .def_op_size = 0x1, + .granularity = 0x1, + .base_high = 0x00 + }, + { + // Kernel Data Segment + + // First 32-bit + .segment_low = 0xFFFF, + .base_low = 0x0000, + + // Next 16-bit (Bit 32 to 47) + .base_mid = 0x00, + .type_bit = 0x2, + .non_system = 0x1, + + .dpl = 0x0, + .segment_present = 0x1, + .segment_limit = 0xF, + .avl = 0x0, + .code_seg_64bit = 0x0, + .def_op_size = 0x1, + .granularity = 0x1, + .base_high = 0x00 + }, + { + /* TODO: User Code Descriptor */ + .segment_low = 0xFFFF, + .base_low = 0x0000, + + // Next 16-bit (Bit 32 to 47) + .base_mid = 0x00, + .type_bit = 0xA, + .non_system = 0x1, + + .dpl = 0x3, + .segment_present = 0x1, + .segment_limit = 0xF, + .avl = 0x0, + .code_seg_64bit = 0x1, + .def_op_size = 0x1, + .granularity = 0x1, + .base_high = 0x00 + }, + + {/* TODO: User Data Descriptor */ + .segment_low = 0xFFFF, + .base_low = 0x0000, + + // Next 16-bit (Bit 32 to 47) + .base_mid = 0x00, + .type_bit = 0x2, + .non_system = 0x1, + + .dpl = 0x3, + .segment_present = 0x1, + .segment_limit = 0xF, + .avl = 0x0, + .code_seg_64bit = 0x0, + .def_op_size = 0x1, + .granularity = 0x1, + .base_high = 0x00 + }, + { + // TSS + .segment_low = sizeof(struct TSSEntry), + .base_low = 0x0000, + + // Next 16-bit (Bit 32 to 47) + .base_mid = 0x00, + .type_bit = 0x9, + .non_system = 0x0, + + .dpl = 0x0, + .segment_present = 0x1, + .segment_limit = (sizeof(struct TSSEntry) & (0xF << 16)) >> 16, + .avl = 0x0, + .code_seg_64bit = 0x0, + .def_op_size = 0x0, + .granularity = 0x0, + .base_high = 0x00 + }, + {0} + } +}; + +/** + * _gdt_gdtr, predefined system GDTR. + * GDT pointed by this variable is already set to point global_descriptor_table above. + * From: https://wiki.osdev.org/Global_Descriptor_Table, GDTR.size is GDT size minus 1. + */ +struct GDTR _gdt_gdtr = { + // Use sizeof operator + .size = sizeof(global_descriptor_table) - 1, + + .address = &global_descriptor_table +}; + +// static struct GlobalDescriptorTable global_descriptor_table = { +// // source: https://wiki.osdev.org/Global_Descriptor_Table +// // source: https://wiki.osdev.org/Segment_Descriptor +// // source: https://wiki.osdev.org/Protected_Mode +// // source: https://wiki.osdev.org/Long_Mode +// // source : https://wiki.osdev.org/IA-32e_Mode +// // source : http://www.independent-software.com/operating-system-development-protected-mode-global-descriptor-table.html +// .table = { +// {/* TODO: Null Descriptor */ +// .segment_low = 0x0000, +// .base_low = 0x0000, +// .base_mid = 0x00, +// .non_system = 0, // S bit +// .type_bit = 0x0, +// .privilege = 0, // DPL +// .valid_bit = 0, // P bit +// .opr_32_bit = 0, // D/B bit +// .long_mode = 0, // L bit +// .granularity = 0, // G bit +// .base_high = 0x00 +// }, + +// {/* TODO: Kernel Code Descriptor */ +// .segment_low = 0xFFFF, +// .base_low = 0x0000, +// .base_mid = 0x00, +// .non_system = 1, // S bit +// .type_bit = 0xA, +// .privilege = 0, // DPL +// .valid_bit = 1, // P bit +// .opr_32_bit = 1, // D/B bit +// .long_mode = 1, // L bit +// .granularity = 1, // G bit +// .base_high = 0x00 +// }, + +// {/* TODO: Kernel Data Descriptor */ +// .segment_low = 0xFFFF, +// .base_low = 0x0000, +// .base_mid = 0x00, +// .non_system = 1, // S bit +// .type_bit = 0x2, +// .privilege = 0, // DPL +// .valid_bit = 1, // P bit +// .opr_32_bit = 1, // D/B bit +// .long_mode = 0, // L bit +// .granularity = 1, // G bit +// .base_high = 0x00 +// }, + +// {/* TODO: User Code Descriptor */ +// .segment_low = 0xFFFF, +// .base_low = 0x0000, +// .base_mid = 0x00, +// .non_system = 1, // S bit +// .type_bit = 0xA, +// .privilege = 3, // DPL +// .valid_bit = 1, // P bit +// .opr_32_bit = 1, // D/B bit +// .long_mode = 1, // L bit +// .granularity = 1, // G bit +// .base_high = 0x00 +// }, + +// {/* TODO: User Data Descriptor */ +// .segment_low = 0xFFFF, +// .base_low = 0x0000, +// .base_mid = 0x00, +// .non_system = 1, // S bit +// .type_bit = 0x2, +// .privilege = 3, // DPL +// .valid_bit = 1, // P bit +// .opr_32_bit = 1, // D/B bit +// .long_mode = 0, // L bit +// .granularity = 1, // G bit +// .base_high = 0x00 +// }, + +// { +// .segment_high = (sizeof(struct TSSEntry) & (0xF << 16)) >> 16, +// .segment_low = sizeof(struct TSSEntry), +// .base_high = 0, +// .base_mid = 0, +// .base_low = 0, +// .non_system = 0, // S bit +// .type_bit = 0x9, +// .privilege = 0, // DPL +// .valid_bit = 1, // P bit +// .opr_32_bit = 1, // D/B bit +// .long_mode = 0, // L bit +// .granularity = 0, // G bit +// }, +// {0} +// } +// }; + +void gdt_install_tss(void) { + uint32_t base = (uint32_t) &_interrupt_tss_entry; + global_descriptor_table.table[5].base_high = (base & (0xFF << 24)) >> 24; + global_descriptor_table.table[5].base_mid = (base & (0xFF << 16)) >> 16; + global_descriptor_table.table[5].base_low = base & 0xFFFF; +} diff --git a/src/inserter/external-inserter.c b/src/inserter/external-inserter.c index db6cbfc..d568210 100644 --- a/src/inserter/external-inserter.c +++ b/src/inserter/external-inserter.c @@ -1,98 +1,101 @@ -#include -#include - -// Usual gcc fixed width integer type -typedef u_int32_t uint32_t; -typedef u_int8_t uint8_t; - -// Manual import from fat32.h, disk.h, & stdmem.h due some issue with size_t -#define BLOCK_SIZE 512 - -struct FAT32DriverRequest { - void *buf; - char name[8]; - char ext[3]; - uint32_t parent_cluster_number; - uint32_t buffer_size; -} __attribute__((packed)); - -void* memcpy(void* restrict dest, const void* restrict src, size_t n); - -void initialize_filesystem_fat32(void); -int8_t read(struct FAT32DriverRequest request); -int8_t read_directory(struct FAT32DriverRequest request); -int8_t write(struct FAT32DriverRequest request); -int8_t delete(struct FAT32DriverRequest request); - - - - -// Global variable -uint8_t *image_storage; -uint8_t *file_buffer; - -void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count) { - for (int i = 0; i < block_count; i++) - memcpy((uint8_t*) ptr + BLOCK_SIZE*i, image_storage + BLOCK_SIZE*(logical_block_address+i), BLOCK_SIZE); -} - -void write_blocks(const void *ptr, uint32_t logical_block_address, uint8_t block_count) { - for (int i = 0; i < block_count; i++) - memcpy(image_storage + BLOCK_SIZE*(logical_block_address+i), (uint8_t*) ptr + BLOCK_SIZE*i, BLOCK_SIZE); -} - - -int main(int argc, char *argv[]) { - if (argc < 4) { - fprintf(stderr, "inserter: ./inserter \n"); - exit(1); - } - - // Read storage into memory, requiring 4 MB memory - image_storage = malloc(4*1024*1024); - file_buffer = malloc(4*1024*1024); - FILE *fptr = fopen(argv[3], "r"); - fread(image_storage, 4*1024*1024, 1, fptr); - fclose(fptr); - - // Read target file, assuming file is less than 4 MiB - FILE *fptr_target = fopen(argv[1], "r"); - size_t filesize = 0; - if (fptr_target == NULL) - filesize = 0; - else { - fread(file_buffer, 4*1024*1024, 1, fptr_target); - fseek(fptr_target, 0, SEEK_END); - filesize = ftell(fptr_target); - fclose(fptr_target); - } - - printf("Filename : %s\n", argv[1]); - printf("Filesize : %ld bytes\n", filesize); - - // FAT32 operations - initialize_filesystem_fat32(); - struct FAT32DriverRequest request = { - .buf = file_buffer, - .ext = "\0\0\0", - .buffer_size = filesize, - }; - sscanf(argv[2], "%u", &request.parent_cluster_number); - sscanf(argv[1], "%8s", request.name); - int retcode = write(request); - if (retcode == 0) - puts("Write success"); - else if (retcode == 1) - puts("Error: File/folder name already exist"); - else if (retcode == 2) - puts("Error: Invalid parent cluster"); - else - puts("Error: Unknown error"); - - // Write image in memory into original, overwrite them - fptr = fopen(argv[3], "w"); - fwrite(image_storage, 4*1024*1024, 1, fptr); - fclose(fptr); - - return 0; +#include +#include + +#include "../lib-header/stdtype.h" +#include "../lib-header/stdmem.h" + +// Usual gcc fixed width integer type +typedef u_int32_t uint32_t; +typedef u_int8_t uint8_t; + +// Manual import from fat32.h, disk.h, & stdmem.h due some issue with size_t +#define BLOCK_SIZE 512 + +struct FAT32DriverRequest { + void *buf; + char name[8]; + char ext[3]; + uint32_t parent_cluster_number; + uint32_t buffer_size; +} __attribute__((packed)); + +void* memcpy(void* restrict dest, const void* restrict src, size_t n); + +void initialize_filesystem_fat32(void); +int8_t read(struct FAT32DriverRequest request); +int8_t read_directory(struct FAT32DriverRequest request); +int8_t write(struct FAT32DriverRequest request); +int8_t delete(struct FAT32DriverRequest request); + + + + +// Global variable +uint8_t *image_storage; +uint8_t *file_buffer; + +void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count) { + for (int i = 0; i < block_count; i++) + memcpy((uint8_t*) ptr + BLOCK_SIZE*i, image_storage + BLOCK_SIZE*(logical_block_address+i), BLOCK_SIZE); +} + +void write_blocks(const void *ptr, uint32_t logical_block_address, uint8_t block_count) { + for (int i = 0; i < block_count; i++) + memcpy(image_storage + BLOCK_SIZE*(logical_block_address+i), (uint8_t*) ptr + BLOCK_SIZE*i, BLOCK_SIZE); +} + + +int main(int argc, char *argv[]) { + if (argc < 4) { + fprintf(stderr, "inserter: ./inserter \n"); + exit(1); + } + + // Read storage into memory, requiring 4 MB memory + image_storage = malloc(4*1024*1024); + file_buffer = malloc(4*1024*1024); + FILE *fptr = fopen(argv[3], "r"); + fread(image_storage, 4*1024*1024, 1, fptr); + fclose(fptr); + + // Read target file, assuming file is less than 4 MiB + FILE *fptr_target = fopen(argv[1], "r"); + size_t filesize = 0; + if (fptr_target == NULL) + filesize = 0; + else { + fread(file_buffer, 4*1024*1024, 1, fptr_target); + fseek(fptr_target, 0, SEEK_END); + filesize = ftell(fptr_target); + fclose(fptr_target); + } + + printf("Filename : %s\n", argv[1]); + printf("Filesize : %d bytes\n", filesize); + + // FAT32 operations + initialize_filesystem_fat32(); + struct FAT32DriverRequest request = { + .buf = file_buffer, + .ext = "\0\0\0", + .buffer_size = filesize, + }; + sscanf(argv[2], "%u", &request.parent_cluster_number); + sscanf(argv[1], "%8s", request.name); + int retcode = write(request); + if (retcode == 0) + puts("Write success"); + else if (retcode == 1) + puts("Error: File/folder name already exist"); + else if (retcode == 2) + puts("Error: Invalid parent cluster"); + else + puts("Error: Unknown error"); + + // Write image in memory into original, overwrite them + fptr = fopen(argv[3], "w"); + fwrite(image_storage, 4*1024*1024, 1, fptr); + fclose(fptr); + + return 0; } \ No newline at end of file diff --git a/src/interrupt/idt.c b/src/interrupt/idt.c index 532b9b9..ef2d3b4 100644 --- a/src/interrupt/idt.c +++ b/src/interrupt/idt.c @@ -1,41 +1,41 @@ -#include "../lib-header/idt.h" - -// void *isr_stub_table[ISR_STUB_TABLE_LIMIT]; -struct IDTR _idt_idtr; -struct InterruptDescriptorTable interrupt_descriptor_table; - -// global initialization of IDT -// = { -// .size = sizeof(interrupt_descriptor_table) - 1, -// .address = (void *) & interrupt_descriptor_table.table[0] -//}; - -void initialize_idt(void) { - // Local initialization of IDT - _idt_idtr.size = sizeof(interrupt_descriptor_table) - 1; - _idt_idtr.address = (void *)&interrupt_descriptor_table.table[0]; - - for (uint8_t i = 0; i < ISR_STUB_TABLE_LIMIT; i++) { - set_interrupt_gate(i, isr_stub_table[i], GDT_KERNEL_CODE_SEGMENT_SELECTOR, - 0); - } - __asm__ volatile("lidt %0" : : "m"(_idt_idtr)); - __asm__ volatile("sti"); -} - -void set_interrupt_gate(uint8_t int_vector, void *handler_address, - uint16_t gdt_seg_selector, uint8_t privilege) { - struct IDTGate *idt_int_gate = &interrupt_descriptor_table.table[int_vector]; - idt_int_gate->offset_low = (uint32_t)handler_address & 0xFFFF; - idt_int_gate->segment = gdt_seg_selector; - idt_int_gate->_reserved = 0b0; - idt_int_gate->dpl = privilege; - idt_int_gate->offset_high = (uint32_t)handler_address >> 16; - - // Target system 32-bit and flag this as valid interrupt gate - idt_int_gate->_r_bit_1 = INTERRUPT_GATE_R_BIT_1; - idt_int_gate->_r_bit_2 = INTERRUPT_GATE_R_BIT_2; - idt_int_gate->_r_bit_3 = INTERRUPT_GATE_R_BIT_3; - idt_int_gate->gate_32 = 1; - idt_int_gate->valid_bit = 1; -} +#include "../lib-header/idt.h" + +// void *isr_stub_table[ISR_STUB_TABLE_LIMIT]; +struct IDTR _idt_idtr; +struct InterruptDescriptorTable interrupt_descriptor_table; + +// global initialization of IDT +// = { +// .size = sizeof(interrupt_descriptor_table) - 1, +// .address = (void *) & interrupt_descriptor_table.table[0] +//}; + +void initialize_idt(void) { + // Local initialization of IDT + _idt_idtr.size = sizeof(interrupt_descriptor_table) - 1; + _idt_idtr.address = (void *)&interrupt_descriptor_table.table[0]; + + for (uint8_t i = 0; i < ISR_STUB_TABLE_LIMIT; i++) { + set_interrupt_gate(i, isr_stub_table[i], GDT_KERNEL_CODE_SEGMENT_SELECTOR, + 0); + } + __asm__ volatile("lidt %0" : : "m"(_idt_idtr)); + __asm__ volatile("sti"); +} + +void set_interrupt_gate(uint8_t int_vector, void *handler_address, + uint16_t gdt_seg_selector, uint8_t privilege) { + struct IDTGate *idt_int_gate = &interrupt_descriptor_table.table[int_vector]; + idt_int_gate->offset_low = (uint32_t)handler_address & 0xFFFF; + idt_int_gate->segment = gdt_seg_selector; + idt_int_gate->_reserved = 0b0; + idt_int_gate->dpl = privilege; + idt_int_gate->offset_high = (uint32_t)handler_address >> 16; + + // Target system 32-bit and flag this as valid interrupt gate + idt_int_gate->_r_bit_1 = INTERRUPT_GATE_R_BIT_1; + idt_int_gate->_r_bit_2 = INTERRUPT_GATE_R_BIT_2; + idt_int_gate->_r_bit_3 = INTERRUPT_GATE_R_BIT_3; + idt_int_gate->gate_32 = 1; + idt_int_gate->valid_bit = 1; +} diff --git a/src/interrupt/interrupt.c b/src/interrupt/interrupt.c index c9514d1..5379a18 100644 --- a/src/interrupt/interrupt.c +++ b/src/interrupt/interrupt.c @@ -1,69 +1,70 @@ -#include "../lib-header/interrupt.h" - - -void io_wait(void) { - out(0x80, 0); -} - -void pic_ack(uint8_t irq) { - if (irq >= 8) - out(PIC2_COMMAND, PIC_ACK); - out(PIC1_COMMAND, PIC_ACK); -} - -void pic_remap(void) { - uint8_t a1, a2; - - // Save masks - a1 = in(PIC1_DATA); - a2 = in(PIC2_DATA); - - // Starts the initialization sequence in cascade mode - out(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); - io_wait(); - out(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); - io_wait(); - out(PIC1_DATA, PIC1_OFFSET); // ICW2: Master PIC vector offset - io_wait(); - out(PIC2_DATA, PIC2_OFFSET); // ICW2: Slave PIC vector offset - io_wait(); - out(PIC1_DATA, 0b0100); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) - io_wait(); - out(PIC2_DATA, 0b0010); // ICW3: tell Slave PIC its cascade identity (0000 0010) - io_wait(); - - out(PIC1_DATA, ICW4_8086); - io_wait(); - out(PIC2_DATA, ICW4_8086); - io_wait(); - - // Restore masks - out(PIC1_DATA, a1); - out(PIC2_DATA, a2); -} - -void main_interrupt_handler ( - __attribute__((unused)) struct CPURegister cpu, - uint32_t int_number, - __attribute__((unused)) struct InterruptStack info -) { - switch (int_number+1) { - case (PIC1_OFFSET + IRQ_KEYBOARD) : - keyboard_isr(); - break; - // ? : CMOS Masuk sini juga kah? - }; -} - -void activate_keyboard_interrupt(void) { - out(PIC1_DATA, PIC_DISABLE_ALL_MASK ^ (1 << IRQ_KEYBOARD)); - out(PIC2_DATA, PIC_DISABLE_ALL_MASK); -} - -void set_tss_kernel_current_stack(void) { - uint32_t stack_ptr; - // Reading base stack frame instead esp - __asm__ volatile ("mov %%ebp, %0": "=r"(stack_ptr) : /* */); - // Add 8 because 4 for ret address and other 4 is for stack_ptr variable - _interrupt_tss_entry.esp0 = stack_ptr + 8; -} +#include "../lib-header/interrupt.h" + +struct TSSEntry _interrupt_tss_entry; + +void io_wait(void) { + out(0x80, 0); +} + +void pic_ack(uint8_t irq) { + if (irq >= 8) + out(PIC2_COMMAND, PIC_ACK); + out(PIC1_COMMAND, PIC_ACK); +} + +void pic_remap(void) { + uint8_t a1, a2; + + // Save masks + a1 = in(PIC1_DATA); + a2 = in(PIC2_DATA); + + // Starts the initialization sequence in cascade mode + out(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); + io_wait(); + out(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); + io_wait(); + out(PIC1_DATA, PIC1_OFFSET); // ICW2: Master PIC vector offset + io_wait(); + out(PIC2_DATA, PIC2_OFFSET); // ICW2: Slave PIC vector offset + io_wait(); + out(PIC1_DATA, 0b0100); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) + io_wait(); + out(PIC2_DATA, 0b0010); // ICW3: tell Slave PIC its cascade identity (0000 0010) + io_wait(); + + out(PIC1_DATA, ICW4_8086); + io_wait(); + out(PIC2_DATA, ICW4_8086); + io_wait(); + + // Restore masks + out(PIC1_DATA, a1); + out(PIC2_DATA, a2); +} + +void main_interrupt_handler ( + __attribute__((unused)) struct CPURegister cpu, + uint32_t int_number, + __attribute__((unused)) struct InterruptStack info +) { + switch (int_number+1) { + case (PIC1_OFFSET + IRQ_KEYBOARD) : + keyboard_isr(); + break; + // ? : CMOS Masuk sini juga kah? + }; +} + +void activate_keyboard_interrupt(void) { + // Activate keyboard interrupt + out(PIC1_DATA, in(PIC1_DATA) & ~(1 << IRQ_KEYBOARD)); +} + +void set_tss_kernel_current_stack(void) { + uint32_t stack_ptr; + // Reading base stack frame instead esp + __asm__ volatile ("mov %%ebp, %0": "=r"(stack_ptr) : /* */); + // Add 8 because 4 for ret address and other 4 is for stack_ptr variable + _interrupt_tss_entry.esp0 = stack_ptr + 8; +} diff --git a/src/interrupt/intsetup.s b/src/interrupt/intsetup.s index ba9eae6..a255ef3 100644 --- a/src/interrupt/intsetup.s +++ b/src/interrupt/intsetup.s @@ -1,127 +1,127 @@ -extern main_interrupt_handler -global isr_stub_table - -; Generic handler section for interrupt -call_generic_handler: - ; Before interrupt_handler_n is called (caller of this generic handler section), - ; stack will have these value that pushed automatically by CPU - ; [esp + 12] eflags - ; [esp + 8 ] cs - ; [esp + 4 ] eip - ; [esp + 0 ] error code - - ; CPURegister - push esp - push ebp - push edx - push ecx - push ebx - push eax - - - ; call the C function - call main_interrupt_handler - - ; restore the registers - pop eax - pop ebx - pop ecx - pop edx - pop ebp - pop esp - - ; restore the esp (interrupt number & error code) - add esp, 8 - - ; return to the code that got interrupted - ; at this point, stack should be structured like this - ; [esp], [esp+4], [esp+8] - ; eip, cs, eflags - ; improper value will cause invalid return address & register - sti - iret - - - -; Macro for creating interrupt handler that only push interrupt number -%macro no_error_code_interrupt_handler 1 -interrupt_handler_%1: - push dword 0 ; push 0 as error code - push dword %1 ; push the interrupt number - jmp call_generic_handler ; jump to the common handler -%endmacro - -%macro error_code_interrupt_handler 1 -interrupt_handler_%1: - push dword %1 - jmp call_generic_handler -%endmacro - -; CPU exception handlers -no_error_code_interrupt_handler 0 ; 0x0 - Division by zero -no_error_code_interrupt_handler 1 ; 0x1 - Debug Exception -no_error_code_interrupt_handler 2 ; 0x2 - NMI, Non-Maskable Interrupt -no_error_code_interrupt_handler 3 ; 0x3 - Breakpoint Exception -no_error_code_interrupt_handler 4 ; 0x4 - INTO Overflow -no_error_code_interrupt_handler 5 ; 0x5 - Out of Bounds -no_error_code_interrupt_handler 6 ; 0x6 - Invalid Opcode -no_error_code_interrupt_handler 7 ; 0x7 - Device Not Available -error_code_interrupt_handler 8 ; 0x8 - Double Fault -no_error_code_interrupt_handler 9 ; 0x9 - Deprecated -error_code_interrupt_handler 10 ; 0xA - Invalid TSS -error_code_interrupt_handler 11 ; 0xB - Segment Not Present -error_code_interrupt_handler 12 ; 0xC - Stack-Segment Fault -error_code_interrupt_handler 13 ; 0xD - General Protection Fault -error_code_interrupt_handler 14 ; 0xE - Page Fault -no_error_code_interrupt_handler 15 ; 0xF - Reserved -no_error_code_interrupt_handler 16 ; 0x10 - x87 Floating-Point Exception -error_code_interrupt_handler 17 ; 0x11 - Alignment Check Exception -no_error_code_interrupt_handler 18 ; 0x12 - Machine Check Exception -no_error_code_interrupt_handler 19 ; 0x13 - SIMD Floating-Point Exception -no_error_code_interrupt_handler 20 ; 0x14 - Virtualization Exception -no_error_code_interrupt_handler 21 ; 0x15 - Control Protection Exception -no_error_code_interrupt_handler 22 ; 0x16 - Reserved -no_error_code_interrupt_handler 23 ; 0x17 - Reserved -no_error_code_interrupt_handler 24 ; 0x18 - Reserved -no_error_code_interrupt_handler 25 ; 0x19 - Reserved -no_error_code_interrupt_handler 26 ; 0x1A - Reserved -no_error_code_interrupt_handler 27 ; 0x1B - Reserved -no_error_code_interrupt_handler 28 ; 0x1C - Hypervisor Injection Exception -no_error_code_interrupt_handler 29 ; 0x1D - VMM Communication Exception -error_code_interrupt_handler 30 ; 0x1E - Security Exception -no_error_code_interrupt_handler 31 ; 0x1F - Reserved - -; User defined interrupt handler -; Assuming PIC1 & PIC2 offset is 0x20 and 0x28 -; 32 - 0x20 - IRQ0: Programmable Interval Timer -; 33 - 0x21 - IRQ1: Keyboard -; 34 - 0x22 - IRQ2: PIC Cascade, used internally -; 35 - 0x23 - IRQ3: COM2, if enabled -; 36 - 0x24 - IRQ4: COM1, if enabled -; 37 - 0x25 - IRQ5: LPT2, if enabled -; 38 - 0x26 - IRQ6: Floppy Disk -; 39 - 0x27 - IRQ7: LPT1 - -; 40 - 0x28 - IRQ8: CMOS real-time clock -; 41 - 0x29 - IRQ9: Free -; 42 - 0x2A - IRQ10: Free -; 43 - 0x2B - IRQ11: Free -; 44 - 0x2C - IRQ12: PS2 Mouse -; 45 - 0x2D - IRQ13: Coprocessor -; 46 - 0x2E - IRQ14: Primary ATA Hard Disk -; 47 - 0x2F - IRQ15: Secondary ATA Hard Disk -%assign i 32 -%rep 32 -no_error_code_interrupt_handler i -%assign i i+1 -%endrep - - - -; ISR stub table, useful for reducing code repetition -isr_stub_table: - %assign i 0 - %rep 64 - dd interrupt_handler_%+i - %assign i i+1 +extern main_interrupt_handler +global isr_stub_table + +; Generic handler section for interrupt +call_generic_handler: + ; Before interrupt_handler_n is called (caller of this generic handler section), + ; stack will have these value that pushed automatically by CPU + ; [esp + 12] eflags + ; [esp + 8 ] cs + ; [esp + 4 ] eip + ; [esp + 0 ] error code + + ; CPURegister + push esp + push ebp + push edx + push ecx + push ebx + push eax + + + ; call the C function + call main_interrupt_handler + + ; restore the registers + pop eax + pop ebx + pop ecx + pop edx + pop ebp + pop esp + + ; restore the esp (interrupt number & error code) + add esp, 8 + + ; return to the code that got interrupted + ; at this point, stack should be structured like this + ; [esp], [esp+4], [esp+8] + ; eip, cs, eflags + ; improper value will cause invalid return address & register + sti + iret + + + +; Macro for creating interrupt handler that only push interrupt number +%macro no_error_code_interrupt_handler 1 +interrupt_handler_%1: + push dword 0 ; push 0 as error code + push dword %1 ; push the interrupt number + jmp call_generic_handler ; jump to the common handler +%endmacro + +%macro error_code_interrupt_handler 1 +interrupt_handler_%1: + push dword %1 + jmp call_generic_handler +%endmacro + +; CPU exception handlers +no_error_code_interrupt_handler 0 ; 0x0 - Division by zero +no_error_code_interrupt_handler 1 ; 0x1 - Debug Exception +no_error_code_interrupt_handler 2 ; 0x2 - NMI, Non-Maskable Interrupt +no_error_code_interrupt_handler 3 ; 0x3 - Breakpoint Exception +no_error_code_interrupt_handler 4 ; 0x4 - INTO Overflow +no_error_code_interrupt_handler 5 ; 0x5 - Out of Bounds +no_error_code_interrupt_handler 6 ; 0x6 - Invalid Opcode +no_error_code_interrupt_handler 7 ; 0x7 - Device Not Available +error_code_interrupt_handler 8 ; 0x8 - Double Fault +no_error_code_interrupt_handler 9 ; 0x9 - Deprecated +error_code_interrupt_handler 10 ; 0xA - Invalid TSS +error_code_interrupt_handler 11 ; 0xB - Segment Not Present +error_code_interrupt_handler 12 ; 0xC - Stack-Segment Fault +error_code_interrupt_handler 13 ; 0xD - General Protection Fault +error_code_interrupt_handler 14 ; 0xE - Page Fault +no_error_code_interrupt_handler 15 ; 0xF - Reserved +no_error_code_interrupt_handler 16 ; 0x10 - x87 Floating-Point Exception +error_code_interrupt_handler 17 ; 0x11 - Alignment Check Exception +no_error_code_interrupt_handler 18 ; 0x12 - Machine Check Exception +no_error_code_interrupt_handler 19 ; 0x13 - SIMD Floating-Point Exception +no_error_code_interrupt_handler 20 ; 0x14 - Virtualization Exception +no_error_code_interrupt_handler 21 ; 0x15 - Control Protection Exception +no_error_code_interrupt_handler 22 ; 0x16 - Reserved +no_error_code_interrupt_handler 23 ; 0x17 - Reserved +no_error_code_interrupt_handler 24 ; 0x18 - Reserved +no_error_code_interrupt_handler 25 ; 0x19 - Reserved +no_error_code_interrupt_handler 26 ; 0x1A - Reserved +no_error_code_interrupt_handler 27 ; 0x1B - Reserved +no_error_code_interrupt_handler 28 ; 0x1C - Hypervisor Injection Exception +no_error_code_interrupt_handler 29 ; 0x1D - VMM Communication Exception +error_code_interrupt_handler 30 ; 0x1E - Security Exception +no_error_code_interrupt_handler 31 ; 0x1F - Reserved + +; User defined interrupt handler +; Assuming PIC1 & PIC2 offset is 0x20 and 0x28 +; 32 - 0x20 - IRQ0: Programmable Interval Timer +; 33 - 0x21 - IRQ1: Keyboard +; 34 - 0x22 - IRQ2: PIC Cascade, used internally +; 35 - 0x23 - IRQ3: COM2, if enabled +; 36 - 0x24 - IRQ4: COM1, if enabled +; 37 - 0x25 - IRQ5: LPT2, if enabled +; 38 - 0x26 - IRQ6: Floppy Disk +; 39 - 0x27 - IRQ7: LPT1 + +; 40 - 0x28 - IRQ8: CMOS real-time clock +; 41 - 0x29 - IRQ9: Free +; 42 - 0x2A - IRQ10: Free +; 43 - 0x2B - IRQ11: Free +; 44 - 0x2C - IRQ12: PS2 Mouse +; 45 - 0x2D - IRQ13: Coprocessor +; 46 - 0x2E - IRQ14: Primary ATA Hard Disk +; 47 - 0x2F - IRQ15: Secondary ATA Hard Disk +%assign i 32 +%rep 32 +no_error_code_interrupt_handler i +%assign i i+1 +%endrep + + + +; ISR stub table, useful for reducing code repetition +isr_stub_table: + %assign i 0 + %rep 64 + dd interrupt_handler_%+i + %assign i i+1 %endrep \ No newline at end of file diff --git a/src/kernel.c b/src/kernel.c index 581a14d..20b4494 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -1,85 +1,101 @@ -#include "lib-header/portio.h" -#include "lib-header/stdtype.h" -#include "lib-header/stdmem.h" -#include "lib-header/gdt.h" -#include "lib-header/framebuffer.h" -#include "lib-header/kernel_loader.h" -#include "lib-header/idt.h" -#include "lib-header/interrupt.h" -#include "lib-header/keyboard.h" -#include "lib-header/disk.h" -#include "lib-header/fat32.h" - -void kernel_setup(void) { - enter_protected_mode(&_gdt_gdtr); - pic_remap(); - initialize_idt(); - // activate_cmos_interrupt(); - framebuffer_clear(); - framebuffer_set_cursor(0, 0); - framebuffer_write_string("> "); - initialize_filesystem_fat32(); - - struct ClusterBuffer cbuf[5]; - for (uint32_t i = 0; i < 5; i++) - for (uint32_t j = 0; j < CLUSTER_SIZE; j++) - cbuf[i].buf[j] = i + 'a'; - - struct FAT32DriverRequest request = { - .buf = cbuf, - .name = "ikanaide", - .ext = "uwu", - .parent_cluster_number = ROOT_CLUSTER_NUMBER, - .buffer_size = 0, - } ; - struct FAT32DriverRequest request_dir = { - .buf = cbuf, - .name = "nbuna1\0\0", - .ext = "\0\0\0", - .parent_cluster_number = ROOT_CLUSTER_NUMBER, - .buffer_size = 0, - } ; - - write(request); // Create folder "ikanaide" - memcpy(request.name, "kano1\0\0\0", 8); - write(request); // Create folder "kano1" - memcpy(request.name, "ikanaide", 8); - memcpy(request.ext, "\0\0\0", 3); - delete(request); // Delete first folder, thus creating hole in FS - - memcpy(request.name, "daijoubu", 8); - memcpy(request.ext, "owo", 3); - request.buffer_size = 5*CLUSTER_SIZE; - write(request); // Create fragmented file "daijoubu" - // delete(request); - - struct ClusterBuffer readcbuf; - read_clusters(&readcbuf, ROOT_CLUSTER_NUMBER+1, 1); - // If read properly, readcbuf should filled with 'a' - - request.buffer_size = 0; - read(request); - request.buffer_size = CLUSTER_SIZE; - read(request); // Failed read due not enough buffer size - request.buffer_size = 5*CLUSTER_SIZE; - read(request); // Success read on file "daijoubu" - memcpy(request.name, "test\0\0\0\0", 8); - read(request); - memcpy(request.name, "\0\0\0\0\0\0\0\0", 8); - read(request); - memcpy(request.name, "daijoubu", 8); - // delete(request); - - write(request_dir); - read_directory(request); - read_directory(request_dir); - memcpy(request_dir.name, "nbunan\0\0", 8); - read_directory(request_dir); - memcpy(request_dir.name, "\0\0\0\0\0\0\0\0", 8); - read_directory(request_dir); - - __asm__("int $0x4"); - while (TRUE) { - keyboard_state_activate(); - } +#include "lib-header/portio.h" +#include "lib-header/stdtype.h" +#include "lib-header/stdmem.h" +#include "lib-header/gdt.h" +#include "lib-header/framebuffer.h" +#include "lib-header/kernel_loader.h" +#include "lib-header/idt.h" +#include "lib-header/interrupt.h" +#include "lib-header/keyboard.h" +#include "lib-header/disk.h" +#include "lib-header/fat32.h" +#include "lib-header/paging.h" + +// TODO : inserter masih segmentation fault + +void kernel_setup(void) { + enter_protected_mode(&_gdt_gdtr); + pic_remap(); + initialize_idt(); + framebuffer_clear(); + framebuffer_set_cursor(0, 0); + framebuffer_write_string("> "); + + activate_keyboard_interrupt(); + initialize_filesystem_fat32(); + gdt_install_tss(); + set_tss_register(); + + // Allocate first 4 MiB virtual memory + allocate_single_user_page_frame((uint8_t*) 0); + + + + struct ClusterBuffer cbuf[5]; + for (uint32_t i = 0; i < 5; i++) + for (uint32_t j = 0; j < CLUSTER_SIZE; j++) + cbuf[i].buf[j] = i + 'a'; + + struct FAT32DriverRequest request = { + .buf = cbuf, + .name = "ikanaide", + .ext = "uwu", + .parent_cluster_number = ROOT_CLUSTER_NUMBER, + .buffer_size = 0, + } ; + struct FAT32DriverRequest request_dir = { + .buf = cbuf, + .name = "nbuna1\0\0", + .ext = "\0\0\0", + .parent_cluster_number = ROOT_CLUSTER_NUMBER, + .buffer_size = 0, + } ; + + write(request); // Create folder "ikanaide" + memcpy(request.name, "kano1\0\0\0", 8); + write(request); // Create folder "kano1" + memcpy(request.name, "ikanaide", 8); + memcpy(request.ext, "\0\0\0", 3); + delete(request); // Delete first folder, thus creating hole in FS + + memcpy(request.name, "daijoubu", 8); + memcpy(request.ext, "owo", 3); + request.buffer_size = 5*CLUSTER_SIZE; + write(request); // Create fragmented file "daijoubu" + // delete(request); + + struct ClusterBuffer readcbuf; + read_clusters(&readcbuf, ROOT_CLUSTER_NUMBER+1, 1); + // If read properly, readcbuf should filled with 'a' + + request.buffer_size = 0; + read(request); + request.buffer_size = CLUSTER_SIZE; + read(request); // Failed read due not enough buffer size + request.buffer_size = 5*CLUSTER_SIZE; + read(request); // Success read on file "daijoubu" + memcpy(request.name, "test\0\0\0\0", 8); + read(request); + memcpy(request.name, "\0\0\0\0\0\0\0\0", 8); + read(request); + memcpy(request.name, "daijoubu", 8); + // delete(request); + + write(request_dir); + read_directory(request); + read_directory(request_dir); + memcpy(request_dir.name, "nbunan\0\0", 8); + read_directory(request_dir); + memcpy(request_dir.name, "\0\0\0\0\0\0\0\0", 8); + read_directory(request_dir); + + __asm__("int $0x4"); + + // Set TSS $esp pointer and jump into shell + set_tss_kernel_current_stack(); + kernel_execute_user_program((uint8_t*) 0); + + while (TRUE) { + keyboard_state_activate(); + } } \ No newline at end of file diff --git a/src/kernel_loader.s b/src/kernel_loader.s index a53eaf6..88c23c4 100644 --- a/src/kernel_loader.s +++ b/src/kernel_loader.s @@ -1,85 +1,107 @@ -global loader ; the entry symbol for ELF -global enter_protected_mode ; go to protected mode -global set_tss_register ; set tss register to GDT entry -extern kernel_setup ; kernel -extern _paging_kernel_page_directory ; kernel page directory - - -KERNEL_VIRTUAL_BASE equ 0xC0000000 ; kernel virtual memory -KERNEL_STACK_SIZE equ 2097152 ; size of stack in bytes -MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant -FLAGS equ 0x0 ; multiboot flags -CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum - ; (magic number + checksum + flags should equal 0) - -section .bss -align 4 ; align at 4 bytes -kernel_stack: ; label points to beginning of memory - resb KERNEL_STACK_SIZE ; reserve stack for the kernel - -section .__mbHeader -align 4 - dd MAGIC_NUMBER ; write the magic number to the machine code, - dd FLAGS ; the flags, - dd CHECKSUM ; and the checksum - -section .setup.text ; start of the text (code) section -loader equ (loader_entrypoint - KERNEL_VIRTUAL_BASE) -loader_entrypoint: ; the loader label (defined as entry point in linker script) - ; Set CR3 (CPU page register) - mov eax, _paging_kernel_page_directory - KERNEL_VIRTUAL_BASE - mov cr3, eax - - ; Use 4 MB paging - mov eax, cr4 - or eax, 0x00000010 ; PSE (4 MB paging) - mov cr4, eax - - ; Enable paging - mov eax, cr0 - or eax, 0x80000000 ; PG flag - mov cr0, eax - - ; Jump into higher half first, cannot use C because call stack is not working - lea eax, [loader_virtual] - jmp eax - -loader_virtual: - mov dword [_paging_kernel_page_directory], 0 - invlpg [0] ; Delete identity mapping and invalidate TLB cache for first page - mov esp, kernel_stack + KERNEL_STACK_SIZE ; Setup stack register to proper location - call kernel_setup ; Call kernel setup -.loop: - jmp .loop ; loop forever - -section .text ; start of the text (code) section -; More details: https://en.wikibooks.org/wiki/X86_Assembly/Protected_Mode -enter_protected_mode: - cli - mov eax, [esp+4] - ; eax at this line will carry GDTR location, dont forget to use square bracket [eax] - - lgdt [eax] - - mov eax, cr0 - ; Set eax with above condition, eax will be copied to CR0 with next instruction - or al, 1 - mov cr0, eax - - - ; Far jump to update cs register - ; Warning: Invalid GDT will raise exception in any instruction below - jmp 0x8:flush_cs -flush_cs: - mov ax, 10h - ; Segments register need to set with 0x10: ss, ds, es - mov ss, ax - mov ds, ax - mov es, ax - - ret - -set_tss_register: - mov ax, 0x28 | 0 ; GDT TSS Selector, ring 0 - ltr ax +global loader ; the entry symbol for ELF +global enter_protected_mode ; go to protected mode +global set_tss_register ; set tss register to GDT entry +extern kernel_setup ; kernel +extern _paging_kernel_page_directory ; kernel page directory + + +KERNEL_VIRTUAL_BASE equ 0xC0000000 ; kernel virtual memory +KERNEL_STACK_SIZE equ 2097152 ; size of stack in bytes +MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant +FLAGS equ 0x0 ; multiboot flags +CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum + ; (magic number + checksum + flags should equal 0) + +section .bss +align 4 ; align at 4 bytes +kernel_stack: ; label points to beginning of memory + resb KERNEL_STACK_SIZE ; reserve stack for the kernel + +section .__mbHeader +align 4 + dd MAGIC_NUMBER ; write the magic number to the machine code, + dd FLAGS ; the flags, + dd CHECKSUM ; and the checksum + +section .setup.text ; start of the text (code) section +loader equ (loader_entrypoint - KERNEL_VIRTUAL_BASE) +loader_entrypoint: ; the loader label (defined as entry point in linker script) + ; Set CR3 (CPU page register) + mov eax, _paging_kernel_page_directory - KERNEL_VIRTUAL_BASE + mov cr3, eax + + ; Use 4 MB paging + mov eax, cr4 + or eax, 0x00000010 ; PSE (4 MB paging) + mov cr4, eax + + ; Enable paging + mov eax, cr0 + or eax, 0x80000000 ; PG flag + mov cr0, eax + + ; Jump into higher half first, cannot use C because call stack is not working + lea eax, [loader_virtual] + jmp eax + +loader_virtual: + mov dword [_paging_kernel_page_directory], 0 + invlpg [0] ; Delete identity mapping and invalidate TLB cache for first page + mov esp, kernel_stack + KERNEL_STACK_SIZE ; Setup stack register to proper location + call kernel_setup ; Call kernel setup +.loop: + jmp .loop ; loop forever + +section .text ; start of the text (code) section +global kernel_execute_user_program ; execute user program from kernel +kernel_execute_user_program: + mov eax, 0x20 | 0x3 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + mov ecx, [esp+4] ; Save this first (before pushing anything to stack) for last push + push eax ; Stack segment selector (GDT_USER_DATA_SELECTOR), user privilege + mov eax, ecx + add eax, 0x400000 - 4 + push eax ; User space stack pointer (esp), move it into last 4 MiB + pushf ; eflags register state, when jump inside user program + mov eax, 0x18 | 0x3 + push eax ; Code segment selector (GDT_USER_CODE_SELECTOR), user privilege + mov eax, ecx + push eax ; eip register to jump back + + iret + + +; More details: https://en.wikibooks.org/wiki/X86_Assembly/Protected_Mode +enter_protected_mode: + cli + mov eax, [esp+4] + ; eax at this line will carry GDTR location, dont forget to use square bracket [eax] + + lgdt [eax] + + mov eax, cr0 + ; Set eax with above condition, eax will be copied to CR0 with next instruction + or al, 1 + mov cr0, eax + + + ; Far jump to update cs register + ; Warning: Invalid GDT will raise exception in any instruction below + jmp 0x8:flush_cs +flush_cs: + mov ax, 10h + ; Segments register need to set with 0x10: ss, ds, es + mov ss, ax + mov ds, ax + mov es, ax + + ret + +set_tss_register: + mov ax, 0x28 | 0 ; GDT TSS Selector, ring 0 + ltr ax ret \ No newline at end of file diff --git a/src/keyboard/keyboard.c b/src/keyboard/keyboard.c index 4e8ebff..1e38a61 100644 --- a/src/keyboard/keyboard.c +++ b/src/keyboard/keyboard.c @@ -1,182 +1,182 @@ -#include "../lib-header/keyboard.h" - -#include "../lib-header/framebuffer.h" -#include "../lib-header/portio.h" -#include "../lib-header/stdmem.h" - -const char keyboard_scancode_1_to_ascii_map[256] = { - 0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', - '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', - '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', - ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', - '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, - '+', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; -// CTRL 29 -// LSHIFT SCANCODE_LSHIFT -const char scancode_capital_letters[] = { - 0, 0x1B, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', - '_', '+', 0, 0, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', - 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', 'H', - 'J', 'K', 'L', ':', '\"', '~', 0, '\\', 'Z', 'X', 'C', 'V', - 'B', 'N', 'M', '<', '>', '?', 0, 0, 0, 0}; - -static struct KeyboardDriverState keyboard_state = {.caps_cond = 0}; -// static int writing = 1; -static int tempCode = 0; -// static int countCode = 0; - -void keyboard_state_activate(void) { keyboard_state.keyboard_input_on = 1; } - -void keyboard_state_deactivate(void) { keyboard_state.keyboard_input_on = 0; } - -void keyboard_isr(void) { - // Get cursor index - struct Cursor cursor = framebuffer_get_cursor(); - int row = cursor.row, col = cursor.col; - - if (!keyboard_state.keyboard_input_on) { - // Set buffer index to zero if not receiving input - keyboard_state.buffer_index = 0; - } else { - // Get scancode - uint8_t scancode = in(KEYBOARD_DATA_PORT); - if (scancode == SCANCODE_LSHIFT) { - // Left shift scancode key down - keyboard_state.shift_pressed = 1; - } - if (scancode == SCANCODE_LSHIFT + SCANCODE_KEYUP_THRESHOLD) { - // Left shift scancode key up - keyboard_state.shift_pressed = 0; - } - - if (scancode == SCANCODE_CAPS) { - // Caps lock scancode - keyboard_state.caps_cond = !keyboard_state.caps_cond; - } - - char mapped_char; - if (!(keyboard_state.shift_pressed ^ keyboard_state.caps_cond)) { - // Handle lowercase letters - mapped_char = keyboard_scancode_1_to_ascii_map[scancode]; - } else { - // Handle uppercase letters - mapped_char = scancode_capital_letters[scancode]; - } - - // Handle if scancode is key down and not capslock - if (scancode < SCANCODE_KEYUP_THRESHOLD && scancode != SCANCODE_LSHIFT && scancode != SCANCODE_CAPS) { - if (scancode != tempCode) { - if (mapped_char == '\b' && keyboard_state.buffer_index > 0) { - if (col == 0) { - framebuffer_set_cursor(row - 1, MAX_COLS - 1); - framebuffer_write(row - 1, MAX_COLS - 1, '\0', WHITE, BLACK); - } else { - framebuffer_set_cursor(row, col - 1); - framebuffer_write(row, col - 1, '\0', WHITE, BLACK); - } - keyboard_state.buffer_index--; - keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = '\0'; - } else if (mapped_char == '\n') { - execute_cmd(keyboard_state.keyboard_buffer); - memset(keyboard_state.keyboard_buffer, 0, 256); - keyboard_state.buffer_index = 0; - } else if (mapped_char != '\b' && scancode != SCANCODE_LEFTARROW) { - keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = - mapped_char; - keyboard_state.buffer_index++; - - int offset = row * MAX_COLS + col; - if (row == (MAX_ROWS - 1) && col == (MAX_COLS - 1)) { - offset = framebuffer_scroll_ln(offset); - } - - framebuffer_write(offset / MAX_COLS, offset % MAX_COLS, mapped_char, WHITE, BLACK); - framebuffer_set_cursor(offset / MAX_COLS, offset % MAX_COLS + 1); - } else if (scancode == SCANCODE_LEFTARROW && col != 2) { - framebuffer_set_cursor(row, col - 1); - } - tempCode = scancode; - if (mapped_char == SCANCODE_ESC) { - clear_screen(); - } - // framebuffer_set_cursor(row, col); - } - } else { - tempCode = scancode; - // countCode = 0; - } - } - // framebuffer_set_cursor(row, col); - pic_ack(IRQ_KEYBOARD); -} - -// Get keyboard buffer values - @param buf Pointer to char buffer, recommended -// size at least KEYBOARD_BUFFER_SIZE -void get_keyboard_buffer(char *buf) { - memcpy(buf, keyboard_state.keyboard_buffer, KEYBOARD_BUFFER_SIZE); - // buf = keyboard_state.keyboard_buffer; -} - -// Check whether keyboard ISR is active or not - @return Equal with -// keyboard_input_on value -bool is_keyboard_blocking(void) { return keyboard_state.keyboard_input_on; } - -void clear_screen() { - framebuffer_clear(); - framebuffer_set_cursor(0, 0); - framebuffer_write_string("> "); - memset(keyboard_state.keyboard_buffer, 0, 256); -} - -int strcmp(char *s1, char *s2) { - int i = 0; - while (s1[i] == s2[i]) { - if (s1[i] == '\0') { - return 0; - } - i++; - } - return s1[i] - s2[i]; -} - -void execute_cmd(char *input) { - // execute command from input - if (strcmp(input, "clear") == 0) { - // clear screen - clear_screen(); - framebuffer_set_cursor(0, 0); - framebuffer_write_string("> "); - } else { - if (strcmp(input, "help") == 0) { - // list of available commands - framebuffer_write_string("\nAvailable commands:\n"); - framebuffer_write_string("clear - Clear the screen\n"); - framebuffer_write_string("help - List of available commands"); - } else if (strcmp(&input[3], "del") == 0) { - framebuffer_write_string("\nDeleting file: "); - framebuffer_write_string(input); - } else { - framebuffer_write_string("\nCommand not found: "); - framebuffer_write_string(input); - } - struct Cursor c = framebuffer_get_cursor(); - framebuffer_set_cursor(c.row + 1, 0); - framebuffer_write_string("> "); - } +#include "../lib-header/keyboard.h" + +#include "../lib-header/framebuffer.h" +#include "../lib-header/portio.h" +#include "../lib-header/stdmem.h" + +const char keyboard_scancode_1_to_ascii_map[256] = { + 0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', + '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', + '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', + ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', + '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, + '+', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +// CTRL 29 +// LSHIFT SCANCODE_LSHIFT +const char scancode_capital_letters[] = { + 0, 0x1B, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', + '_', '+', 0, 0, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', 'H', + 'J', 'K', 'L', ':', '\"', '~', 0, '\\', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', 0, 0, 0, 0}; + +static struct KeyboardDriverState keyboard_state = {.caps_cond = 0}; +// static int writing = 1; +static int tempCode = 0; +// static int countCode = 0; + +void keyboard_state_activate(void) { keyboard_state.keyboard_input_on = 1; } + +void keyboard_state_deactivate(void) { keyboard_state.keyboard_input_on = 0; } + +void keyboard_isr(void) { + // Get cursor index + struct Cursor cursor = framebuffer_get_cursor(); + int row = cursor.row, col = cursor.col; + + if (!keyboard_state.keyboard_input_on) { + // Set buffer index to zero if not receiving input + keyboard_state.buffer_index = 0; + } else { + // Get scancode + uint8_t scancode = in(KEYBOARD_DATA_PORT); + if (scancode == SCANCODE_LSHIFT) { + // Left shift scancode key down + keyboard_state.shift_pressed = 1; + } + if (scancode == SCANCODE_LSHIFT + SCANCODE_KEYUP_THRESHOLD) { + // Left shift scancode key up + keyboard_state.shift_pressed = 0; + } + + if (scancode == SCANCODE_CAPS) { + // Caps lock scancode + keyboard_state.caps_cond = !keyboard_state.caps_cond; + } + + char mapped_char; + if (!(keyboard_state.shift_pressed ^ keyboard_state.caps_cond)) { + // Handle lowercase letters + mapped_char = keyboard_scancode_1_to_ascii_map[scancode]; + } else { + // Handle uppercase letters + mapped_char = scancode_capital_letters[scancode]; + } + + // Handle if scancode is key down and not capslock + if (scancode < SCANCODE_KEYUP_THRESHOLD && scancode != SCANCODE_LSHIFT && scancode != SCANCODE_CAPS) { + if (scancode != tempCode) { + if (mapped_char == '\b' && keyboard_state.buffer_index > 0) { + if (col == 0) { + framebuffer_set_cursor(row - 1, MAX_COLS - 1); + framebuffer_write(row - 1, MAX_COLS - 1, '\0', WHITE, BLACK); + } else { + framebuffer_set_cursor(row, col - 1); + framebuffer_write(row, col - 1, '\0', WHITE, BLACK); + } + keyboard_state.buffer_index--; + keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = '\0'; + } else if (mapped_char == '\n') { + execute_cmd(keyboard_state.keyboard_buffer); + memset(keyboard_state.keyboard_buffer, 0, 256); + keyboard_state.buffer_index = 0; + } else if (mapped_char != '\b' && scancode != SCANCODE_LEFTARROW) { + keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = + mapped_char; + keyboard_state.buffer_index++; + + int offset = row * MAX_COLS + col; + if (row == (MAX_ROWS - 1) && col == (MAX_COLS - 1)) { + offset = framebuffer_scroll_ln(offset); + } + + framebuffer_write(offset / MAX_COLS, offset % MAX_COLS, mapped_char, WHITE, BLACK); + framebuffer_set_cursor(offset / MAX_COLS, offset % MAX_COLS + 1); + } else if (scancode == SCANCODE_LEFTARROW && col != 2) { + framebuffer_set_cursor(row, col - 1); + } + tempCode = scancode; + if (mapped_char == SCANCODE_ESC) { + clear_screen(); + } + // framebuffer_set_cursor(row, col); + } + } else { + tempCode = scancode; + // countCode = 0; + } + } + // framebuffer_set_cursor(row, col); + pic_ack(IRQ_KEYBOARD); +} + +// Get keyboard buffer values - @param buf Pointer to char buffer, recommended +// size at least KEYBOARD_BUFFER_SIZE +void get_keyboard_buffer(char *buf) { + memcpy(buf, keyboard_state.keyboard_buffer, KEYBOARD_BUFFER_SIZE); + // buf = keyboard_state.keyboard_buffer; +} + +// Check whether keyboard ISR is active or not - @return Equal with +// keyboard_input_on value +bool is_keyboard_blocking(void) { return keyboard_state.keyboard_input_on; } + +void clear_screen() { + framebuffer_clear(); + framebuffer_set_cursor(0, 0); + framebuffer_write_string("> "); + memset(keyboard_state.keyboard_buffer, 0, 256); +} + +int strcmp(char *s1, char *s2) { + int i = 0; + while (s1[i] == s2[i]) { + if (s1[i] == '\0') { + return 0; + } + i++; + } + return s1[i] - s2[i]; +} + +void execute_cmd(char *input) { + // execute command from input + if (strcmp(input, "clear") == 0) { + // clear screen + clear_screen(); + framebuffer_set_cursor(0, 0); + framebuffer_write_string("> "); + } else { + if (strcmp(input, "help") == 0) { + // list of available commands + framebuffer_write_string("\nAvailable commands:\n"); + framebuffer_write_string("clear - Clear the screen\n"); + framebuffer_write_string("help - List of available commands"); + } else if (strcmp(&input[3], "del") == 0) { + framebuffer_write_string("\nDeleting file: "); + framebuffer_write_string(input); + } else { + framebuffer_write_string("\nCommand not found: "); + framebuffer_write_string(input); + } + struct Cursor c = framebuffer_get_cursor(); + framebuffer_set_cursor(c.row + 1, 0); + framebuffer_write_string("> "); + } } \ No newline at end of file diff --git a/src/lib-header/cmos.h b/src/lib-header/cmos.h index 2f2bc40..ecce339 100644 --- a/src/lib-header/cmos.h +++ b/src/lib-header/cmos.h @@ -1,58 +1,58 @@ -#ifndef _CMOS_H -#define _CMOS_H - -// TODO : Belum diintegrasiin ke fat32 - -#include "portio.h" -#include "interrupt.h" - -// CMOS -#define CURRENT_YEAR 2023 -#define CMOS_ADDRESS 0x70 -#define CMOS_DATA 0x71 - -#define REG_SECONDS 0x00 -#define REG_MINUTES 0x02 -#define REG_HOURS 0x04 -#define REG_DAY_OF_MONTH 0x07 -#define REG_MONTH 0x08 -#define REG_YEAR 0x09 - -typedef struct dateNtime{ - struct time time; - struct date date; -} __attribute__((packed)); - -typedef struct date -{ - uint8_t day; - uint8_t month; - uint16_t year; -} __attribute__((packed)); - -typedef struct time -{ - uint8_t second; - uint8_t minute; - uint8_t hour; -} __attribute__((packed)); - -void init_cmos(); - -int8_t is_update_cmos(); - -uint8_t get_cmos_reg(int reg); - -void set_cmos_reg(int reg, uint8_t value); - -void read_cmos(); - -void write_cmos(struct dateNtime * dnt); - -int8_t is_leap_year(uint16_t year); - -struct date get_currdate(); - -struct time get_currtime(); - +#ifndef _CMOS_H +#define _CMOS_H + +// TODO : Belum diintegrasiin ke fat32 + +#include "portio.h" +#include "interrupt.h" + +// CMOS +#define CURRENT_YEAR 2023 +#define CMOS_ADDRESS 0x70 +#define CMOS_DATA 0x71 + +#define REG_SECONDS 0x00 +#define REG_MINUTES 0x02 +#define REG_HOURS 0x04 +#define REG_DAY_OF_MONTH 0x07 +#define REG_MONTH 0x08 +#define REG_YEAR 0x09 + +typedef struct dateNtime{ + struct time time; + struct date date; +} __attribute__((packed)); + +typedef struct date +{ + uint8_t day; + uint8_t month; + uint16_t year; +} __attribute__((packed)); + +typedef struct time +{ + uint8_t second; + uint8_t minute; + uint8_t hour; +} __attribute__((packed)); + +void init_cmos(); + +int8_t is_update_cmos(); + +uint8_t get_cmos_reg(int reg); + +void set_cmos_reg(int reg, uint8_t value); + +void read_cmos(); + +void write_cmos(struct dateNtime * dnt); + +int8_t is_leap_year(uint16_t year); + +struct date get_currdate(); + +struct time get_currtime(); + #endif \ No newline at end of file diff --git a/src/lib-header/disk.h b/src/lib-header/disk.h index 7e73d4d..2c10aad 100644 --- a/src/lib-header/disk.h +++ b/src/lib-header/disk.h @@ -1,52 +1,52 @@ -#ifndef _DISK_H -#define _DISK_H - -#include "stdtype.h" - -/* -- ATA PIO status codes -- */ -#define ATA_STATUS_BSY 0x80 -#define ATA_STATUS_RDY 0x40 -#define ATA_STATUS_DRQ 0x08 -#define ATA_STATUS_DF 0x20 -#define ATA_STATUS_ERR 0x01 - -#define BLOCK_SIZE 512 -#define HALF_BLOCK_SIZE (BLOCK_SIZE/2) - - - - - -// Block buffer data type - @param buf Byte buffer with size of BLOCK_SIZE -struct BlockBuffer { - uint8_t buf[BLOCK_SIZE]; -} __attribute__((packed)); - - - - - -/** - * ATA PIO logical block address read blocks. Will blocking until read is completed. - * Note: ATA PIO will use 2-bytes per read/write operation. - * Recommended to use struct BlockBuffer - * - * @param ptr Pointer for storing reading data, this pointer should point to already allocated memory location. - * With allocated size positive integer multiple of BLOCK_SIZE, ex: buf[1024] - * @param logical_block_address Block address to read data from. Use LBA addressing - * @param block_count How many block to read, starting from block logical_block_address to lba-1 - */ -void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count); - -/** - * ATA PIO logical block address write blocks. Will blocking until write is completed. - * Note: ATA PIO will use 2-bytes per read/write operation. - * Recommended to use struct BlockBuffer - * - * @param ptr Pointer to data that to be written into disk. Memory pointed should be positive integer multiple of BLOCK_SIZE - * @param logical_block_address Block address to write data into. Use LBA addressing - * @param block_count How many block to write, starting from block logical_block_address to lba-1 - */ -void write_blocks(const void *ptr, uint32_t logical_block_address, uint8_t block_count); - +#ifndef _DISK_H +#define _DISK_H + +#include "stdtype.h" + +/* -- ATA PIO status codes -- */ +#define ATA_STATUS_BSY 0x80 +#define ATA_STATUS_RDY 0x40 +#define ATA_STATUS_DRQ 0x08 +#define ATA_STATUS_DF 0x20 +#define ATA_STATUS_ERR 0x01 + +#define BLOCK_SIZE 512 +#define HALF_BLOCK_SIZE (BLOCK_SIZE/2) + + + + + +// Block buffer data type - @param buf Byte buffer with size of BLOCK_SIZE +struct BlockBuffer { + uint8_t buf[BLOCK_SIZE]; +} __attribute__((packed)); + + + + + +/** + * ATA PIO logical block address read blocks. Will blocking until read is completed. + * Note: ATA PIO will use 2-bytes per read/write operation. + * Recommended to use struct BlockBuffer + * + * @param ptr Pointer for storing reading data, this pointer should point to already allocated memory location. + * With allocated size positive integer multiple of BLOCK_SIZE, ex: buf[1024] + * @param logical_block_address Block address to read data from. Use LBA addressing + * @param block_count How many block to read, starting from block logical_block_address to lba-1 + */ +void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count); + +/** + * ATA PIO logical block address write blocks. Will blocking until write is completed. + * Note: ATA PIO will use 2-bytes per read/write operation. + * Recommended to use struct BlockBuffer + * + * @param ptr Pointer to data that to be written into disk. Memory pointed should be positive integer multiple of BLOCK_SIZE + * @param logical_block_address Block address to write data into. Use LBA addressing + * @param block_count How many block to write, starting from block logical_block_address to lba-1 + */ +void write_blocks(const void *ptr, uint32_t logical_block_address, uint8_t block_count); + #endif \ No newline at end of file diff --git a/src/lib-header/fat32.h b/src/lib-header/fat32.h index cd7b1e0..7a21d96 100644 --- a/src/lib-header/fat32.h +++ b/src/lib-header/fat32.h @@ -1,259 +1,259 @@ -#ifndef _FAT32_H -#define _FAT32_H - -#include "disk.h" -#include "stdtype.h" - - -/** - * FAT32 - IF2230 edition - 2023 - * Check "IF2230 - Guidebook - Milestone 2" for more details - * https://docs.google.com/document/d/1IFyxHSYYpKgecHcS0T64oDc4bVElaq8tBcm1_mjjGGM/edit# - */ - - - -/* -- IF2230 File System constants -- */ -#define BOOT_SECTOR 0 -#define CLUSTER_BLOCK_COUNT 4 -#define CLUSTER_SIZE (BLOCK_SIZE*CLUSTER_BLOCK_COUNT) -#define CLUSTER_MAP_SIZE 512 - -/* -- FAT32 FileAllocationTable constants -- */ -// FAT reserved value for cluster 0 and 1 in FileAllocationTable -#define CLUSTER_0_VALUE 0x0FFFFFF0 -#define CLUSTER_1_VALUE 0x0FFFFFFF - -// EOF also double as valid cluster / "this is last valid cluster in the chain" -#define FAT32_FAT_END_OF_FILE 0x0FFFFFFF -#define FAT32_FAT_EMPTY_ENTRY 0x00000000 - -#define FAT_CLUSTER_NUMBER 1 -#define ROOT_CLUSTER_NUMBER 2 - -/* -- FAT32 DirectoryEntry constants -- */ -#define ATTR_SUBDIRECTORY 0b00010000 -#define UATTR_NOT_EMPTY 0b10101010 -#define ATTR_ARCHIVE 0b00100000 - - -// Boot sector signature for this file system "FAT32 - IF2230 edition" -extern const uint8_t fs_signature[BLOCK_SIZE]; - -// Cluster buffer data type - @param buf Byte buffer with size of CLUSTER_SIZE -struct ClusterBuffer { - uint8_t buf[CLUSTER_SIZE]; -} __attribute__((packed)); - - - - - -/* -- FAT32 Data Structures -- */ - -/** - * FAT32 FileAllocationTable, for more information about this, check guidebook - * - * @param cluster_map Containing cluster map of FAT32 - */ -struct FAT32FileAllocationTable { - uint32_t cluster_map[CLUSTER_MAP_SIZE]; -} __attribute__((packed)); - -/** - * FAT32 standard 8.3 format - 32 bytes DirectoryEntry, Some detail can be found at: - * https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#Directory_entry, and click show table. - * - * @param name Entry name - * @param ext File extension - * @param attribute Will be used exclusively for subdirectory flag / determining this entry is file or folder - * @param user_attribute If this attribute equal with UATTR_NOT_EMPTY then entry is not empty - * - * @param undelete Unused / optional - * @param create_time Unused / optional - * @param create_date Unused / optional - * @param access_time Unused / optional - * @param cluster_high Upper 16-bit of cluster number - * - * @param modified_time Unused / optional - * @param modified_date Unused / optional - * @param cluster_low Lower 16-bit of cluster number - * @param filesize Filesize of this file, if this is directory / folder, filesize is 0 - */ -struct FAT32DirectoryEntry { - char name[8]; - char ext[3]; - uint8_t attribute; - uint8_t user_attribute; - - bool undelete; - uint16_t create_time; - uint16_t create_date; - uint16_t access_date; - uint16_t cluster_high; - - uint16_t modified_time; - uint16_t modified_date; - uint16_t cluster_low; - uint32_t filesize; -} __attribute__((packed)); - -// FAT32 DirectoryTable, containing directory entry table - @param table Table of DirectoryEntry that span within 1 cluster -struct FAT32DirectoryTable { - struct FAT32DirectoryEntry table[CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry)]; -} __attribute__((packed)); - - - - - -/* -- FAT32 Driver -- */ - -/** - * FAT32DriverState - Contain all driver states - * - * @param fat_table FAT of the system, will be loaded during initialize_filesystem_fat32() - * @param dir_table_buf Buffer for directory table - * @param cluster_buf Buffer for cluster - */ -struct FAT32DriverState { - struct FAT32FileAllocationTable fat_table; - struct FAT32DirectoryTable dir_table_buf; - struct ClusterBuffer cluster_buf; -} __attribute__((packed)); - -/** - * FAT32DriverRequest - Request for Driver CRUD operation - * - * @param buf Pointer pointing to buffer - * @param name Name for directory entry - * @param ext Extension for file - * @param parent_cluster_number Parent directory cluster number, for updating metadata - * @param buffer_size Buffer size, CRUD operation will have different behaviour with this attribute - */ -struct FAT32DriverRequest { - void *buf; - char name[8]; - char ext[3]; - uint32_t parent_cluster_number; - uint32_t buffer_size; -} __attribute__((packed)); - - - - - -/* -- Driver Interfaces -- */ - -/** - * Convert cluster number to logical block address - * - * @param cluster Cluster number to convert - * @return uint32_t Logical Block Address - */ -uint32_t cluster_to_lba(uint32_t cluster); - -/** - * Initialize DirectoryTable value with parent DirectoryEntry and directory name - * - * @param dir_table Pointer to directory table - * @param name 8-byte char for directory name - * @param parent_dir_cluster Parent directory cluster number - */ -void init_directory_table(struct FAT32DirectoryTable *dir_table, char *name, uint32_t parent_dir_cluster); - -/** - * Checking whether filesystem signature is missing or not in boot sector - * - * @return True if memcmp(boot_sector, fs_signature) returning inequality - */ -bool is_empty_storage(void); - -/** - * Create new FAT32 file system. Will write fs_signature into boot sector and - * proper FileAllocationTable (contain CLUSTER_0_VALUE, CLUSTER_1_VALUE, - * and initialized root directory) into cluster number 1 - */ -void create_fat32(void); - -/** - * Initialize file system driver state, if is_empty_storage() then create_fat32() - * Else, read and cache entire FileAllocationTable (located at cluster number 1) into driver state - */ -void initialize_filesystem_fat32(void); - -/** - * Write cluster operation, wrapper for write_blocks(). - * Recommended to use struct ClusterBuffer - * - * @param ptr Pointer to source data - * @param cluster_number Cluster number to write - * @param cluster_count Cluster count to write, due limitation of write_blocks block_count 255 => max cluster_count = 63 - */ -void write_clusters(const void *ptr, uint32_t cluster_number, uint8_t cluster_count); - -/** - * Read cluster operation, wrapper for read_blocks(). - * Recommended to use struct ClusterBuffer - * - * @param ptr Pointer to buffer for reading - * @param cluster_number Cluster number to read - * @param cluster_count Cluster count to read, due limitation of read_blocks block_count 255 => max cluster_count = 63 - */ -void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count); - - - - - -/* -- CRUD Operation -- */ - -/** - * FAT32 Folder / Directory read - * - * @param request buf point to struct FAT32DirectoryTable, - * name is directory name, - * ext is unused, - * parent_cluster_number is target directory table to read, - * buffer_size must be exactly sizeof(struct FAT32DirectoryTable) - * @return Error code: 0 success - 1 not a folder - 2 not found - -1 unknown - */ -int8_t read_directory(struct FAT32DriverRequest request); - - -/** - * FAT32 read, read a file from file system. - * - * @param request All attribute will be used for read, buffer_size will limit reading count - * @return Error code: 0 success - 1 not a file - 2 not enough buffer - 3 not found - -1 unknown - */ -int8_t read(struct FAT32DriverRequest request); - -/** - * FAT32 write, write a file or folder to file system. - * - * @param request All attribute will be used for write, buffer_size == 0 then create a folder / directory - * @return Error code: 0 success - 1 file/folder already exist - 2 invalid parent cluster - -1 unknown - */ -int8_t write(struct FAT32DriverRequest request); - - -/** - * FAT32 delete, delete a file or empty directory (only 1 DirectoryEntry) in file system. - * - * @param request buf and buffer_size is unused - * @return Error code: 0 success - 1 not found - 2 folder is not empty - -1 unknown - */ -int8_t delete(struct FAT32DriverRequest request); - -/** - * FAT32 find empty cluster in file allocation table. - * @brief Find empty cluster in file allocation table. - * - * @param size size of empty_fat_clusters - * - * @return struct EmptyCluster -*/ -struct EmptyCluster find_empty_cluster(uint32_t size); - +#ifndef _FAT32_H +#define _FAT32_H + +#include "disk.h" +#include "stdtype.h" + + +/** + * FAT32 - IF2230 edition - 2023 + * Check "IF2230 - Guidebook - Milestone 2" for more details + * https://docs.google.com/document/d/1IFyxHSYYpKgecHcS0T64oDc4bVElaq8tBcm1_mjjGGM/edit# + */ + + + +/* -- IF2230 File System constants -- */ +#define BOOT_SECTOR 0 +#define CLUSTER_BLOCK_COUNT 4 +#define CLUSTER_SIZE (BLOCK_SIZE*CLUSTER_BLOCK_COUNT) +#define CLUSTER_MAP_SIZE 512 + +/* -- FAT32 FileAllocationTable constants -- */ +// FAT reserved value for cluster 0 and 1 in FileAllocationTable +#define CLUSTER_0_VALUE 0x0FFFFFF0 +#define CLUSTER_1_VALUE 0x0FFFFFFF + +// EOF also double as valid cluster / "this is last valid cluster in the chain" +#define FAT32_FAT_END_OF_FILE 0x0FFFFFFF +#define FAT32_FAT_EMPTY_ENTRY 0x00000000 + +#define FAT_CLUSTER_NUMBER 1 +#define ROOT_CLUSTER_NUMBER 2 + +/* -- FAT32 DirectoryEntry constants -- */ +#define ATTR_SUBDIRECTORY 0b00010000 +#define UATTR_NOT_EMPTY 0b10101010 +#define ATTR_ARCHIVE 0b00100000 + + +// Boot sector signature for this file system "FAT32 - IF2230 edition" +extern const uint8_t fs_signature[BLOCK_SIZE]; + +// Cluster buffer data type - @param buf Byte buffer with size of CLUSTER_SIZE +struct ClusterBuffer { + uint8_t buf[CLUSTER_SIZE]; +} __attribute__((packed)); + + + + + +/* -- FAT32 Data Structures -- */ + +/** + * FAT32 FileAllocationTable, for more information about this, check guidebook + * + * @param cluster_map Containing cluster map of FAT32 + */ +struct FAT32FileAllocationTable { + uint32_t cluster_map[CLUSTER_MAP_SIZE]; +} __attribute__((packed)); + +/** + * FAT32 standard 8.3 format - 32 bytes DirectoryEntry, Some detail can be found at: + * https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#Directory_entry, and click show table. + * + * @param name Entry name + * @param ext File extension + * @param attribute Will be used exclusively for subdirectory flag / determining this entry is file or folder + * @param user_attribute If this attribute equal with UATTR_NOT_EMPTY then entry is not empty + * + * @param undelete Unused / optional + * @param create_time Unused / optional + * @param create_date Unused / optional + * @param access_time Unused / optional + * @param cluster_high Upper 16-bit of cluster number + * + * @param modified_time Unused / optional + * @param modified_date Unused / optional + * @param cluster_low Lower 16-bit of cluster number + * @param filesize Filesize of this file, if this is directory / folder, filesize is 0 + */ +struct FAT32DirectoryEntry { + char name[8]; + char ext[3]; + uint8_t attribute; + uint8_t user_attribute; + + bool undelete; + uint16_t create_time; + uint16_t create_date; + uint16_t access_date; + uint16_t cluster_high; + + uint16_t modified_time; + uint16_t modified_date; + uint16_t cluster_low; + uint32_t filesize; +} __attribute__((packed)); + +// FAT32 DirectoryTable, containing directory entry table - @param table Table of DirectoryEntry that span within 1 cluster +struct FAT32DirectoryTable { + struct FAT32DirectoryEntry table[CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry)]; +} __attribute__((packed)); + + + + + +/* -- FAT32 Driver -- */ + +/** + * FAT32DriverState - Contain all driver states + * + * @param fat_table FAT of the system, will be loaded during initialize_filesystem_fat32() + * @param dir_table_buf Buffer for directory table + * @param cluster_buf Buffer for cluster + */ +struct FAT32DriverState { + struct FAT32FileAllocationTable fat_table; + struct FAT32DirectoryTable dir_table_buf; + struct ClusterBuffer cluster_buf; +} __attribute__((packed)); + +/** + * FAT32DriverRequest - Request for Driver CRUD operation + * + * @param buf Pointer pointing to buffer + * @param name Name for directory entry + * @param ext Extension for file + * @param parent_cluster_number Parent directory cluster number, for updating metadata + * @param buffer_size Buffer size, CRUD operation will have different behaviour with this attribute + */ +struct FAT32DriverRequest { + void *buf; + char name[8]; + char ext[3]; + uint32_t parent_cluster_number; + uint32_t buffer_size; +} __attribute__((packed)); + + + + + +/* -- Driver Interfaces -- */ + +/** + * Convert cluster number to logical block address + * + * @param cluster Cluster number to convert + * @return uint32_t Logical Block Address + */ +uint32_t cluster_to_lba(uint32_t cluster); + +/** + * Initialize DirectoryTable value with parent DirectoryEntry and directory name + * + * @param dir_table Pointer to directory table + * @param name 8-byte char for directory name + * @param parent_dir_cluster Parent directory cluster number + */ +void init_directory_table(struct FAT32DirectoryTable *dir_table, char *name, uint32_t parent_dir_cluster); + +/** + * Checking whether filesystem signature is missing or not in boot sector + * + * @return True if memcmp(boot_sector, fs_signature) returning inequality + */ +bool is_empty_storage(void); + +/** + * Create new FAT32 file system. Will write fs_signature into boot sector and + * proper FileAllocationTable (contain CLUSTER_0_VALUE, CLUSTER_1_VALUE, + * and initialized root directory) into cluster number 1 + */ +void create_fat32(void); + +/** + * Initialize file system driver state, if is_empty_storage() then create_fat32() + * Else, read and cache entire FileAllocationTable (located at cluster number 1) into driver state + */ +void initialize_filesystem_fat32(void); + +/** + * Write cluster operation, wrapper for write_blocks(). + * Recommended to use struct ClusterBuffer + * + * @param ptr Pointer to source data + * @param cluster_number Cluster number to write + * @param cluster_count Cluster count to write, due limitation of write_blocks block_count 255 => max cluster_count = 63 + */ +void write_clusters(const void *ptr, uint32_t cluster_number, uint8_t cluster_count); + +/** + * Read cluster operation, wrapper for read_blocks(). + * Recommended to use struct ClusterBuffer + * + * @param ptr Pointer to buffer for reading + * @param cluster_number Cluster number to read + * @param cluster_count Cluster count to read, due limitation of read_blocks block_count 255 => max cluster_count = 63 + */ +void read_clusters(void *ptr, uint32_t cluster_number, uint8_t cluster_count); + + + + + +/* -- CRUD Operation -- */ + +/** + * FAT32 Folder / Directory read + * + * @param request buf point to struct FAT32DirectoryTable, + * name is directory name, + * ext is unused, + * parent_cluster_number is target directory table to read, + * buffer_size must be exactly sizeof(struct FAT32DirectoryTable) + * @return Error code: 0 success - 1 not a folder - 2 not found - -1 unknown + */ +int8_t read_directory(struct FAT32DriverRequest request); + + +/** + * FAT32 read, read a file from file system. + * + * @param request All attribute will be used for read, buffer_size will limit reading count + * @return Error code: 0 success - 1 not a file - 2 not enough buffer - 3 not found - -1 unknown + */ +int8_t read(struct FAT32DriverRequest request); + +/** + * FAT32 write, write a file or folder to file system. + * + * @param request All attribute will be used for write, buffer_size == 0 then create a folder / directory + * @return Error code: 0 success - 1 file/folder already exist - 2 invalid parent cluster - -1 unknown + */ +int8_t write(struct FAT32DriverRequest request); + + +/** + * FAT32 delete, delete a file or empty directory (only 1 DirectoryEntry) in file system. + * + * @param request buf and buffer_size is unused + * @return Error code: 0 success - 1 not found - 2 folder is not empty - -1 unknown + */ +int8_t delete(struct FAT32DriverRequest request); + +/** + * FAT32 find empty cluster in file allocation table. + * @brief Find empty cluster in file allocation table. + * + * @param size size of empty_fat_clusters + * + * @return struct EmptyCluster +*/ +struct EmptyCluster find_empty_cluster(uint32_t size); + #endif \ No newline at end of file diff --git a/src/lib-header/framebuffer.h b/src/lib-header/framebuffer.h index b298bb6..4929ad0 100644 --- a/src/lib-header/framebuffer.h +++ b/src/lib-header/framebuffer.h @@ -1,80 +1,80 @@ -#ifndef _FRAMEBUFFER_H -#define _FRAMEBUFFER_H - -#include "lib-header/stdtype.h" - -#define MEMORY_FRAMEBUFFER (uint8_t *) 0xC00B8000 -#define CURSOR_PORT_CMD 0x03D4 -#define CURSOR_PORT_DATA 0x03D5 -#define VGA_CURSOR_HIGH 0x0E -#define VGA_CURSOR_LOW 0x0F - -#define MAX_ROWS 25 -#define MAX_COLS 80 -#define WHITE 0x0F -#define BLACK 0x00 - -struct Cursor { - uint8_t row; - uint8_t col; -} __attribute((packed)); - - -/** - * Terminal framebuffer - * Resolution: 80x25 - * Starting at MEMORY_FRAMEBUFFER, - * - Even number memory: Character, 8-bit - * - Odd number memory: Character color lower 4-bit, Background color upper 4-bit -*/ - -/** - * Set framebuffer character and color with corresponding parameter values. - * More details: https://en.wikipedia.org/wiki/BIOS_color_attributes - * - * @param row Vertical location (index start 0) - * @param col Horizontal location (index start 0) - * @param c Character - * @param fg Foreground / Character color - * @param bg Background color - */ -void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg); - -/** - * Set cursor to specified location. Row and column starts from 0 - * - * @param r row - * @param c column -*/ -void framebuffer_set_cursor(uint8_t r, uint8_t c); - -/** - * Get cursor location - * - * @param r row - * @param c column -*/ -struct Cursor framebuffer_get_cursor(); - -/** - * Set all cell in framebuffer character to 0x00 (empty character) - * and color to 0x07 (gray character & black background) - * - */ -void framebuffer_clear(void); - -/** - * @brief Write string to framebuffer - * - * @param char* str - */ -void framebuffer_write_string(char *str); - -/** - * @brief scroll framebuffer by offset lines - * - * @param int offset - * Scroll framebuffer by offset lines - */ -int framebuffer_scroll_ln(int offset); +#ifndef _FRAMEBUFFER_H +#define _FRAMEBUFFER_H + +#include "lib-header/stdtype.h" + +#define MEMORY_FRAMEBUFFER (uint8_t *) 0xC00B8000 +#define CURSOR_PORT_CMD 0x03D4 +#define CURSOR_PORT_DATA 0x03D5 +#define VGA_CURSOR_HIGH 0x0E +#define VGA_CURSOR_LOW 0x0F + +#define MAX_ROWS 25 +#define MAX_COLS 80 +#define WHITE 0x0F +#define BLACK 0x00 + +struct Cursor { + uint8_t row; + uint8_t col; +} __attribute((packed)); + + +/** + * Terminal framebuffer + * Resolution: 80x25 + * Starting at MEMORY_FRAMEBUFFER, + * - Even number memory: Character, 8-bit + * - Odd number memory: Character color lower 4-bit, Background color upper 4-bit +*/ + +/** + * Set framebuffer character and color with corresponding parameter values. + * More details: https://en.wikipedia.org/wiki/BIOS_color_attributes + * + * @param row Vertical location (index start 0) + * @param col Horizontal location (index start 0) + * @param c Character + * @param fg Foreground / Character color + * @param bg Background color + */ +void framebuffer_write(uint8_t row, uint8_t col, char c, uint8_t fg, uint8_t bg); + +/** + * Set cursor to specified location. Row and column starts from 0 + * + * @param r row + * @param c column +*/ +void framebuffer_set_cursor(uint8_t r, uint8_t c); + +/** + * Get cursor location + * + * @param r row + * @param c column +*/ +struct Cursor framebuffer_get_cursor(); + +/** + * Set all cell in framebuffer character to 0x00 (empty character) + * and color to 0x07 (gray character & black background) + * + */ +void framebuffer_clear(void); + +/** + * @brief Write string to framebuffer + * + * @param char* str + */ +void framebuffer_write_string(char *str); + +/** + * @brief scroll framebuffer by offset lines + * + * @param int offset + * Scroll framebuffer by offset lines + */ +int framebuffer_scroll_ln(int offset); #endif \ No newline at end of file diff --git a/src/lib-header/gdt.h b/src/lib-header/gdt.h index 57c7572..2779171 100644 --- a/src/lib-header/gdt.h +++ b/src/lib-header/gdt.h @@ -1,70 +1,71 @@ -#ifndef _GDT_H -#define _GDT_H -#define GDT_USER_CODE_SEGMENT_SELECTOR 0x18 -#define GDT_USER_DATA_SEGMENT_SELECTOR 0x20 -#define GDT_TSS_SELECTOR 0x28 - -#include "lib-header/stdtype.h" - -#define GDT_MAX_ENTRY_COUNT 32 - -extern struct GDTR _gdt_gdtr; - -/** - * Segment Descriptor storing system segment information. - * Struct defined exactly as Intel Manual Segment Descriptor definition (Figure 3-8 Segment Descriptor). - * Manual can be downloaded at www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.html/ - * - * @param segment_low 16-bit lower-bit segment limit - * @param base_low 16-bit lower-bit base address - * @param base_mid 8-bit middle-bit base address - * @param type_bit 4-bit contain type flags - * @param non_system 1-bit contain system - */ -struct SegmentDescriptor { - // First 32-bit - uint16_t segment_low; - uint16_t base_low; - - // Next 16-bit (Bit 32 to 47) - uint8_t base_mid; - uint8_t type_bit : 4; - uint8_t non_system : 1; - - uint8_t dpl : 2; - uint8_t segment_present : 1; - uint8_t segment_limit : 4; - uint8_t avl : 1; - uint8_t code_seg_64bit : 1; - uint8_t def_op_size : 1; - uint8_t granularity : 1; - uint8_t base_high : 8; - -} __attribute__((packed)); - -/** - * Global Descriptor Table containing list of segment descriptor. One GDT already defined in memory.c. - * More details at https://wiki.osdev.org/GDT_Tutorial - * @param table Fixed-width array of SegmentDescriptor with size GDT_MAX_ENTRY_COUNT - */ -struct GlobalDescriptorTable { - struct SegmentDescriptor table[GDT_MAX_ENTRY_COUNT]; -} __attribute__((packed)); - -/** - * GDTR, carrying information where's the GDT located and GDT size. - * Global kernel variable defined at memory.c. - * - * @param size Global Descriptor Table size, use sizeof operator - * @param address GDT address, GDT should already defined properly - */ -struct GDTR { - uint16_t size; - struct GlobalDescriptorTable *address; -} __attribute__((packed)); - - -// Set GDT_TSS_SELECTOR with proper TSS values, accessing _interrupt_tss_entry -void gdt_install_tss(void); - +#ifndef _GDT_H +#define _GDT_H +#define GDT_USER_CODE_SEGMENT_SELECTOR 0x18 +#define GDT_USER_DATA_SEGMENT_SELECTOR 0x20 +#define GDT_TSS_SELECTOR 0x28 + +#include "lib-header/stdtype.h" +#include "lib-header/interrupt.h" + +#define GDT_MAX_ENTRY_COUNT 32 + +extern struct GDTR _gdt_gdtr; + +/** + * Segment Descriptor storing system segment information. + * Struct defined exactly as Intel Manual Segment Descriptor definition (Figure 3-8 Segment Descriptor). + * Manual can be downloaded at www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.html/ + * + * @param segment_low 16-bit lower-bit segment limit + * @param base_low 16-bit lower-bit base address + * @param base_mid 8-bit middle-bit base address + * @param type_bit 4-bit contain type flags + * @param non_system 1-bit contain system + */ +struct SegmentDescriptor { + // First 32-bit + uint16_t segment_low; + uint16_t base_low; + + // Next 16-bit (Bit 32 to 47) + uint8_t base_mid; + uint8_t type_bit : 4; + uint8_t non_system : 1; + + uint8_t dpl : 2; + uint8_t segment_present : 1; + uint8_t segment_limit : 4; + uint8_t avl : 1; + uint8_t code_seg_64bit : 1; + uint8_t def_op_size : 1; + uint8_t granularity : 1; + uint8_t base_high : 8; + +} __attribute__((packed)); + +/** + * Global Descriptor Table containing list of segment descriptor. One GDT already defined in memory.c. + * More details at https://wiki.osdev.org/GDT_Tutorial + * @param table Fixed-width array of SegmentDescriptor with size GDT_MAX_ENTRY_COUNT + */ +struct GlobalDescriptorTable { + struct SegmentDescriptor table[GDT_MAX_ENTRY_COUNT]; +} __attribute__((packed)); + +/** + * GDTR, carrying information where's the GDT located and GDT size. + * Global kernel variable defined at memory.c. + * + * @param size Global Descriptor Table size, use sizeof operator + * @param address GDT address, GDT should already defined properly + */ +struct GDTR { + uint16_t size; + struct GlobalDescriptorTable *address; +} __attribute__((packed)); + + +// Set GDT_TSS_SELECTOR with proper TSS values, accessing _interrupt_tss_entry +void gdt_install_tss(void); + #endif \ No newline at end of file diff --git a/src/lib-header/idt.h b/src/lib-header/idt.h index 0d92fc6..c31b2e6 100644 --- a/src/lib-header/idt.h +++ b/src/lib-header/idt.h @@ -1,100 +1,100 @@ -#ifndef _IDT_H -#define _IDT_H - -#include "stdtype.h" -// #include - -// IDT hard limit, see Intel x86 manual 3a - 6.10 Interrupt Descriptor Table -#define IDT_MAX_ENTRY_COUNT 256 -#define ISR_STUB_TABLE_LIMIT 64 -#define INTERRUPT_GATE_R_BIT_1 0b000 -#define INTERRUPT_GATE_R_BIT_2 0b110 -#define INTERRUPT_GATE_R_BIT_3 0b0 - -// Some GDT Constant -#define GDT_KERNEL_CODE_SEGMENT_SELECTOR 0x8 -#define GDT_KERNEL_DATA_SEGMENT_SELECTOR 0x10 - -// Interrupt Handler / ISR stub for reducing code duplication, this array can be -// iterated in initialize_idt() -extern void *isr_stub_table[ISR_STUB_TABLE_LIMIT]; - -extern struct IDTR _idt_idtr; - -/** - * IDTGate, IDT entry that point into interrupt handler - * Struct defined exactly in Intel x86 Vol 3a - Figure 6-2. IDT Gate Descriptors - * - * @param offset_low Lower 16-bit offset - * @param segment Memory segment - * @param _reserved Reserved bit, bit length: 5 - * @param _r_bit_1 Reserved for idtgate type, bit length: 3 - * @param _r_bit_2 Reserved for idtgate type, bit length: 3 - * @param gate_32 Is this gate size 32-bit? If not then its 16-bit - gate - * @param _r_bit_3 Reserved for idtgate type, bit length: 1 - * @param dpl Descriptor Privilege Level, bit length: 2 - * @param valid_bit Segment present flag, bit length: 1 - * @param offset_high Higher 16-bit offset - * ... - - */ -struct IDTGate { - // First 32-bit (Bit 0 to 31) - uint16_t offset_low; - uint16_t segment; - - uint8_t _reserved : 5; - uint8_t _r_bit_1 : 3; - uint8_t _r_bit_2 : 3; - uint8_t gate_32 : 1; - uint8_t _r_bit_3 : 1; - uint8_t dpl : 2; - uint8_t valid_bit : 1; - uint16_t offset_high; - -} __attribute__((packed)); - -/** - * Interrupt Descriptor Table, containing lists of IDTGate. - * One IDT already defined in idt.c - * - * @param table Fixed-width array of IDTGate with size IDT_MAX_ENTRY_COUNT - */ - -struct InterruptDescriptorTable { - struct IDTGate table[IDT_MAX_ENTRY_COUNT]; -} __attribute__((packed)); - -/** - * IDTR, carrying information where's the IDT located and size. - * Global kernel variable defined at idt.c. - * - * @param size Interrupt Descriptor Table size - * @param address IDT address - */ - -struct IDTR { - uint16_t size; - struct InterruptDescriptorTable *address; -} __attribute__((packed)); - -/** - * Set IDTGate with proper interrupt handler values. - * Will directly edit global IDT variable and set values properly - * - * @param int_vector Interrupt vector to handle - * @param handler_address Interrupt handler address - * @param gdt_seg_selector GDT segment selector, for kernel use - * GDT_KERNEL_CODE_SEGMENT_SELECTOR - * @param privilege Descriptor privilege level - */ -void set_interrupt_gate(uint8_t int_vector, void *handler_address, - uint16_t gdt_seg_selector, uint8_t privilege); - -/** - * Set IDT with proper values and load with lidt - */ -void initialize_idt(void); - +#ifndef _IDT_H +#define _IDT_H + +#include "stdtype.h" +// #include + +// IDT hard limit, see Intel x86 manual 3a - 6.10 Interrupt Descriptor Table +#define IDT_MAX_ENTRY_COUNT 256 +#define ISR_STUB_TABLE_LIMIT 64 +#define INTERRUPT_GATE_R_BIT_1 0b000 +#define INTERRUPT_GATE_R_BIT_2 0b110 +#define INTERRUPT_GATE_R_BIT_3 0b0 + +// Some GDT Constant +#define GDT_KERNEL_CODE_SEGMENT_SELECTOR 0x8 +#define GDT_KERNEL_DATA_SEGMENT_SELECTOR 0x10 + +// Interrupt Handler / ISR stub for reducing code duplication, this array can be +// iterated in initialize_idt() +extern void *isr_stub_table[ISR_STUB_TABLE_LIMIT]; + +extern struct IDTR _idt_idtr; + +/** + * IDTGate, IDT entry that point into interrupt handler + * Struct defined exactly in Intel x86 Vol 3a - Figure 6-2. IDT Gate Descriptors + * + * @param offset_low Lower 16-bit offset + * @param segment Memory segment + * @param _reserved Reserved bit, bit length: 5 + * @param _r_bit_1 Reserved for idtgate type, bit length: 3 + * @param _r_bit_2 Reserved for idtgate type, bit length: 3 + * @param gate_32 Is this gate size 32-bit? If not then its 16-bit + gate + * @param _r_bit_3 Reserved for idtgate type, bit length: 1 + * @param dpl Descriptor Privilege Level, bit length: 2 + * @param valid_bit Segment present flag, bit length: 1 + * @param offset_high Higher 16-bit offset + * ... + + */ +struct IDTGate { + // First 32-bit (Bit 0 to 31) + uint16_t offset_low; + uint16_t segment; + + uint8_t _reserved : 5; + uint8_t _r_bit_1 : 3; + uint8_t _r_bit_2 : 3; + uint8_t gate_32 : 1; + uint8_t _r_bit_3 : 1; + uint8_t dpl : 2; + uint8_t valid_bit : 1; + uint16_t offset_high; + +} __attribute__((packed)); + +/** + * Interrupt Descriptor Table, containing lists of IDTGate. + * One IDT already defined in idt.c + * + * @param table Fixed-width array of IDTGate with size IDT_MAX_ENTRY_COUNT + */ + +struct InterruptDescriptorTable { + struct IDTGate table[IDT_MAX_ENTRY_COUNT]; +} __attribute__((packed)); + +/** + * IDTR, carrying information where's the IDT located and size. + * Global kernel variable defined at idt.c. + * + * @param size Interrupt Descriptor Table size + * @param address IDT address + */ + +struct IDTR { + uint16_t size; + struct InterruptDescriptorTable *address; +} __attribute__((packed)); + +/** + * Set IDTGate with proper interrupt handler values. + * Will directly edit global IDT variable and set values properly + * + * @param int_vector Interrupt vector to handle + * @param handler_address Interrupt handler address + * @param gdt_seg_selector GDT segment selector, for kernel use + * GDT_KERNEL_CODE_SEGMENT_SELECTOR + * @param privilege Descriptor privilege level + */ +void set_interrupt_gate(uint8_t int_vector, void *handler_address, + uint16_t gdt_seg_selector, uint8_t privilege); + +/** + * Set IDT with proper values and load with lidt + */ +void initialize_idt(void); + #endif \ No newline at end of file diff --git a/src/lib-header/interrupt.h b/src/lib-header/interrupt.h index b92c54b..af1d3c9 100644 --- a/src/lib-header/interrupt.h +++ b/src/lib-header/interrupt.h @@ -1,143 +1,143 @@ -#ifndef _INTERRUPT_H -#define _INTERRUPT_H - -#include "stdtype.h" -#include "portio.h" -#include "keyboard.h" -#include "framebuffer.h" -extern struct TSSEntry _interrupt_tss_entry; - -/** - * TSSEntry, Task State Segment. Used when jumping back to ring 0 / kernel - */ -struct TSSEntry { - uint32_t prev_tss; // Previous TSS - uint32_t esp0; // Stack pointer to load when changing to kernel mode - uint32_t ss0; // Stack segment to load when changing to kernel mode - // Unused variables - uint32_t unused_register[23]; -} __attribute__((packed)); - -// Set kernel stack in TSS -void set_tss_kernel_current_stack(void); - -/* -- PIC constants -- */ - -// PIC interrupt offset -#define PIC1_OFFSET 0x20 -#define PIC2_OFFSET 0x28 - -// PIC ports -#define PIC1 0x20 -#define PIC2 0xA0 -#define PIC1_COMMAND PIC1 -#define PIC1_DATA (PIC1 + 1) -#define PIC2_COMMAND PIC2 -#define PIC2_DATA (PIC2 + 1) - -// PIC ACK & mask constant -#define PIC_ACK 0x20 -#define PIC_DISABLE_ALL_MASK 0xFF - -// PIC remap constants -#define ICW1_ICW4 0x01 /* ICW4 (not) needed */ -#define ICW1_SINGLE 0x02 /* Single (cascade) mode */ -#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ -#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ -#define ICW1_INIT 0x10 /* Initialization - required! */ - -#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ -#define ICW4_AUTO 0x02 /* Auto (normal) EOI */ -#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ -#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ -#define ICW4_SFNM 0x10 /* Special fully nested (not) */ - - -/* -- PICs IRQ list -- */ - -// PIC Master -#define IRQ_TIMER 0 -#define IRQ_KEYBOARD 1 -#define IRQ_CASCADE 2 -#define IRQ_COM2 3 -#define IRQ_COM1 4 -#define IRQ_LPT2 5 -#define IRQ_FLOPPY_DISK 6 -#define IRQ_LPT1_SPUR 7 - -// PIC Slave -#define IRQ_CMOS 8 -#define IRQ_PERIPHERAL_1 9 -#define IRQ_PERIPHERAL_2 10 -#define IRQ_PERIPHERAL_3 11 -#define IRQ_MOUSE 12 -#define IRQ_FPU 13 -#define IRQ_PRIMARY_ATA 14 -#define IRQ_SECOND_ATA 15 - - -/** - * CPURegister, store CPU registers that can be used for interrupt handler / ISRs - * - * @param gp_register CPU general purpose register (a, b, c, d) - * @param stack_register CPU stack register (bp, sp) - */ -struct CPURegister { - uint32_t eax; - uint32_t ebx; - uint32_t ecx; - uint32_t edx; - uint32_t ebp; - uint32_t esp; -} __attribute__((packed)); - -/** - * InterruptInfo, data pushed by CPU when interrupt / exception is raised. - * Refer to Intel x86 Vol 3a: Figure 6-4 Stack usage on transfer to Interrupt. - * - * Note, when returning from interrupt handler with iret, esp must be pointing to eip pushed before - * or in other words, CPURegister, int_number and error_code should be pop-ed from stack. - * - * @param error_code Error code that pushed with the exception - * @param eip Instruction pointer where interrupt is raised - * @param cs Code segment selector where interrupt is raised - * @param eflags CPU eflags register when interrupt is raised - */ -struct InterruptStack { - uint32_t error_code; - uint32_t eip; - uint32_t cs; - uint32_t eflags; -} __attribute__((packed)); - - - - - -// Activate PIC mask for keyboard only -void activate_keyboard_interrupt(void); - -// I/O port wait, around 1-4 microsecond, for I/O synchronization purpose -void io_wait(void); - -// Send ACK to PIC - @param irq Interrupt request number destination, note: this function already include PIC1_OFFSET -void pic_ack(uint8_t irq); - -// Shift PIC interrupt number to PIC1_OFFSET and PIC2_OFFSET (master and slave) -void pic_remap(void); - -/** - * Main interrupt handler when any interrupt / exception is raised. - * Do not call this function normally. - * - * This function will be called first if any INT 0x00 - 0x40 is raised, - * and will call proper ISR for respective interrupt / exception. - * - * Again, this function is not for normal function call, all parameter will be automatically set when interrupt is called. - * @param cpu CPU register when interrupt is raised - * @param int_number Interrupt number that trigger interrupt exception - * @param info Information about interrupt that pushed automatically by CPU - */ -void main_interrupt_handler(struct CPURegister cpu, uint32_t int_number, struct InterruptStack info); - +#ifndef _INTERRUPT_H +#define _INTERRUPT_H + +#include "stdtype.h" +#include "portio.h" +#include "keyboard.h" +#include "framebuffer.h" +extern struct TSSEntry _interrupt_tss_entry; + +/** + * TSSEntry, Task State Segment. Used when jumping back to ring 0 / kernel + */ +struct TSSEntry { + uint32_t prev_tss; // Previous TSS + uint32_t esp0; // Stack pointer to load when changing to kernel mode + uint32_t ss0; // Stack segment to load when changing to kernel mode + // Unused variables + uint32_t unused_register[23]; +} __attribute__((packed)); + +// Set kernel stack in TSS +void set_tss_kernel_current_stack(void); + +/* -- PIC constants -- */ + +// PIC interrupt offset +#define PIC1_OFFSET 0x20 +#define PIC2_OFFSET 0x28 + +// PIC ports +#define PIC1 0x20 +#define PIC2 0xA0 +#define PIC1_COMMAND PIC1 +#define PIC1_DATA (PIC1 + 1) +#define PIC2_COMMAND PIC2 +#define PIC2_DATA (PIC2 + 1) + +// PIC ACK & mask constant +#define PIC_ACK 0x20 +#define PIC_DISABLE_ALL_MASK 0xFF + +// PIC remap constants +#define ICW1_ICW4 0x01 /* ICW4 (not) needed */ +#define ICW1_SINGLE 0x02 /* Single (cascade) mode */ +#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ +#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ +#define ICW1_INIT 0x10 /* Initialization - required! */ + +#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ +#define ICW4_AUTO 0x02 /* Auto (normal) EOI */ +#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ +#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ +#define ICW4_SFNM 0x10 /* Special fully nested (not) */ + + +/* -- PICs IRQ list -- */ + +// PIC Master +#define IRQ_TIMER 0 +#define IRQ_KEYBOARD 1 +#define IRQ_CASCADE 2 +#define IRQ_COM2 3 +#define IRQ_COM1 4 +#define IRQ_LPT2 5 +#define IRQ_FLOPPY_DISK 6 +#define IRQ_LPT1_SPUR 7 + +// PIC Slave +#define IRQ_CMOS 8 +#define IRQ_PERIPHERAL_1 9 +#define IRQ_PERIPHERAL_2 10 +#define IRQ_PERIPHERAL_3 11 +#define IRQ_MOUSE 12 +#define IRQ_FPU 13 +#define IRQ_PRIMARY_ATA 14 +#define IRQ_SECOND_ATA 15 + + +/** + * CPURegister, store CPU registers that can be used for interrupt handler / ISRs + * + * @param gp_register CPU general purpose register (a, b, c, d) + * @param stack_register CPU stack register (bp, sp) + */ +struct CPURegister { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t ebp; + uint32_t esp; +} __attribute__((packed)); + +/** + * InterruptInfo, data pushed by CPU when interrupt / exception is raised. + * Refer to Intel x86 Vol 3a: Figure 6-4 Stack usage on transfer to Interrupt. + * + * Note, when returning from interrupt handler with iret, esp must be pointing to eip pushed before + * or in other words, CPURegister, int_number and error_code should be pop-ed from stack. + * + * @param error_code Error code that pushed with the exception + * @param eip Instruction pointer where interrupt is raised + * @param cs Code segment selector where interrupt is raised + * @param eflags CPU eflags register when interrupt is raised + */ +struct InterruptStack { + uint32_t error_code; + uint32_t eip; + uint32_t cs; + uint32_t eflags; +} __attribute__((packed)); + + + + + +// Activate PIC mask for keyboard only +void activate_keyboard_interrupt(void); + +// I/O port wait, around 1-4 microsecond, for I/O synchronization purpose +void io_wait(void); + +// Send ACK to PIC - @param irq Interrupt request number destination, note: this function already include PIC1_OFFSET +void pic_ack(uint8_t irq); + +// Shift PIC interrupt number to PIC1_OFFSET and PIC2_OFFSET (master and slave) +void pic_remap(void); + +/** + * Main interrupt handler when any interrupt / exception is raised. + * Do not call this function normally. + * + * This function will be called first if any INT 0x00 - 0x40 is raised, + * and will call proper ISR for respective interrupt / exception. + * + * Again, this function is not for normal function call, all parameter will be automatically set when interrupt is called. + * @param cpu CPU register when interrupt is raised + * @param int_number Interrupt number that trigger interrupt exception + * @param info Information about interrupt that pushed automatically by CPU + */ +void main_interrupt_handler(struct CPURegister cpu, uint32_t int_number, struct InterruptStack info); + #endif \ No newline at end of file diff --git a/src/lib-header/kernel_loader.h b/src/lib-header/kernel_loader.h index 65ae26e..4dcd005 100644 --- a/src/lib-header/kernel_loader.h +++ b/src/lib-header/kernel_loader.h @@ -1,12 +1,34 @@ -#ifndef _KERNEL_LOADER -#define _KERNEL_LOADER - -/** - * Load GDT from gdtr and launch protected mode. This function defined in asm source code. - * - * @param gdtr Pointer to already defined & initialized GDTR - * @warning Invalid address / definition of GDT will cause bootloop after calling this function. - */ -extern void enter_protected_mode(struct GDTR *gdtr); - +#ifndef _KERNEL_LOADER +#define _KERNEL_LOADER + +/** + * Load GDT from gdtr and launch protected mode. This function defined in asm source code. + * + * @param gdtr Pointer to already defined & initialized GDTR + * @warning Invalid address / definition of GDT will cause bootloop after calling this function. + */ +extern void enter_protected_mode(struct GDTR *gdtr); + + +// Optional linker variable : Pointing to kernel start & end address +// Note : Use & operator, example : a = (uint32_t) &_linker_kernel_stack_top; +extern uint32_t _linker_kernel_virtual_addr_start; +extern uint32_t _linker_kernel_virtual_addr_end; +extern uint32_t _linker_kernel_physical_addr_start; +extern uint32_t _linker_kernel_physical_addr_end; +extern uint32_t _linker_kernel_stack_top; + +/** + * Execute user program from kernel, one way jump. This function is defined in asm source code. + * + * @param virtual_addr Pointer into user program that already in memory + * @warning Assuming pointed memory is properly loaded + */ +extern void kernel_execute_user_program(void *virtual_addr); + +/** + * Set the tss register pointing to GDT_TSS_SELECTOR with ring 0 + */ +extern void set_tss_register(void); // Note : Already implemented in kernel_loader.asm + #endif \ No newline at end of file diff --git a/src/lib-header/keyboard.h b/src/lib-header/keyboard.h index 64632eb..65c6874 100644 --- a/src/lib-header/keyboard.h +++ b/src/lib-header/keyboard.h @@ -1,117 +1,117 @@ -#ifndef _USER_ISR_H -#define _USER_ISR_H - -#include "stdtype.h" -#include "interrupt.h" -#include "portio.h" - -#define EXT_SCANCODE_UP 0x48 -#define EXT_SCANCODE_DOWN 0x50 -#define EXT_SCANCODE_LEFT 0x4B -#define EXT_SCANCODE_RIGHT 0x4D - -#define KEYBOARD_DATA_PORT 0x60 -#define EXTENDED_SCANCODE_BYTE 0xE0 - -#define KEYBOARD_BUFFER_SIZE 256 - -#define SC_MAX 57 -#define SCANCODE_KEYUP_THRESHOLD 0x80 -#define SCANCODE_LSHIFT 42 -#define SCANCODE_CAPS 58 -#define SCANCODE_LEFTARROW 75 -#define SCANCODE_ESC 27 - -/** - * keyboard_scancode_1_to_ascii_map[256], Convert scancode values that correspond to ASCII printables - * How to use this array: ascii_char = k[scancode] - * - * By default, QEMU using scancode set 1 (from empirical testing) - */ -extern const char keyboard_scancode_1_to_ascii_map[256]; - -/** - * KeyboardDriverState - Contain all driver states - * - * @param read_extended_mode Optional, can be used for signaling next read is extended scancode (ex. arrow keys) - * @param keyboard_input_on Indicate whether keyboard ISR is activated or not - * @param buffer_index Used for keyboard_buffer index - * @param keyboard_buffer Storing keyboard input values in ASCII - */ -struct KeyboardDriverState { - bool read_extended_mode; - bool keyboard_input_on; - bool shift_pressed; - bool ctrl_pressed; - bool alt_pressed; - bool caps_cond; - uint8_t buffer_index; - char keyboard_buffer[KEYBOARD_BUFFER_SIZE]; -} __attribute((packed)); - - - - - -/* -- Driver Interfaces -- */ - -// Activate keyboard ISR / start listen keyboard & save to buffer -void keyboard_state_activate(void); - -// Deactivate keyboard ISR / stop listening keyboard interrupt -void keyboard_state_deactivate(void); - -// Get keyboard buffer values - @param buf Pointer to char buffer, recommended size at least KEYBOARD_BUFFER_SIZE -void get_keyboard_buffer(char *buf); - -// Check whether keyboard ISR is active or not - @return Equal with keyboard_input_on value -bool is_keyboard_blocking(void); - - -/* -- Keyboard Interrupt Service Routine -- */ - -/** - * Handling keyboard interrupt & process scancodes into ASCII character. - * Will start listen and process keyboard scancode if keyboard_input_on. - * - * Will only print printable character into framebuffer. - * Stop processing when enter key (line feed) is pressed. - * - * Note that, with keyboard interrupt & ISR, keyboard reading is non-blocking. - * This can be made into blocking input with `while (is_keyboard_blocking());` - * after calling `keyboard_state_activate();` - */ -void keyboard_isr(void); - -/** - * @brief Clear screen - * - * - */ -void clear_screen(); - -/** - * @brief Print string to screen - * - * @param char* s - * @param int n - */ -void append(char s[], char n); - -/** - * @brief Compare two string - * - * @param s1 - * @param s2 - * @return int - */ -int strcmp(char s1[], char s2[]); - -/** - * @brief Execute command from input - * - * @param char* input - */ -void execute_cmd(char *input); - +#ifndef _USER_ISR_H +#define _USER_ISR_H + +#include "stdtype.h" +#include "interrupt.h" +#include "portio.h" + +#define EXT_SCANCODE_UP 0x48 +#define EXT_SCANCODE_DOWN 0x50 +#define EXT_SCANCODE_LEFT 0x4B +#define EXT_SCANCODE_RIGHT 0x4D + +#define KEYBOARD_DATA_PORT 0x60 +#define EXTENDED_SCANCODE_BYTE 0xE0 + +#define KEYBOARD_BUFFER_SIZE 256 + +#define SC_MAX 57 +#define SCANCODE_KEYUP_THRESHOLD 0x80 +#define SCANCODE_LSHIFT 42 +#define SCANCODE_CAPS 58 +#define SCANCODE_LEFTARROW 75 +#define SCANCODE_ESC 27 + +/** + * keyboard_scancode_1_to_ascii_map[256], Convert scancode values that correspond to ASCII printables + * How to use this array: ascii_char = k[scancode] + * + * By default, QEMU using scancode set 1 (from empirical testing) + */ +extern const char keyboard_scancode_1_to_ascii_map[256]; + +/** + * KeyboardDriverState - Contain all driver states + * + * @param read_extended_mode Optional, can be used for signaling next read is extended scancode (ex. arrow keys) + * @param keyboard_input_on Indicate whether keyboard ISR is activated or not + * @param buffer_index Used for keyboard_buffer index + * @param keyboard_buffer Storing keyboard input values in ASCII + */ +struct KeyboardDriverState { + bool read_extended_mode; + bool keyboard_input_on; + bool shift_pressed; + bool ctrl_pressed; + bool alt_pressed; + bool caps_cond; + uint8_t buffer_index; + char keyboard_buffer[KEYBOARD_BUFFER_SIZE]; +} __attribute((packed)); + + + + + +/* -- Driver Interfaces -- */ + +// Activate keyboard ISR / start listen keyboard & save to buffer +void keyboard_state_activate(void); + +// Deactivate keyboard ISR / stop listening keyboard interrupt +void keyboard_state_deactivate(void); + +// Get keyboard buffer values - @param buf Pointer to char buffer, recommended size at least KEYBOARD_BUFFER_SIZE +void get_keyboard_buffer(char *buf); + +// Check whether keyboard ISR is active or not - @return Equal with keyboard_input_on value +bool is_keyboard_blocking(void); + + +/* -- Keyboard Interrupt Service Routine -- */ + +/** + * Handling keyboard interrupt & process scancodes into ASCII character. + * Will start listen and process keyboard scancode if keyboard_input_on. + * + * Will only print printable character into framebuffer. + * Stop processing when enter key (line feed) is pressed. + * + * Note that, with keyboard interrupt & ISR, keyboard reading is non-blocking. + * This can be made into blocking input with `while (is_keyboard_blocking());` + * after calling `keyboard_state_activate();` + */ +void keyboard_isr(void); + +/** + * @brief Clear screen + * + * + */ +void clear_screen(); + +/** + * @brief Print string to screen + * + * @param char* s + * @param int n + */ +void append(char s[], char n); + +/** + * @brief Compare two string + * + * @param s1 + * @param s2 + * @return int + */ +int strcmp(char s1[], char s2[]); + +/** + * @brief Execute command from input + * + * @param char* input + */ +void execute_cmd(char *input); + #endif \ No newline at end of file diff --git a/src/lib-header/paging.h b/src/lib-header/paging.h index 574b8f0..9c84b12 100644 --- a/src/lib-header/paging.h +++ b/src/lib-header/paging.h @@ -1,112 +1,112 @@ -#ifndef _PAGING_H -#define _PAGING_H - -#include "./stdtype.h" - -#define PAGE_ENTRY_COUNT 1024 -#define PAGE_FRAME_SIZE (4*1024*1024) - -// Operating system page directory, using page size PAGE_FRAME_SIZE (4 MiB) -extern struct PageDirectory _paging_kernel_page_directory; - -// ? : Activating paging - - -/** - * Page Directory Entry Flag, only first 8 bit - * - * @param present_bit Indicate whether this entry is exist or not - * @param read_write_bit - * @param user_supervisor_bit - * @param write_through_bit - * @param cache_disable_bit - * @param accessed_bit - * @param available_bit - * @param page_size_bit - */ -struct PageDirectoryEntryFlag { - uint8_t present_bit : 1; - // TODO : Continue. Note: Only first 8 bit flags - uint8_t write_bit : 1; - uint8_t user_bit : 1; - uint8_t write_through_bit : 1; - uint8_t cache_disable_bit : 1; - uint8_t accessed_bit : 1; - uint8_t available_bit : 1; - uint8_t use_pagesize_4_mb : 1; -} __attribute__((packed)); - -/** - * Page Directory Entry, for page size 4 MB. - * Check Intel Manual 3a - Ch 4 Paging - Figure 4-4 PDE: 4MB page - * - * @param flag Contain 8-bit page directory entry flag - * @param global_page Is this page translation global (also cannot be flushed) - * ... - * Note: - * - Assume "Bits 39:32 of address" (higher_address) is 8-bit and Reserved is 1 - * - "Bits 31:22 of address" is called lower_address in kit - */ -struct PageDirectoryEntry { - struct PageDirectoryEntryFlag flag; - uint16_t global_page : 1; - // TODO : Continue, Use uint16_t + bitfield here, Do not use uint8_t - uint16_t available : 3; - uint16_t pat_bit : 1; - uint16_t higher_address: 8; - uint16_t reserved : 1; - uint16_t lower_address : 10; -} __attribute__((packed)); - -/** - * Page Directory, contain array of PageDirectoryEntry. - * Note: This data structure not only can be manipulated by kernel, - * MMU operation, TLB hit & miss also affecting this data structure (dirty, accessed bit, etc). - * Warning: Address must be aligned in 4 KB (listed on Intel Manual), use __attribute__((aligned(0x1000))), - * unaligned definition of PageDirectory will cause triple fault - * - * @param table Fixed-width array of PageDirectoryEntry with size PAGE_ENTRY_COUNT - */ -struct PageDirectory { - // TODO : Implement - struct PageDirectoryEntry table[PAGE_ENTRY_COUNT]; -} __attribute__((aligned(0x1000))); - -/** - * Containing page driver states - * - * @param last_available_physical_addr Pointer to last empty physical addr (multiple of 4 MiB) - */ -struct PageDriverState { - uint8_t *last_available_physical_addr; -} __attribute__((packed)); - - -/** - * update_page_directory, - * Edit _paging_kernel_page_directory with respective parameter - * - * @param physical_addr Physical address to map - * @param virtual_addr Virtual address to map - * @param flag Page entry flags - */ -void update_page_directory(void *physical_addr, void *virtual_addr, struct PageDirectoryEntryFlag flag); - -/** - * flush_single_tlb, - * invalidate page that contain virtual address in parameter - * - * @param virtual_addr Virtual address to flush - */ -void flush_single_tlb(void *virtual_addr); - -/** - * Allocate user memory into specified virtual memory address. - * Multiple call on same virtual address will unmap previous physical address and change it into new one. - * - * @param virtual_addr Virtual address to be mapped - * @return int8_t 0 success, -1 for failed allocation - */ -int8_t allocate_single_user_page_frame(void *virtual_addr); - +#ifndef _PAGING_H +#define _PAGING_H + +#include "./stdtype.h" + +#define PAGE_ENTRY_COUNT 1024 +#define PAGE_FRAME_SIZE (4*1024*1024) + +// Operating system page directory, using page size PAGE_FRAME_SIZE (4 MiB) +extern struct PageDirectory _paging_kernel_page_directory; + +// ? : Activating paging + + +/** + * Page Directory Entry Flag, only first 8 bit + * + * @param present_bit Indicate whether this entry is exist or not + * @param read_write_bit + * @param user_supervisor_bit + * @param write_through_bit + * @param cache_disable_bit + * @param accessed_bit + * @param available_bit + * @param page_size_bit + */ +struct PageDirectoryEntryFlag { + uint8_t present_bit : 1; + // TODO : Continue. Note: Only first 8 bit flags + uint8_t write_bit : 1; + uint8_t user_bit : 1; + uint8_t write_through_bit : 1; + uint8_t cache_disable_bit : 1; + uint8_t accessed_bit : 1; + uint8_t available_bit : 1; + uint8_t use_pagesize_4_mb : 1; +} __attribute__((packed)); + +/** + * Page Directory Entry, for page size 4 MB. + * Check Intel Manual 3a - Ch 4 Paging - Figure 4-4 PDE: 4MB page + * + * @param flag Contain 8-bit page directory entry flag + * @param global_page Is this page translation global (also cannot be flushed) + * ... + * Note: + * - Assume "Bits 39:32 of address" (higher_address) is 8-bit and Reserved is 1 + * - "Bits 31:22 of address" is called lower_address in kit + */ +struct PageDirectoryEntry { + struct PageDirectoryEntryFlag flag; + uint16_t global_page : 1; + // TODO : Continue, Use uint16_t + bitfield here, Do not use uint8_t + uint16_t available : 3; + uint16_t pat_bit : 1; + uint16_t higher_address: 8; + uint16_t reserved : 1; + uint16_t lower_address : 10; +} __attribute__((packed)); + +/** + * Page Directory, contain array of PageDirectoryEntry. + * Note: This data structure not only can be manipulated by kernel, + * MMU operation, TLB hit & miss also affecting this data structure (dirty, accessed bit, etc). + * Warning: Address must be aligned in 4 KB (listed on Intel Manual), use __attribute__((aligned(0x1000))), + * unaligned definition of PageDirectory will cause triple fault + * + * @param table Fixed-width array of PageDirectoryEntry with size PAGE_ENTRY_COUNT + */ +struct PageDirectory { + // TODO : Implement + struct PageDirectoryEntry table[PAGE_ENTRY_COUNT]; +} __attribute__((aligned(0x1000))); + +/** + * Containing page driver states + * + * @param last_available_physical_addr Pointer to last empty physical addr (multiple of 4 MiB) + */ +struct PageDriverState { + uint8_t *last_available_physical_addr; +} __attribute__((packed)); + + +/** + * update_page_directory, + * Edit _paging_kernel_page_directory with respective parameter + * + * @param physical_addr Physical address to map + * @param virtual_addr Virtual address to map + * @param flag Page entry flags + */ +void update_page_directory(void *physical_addr, void *virtual_addr, struct PageDirectoryEntryFlag flag); + +/** + * flush_single_tlb, + * invalidate page that contain virtual address in parameter + * + * @param virtual_addr Virtual address to flush + */ +void flush_single_tlb(void *virtual_addr); + +/** + * Allocate user memory into specified virtual memory address. + * Multiple call on same virtual address will unmap previous physical address and change it into new one. + * + * @param virtual_addr Virtual address to be mapped + * @return int8_t 0 success, -1 for failed allocation + */ +int8_t allocate_single_user_page_frame(void *virtual_addr); + #endif \ No newline at end of file diff --git a/src/lib-header/portio.h b/src/lib-header/portio.h index ca3721a..21395e4 100644 --- a/src/lib-header/portio.h +++ b/src/lib-header/portio.h @@ -1,37 +1,37 @@ -#ifndef _PORTIO_H -#define _PORTIO_H - -#include "stdtype.h" - -/** out: - * Sends the given data to the given I/O port - * - * @param port The I/O port to send the data to - * @param data The data to send to the I/O port - */ -void out(uint16_t port, uint8_t data); - -/** in: - * Read data from the given I/O port - * - * @param port The I/O port to request the data - * @return Recieved data from the corresponding I/O port - */ -uint8_t in(uint16_t port); - -/** in: - * Read data from the given I/O port - * - * @param port The I/O port to request the data - * @return Recieved data from the corresponding I/O port in 16 bit - */ -uint16_t in16(uint16_t port); - -/** out: - * Sends the given data to the given I/O port - * - * @param port The I/O port to send the data to - * @param data The data to send to the I/O port in 16 bit - */ -void out16(uint16_t port, uint16_t data); +#ifndef _PORTIO_H +#define _PORTIO_H + +#include "stdtype.h" + +/** out: + * Sends the given data to the given I/O port + * + * @param port The I/O port to send the data to + * @param data The data to send to the I/O port + */ +void out(uint16_t port, uint8_t data); + +/** in: + * Read data from the given I/O port + * + * @param port The I/O port to request the data + * @return Recieved data from the corresponding I/O port + */ +uint8_t in(uint16_t port); + +/** in: + * Read data from the given I/O port + * + * @param port The I/O port to request the data + * @return Recieved data from the corresponding I/O port in 16 bit + */ +uint16_t in16(uint16_t port); + +/** out: + * Sends the given data to the given I/O port + * + * @param port The I/O port to send the data to + * @param data The data to send to the I/O port in 16 bit + */ +void out16(uint16_t port, uint16_t data); #endif \ No newline at end of file diff --git a/src/lib-header/stdmem.h b/src/lib-header/stdmem.h index 70b0f01..8f24041 100644 --- a/src/lib-header/stdmem.h +++ b/src/lib-header/stdmem.h @@ -1,56 +1,56 @@ -#ifndef _STDMEM_H -#define _STDMEM_H - -#include "lib-header/stdtype.h" - -#define VIDEO_ADDR 0xB8000 - -/** - * C standard memset, check man memset or - * https://man7.org/linux/man-pages/man3/memset.3.html for more details - * - * @param s Pointer to memory area to set - * @param c Constant byte value for filling memory area - * @param n Memory area size in byte - * - * @return Pointer s -*/ -void* memset(void *s, int c, size_t n); - -/** - * C standard memcpy, check man memcpy or - * https://man7.org/linux/man-pages/man3/memcpy.3.html for more details - * - * @param dest Starting location for memory area to set - * @param src Pointer to source memory - * @param n Memory area size in byte - * - * @return Pointer dest -*/ -void* memcpy(void* restrict dest, const void* restrict src, size_t n); - -/** - * C standard memcmp, check man memcmp or - * https://man7.org/linux/man-pages/man3/memcmp.3.html for more details - * - * @param s1 Pointer to first memory area - * @param s2 Pointer to second memory area - * @param n Memory area size in byte - * - * @return Integer as error code, zero for equality, non-zero for inequality -*/ -int memcmp(const void *s1, const void *s2, size_t n); - -/** - * C standard memmove, check man memmove or - * https://man7.org/linux/man-pages/man3/memmove.3.html for more details - * - * @param dest Pointer to destination memory - * @param src Pointer to source memory - * @param n Memory area size in byte - * - * @return Pointer dest -*/ -void *memmove(void *dest, const void *src, size_t n); - +#ifndef _STDMEM_H +#define _STDMEM_H + +#include "stdtype.h" + +#define VIDEO_ADDR 0xB8000 + +/** + * C standard memset, check man memset or + * https://man7.org/linux/man-pages/man3/memset.3.html for more details + * + * @param s Pointer to memory area to set + * @param c Constant byte value for filling memory area + * @param n Memory area size in byte + * + * @return Pointer s +*/ +void* memset(void *s, int c, size_t n); + +/** + * C standard memcpy, check man memcpy or + * https://man7.org/linux/man-pages/man3/memcpy.3.html for more details + * + * @param dest Starting location for memory area to set + * @param src Pointer to source memory + * @param n Memory area size in byte + * + * @return Pointer dest +*/ +void* memcpy(void* restrict dest, const void* restrict src, size_t n); + +/** + * C standard memcmp, check man memcmp or + * https://man7.org/linux/man-pages/man3/memcmp.3.html for more details + * + * @param s1 Pointer to first memory area + * @param s2 Pointer to second memory area + * @param n Memory area size in byte + * + * @return Integer as error code, zero for equality, non-zero for inequality +*/ +int memcmp(const void *s1, const void *s2, size_t n); + +/** + * C standard memmove, check man memmove or + * https://man7.org/linux/man-pages/man3/memmove.3.html for more details + * + * @param dest Pointer to destination memory + * @param src Pointer to source memory + * @param n Memory area size in byte + * + * @return Pointer dest +*/ +void *memmove(void *dest, const void *src, size_t n); + #endif \ No newline at end of file diff --git a/src/lib-header/stdtype.h b/src/lib-header/stdtype.h index ff33d62..e8a02e8 100644 --- a/src/lib-header/stdtype.h +++ b/src/lib-header/stdtype.h @@ -1,46 +1,48 @@ -#ifndef _STDTYPE -#define _STDTYPE - -/** - * Unsigned integer representing object size -*/ -typedef unsigned int size_t; - -/** - * 32-bit unsigned integer - */ -typedef unsigned int uint32_t; - -/** - * 16-bit unsigned integer - */ -typedef unsigned short uint16_t; - -/** - * 8-bit unsigned integer - */ -typedef unsigned char uint8_t; - -/** - * 32-bit signed integer - */ -typedef signed int int32_t; - -/** - * 16-bit signed integer - */ -typedef signed short int16_t; - -/** - * 8-bit signed integer - */ -typedef signed char int8_t; - -/** - * Naive implementation of boolean (uint8_t) -*/ -typedef uint8_t bool; -#define TRUE 1 -#define FALSE 0 - +#ifndef _STDTYPE +#define _STDTYPE + +/** + * Unsigned integer representing object size +*/ +// typedef unsigned int size_t; +#define size_t unsigned int // override size_t from stdio.h + +/** + * @brief + * + */ +typedef unsigned int uint32_t; + +/** + * 16-bit unsigned integer + */ +typedef unsigned short uint16_t; + +/** + * 8-bit unsigned integer + */ +typedef unsigned char uint8_t; + +/** + * 32-bit signed integer + */ +typedef signed int int32_t; + +/** + * 16-bit signed integer + */ +typedef signed short int16_t; + +/** + * 8-bit signed integer + */ +typedef signed char int8_t; + +/** + * Naive implementation of boolean (uint8_t) +*/ +typedef uint8_t bool; +#define TRUE 1 +#define FALSE 0 + #endif \ No newline at end of file diff --git a/src/linker.ld b/src/linker.ld index 4bb0d99..ec2ef84 100644 --- a/src/linker.ld +++ b/src/linker.ld @@ -1,45 +1,45 @@ -ENTRY(loader) /* the name of the entry label */ - -SECTIONS { - . = 0xC0100000; /* use relocation address (memory references) at 0xC010 0000 */ - - /* Optional variable that can be used in kernel, starting address of kernel */ - _linker_kernel_virtual_addr_start = .; - _linker_kernel_physical_addr_start = . - 0xC0000000; - - .__mbHeader ALIGN (0x1000) : AT (ADDR (.__mbHeader) - 0xC0000000) - { - *(.__mbHeader) /* put GRUB multiboot header at front */ - } - - .setup.text ALIGN (0x1000) : AT (ADDR (.setup.text) - 0xC0000000) - { - *(.setup.text) /* initial setup code */ - } - - .text ALIGN (0x1000) : AT (ADDR (.text) - 0xC0000000) - { - *(.text) /* all text sections from all files */ - } - - .rodata ALIGN (0x1000) : AT (ADDR (.rodata) - 0xC0000000) - { - *(.rodata*) /* all read-only data sections from all files */ - } - - .data ALIGN (0x1000) : AT (ADDR (.data) - 0xC0000000) - { - *(.data) /* all data sections from all files */ - } - - .bss ALIGN (0x1000) : AT (ADDR (.bss) - 0xC0000000) - { - *(COMMON) /* all COMMON sections from all files */ - *(.bss) /* all bss sections from all files */ - kernel_loader.o(.bss) - _linker_kernel_stack_top = .; - } - /* Optional variable that can be used in kernel, show end address of kernel */ - _linker_kernel_virtual_addr_end = .; - _linker_kernel_physical_addr_end = . - 0xC0000000; -} +ENTRY(loader) /* the name of the entry label */ + +SECTIONS { + . = 0xC0100000; /* use relocation address (memory references) at 0xC010 0000 */ + + /* Optional variable that can be used in kernel, starting address of kernel */ + _linker_kernel_virtual_addr_start = .; + _linker_kernel_physical_addr_start = . - 0xC0000000; + + .__mbHeader ALIGN (0x1000) : AT (ADDR (.__mbHeader) - 0xC0000000) + { + *(.__mbHeader) /* put GRUB multiboot header at front */ + } + + .setup.text ALIGN (0x1000) : AT (ADDR (.setup.text) - 0xC0000000) + { + *(.setup.text) /* initial setup code */ + } + + .text ALIGN (0x1000) : AT (ADDR (.text) - 0xC0000000) + { + *(.text) /* all text sections from all files */ + } + + .rodata ALIGN (0x1000) : AT (ADDR (.rodata) - 0xC0000000) + { + *(.rodata*) /* all read-only data sections from all files */ + } + + .data ALIGN (0x1000) : AT (ADDR (.data) - 0xC0000000) + { + *(.data) /* all data sections from all files */ + } + + .bss ALIGN (0x1000) : AT (ADDR (.bss) - 0xC0000000) + { + *(COMMON) /* all COMMON sections from all files */ + *(.bss) /* all bss sections from all files */ + kernel_loader.o(.bss) + _linker_kernel_stack_top = .; + } + /* Optional variable that can be used in kernel, show end address of kernel */ + _linker_kernel_virtual_addr_end = .; + _linker_kernel_physical_addr_end = . - 0xC0000000; +} diff --git a/src/menu.lst b/src/menu.lst index 5c5d15c..7c06fd8 100644 --- a/src/menu.lst +++ b/src/menu.lst @@ -1,5 +1,5 @@ -default 0 -timeout 0 - -title os -kernel /boot/kernel +default 0 +timeout 0 + +title os +kernel /boot/kernel diff --git a/src/paging/kernel_loader.s b/src/paging/kernel_loader.s index a0ddd3b..53549cb 100644 --- a/src/paging/kernel_loader.s +++ b/src/paging/kernel_loader.s @@ -1,90 +1,90 @@ -global loader ; the entry symbol for ELF -global enter_protected_mode ; go to protected mode -global set_tss_register ; set tss register to GDT entry -extern kernel_setup ; kernel C entrypoint -extern _paging_kernel_page_directory ; kernel page directory - -KERNEL_VIRTUAL_BASE equ 0xC0000000 ; kernel virtual memory -KERNEL_STACK_SIZE equ 2097152 ; size of stack in bytes -MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant -FLAGS equ 0x0 ; multiboot flags -CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum - ; (magic number + checksum + flags should equal 0) - - -section .bss -align 4 ; align at 4 bytes -kernel_stack: ; label points to beginning of memory - resb KERNEL_STACK_SIZE ; reserve stack for the kernel - - -section .multiboot ; GRUB multiboot header -align 4 ; the code must be 4 byte aligned - dd MAGIC_NUMBER ; write the magic number to the machine code, - dd FLAGS ; the flags, - dd CHECKSUM ; and the checksum - - - -section .setup.text ; start of the text (code) section -loader equ (loader_entrypoint - KERNEL_VIRTUAL_BASE) -loader_entrypoint: ; the loader label (defined as entry point in linker script) - ; Set CR3 (CPU page register) - mov eax, _paging_kernel_page_directory - KERNEL_VIRTUAL_BASE - mov cr3, eax - - ; Use 4 MB paging - mov eax, cr4 - or eax, 0x00000010 ; PSE (4 MB paging) - mov cr4, eax - - ; Enable paging - mov eax, cr0 - or eax, 0x80000000 ; PG flag - mov cr0, eax - - ; Jump into higher half first, cannot use C because call stack is still not working - lea eax, [loader_virtual] - jmp eax - -loader_virtual: - mov dword [_paging_kernel_page_directory], 0 - invlpg [0] ; Delete identity mapping and invalidate TLB cache for first page - mov esp, kernel_stack + KERNEL_STACK_SIZE ; Setup stack register to proper location - call kernel_setup -.loop: - jmp .loop ; loop forever - - - -section .text -; More details: https://en.wikibooks.org/wiki/X86_Assembly/Protected_Mode -enter_protected_mode: - ; Load GDT from GDTDescriptor - cli - mov eax, [esp+4] - lgdt [eax] - - ; Set Protection Enable bit-flag in Control Register 0 (CR0) - ; Or in other words: Switch to protected mode - mov eax, cr0 - or eax, 1 - mov cr0, eax - - ; Far jump to update cs register - ; Warning: Invalid GDT will raise exception in any instruction below - jmp 0x8:flush_cs -flush_cs: - ; Update all segment register - mov ax, 10h - mov ss, ax - mov ds, ax - mov es, ax - - ret - -set_tss_register: - mov ax, 0x28 | 0 ; GDT TSS Selector, ring 0 - ltr ax - ret - +global loader ; the entry symbol for ELF +global enter_protected_mode ; go to protected mode +global set_tss_register ; set tss register to GDT entry +extern kernel_setup ; kernel C entrypoint +extern _paging_kernel_page_directory ; kernel page directory + +KERNEL_VIRTUAL_BASE equ 0xC0000000 ; kernel virtual memory +KERNEL_STACK_SIZE equ 2097152 ; size of stack in bytes +MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant +FLAGS equ 0x0 ; multiboot flags +CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum + ; (magic number + checksum + flags should equal 0) + + +section .bss +align 4 ; align at 4 bytes +kernel_stack: ; label points to beginning of memory + resb KERNEL_STACK_SIZE ; reserve stack for the kernel + + +section .multiboot ; GRUB multiboot header +align 4 ; the code must be 4 byte aligned + dd MAGIC_NUMBER ; write the magic number to the machine code, + dd FLAGS ; the flags, + dd CHECKSUM ; and the checksum + + + +section .setup.text ; start of the text (code) section +loader equ (loader_entrypoint - KERNEL_VIRTUAL_BASE) +loader_entrypoint: ; the loader label (defined as entry point in linker script) + ; Set CR3 (CPU page register) + mov eax, _paging_kernel_page_directory - KERNEL_VIRTUAL_BASE + mov cr3, eax + + ; Use 4 MB paging + mov eax, cr4 + or eax, 0x00000010 ; PSE (4 MB paging) + mov cr4, eax + + ; Enable paging + mov eax, cr0 + or eax, 0x80000000 ; PG flag + mov cr0, eax + + ; Jump into higher half first, cannot use C because call stack is still not working + lea eax, [loader_virtual] + jmp eax + +loader_virtual: + mov dword [_paging_kernel_page_directory], 0 + invlpg [0] ; Delete identity mapping and invalidate TLB cache for first page + mov esp, kernel_stack + KERNEL_STACK_SIZE ; Setup stack register to proper location + call kernel_setup +.loop: + jmp .loop ; loop forever + + + +section .text +; More details: https://en.wikibooks.org/wiki/X86_Assembly/Protected_Mode +enter_protected_mode: + ; Load GDT from GDTDescriptor + cli + mov eax, [esp+4] + lgdt [eax] + + ; Set Protection Enable bit-flag in Control Register 0 (CR0) + ; Or in other words: Switch to protected mode + mov eax, cr0 + or eax, 1 + mov cr0, eax + + ; Far jump to update cs register + ; Warning: Invalid GDT will raise exception in any instruction below + jmp 0x8:flush_cs +flush_cs: + ; Update all segment register + mov ax, 10h + mov ss, ax + mov ds, ax + mov es, ax + + ret + +set_tss_register: + mov ax, 0x28 | 0 ; GDT TSS Selector, ring 0 + ltr ax + ret + diff --git a/src/paging/paging.c b/src/paging/paging.c index 21f5358..e1f7690 100644 --- a/src/paging/paging.c +++ b/src/paging/paging.c @@ -1,50 +1,50 @@ -#include "../lib-header/paging.h" - -__attribute__((aligned(0x1000))) struct PageDirectory _paging_kernel_page_directory = { - .table = { - [0] = { - .flag.present_bit = 1, - .flag.write_bit = 1, - .lower_address = 0, - .flag.use_pagesize_4_mb = 1, - }, - [0x300] = { - .flag.present_bit = 1, - .flag.write_bit = 1, - .lower_address = 0, - .flag.use_pagesize_4_mb = 1, - }, - } -}; - -static struct PageDriverState page_driver_state = { - .last_available_physical_addr = (uint8_t*) 0 + PAGE_FRAME_SIZE, -}; - -void update_page_directory_entry(void *physical_addr, void *virtual_addr, struct PageDirectoryEntryFlag flag) { - uint32_t page_index = ((uint32_t) virtual_addr >> 22) & 0x3FF; - - _paging_kernel_page_directory.table[page_index].flag = flag; - _paging_kernel_page_directory.table[page_index].lower_address = ((uint32_t)physical_addr >> 22) & 0x3FF; - flush_single_tlb(virtual_addr); -} - -int8_t allocate_single_user_page_frame(void *virtual_addr) { - uint32_t start = (uint32_t) page_driver_state.last_available_physical_addr; - if (start == 0x100000) { - return -1; - } else { - update_page_directory_entry((void*) start, virtual_addr, (struct PageDirectoryEntryFlag) { - .present_bit = 1, - .write_bit = 1, - .user_bit = 1, - .use_pagesize_4_mb = 1, - }); - page_driver_state.last_available_physical_addr = (uint8_t*) start + PAGE_FRAME_SIZE; - return 0; - } -} - -void flush_single_tlb(void *virtual_addr) { - asm volatile("invlpg (%0)" : /* */ : "b"(virtual_addr): "memory"); +#include "../lib-header/paging.h" + +__attribute__((aligned(0x1000))) struct PageDirectory _paging_kernel_page_directory = { + .table = { + [0] = { + .flag.present_bit = 1, + .flag.write_bit = 1, + .lower_address = 0, + .flag.use_pagesize_4_mb = 1, + }, + [0x300] = { + .flag.present_bit = 1, + .flag.write_bit = 1, + .lower_address = 0, + .flag.use_pagesize_4_mb = 1, + }, + } +}; + +static struct PageDriverState page_driver_state = { + .last_available_physical_addr = (uint8_t*) 0 + PAGE_FRAME_SIZE, +}; + +void update_page_directory_entry(void *physical_addr, void *virtual_addr, struct PageDirectoryEntryFlag flag) { + uint32_t page_index = ((uint32_t) virtual_addr >> 22) & 0x3FF; + + _paging_kernel_page_directory.table[page_index].flag = flag; + _paging_kernel_page_directory.table[page_index].lower_address = ((uint32_t)physical_addr >> 22) & 0x3FF; + flush_single_tlb(virtual_addr); +} + +int8_t allocate_single_user_page_frame(void *virtual_addr) { + uint32_t start = (uint32_t) page_driver_state.last_available_physical_addr; + if (start == 0x100000) { + return -1; + } else { + update_page_directory_entry((void*) start, virtual_addr, (struct PageDirectoryEntryFlag) { + .present_bit = 1, + .write_bit = 1, + .user_bit = 1, + .use_pagesize_4_mb = 1, + }); + page_driver_state.last_available_physical_addr = (uint8_t*) start + PAGE_FRAME_SIZE; + return 0; + } +} + +void flush_single_tlb(void *virtual_addr) { + asm volatile("invlpg (%0)" : /* */ : "b"(virtual_addr): "memory"); } \ No newline at end of file diff --git a/src/portio.c b/src/portio.c index c281831..e1f7b60 100644 --- a/src/portio.c +++ b/src/portio.c @@ -1,43 +1,43 @@ -#include "lib-header/stdtype.h" -#include "lib-header/portio.h" - -/** x86 inb/outb: - * @param dx target port - * @param al input/output byte - */ - -void out(uint16_t port, uint8_t data) { - __asm__( - "outb %0, %1" - : // - : "a"(data), "Nd"(port) - ); -} - -uint8_t in(uint16_t port) { - uint8_t result; - __asm__ volatile( - "inb %1, %0" - : "=a"(result) - : "Nd"(port) - ); - return result; -} - -uint16_t in16(uint16_t port) { - uint16_t result; - __asm__ volatile( - "inw %1, %0" - : "=a" (result) - : "Nd" (port) - ); - return result; -} - -void out16(uint16_t port, uint16_t data) { - __asm__ volatile( - "outw %0, %1" - : // - : "a"(data), "Nd"(port) - ); +#include "lib-header/stdtype.h" +#include "lib-header/portio.h" + +/** x86 inb/outb: + * @param dx target port + * @param al input/output byte + */ + +void out(uint16_t port, uint8_t data) { + __asm__( + "outb %0, %1" + : // + : "a"(data), "Nd"(port) + ); +} + +uint8_t in(uint16_t port) { + uint8_t result; + __asm__ volatile( + "inb %1, %0" + : "=a"(result) + : "Nd"(port) + ); + return result; +} + +uint16_t in16(uint16_t port) { + uint16_t result; + __asm__ volatile( + "inw %1, %0" + : "=a" (result) + : "Nd" (port) + ); + return result; +} + +void out16(uint16_t port, uint16_t data) { + __asm__ volatile( + "outw %0, %1" + : // + : "a"(data), "Nd"(port) + ); } \ No newline at end of file diff --git a/src/stdmem.c b/src/stdmem.c index c96b48f..1c9f8a2 100644 --- a/src/stdmem.c +++ b/src/stdmem.c @@ -1,44 +1,44 @@ -#include "lib-header/stdtype.h" -#include "lib-header/stdmem.h" - -void* memset(void *s, int c, size_t n) { - uint8_t *buf = (uint8_t*) s; - for (size_t i = 0; i < n; i++) - buf[i] = (uint8_t) c; - return s; -} - -void* memcpy(void* restrict dest, const void* restrict src, size_t n) { - uint8_t *dstbuf = (uint8_t*) dest; - const uint8_t *srcbuf = (const uint8_t*) src; - for (size_t i = 0; i < n; i++) - dstbuf[i] = srcbuf[i]; - return dstbuf; -} - -int memcmp(const void *s1, const void *s2, size_t n) { - const uint8_t *buf1 = (const uint8_t*) s1; - const uint8_t *buf2 = (const uint8_t*) s2; - for (size_t i = 0; i < n; i++) { - if (buf1[i] < buf2[i]) - return -1; - else if (buf1[i] > buf2[i]) - return 1; - } - - return 0; -} - -void *memmove(void *dest, const void *src, size_t n) { - uint8_t *dstbuf = (uint8_t*) dest; - const uint8_t *srcbuf = (const uint8_t*) src; - if (dstbuf < srcbuf) { - for (size_t i = 0; i < n; i++) - dstbuf[i] = srcbuf[i]; - } else { - for (size_t i = n; i != 0; i--) - dstbuf[i-1] = srcbuf[i-1]; - } - - return dest; +#include "lib-header/stdtype.h" +#include "lib-header/stdmem.h" + +void* memset(void *s, int c, size_t n) { + uint8_t *buf = (uint8_t*) s; + for (size_t i = 0; i < n; i++) + buf[i] = (uint8_t) c; + return s; +} + +void* memcpy(void* restrict dest, const void* restrict src, size_t n) { + uint8_t *dstbuf = (uint8_t*) dest; + const uint8_t *srcbuf = (const uint8_t*) src; + for (size_t i = 0; i < n; i++) + dstbuf[i] = srcbuf[i]; + return dstbuf; +} + +int memcmp(const void *s1, const void *s2, size_t n) { + const uint8_t *buf1 = (const uint8_t*) s1; + const uint8_t *buf2 = (const uint8_t*) s2; + for (size_t i = 0; i < n; i++) { + if (buf1[i] < buf2[i]) + return -1; + else if (buf1[i] > buf2[i]) + return 1; + } + + return 0; +} + +void *memmove(void *dest, const void *src, size_t n) { + uint8_t *dstbuf = (uint8_t*) dest; + const uint8_t *srcbuf = (const uint8_t*) src; + if (dstbuf < srcbuf) { + for (size_t i = 0; i < n; i++) + dstbuf[i] = srcbuf[i]; + } else { + for (size_t i = n; i != 0; i--) + dstbuf[i-1] = srcbuf[i-1]; + } + + return dest; } \ No newline at end of file diff --git a/src/user-entry.s b/src/user-entry.s new file mode 100644 index 0000000..35a3e3c --- /dev/null +++ b/src/user-entry.s @@ -0,0 +1,7 @@ +global _start +extern main + +section .text +_start: + call main + jmp $ diff --git a/src/user-linker.ld b/src/user-linker.ld new file mode 100644 index 0000000..545898c --- /dev/null +++ b/src/user-linker.ld @@ -0,0 +1,24 @@ +ENTRY(_start) +OUTPUT_FORMAT("binary") + +SECTIONS { + . = 0x00000000; /* Assuming OS will load this program at virtual address 0x00000000 */ + + .text ALIGN(4): + { + user-entry.o(.text) /* Put user-entry at front of executable */ + *(.text) + } + + .data ALIGN(4): + { + *(.data) + } + + .rodata ALIGN(4): + { + *(.rodata*) + } + _linker_user_program_end = .; + ASSERT ((_linker_user_program_end <= 1 * 1024 * 1024), "Error: User program linking result is more than 1 MiB") +} diff --git a/src/user-shell.c b/src/user-shell.c new file mode 100644 index 0000000..fa141a8 --- /dev/null +++ b/src/user-shell.c @@ -0,0 +1,7 @@ +#include "lib-header/stdtype.h" + +int main(void) { + __asm__ volatile("mov %0, %%eax" : /* */ : "r"(0xDEADBEEF)); + while (TRUE); + return 0; +} \ No newline at end of file diff --git a/src/user-shell.s b/src/user-shell.s new file mode 100644 index 0000000..3b5a968 --- /dev/null +++ b/src/user-shell.s @@ -0,0 +1,7 @@ +#include "lib-header/stdtype.h" + +int main(void) { + __asm__ volatile("mov %0, %%eax" : /* */ : "r"(0xDEADBEEF)); + while (TRUE); + return 0; +} From f896c7364c92c19d5329e7ed24f2abbedceb0b42 Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Fri, 21 Apr 2023 23:54:41 +0700 Subject: [PATCH 33/52] fix: 3.1 alhamdulillah aman Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: Nigel Sahl Co-authored-by: Rava Maulana --- cd.iso | Bin 372736 -> 0 bytes makefile | 5 +- src/kernel_loader.s | 89 ++++++++++++++++++---------- src/lib-header/framebuffer.h | 2 +- src/{paging => lib-header}/paging.h | 25 +++++++- src/linker.ld | 28 ++++++--- src/paging/paging.c | 20 +++---- 7 files changed, 116 insertions(+), 53 deletions(-) delete mode 100644 cd.iso rename src/{paging => lib-header}/paging.h (79%) diff --git a/cd.iso b/cd.iso deleted file mode 100644 index 5b11db49e49ef947e61f2b9be0f599789579111b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 372736 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 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+0D*4? Gf&T@vlbTQf diff --git a/makefile b/makefile index dc2f8d5..016192e 100644 --- a/makefile +++ b/makefile @@ -9,6 +9,7 @@ INTERRUPT_FOLDER = interrupt KEYBOARD_FOLDER = keyboard FILESYSTEM_FOLDER = filesystem FRAMEBUFFER_FOLDER = framebuffer +PAGING_FOLDER = paging # SOURCE_FOLDER = bin/iso/boot/grub OUTPUT_FOLDER = bin ISO_NAME = OSyikkk @@ -51,7 +52,7 @@ kernel: @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(KEYBOARD_FOLDER)/keyboard.c -o $(OUTPUT_FOLDER)/keyboard.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/disk.c -o $(OUTPUT_FOLDER)/disk.o @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/fat32.c -o $(OUTPUT_FOLDER)/fat32.o - + @$(CC) $(CFLAGS) $(SOURCE_FOLDER)/$(PAGING_FOLDER)/paging.c -o $(OUTPUT_FOLDER)/paging.o @$(LIN) $(LFLAGS) bin/*.o -o $(OUTPUT_FOLDER)/kernel @echo Linking object files and generate elf32... @@ -73,4 +74,4 @@ iso: kernel -boot-info-table \ -o $(OUTPUT_FOLDER)/$(ISO_NAME).iso \ $(OUTPUT_FOLDER)/iso - + \ No newline at end of file diff --git a/src/kernel_loader.s b/src/kernel_loader.s index 738278d..16ba590 100644 --- a/src/kernel_loader.s +++ b/src/kernel_loader.s @@ -1,60 +1,89 @@ -global loader ; the entry symbol for ELF -global enter_protected_mode ; go to protected mode -extern kernel_setup ; kernel +global loader ; the entry symbol for ELF +global enter_protected_mode ; go to protected mode +global set_tss_register ; set tss register to GDT entry +extern kernel_setup ; kernel C entrypoint +extern _paging_kernel_page_directory ; kernel page directory +KERNEL_VIRTUAL_BASE equ 0xC0000000 ; kernel virtual memory +KERNEL_STACK_SIZE equ 2097152 ; size of stack in bytes +MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant +FLAGS equ 0x0 ; multiboot flags +CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum + ; (magic number + checksum + flags should equal 0) -KERNEL_STACK_SIZE equ 2097152 ; size of stack in bytes -MAGIC_NUMBER equ 0x1BADB002 ; define the magic number constant -FLAGS equ 0x0 ; multiboot flags -CHECKSUM equ -MAGIC_NUMBER ; calculate the checksum - ; (magic number + checksum + flags should equal 0) - section .bss -align 4 ; align at 4 bytes -kernel_stack: ; label points to beginning of memory - resb KERNEL_STACK_SIZE ; reserve stack for the kernel +align 4 ; align at 4 bytes +kernel_stack: ; label points to beginning of memory + resb KERNEL_STACK_SIZE ; reserve stack for the kernel + + +section .multiboot ; GRUB multiboot header +align 4 ; the code must be 4 byte aligned + dd MAGIC_NUMBER ; write the magic number to the machine code, + dd FLAGS ; the flags, + dd CHECKSUM ; and the checksum + -section .__mbHeader -align 4 - dd MAGIC_NUMBER ; write the magic number to the machine code, - dd FLAGS ; the flags, - dd CHECKSUM ; and the checksum -section .text ; start of the text (code) section +section .setup.text ; start of the text (code) section +loader equ (loader_entrypoint - KERNEL_VIRTUAL_BASE) +loader_entrypoint: ; the loader label (defined as entry point in linker script) + ; Set CR3 (CPU page register) + mov eax, _paging_kernel_page_directory - KERNEL_VIRTUAL_BASE + mov cr3, eax + ; Use 4 MB paging + mov eax, cr4 + or eax, 0x00000010 ; PSE (4 MB paging) + mov cr4, eax + ; Enable paging + mov eax, cr0 + or eax, 0x80000000 ; PG flag + mov cr0, eax -loader: ; the loader label (defined as entry point in linker script) - mov esp, kernel_stack + KERNEL_STACK_SIZE ; setup stack register to proper location + ; Jump into higher half first, cannot use C because call stack is still not working + lea eax, [loader_virtual] + jmp eax + +loader_virtual: + mov dword [_paging_kernel_page_directory], 0 + invlpg [0] ; Delete identity mapping and invalidate TLB cache for first page + mov esp, kernel_stack + KERNEL_STACK_SIZE ; Setup stack register to proper location call kernel_setup .loop: - jmp .loop ; loop forever + jmp .loop ; loop forever + +section .text ; More details: https://en.wikibooks.org/wiki/X86_Assembly/Protected_Mode enter_protected_mode: + ; Load GDT from GDTDescriptor cli mov eax, [esp+4] - ; eax at this line will carry GDTR location, dont forget to use square bracket [eax] - lgdt [eax] - + + ; Set Protection Enable bit-flag in Control Register 0 (CR0) + ; Or in other words: Switch to protected mode mov eax, cr0 - ; Set eax with above condition, eax will be copied to CR0 with next instruction - or al, 1 + or eax, 1 mov cr0, eax - ; Far jump to update cs register ; Warning: Invalid GDT will raise exception in any instruction below jmp 0x8:flush_cs flush_cs: + ; Update all segment register mov ax, 10h - ; Segments register need to set with 0x10: ss, ds, es mov ss, ax mov ds, ax mov es, ax - - + + ret + +set_tss_register: + mov ax, 0x28 | 0 ; GDT TSS Selector, ring 0 + ltr ax ret diff --git a/src/lib-header/framebuffer.h b/src/lib-header/framebuffer.h index 5185330..b298bb6 100644 --- a/src/lib-header/framebuffer.h +++ b/src/lib-header/framebuffer.h @@ -3,7 +3,7 @@ #include "lib-header/stdtype.h" -#define MEMORY_FRAMEBUFFER (uint8_t *) 0xB8000 +#define MEMORY_FRAMEBUFFER (uint8_t *) 0xC00B8000 #define CURSOR_PORT_CMD 0x03D4 #define CURSOR_PORT_DATA 0x03D5 #define VGA_CURSOR_HIGH 0x0E diff --git a/src/paging/paging.h b/src/lib-header/paging.h similarity index 79% rename from src/paging/paging.h rename to src/lib-header/paging.h index 7e998b2..30b1fc7 100644 --- a/src/paging/paging.h +++ b/src/lib-header/paging.h @@ -16,11 +16,24 @@ extern struct PageDirectory _paging_kernel_page_directory; * Page Directory Entry Flag, only first 8 bit * * @param present_bit Indicate whether this entry is exist or not - * ... - */ + * @param read_write_bit + * @param user_supervisor_bit + * @param write_through_bit + * @param cache_disable_bit + * @param accessed_bit + * @param available_bit + * @param page_size_bit + * */ struct PageDirectoryEntryFlag { uint8_t present_bit : 1; // TODO : Continue. Note: Only first 8 bit flags + uint8_t write_bit : 1; + uint8_t user_bit : 1; + uint8_t write_through_bit : 1; + uint8_t cache_disable_bit : 1; + uint8_t accessed_bit : 1; + uint8_t available_bit : 1; + uint8_t use_pagesize_4_mb : 1; } __attribute__((packed)); /** @@ -38,6 +51,11 @@ struct PageDirectoryEntry { struct PageDirectoryEntryFlag flag; uint16_t global_page : 1; // TODO : Continue, Use uint16_t + bitfield here, Do not use uint8_t + uint16_t available : 3; + uint16_t pat_bit : 1; + uint16_t higher_address: 8; + uint16_t reserved : 1; + uint16_t lower_address : 10; } __attribute__((packed)); /** @@ -51,7 +69,8 @@ struct PageDirectoryEntry { */ struct PageDirectory { // TODO : Implement -} __attribute__((packed)); + struct PageDirectoryEntry table[PAGE_ENTRY_COUNT]; +} __attribute__((aligned(0x1000))); /** * Containing page driver states diff --git a/src/linker.ld b/src/linker.ld index d5c80f7..78897f2 100644 --- a/src/linker.ld +++ b/src/linker.ld @@ -1,31 +1,45 @@ ENTRY(loader) /* the name of the entry label */ +/* relocation on address at 0xC001 0000, but load address (physical location) still at 0x100000 */ SECTIONS { - . = 0x00100000; /* the code should be loaded at 1 MB */ + . = 0xC0100000; /* use relocation address (memory references) at 0xC010 0000 */ - .__mbHeader : /* align at 4 KB */ + /* Optional variable that can be used in kernel, starting address of kernel */ + _linker_kernel_virtual_addr_start = .; + _linker_kernel_physical_addr_start = . - 0xC0000000; + .multiboot ALIGN (0x1000) : AT (ADDR (.multiboot) - 0xC0000000) { - *(.__mbHeader) /* all text sections from all files */ + *(.multiboot) /* put GRUB multiboot header at front */ } - .text ALIGN (0x1000) : /* align at 4 KB */ + .setup.text ALIGN (0x1000) : AT (ADDR (.setup.text) - 0xC0000000) + { + *(.setup.text) /* initial setup code */ + } + + .text ALIGN (0x1000) : AT (ADDR (.text) - 0xC0000000) { *(.text) /* all text sections from all files */ } - .rodata ALIGN (0x1000) : /* align at 4 KB */ + .rodata ALIGN (0x1000) : AT (ADDR (.rodata) - 0xC0000000) { *(.rodata*) /* all read-only data sections from all files */ } - .data ALIGN (0x1000) : /* align at 4 KB */ + .data ALIGN (0x1000) : AT (ADDR (.data) - 0xC0000000) { *(.data) /* all data sections from all files */ } - .bss ALIGN (0x1000) : /* align at 4 KB */ + .bss ALIGN (0x1000) : AT (ADDR (.bss) - 0xC0000000) { *(COMMON) /* all COMMON sections from all files */ *(.bss) /* all bss sections from all files */ + bin/kernel_loader.o(.bss) + _linker_kernel_stack_top = .; } + /* Optional variable that can be used in kernel, show end address of kernel */ + _linker_kernel_virtual_addr_end = .; + _linker_kernel_physical_addr_end = . - 0xC0000000; } diff --git a/src/paging/paging.c b/src/paging/paging.c index 926466c..9eebd9f 100644 --- a/src/paging/paging.c +++ b/src/paging/paging.c @@ -1,4 +1,4 @@ -#include "lib-header/paging.h" +#include "../lib-header/paging.h" __attribute__((aligned(0x1000))) struct PageDirectory _paging_kernel_page_directory = { .table = { @@ -17,9 +17,9 @@ __attribute__((aligned(0x1000))) struct PageDirectory _paging_kernel_page_direct } }; -static struct PageDriverState page_driver_state = { - .last_available_physical_addr = (uint8_t*) 0 + PAGE_FRAME_SIZE, -}; +// static struct PageDriverState page_driver_state = { +// .last_available_physical_addr = (uint8_t*) 0 + PAGE_FRAME_SIZE, +// }; void update_page_directory_entry(void *physical_addr, void *virtual_addr, struct PageDirectoryEntryFlag flag) { uint32_t page_index = ((uint32_t) virtual_addr >> 22) & 0x3FF; @@ -29,13 +29,13 @@ void update_page_directory_entry(void *physical_addr, void *virtual_addr, struct flush_single_tlb(virtual_addr); } -int8_t allocate_single_user_page_frame(void *virtual_addr) { - // Using default QEMU config (128 MiB max memory) - uint32_t last_physical_addr = (uint32_t) page_driver_state.last_available_physical_addr; +// int8_t allocate_single_user_page_frame(void *virtual_addr) { +// // Using default QEMU config (128 MiB max memory) +// // uint32_t last_physical_addr = (uint32_t) page_driver_state.last_available_physical_addr; - // TODO : Allocate Page Directory Entry with user privilege - return -1; -} +// // TODO : Allocate Page Directory Entry with user privilege +// return -1; +// } void flush_single_tlb(void *virtual_addr) { asm volatile("invlpg (%0)" : /* */ : "b"(virtual_addr): "memory"); From 7d1fe63e65c9beec062578b29637f2e4332bd78b Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Sun, 23 Apr 2023 13:33:41 +0700 Subject: [PATCH 34/52] feat: implement 3.2.1 (not finished) Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: Nigel Sahl Co-authored-by: Rava Maulana --- .vscode/launch.json | 29 +++++++++++++++++++++++++++++ .vscode/settings.json | 6 +++++- .vscode/tasks.json | 16 ++++++++++++++++ makefile | 8 +++++++- src/filesystem/fat32.c | 1 + src/inserter/external-inserter.c | 17 +++++++++++++---- src/interrupt/interrupt.c | 2 +- src/kernel.c | 1 + src/lib-header/stdmem.h | 3 ++- 9 files changed, 75 insertions(+), 8 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index ca6c8fc..cd99e1d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -38,5 +38,34 @@ ], "avoidWindowsConsoleRedirection": true }, + { + "name": "Inserter", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/inserter", + "args": ["shell", "2", "storage.bin"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin", + "environment": [], + "preLaunchTask": "Build Inserter", + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + }, + { + "text": "set output-radix 16", + "description": "Use hexadecimal output" + }, + ] + } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index d7f5ca5..91010b2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,10 @@ "stdtype.h": "c", "portio.h": "c", "cmos.h": "c", - "interrupt.h": "c" + "interrupt.h": "c", + "atomic": "c", + "cstdint": "c", + "random": "c", + "*.tcc": "c" }, } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9617140..b7f288c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -45,6 +45,22 @@ "type": "shell", "label": "Exit QEMU", "command": "pkill -f qemu || test $? -eq 1" + }, + { + "type": "cppbuild", + "label": "Build Inserter", + "command": "make", + "args": [ + "inserter", + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "group": { + "kind": "build", + "isDefault": true + } } + ], } \ No newline at end of file diff --git a/makefile b/makefile index 016192e..6f956fd 100644 --- a/makefile +++ b/makefile @@ -10,6 +10,7 @@ KEYBOARD_FOLDER = keyboard FILESYSTEM_FOLDER = filesystem FRAMEBUFFER_FOLDER = framebuffer PAGING_FOLDER = paging +INSERTER_FOLDER = inserter # SOURCE_FOLDER = bin/iso/boot/grub OUTPUT_FOLDER = bin ISO_NAME = OSyikkk @@ -74,4 +75,9 @@ iso: kernel -boot-info-table \ -o $(OUTPUT_FOLDER)/$(ISO_NAME).iso \ $(OUTPUT_FOLDER)/iso - \ No newline at end of file + +inserter: + @$(CC) -Wno-builtin-declaration-mismatch -g \ + $(SOURCE_FOLDER)/stdmem.c $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/fat32.c \ + $(SOURCE_FOLDER)/$(INSERTER_FOLDER)/external-inserter.c \ + -o $(OUTPUT_FOLDER)/inserter diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index 18c8566..77723c2 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -1,6 +1,7 @@ #include "../lib-header/stdtype.h" #include "../lib-header/fat32.h" #include "../lib-header/stdmem.h" +#include "../lib-header/disk.h" // #include "../lib-header/cmos.h" static struct FAT32DriverState driver_state; diff --git a/src/inserter/external-inserter.c b/src/inserter/external-inserter.c index db6cbfc..5f17ff1 100644 --- a/src/inserter/external-inserter.c +++ b/src/inserter/external-inserter.c @@ -1,9 +1,14 @@ #include #include +// #include "../lib-header/disk.h" // Usual gcc fixed width integer type -typedef u_int32_t uint32_t; -typedef u_int8_t uint8_t; +// typedef u_int32_t uint32_t; +// typedef u_int8_t uint8_t; + +typedef unsigned int uint32_t; +typedef unsigned int uint8_t; +typedef signed char int8_t; // Manual import from fat32.h, disk.h, & stdmem.h due some issue with size_t #define BLOCK_SIZE 512 @@ -25,12 +30,15 @@ int8_t write(struct FAT32DriverRequest request); int8_t delete(struct FAT32DriverRequest request); - - // Global variable uint8_t *image_storage; uint8_t *file_buffer; +// void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count) { +// for (int i = 0; i < block_count; i++) +// memcpy((uint8_t*) ptr + BLOCK_SIZE*i, image_storage + logical_block_address * BLOCK_SIZE / 4 + BLOCK_SIZE*(i) / 4, BLOCK_SIZE); +// } + void read_blocks(void *ptr, uint32_t logical_block_address, uint8_t block_count) { for (int i = 0; i < block_count; i++) memcpy((uint8_t*) ptr + BLOCK_SIZE*i, image_storage + BLOCK_SIZE*(logical_block_address+i), BLOCK_SIZE); @@ -72,6 +80,7 @@ int main(int argc, char *argv[]) { // FAT32 operations initialize_filesystem_fat32(); + struct FAT32DriverRequest request = { .buf = file_buffer, .ext = "\0\0\0", diff --git a/src/interrupt/interrupt.c b/src/interrupt/interrupt.c index 0eab274..574995c 100644 --- a/src/interrupt/interrupt.c +++ b/src/interrupt/interrupt.c @@ -47,7 +47,7 @@ void main_interrupt_handler ( uint32_t int_number, __attribute__((unused)) struct InterruptStack info ) { - switch (int_number+1) { + switch (int_number) { case (PIC1_OFFSET + IRQ_KEYBOARD) : keyboard_isr(); break; diff --git a/src/kernel.c b/src/kernel.c index 581a14d..32c0ed4 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -18,6 +18,7 @@ void kernel_setup(void) { framebuffer_clear(); framebuffer_set_cursor(0, 0); framebuffer_write_string("> "); + activate_keyboard_interrupt(); initialize_filesystem_fat32(); struct ClusterBuffer cbuf[5]; diff --git a/src/lib-header/stdmem.h b/src/lib-header/stdmem.h index 70b0f01..1484e48 100644 --- a/src/lib-header/stdmem.h +++ b/src/lib-header/stdmem.h @@ -1,7 +1,8 @@ #ifndef _STDMEM_H #define _STDMEM_H -#include "lib-header/stdtype.h" +// #include "stdtype.h" +#include "../lib-header/stdtype.h" #define VIDEO_ADDR 0xB8000 From 700d1929a8366139d3e1b7daa84501ffd31dca37 Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Sun, 23 Apr 2023 16:42:18 +0700 Subject: [PATCH 35/52] fix: inserter 3.1 --- other/shell | Bin 0 -> 104 bytes src/inserter/external-inserter.c | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100755 other/shell diff --git a/other/shell b/other/shell new file mode 100755 index 0000000000000000000000000000000000000000..e2eec59c13b5c48173a7583a77fc03f54dcd8960 GIT binary patch literal 104 zcmaFC#K6Gt`rl`Mx!<9kPj|fExAtD=f!F^SM1aB|z*rT;z*xhNDRAZEXk~KZWd$->fS3n}L2P%wPzIpU2S8%XKnwtQ-WLG? literal 0 HcmV?d00001 diff --git a/src/inserter/external-inserter.c b/src/inserter/external-inserter.c index 5f17ff1..c7c7907 100644 --- a/src/inserter/external-inserter.c +++ b/src/inserter/external-inserter.c @@ -7,7 +7,7 @@ // typedef u_int8_t uint8_t; typedef unsigned int uint32_t; -typedef unsigned int uint8_t; +typedef unsigned char uint8_t; typedef signed char int8_t; // Manual import from fat32.h, disk.h, & stdmem.h due some issue with size_t From da1e433510c09ba9915b2e27d126bb845a0dec80 Mon Sep 17 00:00:00 2001 From: hanifmz07 <16521066@std.stei.itb.ac.id> Date: Wed, 26 Apr 2023 22:16:47 +0700 Subject: [PATCH 36/52] feat: implement 3.2 Co-authored-by: Rava Maulana Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: Nigel Sahl <13521043@mahasiswa.itb.ac.id> --- .vscode/settings.json | 3 +- makefile | 16 ++++++++ src/filesystem/fat32.c | 1 - src/gdt.c | 68 +++++++++++++++++++++++++++++++- src/inserter/external-inserter.c | 6 ++- src/interrupt/interrupt.c | 15 ++++++- src/kernel.c | 31 ++++++++++++--- src/kernel_loader.s | 22 +++++++++++ src/lib-header/gdt.h | 9 +++++ src/lib-header/interrupt.h | 16 +++++++- src/lib-header/kernel_loader.h | 22 +++++++++++ src/paging/paging.c | 28 ++++++++----- src/user-entry.s | 7 ++++ src/user-linker.ld | 24 +++++++++++ src/user-shell.c | 7 ++++ 15 files changed, 253 insertions(+), 22 deletions(-) create mode 100644 src/user-entry.s create mode 100644 src/user-linker.ld create mode 100644 src/user-shell.c diff --git a/.vscode/settings.json b/.vscode/settings.json index 91010b2..57392af 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,6 +10,7 @@ "atomic": "c", "cstdint": "c", "random": "c", - "*.tcc": "c" + "*.tcc": "c", + "gdt.h": "c" }, } \ No newline at end of file diff --git a/makefile b/makefile index 6f956fd..84f5200 100644 --- a/makefile +++ b/makefile @@ -35,6 +35,9 @@ all: build build: iso clean: rm -rf *.o *.iso $(OUTPUT_FOLDER)/kernel + +first : disk insert-shell + disk: @qemu-img create -f raw $(OUTPUT_FOLDER)/$(DISK_NAME).bin 4M @@ -81,3 +84,16 @@ inserter: $(SOURCE_FOLDER)/stdmem.c $(SOURCE_FOLDER)/$(FILESYSTEM_FOLDER)/fat32.c \ $(SOURCE_FOLDER)/$(INSERTER_FOLDER)/external-inserter.c \ -o $(OUTPUT_FOLDER)/inserter + +user-shell: + @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/user-entry.s -o user-entry.o + @$(CC) $(CFLAGS) -fno-pie $(SOURCE_FOLDER)/user-shell.c -o user-shell.o + @$(LIN) -T $(SOURCE_FOLDER)/user-linker.ld -melf_i386 \ + user-entry.o user-shell.o -o $(OUTPUT_FOLDER)/shell + @echo Linking object shell object files and generate flat binary... + @size --target=binary bin/shell + @rm -f *.o + +insert-shell: inserter user-shell + @echo Inserting shell into root directory... + @cd $(OUTPUT_FOLDER); ./inserter shell 2 $(DISK_NAME).bin diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index 77723c2..739c7c0 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -224,7 +224,6 @@ int8_t read(struct FAT32DriverRequest request){ cluster_number = driver_state.fat_table.cluster_map[cluster_number]; j++; } - read_clusters((struct ClusterBuffer*) (request.buf) + j, cluster_number, 1); return error_code = 0; } } diff --git a/src/gdt.c b/src/gdt.c index 241a88c..3e2ee7b 100644 --- a/src/gdt.c +++ b/src/gdt.c @@ -6,7 +6,7 @@ * Initial SegmentDescriptor already set properly according to GDT definition in Intel Manual & OSDev. * Table entry : [{Null Descriptor}, {Kernel Code}, {Kernel Data (variable, etc)}, ...]. */ -struct GlobalDescriptorTable global_descriptor_table = { +static struct GlobalDescriptorTable global_descriptor_table = { .table = { { // First 32-bit @@ -68,7 +68,65 @@ struct GlobalDescriptorTable global_descriptor_table = { .def_op_size = 0x1, .granularity = 0x1, .base_high = 0x00 - } + }, + { + /* TODO: User Code Descriptor */ + .segment_low = 0xFFFF, + .base_low = 0x0000, + + // Next 16-bit (Bit 32 to 47) + .base_mid = 0x00, + .type_bit = 0xA, + .non_system = 0x1, + + .dpl = 0x3, + .segment_present = 0x1, + .segment_limit = 0xF, + .avl = 0x0, + .code_seg_64bit = 0x1, + .def_op_size = 0x1, + .granularity = 0x1, + .base_high = 0x00 + }, + + {/* TODO: User Data Descriptor */ + .segment_low = 0xFFFF, + .base_low = 0x0000, + + // Next 16-bit (Bit 32 to 47) + .base_mid = 0x00, + .type_bit = 0x2, + .non_system = 0x1, + + .dpl = 0x3, + .segment_present = 0x1, + .segment_limit = 0xF, + .avl = 0x0, + .code_seg_64bit = 0x0, + .def_op_size = 0x1, + .granularity = 0x1, + .base_high = 0x00 + }, + { + // TSS + .segment_low = sizeof(struct TSSEntry), + .base_low = 0x0000, + + // Next 16-bit (Bit 32 to 47) + .base_mid = 0x00, + .type_bit = 0x9, + .non_system = 0x0, + + .dpl = 0x0, + .segment_present = 0x1, + .segment_limit = (sizeof(struct TSSEntry) & (0xF << 16)) >> 16, + .avl = 0x0, + .code_seg_64bit = 0x0, + .def_op_size = 0x1, + .granularity = 0x0, + .base_high = 0x00 + }, + {0} } }; @@ -84,3 +142,9 @@ struct GDTR _gdt_gdtr = { .address = &global_descriptor_table }; +void gdt_install_tss(void) { + uint32_t base = (uint32_t) &_interrupt_tss_entry; + global_descriptor_table.table[5].base_high = (base & (0xFF << 24)) >> 24; + global_descriptor_table.table[5].base_mid = (base & (0xFF << 16)) >> 16; + global_descriptor_table.table[5].base_low = base & 0xFFFF; +} diff --git a/src/inserter/external-inserter.c b/src/inserter/external-inserter.c index c7c7907..f684165 100644 --- a/src/inserter/external-inserter.c +++ b/src/inserter/external-inserter.c @@ -80,7 +80,11 @@ int main(int argc, char *argv[]) { // FAT32 operations initialize_filesystem_fat32(); - + + if(filesize != 0) { + filesize = ((filesize-1) / 0x800 + 1) * 0x800; // 0x800 is cluster size in bytes + } + struct FAT32DriverRequest request = { .buf = file_buffer, .ext = "\0\0\0", diff --git a/src/interrupt/interrupt.c b/src/interrupt/interrupt.c index 574995c..259a022 100644 --- a/src/interrupt/interrupt.c +++ b/src/interrupt/interrupt.c @@ -1,5 +1,6 @@ #include "../lib-header/interrupt.h" +struct TSSEntry _interrupt_tss_entry; void io_wait(void) { out(0x80, 0); @@ -51,11 +52,21 @@ void main_interrupt_handler ( case (PIC1_OFFSET + IRQ_KEYBOARD) : keyboard_isr(); break; - // ? : CMOS Masuk sini juga kah? + case PAGE_FAULT: + __asm__("hlt"); + break; }; } void activate_keyboard_interrupt(void) { out(PIC1_DATA, PIC_DISABLE_ALL_MASK ^ (1 << IRQ_KEYBOARD)); out(PIC2_DATA, PIC_DISABLE_ALL_MASK); -} \ No newline at end of file +} + +void set_tss_kernel_current_stack(void) { + uint32_t stack_ptr; + // Reading base stack frame instead esp + __asm__ volatile ("mov %%ebp, %0": "=r"(stack_ptr) : /* */); + // Add 8 because 4 for ret address and other 4 is for stack_ptr variable + _interrupt_tss_entry.esp0 = stack_ptr + 8; +} diff --git a/src/kernel.c b/src/kernel.c index 32c0ed4..5c0590b 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -9,17 +9,23 @@ #include "lib-header/keyboard.h" #include "lib-header/disk.h" #include "lib-header/fat32.h" +#include "lib-header/paging.h" + void kernel_setup(void) { - enter_protected_mode(&_gdt_gdtr); + + enter_protected_mode(&_gdt_gdtr); pic_remap(); initialize_idt(); - // activate_cmos_interrupt(); + activate_keyboard_interrupt(); framebuffer_clear(); framebuffer_set_cursor(0, 0); - framebuffer_write_string("> "); - activate_keyboard_interrupt(); initialize_filesystem_fat32(); + gdt_install_tss(); + set_tss_register(); + + // Allocate first 4 MiB virtual memory + allocate_single_user_page_frame((uint8_t*) 0); struct ClusterBuffer cbuf[5]; for (uint32_t i = 0; i < 5; i++) @@ -41,6 +47,7 @@ void kernel_setup(void) { .buffer_size = 0, } ; + read(request); write(request); // Create folder "ikanaide" memcpy(request.name, "kano1\0\0\0", 8); write(request); // Create folder "kano1" @@ -79,7 +86,21 @@ void kernel_setup(void) { memcpy(request_dir.name, "\0\0\0\0\0\0\0\0", 8); read_directory(request_dir); - __asm__("int $0x4"); + // Write shell into memory + struct FAT32DriverRequest request_shell = { + .buf = (uint8_t*) 0, + .name = "shell", + .ext = "\0\0\0", + .parent_cluster_number = ROOT_CLUSTER_NUMBER, + .buffer_size = 0x100000, + }; + read(request_shell); + + // Set TSS $esp pointer and jump into shell + set_tss_kernel_current_stack(); + kernel_execute_user_program((uint8_t*) 0); + + // __asm__("int $0x4"); while (TRUE) { keyboard_state_activate(); } diff --git a/src/kernel_loader.s b/src/kernel_loader.s index 16ba590..2e128f7 100644 --- a/src/kernel_loader.s +++ b/src/kernel_loader.s @@ -58,6 +58,28 @@ loader_virtual: section .text + +global kernel_execute_user_program ; execute user program from kernel +kernel_execute_user_program: + mov eax, 0x20 | 0x3 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + mov ecx, [esp+4] ; Save this first (before pushing anything to stack) for last push + push eax ; Stack segment selector (GDT_USER_DATA_SELECTOR), user privilege + mov eax, ecx + add eax, 0x400000 - 4 + push eax ; User space stack pointer (esp), move it into last 4 MiB + pushf ; eflags register state, when jump inside user program + mov eax, 0x18 | 0x3 + push eax ; Code segment selector (GDT_USER_CODE_SELECTOR), user privilege + mov eax, ecx + push eax ; eip register to jump back + + iret + ; More details: https://en.wikibooks.org/wiki/X86_Assembly/Protected_Mode enter_protected_mode: ; Load GDT from GDTDescriptor diff --git a/src/lib-header/gdt.h b/src/lib-header/gdt.h index afce048..cd2ecb4 100644 --- a/src/lib-header/gdt.h +++ b/src/lib-header/gdt.h @@ -2,9 +2,14 @@ #define _GDT_H #include "lib-header/stdtype.h" +#include "lib-header/interrupt.h" #define GDT_MAX_ENTRY_COUNT 32 +#define GDT_USER_CODE_SEGMENT_SELECTOR 0x18 +#define GDT_USER_DATA_SEGMENT_SELECTOR 0x20 +#define GDT_TSS_SELECTOR 0x28 + extern struct GDTR _gdt_gdtr; /** @@ -60,4 +65,8 @@ struct GDTR { struct GlobalDescriptorTable *address; } __attribute__((packed)); +// Set GDT_TSS_SELECTOR with proper TSS values, accessing _interrupt_tss_entry +void gdt_install_tss(void); + + #endif \ No newline at end of file diff --git a/src/lib-header/interrupt.h b/src/lib-header/interrupt.h index e253bae..a2b9d4d 100644 --- a/src/lib-header/interrupt.h +++ b/src/lib-header/interrupt.h @@ -60,6 +60,9 @@ #define IRQ_PRIMARY_ATA 14 #define IRQ_SECOND_ATA 15 +#define PAGE_FAULT 0xE +extern struct TSSEntry _interrupt_tss_entry; + /** * CPURegister, store CPU registers that can be used for interrupt handler / ISRs @@ -95,9 +98,20 @@ struct InterruptStack { uint32_t eflags; } __attribute__((packed)); +/** + * TSSEntry, Task State Segment. Used when jumping back to ring 0 / kernel + */ +struct TSSEntry { + uint32_t prev_tss; // Previous TSS + uint32_t esp0; // Stack pointer to load when changing to kernel mode + uint32_t ss0; // Stack segment to load when changing to kernel mode + // Unused variables + uint32_t unused_register[23]; +} __attribute__((packed)); - +// Set kernel stack in TSS +void set_tss_kernel_current_stack(void); // Activate PIC mask for keyboard only void activate_keyboard_interrupt(void); diff --git a/src/lib-header/kernel_loader.h b/src/lib-header/kernel_loader.h index 65ae26e..c2c1d96 100644 --- a/src/lib-header/kernel_loader.h +++ b/src/lib-header/kernel_loader.h @@ -1,7 +1,29 @@ #ifndef _KERNEL_LOADER #define _KERNEL_LOADER +// Optional linker variable : Pointing to kernel start & end address +// Note : Use & operator, example : a = (uint32_t) &_linker_kernel_stack_top; +extern uint32_t _linker_kernel_virtual_addr_start; +extern uint32_t _linker_kernel_virtual_addr_end; +extern uint32_t _linker_kernel_physical_addr_start; +extern uint32_t _linker_kernel_physical_addr_end; +extern uint32_t _linker_kernel_stack_top; + +/** + * Execute user program from kernel, one way jump. This function is defined in asm source code. + * + * @param virtual_addr Pointer into user program that already in memory + * @warning Assuming pointed memory is properly loaded + */ +extern void kernel_execute_user_program(void *virtual_addr); + /** + * Set the tss register pointing to GDT_TSS_SELECTOR with ring 0 + */ +extern void set_tss_register(void); // Note : Already implemented in kernel_loader.asm + +/** + * * Load GDT from gdtr and launch protected mode. This function defined in asm source code. * * @param gdtr Pointer to already defined & initialized GDTR diff --git a/src/paging/paging.c b/src/paging/paging.c index 9eebd9f..eec421b 100644 --- a/src/paging/paging.c +++ b/src/paging/paging.c @@ -17,9 +17,9 @@ __attribute__((aligned(0x1000))) struct PageDirectory _paging_kernel_page_direct } }; -// static struct PageDriverState page_driver_state = { -// .last_available_physical_addr = (uint8_t*) 0 + PAGE_FRAME_SIZE, -// }; +static struct PageDriverState page_driver_state = { + .last_available_physical_addr = (uint8_t*) 0 + PAGE_FRAME_SIZE, +}; void update_page_directory_entry(void *physical_addr, void *virtual_addr, struct PageDirectoryEntryFlag flag) { uint32_t page_index = ((uint32_t) virtual_addr >> 22) & 0x3FF; @@ -29,13 +29,23 @@ void update_page_directory_entry(void *physical_addr, void *virtual_addr, struct flush_single_tlb(virtual_addr); } -// int8_t allocate_single_user_page_frame(void *virtual_addr) { -// // Using default QEMU config (128 MiB max memory) -// // uint32_t last_physical_addr = (uint32_t) page_driver_state.last_available_physical_addr; +int8_t allocate_single_user_page_frame(void *virtual_addr) { + // Using default QEMU config (128 MiB max memory) + uint32_t start = (uint32_t) page_driver_state.last_available_physical_addr; + if (start == 0x100000) { + return -1; + } else { + update_page_directory_entry((void*) start, virtual_addr, (struct PageDirectoryEntryFlag) { + .present_bit = 1, + .write_bit = 1, + .user_bit = 1, + .use_pagesize_4_mb = 1, + }); + page_driver_state.last_available_physical_addr = (uint8_t*) start + PAGE_FRAME_SIZE; + return 0; + } -// // TODO : Allocate Page Directory Entry with user privilege -// return -1; -// } +} void flush_single_tlb(void *virtual_addr) { asm volatile("invlpg (%0)" : /* */ : "b"(virtual_addr): "memory"); diff --git a/src/user-entry.s b/src/user-entry.s new file mode 100644 index 0000000..8fdc8a2 --- /dev/null +++ b/src/user-entry.s @@ -0,0 +1,7 @@ +global _start +extern main + +section .text +_start: + call main + jmp $ \ No newline at end of file diff --git a/src/user-linker.ld b/src/user-linker.ld new file mode 100644 index 0000000..545898c --- /dev/null +++ b/src/user-linker.ld @@ -0,0 +1,24 @@ +ENTRY(_start) +OUTPUT_FORMAT("binary") + +SECTIONS { + . = 0x00000000; /* Assuming OS will load this program at virtual address 0x00000000 */ + + .text ALIGN(4): + { + user-entry.o(.text) /* Put user-entry at front of executable */ + *(.text) + } + + .data ALIGN(4): + { + *(.data) + } + + .rodata ALIGN(4): + { + *(.rodata*) + } + _linker_user_program_end = .; + ASSERT ((_linker_user_program_end <= 1 * 1024 * 1024), "Error: User program linking result is more than 1 MiB") +} diff --git a/src/user-shell.c b/src/user-shell.c new file mode 100644 index 0000000..3b5a968 --- /dev/null +++ b/src/user-shell.c @@ -0,0 +1,7 @@ +#include "lib-header/stdtype.h" + +int main(void) { + __asm__ volatile("mov %0, %%eax" : /* */ : "r"(0xDEADBEEF)); + while (TRUE); + return 0; +} From 433e90c88fde51b2f8b9457acdc89ab0be47eb65 Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Wed, 26 Apr 2023 23:55:38 +0700 Subject: [PATCH 37/52] feat: implement 3.3.1 Co-authored-by: Nigel Sahl <13521043@std.stei.itb.ac.id> Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: Rava Maulana --- .vscode/settings.json | 4 +++- makefile | 2 +- src/interrupt/idt.c | 9 ++++++-- src/interrupt/interrupt.c | 47 ++++++++++++++++++++++++++++++++++----- src/kernel.c | 1 + src/user-shell.c | 41 ++++++++++++++++++++++++++++++++-- 6 files changed, 93 insertions(+), 11 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 57392af..c398954 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,6 +11,8 @@ "cstdint": "c", "random": "c", "*.tcc": "c", - "gdt.h": "c" + "gdt.h": "c", + "cstdio": "c", + "cstring": "c" }, } \ No newline at end of file diff --git a/makefile b/makefile index 84f5200..8c65ba6 100644 --- a/makefile +++ b/makefile @@ -63,7 +63,7 @@ kernel: @rm -f *.o iso: kernel - # @rm -r $(OUTPUT_FOLDER)/iso/ + @rm -r $(OUTPUT_FOLDER)/iso/ @mkdir -p $(OUTPUT_FOLDER)/iso/boot/grub @cp $(OUTPUT_FOLDER)/kernel $(OUTPUT_FOLDER)/iso/boot/ @cp other/grub1 $(OUTPUT_FOLDER)/iso/boot/grub/ diff --git a/src/interrupt/idt.c b/src/interrupt/idt.c index 532b9b9..a9115c0 100644 --- a/src/interrupt/idt.c +++ b/src/interrupt/idt.c @@ -16,8 +16,13 @@ void initialize_idt(void) { _idt_idtr.address = (void *)&interrupt_descriptor_table.table[0]; for (uint8_t i = 0; i < ISR_STUB_TABLE_LIMIT; i++) { - set_interrupt_gate(i, isr_stub_table[i], GDT_KERNEL_CODE_SEGMENT_SELECTOR, - 0); + if (i>=0x30 && i<=0x3F) { + set_interrupt_gate(i, isr_stub_table[i], GDT_KERNEL_CODE_SEGMENT_SELECTOR, + 0x3); + } else { + set_interrupt_gate(i, isr_stub_table[i], GDT_KERNEL_CODE_SEGMENT_SELECTOR, + 0x0); + } } __asm__ volatile("lidt %0" : : "m"(_idt_idtr)); __asm__ volatile("sti"); diff --git a/src/interrupt/interrupt.c b/src/interrupt/interrupt.c index 259a022..da933e7 100644 --- a/src/interrupt/interrupt.c +++ b/src/interrupt/interrupt.c @@ -1,6 +1,11 @@ #include "../lib-header/interrupt.h" +#include "../lib-header/idt.h" +#include "../lib-header/fat32.h" +#include "../lib-header/stdmem.h" -struct TSSEntry _interrupt_tss_entry; +struct TSSEntry _interrupt_tss_entry = { + .ss0 = GDT_KERNEL_DATA_SEGMENT_SELECTOR, +}; void io_wait(void) { out(0x80, 0); @@ -43,6 +48,38 @@ void pic_remap(void) { out(PIC2_DATA, a2); } +void activate_keyboard_interrupt(void) { + out(PIC1_DATA, PIC_DISABLE_ALL_MASK ^ (1 << IRQ_KEYBOARD)); + out(PIC2_DATA, PIC_DISABLE_ALL_MASK); +} + +void syscall(struct CPURegister cpu, __attribute__((unused)) struct InterruptStack info) { + if (cpu.eax == 0) { + struct FAT32DriverRequest request = *(struct FAT32DriverRequest*) cpu.ebx; + *((int8_t*) cpu.ecx) = read(request); + } else if (cpu.eax == 1) { + // TODO: buat read_directory + + } else if (cpu.eax == 2) { + // TODO: buat write + + } else if (cpu.eax == 3) { + // TODO: buat delete + + } else if (cpu.eax == 4) { + keyboard_state_activate(); + __asm__("sti"); // Due IRQ is disabled when main_interrupt_handler() called + while (is_keyboard_blocking()); + char buf[KEYBOARD_BUFFER_SIZE]; + get_keyboard_buffer(buf); + memcpy((char *) cpu.ebx, buf, cpu.ecx); + } else if (cpu.eax == 5) { + // TODO: buat puts + puts((char *) cpu.ebx, cpu.ecx, cpu.edx); // Modified puts() on kernel side + } + +} + void main_interrupt_handler ( __attribute__((unused)) struct CPURegister cpu, uint32_t int_number, @@ -55,13 +92,13 @@ void main_interrupt_handler ( case PAGE_FAULT: __asm__("hlt"); break; + case 0x30: + syscall(cpu, info); + break; }; } -void activate_keyboard_interrupt(void) { - out(PIC1_DATA, PIC_DISABLE_ALL_MASK ^ (1 << IRQ_KEYBOARD)); - out(PIC2_DATA, PIC_DISABLE_ALL_MASK); -} + void set_tss_kernel_current_stack(void) { uint32_t stack_ptr; diff --git a/src/kernel.c b/src/kernel.c index 5c0590b..3185ee4 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -20,6 +20,7 @@ void kernel_setup(void) { activate_keyboard_interrupt(); framebuffer_clear(); framebuffer_set_cursor(0, 0); + framebuffer_write_string("> "); initialize_filesystem_fat32(); gdt_install_tss(); set_tss_register(); diff --git a/src/user-shell.c b/src/user-shell.c index 3b5a968..45b5dff 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -1,7 +1,44 @@ #include "lib-header/stdtype.h" +#include "lib-header/fat32.h" + + +// int main(void) { +// __asm__ volatile("mov %0, %%eax" : /* */ : "r"(0xDEADBEEF)); +// __asm__ volatile("int $0x30"); +// // syscall_user(5, (uint32_t) "owo\n", 4, 0xF); +// // while (TRUE); +// return 0; +// } + +void syscall_user(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx) { + __asm__ volatile("mov %0, %%ebx" : /* */ : "r"(ebx)); + __asm__ volatile("mov %0, %%ecx" : /* */ : "r"(ecx)); + __asm__ volatile("mov %0, %%edx" : /* */ : "r"(edx)); + __asm__ volatile("mov %0, %%eax" : /* */ : "r"(eax)); + // Note : gcc usually use %eax as intermediate register, + // so it need to be the last one to mov + __asm__ volatile("int $0x30"); +} int main(void) { - __asm__ volatile("mov %0, %%eax" : /* */ : "r"(0xDEADBEEF)); - while (TRUE); + // struct ClusterBuffer cl = {0}; + // struct FAT32DriverRequest request = { + // .buf = &cl, + // .name = "ikanaide", + // .ext = "\0\0\0", + // .parent_cluster_number = ROOT_CLUSTER_NUMBER, + // .buffer_size = CLUSTER_SIZE, + // }; + // int32_t retcode; + // syscall_user(0, (uint32_t) &request, (uint32_t) &retcode, 0); + // if (retcode == 0) + // syscall_user(5, (uint32_t) "owo\n", 4, 0xF); + + char buf[16]; + while (TRUE) { + syscall_user(4, (uint32_t) buf, 16, 0); + // syscall_user(5, (uint32_t) buf, 16, 0xF); + } + return 0; } From 47b7168d1c99c5d79a3be37d3e40bcffb5018d96 Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Wed, 26 Apr 2023 23:58:24 +0700 Subject: [PATCH 38/52] fix: comment puts (not yet implemented) Co-authored-by: Nigel Sahl <13521043@std.stei.itb.ac.id> Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: Rava Maulana --- src/interrupt/interrupt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interrupt/interrupt.c b/src/interrupt/interrupt.c index da933e7..cda4ef2 100644 --- a/src/interrupt/interrupt.c +++ b/src/interrupt/interrupt.c @@ -75,7 +75,7 @@ void syscall(struct CPURegister cpu, __attribute__((unused)) struct InterruptSta memcpy((char *) cpu.ebx, buf, cpu.ecx); } else if (cpu.eax == 5) { // TODO: buat puts - puts((char *) cpu.ebx, cpu.ecx, cpu.edx); // Modified puts() on kernel side + // puts((char *) cpu.ebx, cpu.ecx, cpu.edx); // Modified puts() on kernel side } } From c8b9fc2b9ebe25a2876e033679529e6272d848a1 Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Thu, 27 Apr 2023 12:45:10 +0700 Subject: [PATCH 39/52] feat: implement several common syscall Co-authored-by: Rava Maulana Co-authored-by: Nigel Sahl <13521043@std.stei.itb.ac.id> Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> --- src/framebuffer/framebuffer.c | 4 +- src/interrupt/interrupt.c | 28 ++++++++++---- src/kernel.c | 2 +- src/keyboard/keyboard.c | 64 +++++++++++++++++--------------- src/lib-header/framebuffer.h | 2 +- src/lib-header/keyboard.h | 2 + src/user-shell.c | 70 ++++++++++++++++++++++++++--------- 7 files changed, 115 insertions(+), 57 deletions(-) diff --git a/src/framebuffer/framebuffer.c b/src/framebuffer/framebuffer.c index 252d16d..2cda79c 100644 --- a/src/framebuffer/framebuffer.c +++ b/src/framebuffer/framebuffer.c @@ -51,7 +51,7 @@ void framebuffer_clear(void) { } } -void framebuffer_write_string(char * str) { +void framebuffer_write_string(char * str, uint8_t text_color) { struct Cursor c = framebuffer_get_cursor(); int offset = c.row * MAX_COLS + c.col; int i = 0; @@ -62,7 +62,7 @@ void framebuffer_write_string(char * str) { if (str[i] == '\n') { offset = (offset / MAX_COLS + 1) * MAX_COLS; } else { - framebuffer_write(offset / MAX_COLS, offset % MAX_COLS, str[i], WHITE, BLACK); + framebuffer_write(offset / MAX_COLS, offset % MAX_COLS, str[i], text_color, BLACK); offset += 1; } i++; diff --git a/src/interrupt/interrupt.c b/src/interrupt/interrupt.c index cda4ef2..ef6d1ab 100644 --- a/src/interrupt/interrupt.c +++ b/src/interrupt/interrupt.c @@ -58,24 +58,38 @@ void syscall(struct CPURegister cpu, __attribute__((unused)) struct InterruptSta struct FAT32DriverRequest request = *(struct FAT32DriverRequest*) cpu.ebx; *((int8_t*) cpu.ecx) = read(request); } else if (cpu.eax == 1) { - // TODO: buat read_directory - + struct FAT32DriverRequest request = *(struct FAT32DriverRequest*) cpu.ebx; + *((int8_t*) cpu.ecx) = read_directory(request); } else if (cpu.eax == 2) { - // TODO: buat write - + struct FAT32DriverRequest request = *(struct FAT32DriverRequest*) cpu.ebx; + *((int8_t*) cpu.ecx) = write(request); } else if (cpu.eax == 3) { - // TODO: buat delete + // Delete file + struct FAT32DriverRequest request = *(struct FAT32DriverRequest*) cpu.ebx; + *((int8_t*) cpu.ecx) = delete(request); } else if (cpu.eax == 4) { + // Keyboard keyboard_state_activate(); __asm__("sti"); // Due IRQ is disabled when main_interrupt_handler() called while (is_keyboard_blocking()); char buf[KEYBOARD_BUFFER_SIZE]; get_keyboard_buffer(buf); memcpy((char *) cpu.ebx, buf, cpu.ecx); + // execute_cmd(buf); + reset_keyboard_buffer(); } else if (cpu.eax == 5) { - // TODO: buat puts - // puts((char *) cpu.ebx, cpu.ecx, cpu.edx); // Modified puts() on kernel side + // Write string to framebuffer + framebuffer_write_string((char*) cpu.ebx, (uint8_t) cpu.edx); + } else if (cpu.eax == 6){ + struct Cursor c = framebuffer_get_cursor(); + framebuffer_set_cursor(c.row + cpu.ebx, cpu.ecx); + } else if (cpu.eax == 7){ + clear_screen(); + } else if (cpu.eax == 8){ + + } else if (cpu.eax == 9){ + } } diff --git a/src/kernel.c b/src/kernel.c index 3185ee4..36334cd 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -20,7 +20,7 @@ void kernel_setup(void) { activate_keyboard_interrupt(); framebuffer_clear(); framebuffer_set_cursor(0, 0); - framebuffer_write_string("> "); + framebuffer_write_string("> ", 0x0F); initialize_filesystem_fat32(); gdt_install_tss(); set_tss_register(); diff --git a/src/keyboard/keyboard.c b/src/keyboard/keyboard.c index 4e8ebff..b442cd6 100644 --- a/src/keyboard/keyboard.c +++ b/src/keyboard/keyboard.c @@ -93,9 +93,10 @@ void keyboard_isr(void) { keyboard_state.buffer_index--; keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = '\0'; } else if (mapped_char == '\n') { - execute_cmd(keyboard_state.keyboard_buffer); - memset(keyboard_state.keyboard_buffer, 0, 256); - keyboard_state.buffer_index = 0; + // execute_cmd(keyboard_state.keyboard_buffer); + // memset(keyboard_state.keyboard_buffer, 0, 256); + // keyboard_state.buffer_index = 0; + keyboard_state_deactivate(); } else if (mapped_char != '\b' && scancode != SCANCODE_LEFTARROW) { keyboard_state.keyboard_buffer[keyboard_state.buffer_index] = mapped_char; @@ -126,6 +127,11 @@ void keyboard_isr(void) { pic_ack(IRQ_KEYBOARD); } +void reset_keyboard_buffer(void) { + memset(keyboard_state.keyboard_buffer, 0, KEYBOARD_BUFFER_SIZE); + keyboard_state.buffer_index = 0; +} + // Get keyboard buffer values - @param buf Pointer to char buffer, recommended // size at least KEYBOARD_BUFFER_SIZE void get_keyboard_buffer(char *buf) { @@ -140,7 +146,7 @@ bool is_keyboard_blocking(void) { return keyboard_state.keyboard_input_on; } void clear_screen() { framebuffer_clear(); framebuffer_set_cursor(0, 0); - framebuffer_write_string("> "); + framebuffer_write_string("> ", WHITE); memset(keyboard_state.keyboard_buffer, 0, 256); } @@ -155,28 +161,28 @@ int strcmp(char *s1, char *s2) { return s1[i] - s2[i]; } -void execute_cmd(char *input) { - // execute command from input - if (strcmp(input, "clear") == 0) { - // clear screen - clear_screen(); - framebuffer_set_cursor(0, 0); - framebuffer_write_string("> "); - } else { - if (strcmp(input, "help") == 0) { - // list of available commands - framebuffer_write_string("\nAvailable commands:\n"); - framebuffer_write_string("clear - Clear the screen\n"); - framebuffer_write_string("help - List of available commands"); - } else if (strcmp(&input[3], "del") == 0) { - framebuffer_write_string("\nDeleting file: "); - framebuffer_write_string(input); - } else { - framebuffer_write_string("\nCommand not found: "); - framebuffer_write_string(input); - } - struct Cursor c = framebuffer_get_cursor(); - framebuffer_set_cursor(c.row + 1, 0); - framebuffer_write_string("> "); - } -} \ No newline at end of file +// void execute_cmd(char *input) { +// // execute command from input +// if (strcmp(input, "clear") == 0) { +// // clear screen +// clear_screen(); +// framebuffer_set_cursor(0, 0); +// framebuffer_write_string("> ", WHITE); +// } else { +// if (strcmp(input, "help") == 0) { +// // list of available commands +// framebuffer_write_string("\nAvailable commands:\n", WHITE); +// framebuffer_write_string("clear - Clear the screen\n", WHITE); +// framebuffer_write_string("help - List of available commands", WHITE); +// } else if (strcmp(&input[2], "rm") == 0) { +// framebuffer_write_string("\nDeleting file: ", WHITE); +// framebuffer_write_string(input, WHITE); +// } else { +// framebuffer_write_string("\nCommand not found: ", WHITE); +// framebuffer_write_string(input, WHITE); +// } +// struct Cursor c = framebuffer_get_cursor(); +// framebuffer_set_cursor(c.row + 1, 0); +// framebuffer_write_string("> ", WHITE); +// } +// } \ No newline at end of file diff --git a/src/lib-header/framebuffer.h b/src/lib-header/framebuffer.h index b298bb6..6adbfb7 100644 --- a/src/lib-header/framebuffer.h +++ b/src/lib-header/framebuffer.h @@ -68,7 +68,7 @@ void framebuffer_clear(void); * * @param char* str */ -void framebuffer_write_string(char *str); +void framebuffer_write_string(char *str, uint8_t text_color); /** * @brief scroll framebuffer by offset lines diff --git a/src/lib-header/keyboard.h b/src/lib-header/keyboard.h index 64632eb..19ede0c 100644 --- a/src/lib-header/keyboard.h +++ b/src/lib-header/keyboard.h @@ -67,6 +67,8 @@ void get_keyboard_buffer(char *buf); // Check whether keyboard ISR is active or not - @return Equal with keyboard_input_on value bool is_keyboard_blocking(void); +// Reset keyboard buffer to /0 char +void reset_keyboard_buffer(void); /* -- Keyboard Interrupt Service Routine -- */ diff --git a/src/user-shell.c b/src/user-shell.c index 45b5dff..eea9a7e 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -1,15 +1,6 @@ #include "lib-header/stdtype.h" #include "lib-header/fat32.h" - -// int main(void) { -// __asm__ volatile("mov %0, %%eax" : /* */ : "r"(0xDEADBEEF)); -// __asm__ volatile("int $0x30"); -// // syscall_user(5, (uint32_t) "owo\n", 4, 0xF); -// // while (TRUE); -// return 0; -// } - void syscall_user(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx) { __asm__ volatile("mov %0, %%ebx" : /* */ : "r"(ebx)); __asm__ volatile("mov %0, %%ecx" : /* */ : "r"(ecx)); @@ -20,25 +11,70 @@ void syscall_user(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx) { __asm__ volatile("int $0x30"); } +int strcmp(char *s1, char *s2) { + int i = 0; + while (s1[i] == s2[i]) { + if (s1[i] == '\0') { + return 0; + } + i++; + } + return s1[i] - s2[i]; +} + +void execute_cmd(char *input) { + // execute command from input + if (strcmp(input, "clear") == 0) { + // clear screen + // clear_screen(); + syscall_user(7, 0, 0, 0); + // framebuffer_set_cursor(0, 0); + syscall_user(6, 0, 0, 0); + syscall_user(5, (uint32_t) "> ", 256, 0xF); + } else { + if (strcmp(input, "help") == 0) { + // list of available commands + syscall_user(5, (uint32_t) "\nAvailable commands:\n", 256, 0xF); + syscall_user(5, (uint32_t) "clear - Clear the screen\n", 256, 0xF); + syscall_user(5, (uint32_t) "help - List of available commands", 256, 0xF); + } else if (strcmp(input, "rm") == 0) { + syscall_user(5, (uint32_t) "\nDeleting file: ", 256, 0xF); + syscall_user(5, (uint32_t) input, 256, 0xF); + } else { + syscall_user(5, (uint32_t) "\nCommand not found: ", 256, 0xF); + syscall_user(5, (uint32_t) input, 256, 0xF); + } + // framebuffer_set_cursor(c.row + 1, 0); + syscall_user(6, 1, 0, 0); + syscall_user(5, (uint32_t) "> ", 256, 0xF); + } +} + int main(void) { - // struct ClusterBuffer cl = {0}; + // struct ClusterBuffer cl[5]; // struct FAT32DriverRequest request = { // .buf = &cl, - // .name = "ikanaide", - // .ext = "\0\0\0", + // .name = "daijoubu", + // .ext = "owo", // .parent_cluster_number = ROOT_CLUSTER_NUMBER, - // .buffer_size = CLUSTER_SIZE, + // .buffer_size = 5*CLUSTER_SIZE, // }; // int32_t retcode; // syscall_user(0, (uint32_t) &request, (uint32_t) &retcode, 0); - // if (retcode == 0) + // syscall_user(5, retcode, 4, 0xF); + // if (retcode == 0){ // syscall_user(5, (uint32_t) "owo\n", 4, 0xF); + // } - char buf[16]; + char buf[256]; while (TRUE) { - syscall_user(4, (uint32_t) buf, 16, 0); - // syscall_user(5, (uint32_t) buf, 16, 0xF); + syscall_user(4, (uint32_t) buf, 256, 0); // apakah ini + execute_cmd(buf); + // syscall_user(5, (uint32_t) buf, 256, 0xF); + // framebuffer_write_string(buf, 0x0F); } + + return 0; } From c33573ea9a710dddd3f92320aa36352685bb4c3f Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Fri, 28 Apr 2023 00:04:46 +0700 Subject: [PATCH 40/52] feat: implement cd and ls (not finished) Co-authored-by: Rava Maulana Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: Nigel Sahl <13521043@std.stei.itb.ac.id> --- .vscode/launch.json | 34 +++ .vscode/settings.json | 4 +- makefile | 18 ++ src/kernel.c | 96 +++++---- src/user-shell.c | 473 ++++++++++++++++++++++++++++++++++++++---- 5 files changed, 546 insertions(+), 79 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index cd99e1d..80ae2ce 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -31,11 +31,16 @@ "text": "symbol-file kernel", "description": "Get kernel symbols" }, + { + "text": "add-symbol-file shell_elf", + "description": "Get shell symbols" + }, { "text": "set output-radix 16", "description": "Use hexadecimal output" } ], + "avoidWindowsConsoleRedirection": true }, { @@ -66,6 +71,35 @@ "description": "Use hexadecimal output" }, ] + }, + { + "name": "User Mode", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/user", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin", + "environment": [], + "preLaunchTask": "Build User", + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + }, + { + "text": "set output-radix 16", + "description": "Use hexadecimal output" + }, + ] } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index c398954..70c1ac4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,6 +13,8 @@ "*.tcc": "c", "gdt.h": "c", "cstdio": "c", - "cstring": "c" + "cstring": "c", + "typeindex": "c", + "typeinfo": "c" }, } \ No newline at end of file diff --git a/makefile b/makefile index 8c65ba6..b1e3657 100644 --- a/makefile +++ b/makefile @@ -38,6 +38,8 @@ clean: first : disk insert-shell +first-debug : disk insert-shell-debug + disk: @qemu-img create -f raw $(OUTPUT_FOLDER)/$(DISK_NAME).bin 4M @@ -94,6 +96,22 @@ user-shell: @size --target=binary bin/shell @rm -f *.o +user-shell-debug: + @$(ASM) $(AFLAGS) $(SOURCE_FOLDER)/user-entry.s -o user-entry.o + @$(CC) $(CFLAGS) -fno-pie $(SOURCE_FOLDER)/user-shell.c -o user-shell.o + @$(LIN) -T $(SOURCE_FOLDER)/user-linker.ld -melf_i386 \ + user-entry.o user-shell.o -o $(OUTPUT_FOLDER)/shell + @echo Linking object shell object files and generate flat binary... + @$(LIN) -T $(SOURCE_FOLDER)/user-linker.ld -melf_i386 --oformat=elf32-i386\ + user-entry.o user-shell.o -o $(OUTPUT_FOLDER)/shell_elf + @echo Linking object shell object files and generate ELF32 for debugging... + @size --target=binary bin/shell + @rm -f *.o + insert-shell: inserter user-shell @echo Inserting shell into root directory... @cd $(OUTPUT_FOLDER); ./inserter shell 2 $(DISK_NAME).bin + +insert-shell-debug: inserter user-shell-debug + @echo Inserting shell into root directory... + @cd $(OUTPUT_FOLDER); ./inserter shell 2 $(DISK_NAME).bin diff --git a/src/kernel.c b/src/kernel.c index 3d81c3c..c494745 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -20,7 +20,7 @@ void kernel_setup(void) { activate_keyboard_interrupt(); framebuffer_clear(); framebuffer_set_cursor(0, 0); - framebuffer_write_string("> ", 0x0F); + // framebuffer_write_string("> ", 0x0F); initialize_filesystem_fat32(); gdt_install_tss(); set_tss_register(); @@ -36,58 +36,82 @@ void kernel_setup(void) { struct FAT32DriverRequest request = { .buf = cbuf, .name = "ikanaide", - .ext = "uwu", - .parent_cluster_number = ROOT_CLUSTER_NUMBER, - .buffer_size = 0, - } ; - struct FAT32DriverRequest request_dir = { - .buf = cbuf, - .name = "nbuna1\0\0", .ext = "\0\0\0", .parent_cluster_number = ROOT_CLUSTER_NUMBER, .buffer_size = 0, } ; + // struct FAT32DriverRequest request_dir = { + // .buf = cbuf, + // .name = "nbuna1\0\0", + // .ext = "\0\0\0", + // .parent_cluster_number = ROOT_CLUSTER_NUMBER, + // .buffer_size = 0, + // } ; read(request); write(request); // Create folder "ikanaide" memcpy(request.name, "kano1\0\0\0", 8); write(request); // Create folder "kano1" - memcpy(request.name, "ikanaide", 8); - memcpy(request.ext, "\0\0\0", 3); - delete(request); // Delete first folder, thus creating hole in FS + // memcpy(request.name, "ikanaide", 8); + // memcpy(request.ext, "\0\0\0", 3); + // delete(request); // Delete first folder, thus creating hole in FS + + + + // request.buffer_size = 0; + // read(request); + // request.buffer_size = CLUSTER_SIZE; + // read(request); // Failed read due not enough buffer size + // request.buffer_size = 5*CLUSTER_SIZE; + // read(request); // Success read on file "daijoubu" + // memcpy(request.name, "test\0\0\0\0", 8); + // read(request); + // memcpy(request.name, "\0\0\0\0\0\0\0\0", 8); + // read(request); + // memcpy(request.name, "daijoubu", 8); + // delete(request); + + // write(request_dir); + // read_directory(request); + // read_directory(request_dir); + // memcpy(request_dir.name, "nbunan\0\0", 8); + // read_directory(request_dir); + // memcpy(request_dir.name, "\0\0\0\0\0\0\0\0", 8); + // read_directory(request_dir); + + // Write shell into memory + + + struct FAT32DriverRequest request_kano2 = { + .buf = cbuf, + .name = "kano2\0\0\0", + .ext = "\0\0\0", + .parent_cluster_number = 0x8, + .buffer_size = 0, + }; + + write(request_kano2); memcpy(request.name, "daijoubu", 8); - memcpy(request.ext, "owo", 3); + memcpy(request.ext, "uwu", 3); + request.parent_cluster_number = 0x9; request.buffer_size = 5*CLUSTER_SIZE; - write(request); // Create fragmented file "daijoubu" + write(request); // delete(request); - struct ClusterBuffer readcbuf; - read_clusters(&readcbuf, ROOT_CLUSTER_NUMBER+1, 1); - // If read properly, readcbuf should filled with 'a' + // struct ClusterBuffer readcbuf; + // read_clusters(&readcbuf, ROOT_CLUSTER_NUMBER+1, 1); - request.buffer_size = 0; - read(request); - request.buffer_size = CLUSTER_SIZE; - read(request); // Failed read due not enough buffer size - request.buffer_size = 5*CLUSTER_SIZE; - read(request); // Success read on file "daijoubu" - memcpy(request.name, "test\0\0\0\0", 8); - read(request); - memcpy(request.name, "\0\0\0\0\0\0\0\0", 8); - read(request); - memcpy(request.name, "daijoubu", 8); + // memcpy(request.name, "daijoubu", 8); + // memcpy(request.ext, "owo", 3); + // request.buffer_size = 5*CLUSTER_SIZE; + // write(request); // Create fragmented file "daijoubu" // delete(request); - write(request_dir); - read_directory(request); - read_directory(request_dir); - memcpy(request_dir.name, "nbunan\0\0", 8); - read_directory(request_dir); - memcpy(request_dir.name, "\0\0\0\0\0\0\0\0", 8); - read_directory(request_dir); + // struct ClusterBuffer readcbuf; + // read_clusters(&readcbuf, ROOT_CLUSTER_NUMBER+1, 1); + // If read properly, readcbuf should filled with 'a' - // Write shell into memory struct FAT32DriverRequest request_shell = { .buf = (uint8_t*) 0, .name = "shell", @@ -103,6 +127,6 @@ void kernel_setup(void) { // __asm__("int $0x4"); while (TRUE) { - keyboard_state_activate(); + // keyboard_state_activate(); } } diff --git a/src/user-shell.c b/src/user-shell.c index eea9a7e..754040a 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -1,6 +1,29 @@ #include "lib-header/stdtype.h" #include "lib-header/fat32.h" +#define BLACK 0x00 +#define DARK_BLUE 0x01 +#define DARK_GREEN 0x2 +#define DARK_AQUA 0x3 +#define DARK_RED 0x4 +#define DARK_PURPLE 0x5 +#define GOLD 0x6 +#define GRAY 0x7 +#define DARK_GRAY 0x8 +#define BLUE 0x09 +#define GREEN 0x0A +#define AQUA 0x0B +#define RED 0x0C +#define LIGHT_PURPLE 0x0D +#define YELLOW 0x0E +#define WHITE 0x0F +#define KEYBOARD_BUFFER_SIZE 256 + +// TODO: Change max stack +uint32_t DIR_NUMBER_STACK [256] = {2}; +char DIR_NAME_STACK [256][9] = {"ROOT\0\0\0\0\0"}; +uint8_t DIR_STACK_LENGTH = 1; + void syscall_user(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx) { __asm__ volatile("mov %0, %%ebx" : /* */ : "r"(ebx)); __asm__ volatile("mov %0, %%ecx" : /* */ : "r"(ecx)); @@ -22,59 +45,425 @@ int strcmp(char *s1, char *s2) { return s1[i] - s2[i]; } -void execute_cmd(char *input) { +void string_combine(char* s1, char* s2, char* res) { + int i = 0; + while (s1[i] != '\0') { + res[i] = s1[i]; + i++; + } + int j = 0; + while (s2[j] != '\0') { + res[i+j] = s2[j]; + j++; + } + res[i+j] = '\0'; +} + +void print_home() { + char home [256]; + char base [9] = "OSyikkk:\0"; + char path [2] = "\0"; + + for(int i = 0; i < DIR_STACK_LENGTH; i++) { + if(i == 0) { + string_combine(base, path, home); + } else { + string_combine(home, path, home); + string_combine(home, DIR_NAME_STACK[i], home); + } + } + + syscall_user(5, (uint32_t) home, KEYBOARD_BUFFER_SIZE, BLUE); + syscall_user(5, (uint32_t) "> ", KEYBOARD_BUFFER_SIZE, WHITE); +} + +void change_directory(char* new_dir) { + struct FAT32DirectoryTable req_table; + struct FAT32DriverRequest request = { + .buf = &req_table, + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + .ext = "\0\0\0", + .buffer_size = 0, + }; + + if(strcmp(new_dir, "..") == 0) { + if(DIR_STACK_LENGTH <= 1) { + return; + } else { + DIR_STACK_LENGTH--; + } + } else if (strcmp(new_dir, ".") == 0) { + return; + } else { + for(int i = 0; i < 8; i++) { + request.name[i] = new_dir[i]; + } + + int8_t retcode; + syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + + if(retcode != 0) { + syscall_user(5, (uint32_t) "INVALID DIRECTORY\n", 18, DARK_GREEN); + return; + } + + struct FAT32DirectoryTable parent_table; + struct FAT32DriverRequest parent_request = { + .buf = &parent_table, + .ext = "\0\0\0", + .buffer_size = 0, + }; + + for(int i = 0; i < 8; i++) { + parent_request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; + } + + if(DIR_STACK_LENGTH <= 1) { + parent_request.parent_cluster_number = ROOT_CLUSTER_NUMBER; + } else { + parent_request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; + } + + syscall_user(1, (uint32_t) &parent_request, (uint32_t) &retcode, 0); + if(retcode != 0) { + syscall_user(5, (uint32_t) "SHELL ERROR\n", 100, DARK_GREEN); + return; + } + + for(int j = 0; j < 8; j++) { + DIR_NAME_STACK[DIR_STACK_LENGTH][j] = request.name[j]; + } + DIR_NAME_STACK[DIR_STACK_LENGTH][8] = '\0'; + DIR_STACK_LENGTH++; + + for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + char curr_name [9]; + for(int j = 0; j < 8; j++) { + curr_name[j] = parent_table.table[i].name[j]; + } + curr_name[8] = '\0'; + + if(strcmp(curr_name, DIR_NAME_STACK[DIR_STACK_LENGTH - 1]) == 0) { + uint32_t curr_cluster = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; + + DIR_NUMBER_STACK[DIR_STACK_LENGTH-1] = curr_cluster; + } + } + } +} + +void list_current_directory() { + struct FAT32DirectoryTable current_table; + struct FAT32DriverRequest request = { + .buf = ¤t_table, + .ext = "\0\0\0", + .buffer_size = 0, + }; + + for(int i = 0; i < 8; i++) { + request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; + } + + if(DIR_STACK_LENGTH <= 1) { + request.parent_cluster_number = ROOT_CLUSTER_NUMBER; + } else { + request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; + } + + int8_t retcode; + syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + + if(retcode != 0) { + syscall_user(5, (uint32_t) "SHELL ERROR\n", 6, DARK_GREEN); + } + + for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + if(current_table.table[i].user_attribute == UATTR_NOT_EMPTY) { + char filename[9]; + for(int j = 0; j < 8; j++) { + filename[j] = current_table.table[i].name[j]; + } + filename[8] = '\0'; + + if(current_table.table[i].attribute == ATTR_ARCHIVE) { + syscall_user(5, (uint32_t) filename, 8, WHITE); + + if(current_table.table[i].ext[0] != '\0') { + char ext_name[4]; + for(int j = 0; j < 3; j++) { + ext_name[j] = current_table.table[i].ext[j]; + } + ext_name[3] = '\0'; + + syscall_user(5, (uint32_t) ".", 1, WHITE); + syscall_user(5, (uint32_t)ext_name, 3, WHITE); + } + } else { + syscall_user(5, (uint32_t) filename, 8, AQUA); + } + + syscall_user(5, (uint32_t) " ", 1, WHITE); + } + } +} + +void clear_screen(){ + syscall_user(7, 0, 0, 0); + syscall_user(6, 0, 0, 0); + print_home(); + // syscall_user(5, (uint32_t) "> ", 256, 0xF); +} + +void execute_cmd(char *input, char* home) { + // remove space in the first character + while (input[0] == ' ') { + for (int i = 0; i < 256; i++) { + input[i] = input[i+1]; + } + } + // array of string input + char cmd[40][10]; + + // initialize array with \0 + for (int i = 0; i < 40; i++) { + for (int j = 0; j < 10; j++) { + cmd[i][j] = '\0'; + } + } + int neff = 0; + int i = 0; + while (input[i] != '\0') { + int j = 0; + while (input[i] != ' ' && input[i] != '\0' && input[i] != '/') { + cmd[neff][j] = input[i]; + j++; + i++; + } + cmd[neff][j] = '\0'; + + neff++; + + while(input[i] == ' ') { + i++; + } + + if (input[i] == '/') { + i++; + } + } + // execute command from input - if (strcmp(input, "clear") == 0) { - // clear screen - // clear_screen(); - syscall_user(7, 0, 0, 0); - // framebuffer_set_cursor(0, 0); - syscall_user(6, 0, 0, 0); - syscall_user(5, (uint32_t) "> ", 256, 0xF); + if (strcmp(cmd[0], "clear") == 0) { + clear_screen(); } else { - if (strcmp(input, "help") == 0) { + syscall_user(5, (uint32_t) "\n", 1, WHITE); + + if (strcmp(cmd[0], "help") == 0) { // list of available commands - syscall_user(5, (uint32_t) "\nAvailable commands:\n", 256, 0xF); - syscall_user(5, (uint32_t) "clear - Clear the screen\n", 256, 0xF); - syscall_user(5, (uint32_t) "help - List of available commands", 256, 0xF); - } else if (strcmp(input, "rm") == 0) { - syscall_user(5, (uint32_t) "\nDeleting file: ", 256, 0xF); - syscall_user(5, (uint32_t) input, 256, 0xF); + syscall_user(5, (uint32_t) "Available commands:\n", KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) "clear - Clear the screen\n", KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) "help - List of available commands", KEYBOARD_BUFFER_SIZE, WHITE); + } else if(strcmp(cmd[0], "cd") == 0) { + // cd : Pindah ke folder yang dituju + if (cmd[1][0] == '\0') { + // cd + // change_directory(home); + DIR_STACK_LENGTH = 1; + } + int i = 1; + while (cmd[i][0] != '\0') { + if (cmd[i][0] == '.') { + if (cmd[i][1] == '.') { + // cd .. + change_directory(".."); + } else { + // cd . + change_directory("."); + } + } else { + // cd + change_directory(cmd[i]); + } + i++; + } + // change_directory(cmd[1]); + + } else if(strcmp(cmd[0], "ls") == 0 ) { + list_current_directory(); + } else if(strcmp(cmd[0], "mkdir") == 0) { + // mkdir : Membuat sebuah folder kosong baru + if (cmd[1][0]=='\0'){ + syscall_user(5, (uint32_t) "mkdir: missing operand\n", 31, WHITE); + } + // else { + // struct FAT32DirectoryTable current_table; + // struct FAT32DriverRequest request = { + // .buf = ¤t_table, + // .ext = "\0\0\0", + // .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + // .buffer_size = 0, + // }; + // for(int i = 0; i < 8; i++) { + // request.name[i] = cmd[1][i]; + // } + // int8_t retcode; + // syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + + // if(retcode != 0) { + // syscall_user(5, (uint32_t) "INVALID DIRECTORY", 18, DARK_GREEN); + // } else { + // for(int i = 0; i < 8; i++) { + // dir_name[i] = request.name[i]; + // } + // string_combine(home, request.name, home); + // } + + + } else if(strcmp(cmd[0], "cat") == 0) { + // cat : Menuliskan sebuah file sebagai text file ke layar (Gunakan format LF newline) + if (cmd[1][0]=='\0'){ + syscall_user(5, (uint32_t) "cat: missing operand\n", 29, WHITE); + } + else{ + struct FAT32DirectoryTable current_table; + struct FAT32DriverRequest request = { + .buf = ¤t_table, + .ext = "\0\0\0", + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + .buffer_size = 0, + }; + for (int i=0;i<8;i++){ + request.name[i]=cmd[1][i]; + } + int8_t retcode; + syscall_user(0, (uint32_t) &request, (uint32_t) &retcode, 0); + if (retcode!=0){ + char error[50]; + string_combine("cat: ", cmd[1], error); + string_combine(error, ": No such file or directory\n", error); + syscall_user(5, (uint32_t) error, 50, WHITE); + } + // else{ + // char fileIn[current_table.filesize]; + // for (int i=0;i0){ + // char buffer[CLUSTER_SIZE]; + // struct FAT32DriverRequest request2 = { + // .buf = buffer, + // .ext = "\0\0\0", + // .parent_cluster_number = *parent_cluster_number, + // .buffer_size = 0, + // }; + // for (int i=0;i<8;i++){ + // request2.name[i]=cmd[1][i]; + // } + // syscall_user(1, (uint32_t) &request2, (uint32_t) &retcode, 0); + // string_combine(fileIn, buffer, fileIn); + // // syscall_user(5, (uint32_t) buffer, CLUSTER_SIZE, WHITE); + // current_table.filesize-=CLUSTER_SIZE; + // } + // } + // while (fileIn[i]!='\0'){ + // if (fileIn[i]=='\n'){ + // syscall_user(5, (uint32_t) "\n", 1, WHITE); + // } + // else{ + // syscall_user(5, (uint32_t) &fileIn[i], 1, WHITE); + // } + // i++; + // } + } + + } else if(strcmp(cmd[0], "cp") == 0) { + // cp : Mengcopy suatu file (Folder menjadi bonus) + + // cek apakah ada argumen dan ada tepat 2 argumen + if (cmd[1][0]=='\0' || cmd[2][0]=='\0' || cmd[3][0]!='\0'){ + syscall_user(5, (uint32_t) "cp: No such file or directory\n", 31, WHITE); + } else { + // cek apakah file yang mau dicopy ada + // ?: parent cluster yang file + struct FAT32DirectoryTable current_table; + struct FAT32DriverRequest request = { + .buf = ¤t_table, + .ext = "\0\0\0", + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + .buffer_size = 0, + }; + + for (int i=0;i<8;i++){ + request.name[i]=cmd[1][i]; + } + for (int i=0;i<3;i++){ + request.ext[i]=cmd[1][i+9]; + } + + int8_t retcode; + syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + + if (retcode!=0){ + // file + syscall_user(5, (uint32_t) "cp: No such file or directory\n", 31, WHITE); + } else { + + } + } + + } else if(strcmp(cmd[0], "mv") == 0) { + // mv : Memindah dan merename lokasi file/folder + + } else if(strcmp(cmd[0], "whereis") == 0) { + // whereis - Mencari file/folder dengan nama yang sama diseluruh file system + + } else if(strcmp(cmd[0], "echo") == 0) { + // echo : Menuliskan string ke layar + syscall_user(5, (uint32_t) cmd[1], 1, WHITE); + + } else if (strcmp(cmd[0], "rm") == 0) { + // rm - Menghapus suatu file (Folder menjadi bonus) + if (cmd[0][1]=='\0'){ + syscall_user(5, (uint32_t) "rm: missing operand\n", 29, WHITE); + } + else{ + syscall_user(5, (uint32_t) "\nDeleting file: ", KEYBOARD_BUFFER_SIZE, WHITE); + // struct FAT32DirectoryTable current_table; + // struct FAT32DriverRequest request = { + // .buf = ¤t_table, + // .ext = "\0\0\0", + // .parent_cluster_number = *parent_cluster_number, + // .buffer_size = 0, + // }; + // int8_t retcode; + // syscall_user(3, (uint32_t) &request, (uint32_t) &retcode, 0); + + } } else { - syscall_user(5, (uint32_t) "\nCommand not found: ", 256, 0xF); - syscall_user(5, (uint32_t) input, 256, 0xF); + syscall_user(5, (uint32_t) "\nCommand not found: ", KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) input, KEYBOARD_BUFFER_SIZE, WHITE); } - // framebuffer_set_cursor(c.row + 1, 0); syscall_user(6, 1, 0, 0); - syscall_user(5, (uint32_t) "> ", 256, 0xF); + // syscall_user(5, (uint32_t) "> ", KEYBOARD_BUFFER_SIZE, WHITE); + print_home(home); } } -int main(void) { - // struct ClusterBuffer cl[5]; - // struct FAT32DriverRequest request = { - // .buf = &cl, - // .name = "daijoubu", - // .ext = "owo", - // .parent_cluster_number = ROOT_CLUSTER_NUMBER, - // .buffer_size = 5*CLUSTER_SIZE, - // }; - // int32_t retcode; - // syscall_user(0, (uint32_t) &request, (uint32_t) &retcode, 0); - // syscall_user(5, retcode, 4, 0xF); - // if (retcode == 0){ - // syscall_user(5, (uint32_t) "owo\n", 4, 0xF); - // } - - char buf[256]; +int main(void) { + char base [9] = "OSyikkk:\0"; + char path [256] = "/\0"; + // char curr_dir [8] = "ROOT\0\0\0\0"; + + char home [264]; + string_combine(base, path, home); + + print_home(home); + + char buf[KEYBOARD_BUFFER_SIZE]; while (TRUE) { - syscall_user(4, (uint32_t) buf, 256, 0); // apakah ini - execute_cmd(buf); - // syscall_user(5, (uint32_t) buf, 256, 0xF); - // framebuffer_write_string(buf, 0x0F); + syscall_user(4, (uint32_t) buf, KEYBOARD_BUFFER_SIZE, 0); + execute_cmd(buf, home); } - - return 0; } From dab930e5fc7e83eae6563b73810a3d5b61093e19 Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Fri, 28 Apr 2023 17:12:36 +0700 Subject: [PATCH 41/52] feat: update mkdir, ls, rm, cp Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: Rava Maulana Co-authored-by: Nigel Sahl <13521043@std.stei.itb.ac.id> --- src/filesystem/fat32.c | 4 + src/interrupt/interrupt.c | 10 +- src/kernel.c | 169 ++++++------- src/user-shell.c | 495 +++++++++++++++++++++++++++++--------- 4 files changed, 482 insertions(+), 196 deletions(-) diff --git a/src/filesystem/fat32.c b/src/filesystem/fat32.c index 77e7334..68055cc 100644 --- a/src/filesystem/fat32.c +++ b/src/filesystem/fat32.c @@ -293,6 +293,10 @@ int8_t write(struct FAT32DriverRequest request){ return error_code = 1; } + struct FAT32DirectoryTable new_table; + init_directory_table(&new_table, request.name, i-1); + write_clusters(&new_table, i-1, 1); + parent_table.table[idx_empty].cluster_high = (uint16_t) (((i-1) >> 16) & 0xFFFF); parent_table.table[idx_empty].cluster_low = (uint16_t) ((i-1) & 0xFFFF); } else { diff --git a/src/interrupt/interrupt.c b/src/interrupt/interrupt.c index ef6d1ab..6d6cd40 100644 --- a/src/interrupt/interrupt.c +++ b/src/interrupt/interrupt.c @@ -55,19 +55,21 @@ void activate_keyboard_interrupt(void) { void syscall(struct CPURegister cpu, __attribute__((unused)) struct InterruptStack info) { if (cpu.eax == 0) { + // Read file struct FAT32DriverRequest request = *(struct FAT32DriverRequest*) cpu.ebx; *((int8_t*) cpu.ecx) = read(request); } else if (cpu.eax == 1) { + // Read directory struct FAT32DriverRequest request = *(struct FAT32DriverRequest*) cpu.ebx; *((int8_t*) cpu.ecx) = read_directory(request); } else if (cpu.eax == 2) { + // Write file struct FAT32DriverRequest request = *(struct FAT32DriverRequest*) cpu.ebx; *((int8_t*) cpu.ecx) = write(request); } else if (cpu.eax == 3) { // Delete file struct FAT32DriverRequest request = *(struct FAT32DriverRequest*) cpu.ebx; *((int8_t*) cpu.ecx) = delete(request); - } else if (cpu.eax == 4) { // Keyboard keyboard_state_activate(); @@ -82,14 +84,16 @@ void syscall(struct CPURegister cpu, __attribute__((unused)) struct InterruptSta // Write string to framebuffer framebuffer_write_string((char*) cpu.ebx, (uint8_t) cpu.edx); } else if (cpu.eax == 6){ + // Set cursor position struct Cursor c = framebuffer_get_cursor(); framebuffer_set_cursor(c.row + cpu.ebx, cpu.ecx); } else if (cpu.eax == 7){ + // Clear screen clear_screen(); } else if (cpu.eax == 8){ - + // } else if (cpu.eax == 9){ - + // } } diff --git a/src/kernel.c b/src/kernel.c index c494745..8dbf3ea 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -28,89 +28,94 @@ void kernel_setup(void) { // Allocate first 4 MiB virtual memory allocate_single_user_page_frame((uint8_t*) 0); - struct ClusterBuffer cbuf[5]; - for (uint32_t i = 0; i < 5; i++) - for (uint32_t j = 0; j < CLUSTER_SIZE; j++) - cbuf[i].buf[j] = i + 'a'; - - struct FAT32DriverRequest request = { - .buf = cbuf, - .name = "ikanaide", - .ext = "\0\0\0", - .parent_cluster_number = ROOT_CLUSTER_NUMBER, - .buffer_size = 0, - } ; - // struct FAT32DriverRequest request_dir = { - // .buf = cbuf, - // .name = "nbuna1\0\0", - // .ext = "\0\0\0", - // .parent_cluster_number = ROOT_CLUSTER_NUMBER, - // .buffer_size = 0, - // } ; - - read(request); - write(request); // Create folder "ikanaide" - memcpy(request.name, "kano1\0\0\0", 8); - write(request); // Create folder "kano1" - // memcpy(request.name, "ikanaide", 8); - // memcpy(request.ext, "\0\0\0", 3); - // delete(request); // Delete first folder, thus creating hole in FS - - - - // request.buffer_size = 0; - // read(request); - // request.buffer_size = CLUSTER_SIZE; - // read(request); // Failed read due not enough buffer size - // request.buffer_size = 5*CLUSTER_SIZE; - // read(request); // Success read on file "daijoubu" - // memcpy(request.name, "test\0\0\0\0", 8); - // read(request); - // memcpy(request.name, "\0\0\0\0\0\0\0\0", 8); - // read(request); - // memcpy(request.name, "daijoubu", 8); - // delete(request); - - // write(request_dir); - // read_directory(request); - // read_directory(request_dir); - // memcpy(request_dir.name, "nbunan\0\0", 8); - // read_directory(request_dir); - // memcpy(request_dir.name, "\0\0\0\0\0\0\0\0", 8); - // read_directory(request_dir); - - // Write shell into memory - - - struct FAT32DriverRequest request_kano2 = { - .buf = cbuf, - .name = "kano2\0\0\0", - .ext = "\0\0\0", - .parent_cluster_number = 0x8, - .buffer_size = 0, - }; - - write(request_kano2); - - memcpy(request.name, "daijoubu", 8); - memcpy(request.ext, "uwu", 3); - request.parent_cluster_number = 0x9; - request.buffer_size = 5*CLUSTER_SIZE; - write(request); - // delete(request); - - // struct ClusterBuffer readcbuf; - // read_clusters(&readcbuf, ROOT_CLUSTER_NUMBER+1, 1); - - // memcpy(request.name, "daijoubu", 8); - // memcpy(request.ext, "owo", 3); - // request.buffer_size = 5*CLUSTER_SIZE; - // write(request); // Create fragmented file "daijoubu" - // delete(request); + uint8_t first = 1; + + if (first == 1) { + struct ClusterBuffer cbuf[5]; + for (uint32_t i = 0; i < 5; i++) + for (uint32_t j = 0; j < CLUSTER_SIZE; j++) + cbuf[i].buf[j] = i + 'a'; + + struct FAT32DriverRequest request = { + .buf = cbuf, + .name = "ikanaide", + .ext = "\0\0\0", + .parent_cluster_number = ROOT_CLUSTER_NUMBER, + .buffer_size = 0, + } ; + // struct FAT32DriverRequest request_dir = { + // .buf = cbuf, + // .name = "nbuna1\0\0", + // .ext = "\0\0\0", + // .parent_cluster_number = ROOT_CLUSTER_NUMBER, + // .buffer_size = 0, + // } ; + + read(request); + write(request); // Create folder "ikanaide" + memcpy(request.name, "kano1\0\0\0", 8); + write(request); // Create folder "kano1" + // memcpy(request.name, "ikanaide", 8); + // memcpy(request.ext, "\0\0\0", 3); + // delete(request); // Delete first folder, thus creating hole in FS + + + + // request.buffer_size = 0; + // read(request); + // request.buffer_size = CLUSTER_SIZE; + // read(request); // Failed read due not enough buffer size + // request.buffer_size = 5*CLUSTER_SIZE; + // read(request); // Success read on file "daijoubu" + // memcpy(request.name, "test\0\0\0\0", 8); + // read(request); + // memcpy(request.name, "\0\0\0\0\0\0\0\0", 8); + // read(request); + // memcpy(request.name, "daijoubu", 8); + // delete(request); + + // write(request_dir); + // read_directory(request); + // read_directory(request_dir); + // memcpy(request_dir.name, "nbunan\0\0", 8); + // read_directory(request_dir); + // memcpy(request_dir.name, "\0\0\0\0\0\0\0\0", 8); + // read_directory(request_dir); + + // Write shell into memory + + + struct FAT32DriverRequest request_kano2 = { + .buf = cbuf, + .name = "kano2\0\0\0", + .ext = "\0\0\0", + .parent_cluster_number = 0xA, + .buffer_size = 0, + }; + + write(request_kano2); + + memcpy(request.name, "daijoubu", 8); + memcpy(request.ext, "uwu", 3); + request.parent_cluster_number = 0xB; + request.buffer_size = 5*CLUSTER_SIZE; + write(request); + // delete(request); + + // struct ClusterBuffer readcbuf; + // read_clusters(&readcbuf, ROOT_CLUSTER_NUMBER+1, 1); + + // memcpy(request.name, "daijoubu", 8); + // memcpy(request.ext, "owo", 3); + // request.buffer_size = 5*CLUSTER_SIZE; + // write(request); // Create fragmented file "daijoubu" + // delete(request); + + // struct ClusterBuffer readcbuf; + // read_clusters(&readcbuf, ROOT_CLUSTER_NUMBER+1, 1); + // If read properly, readcbuf should filled with 'a' + } - // struct ClusterBuffer readcbuf; - // read_clusters(&readcbuf, ROOT_CLUSTER_NUMBER+1, 1); - // If read properly, readcbuf should filled with 'a' struct FAT32DriverRequest request_shell = { .buf = (uint8_t*) 0, diff --git a/src/user-shell.c b/src/user-shell.c index 754040a..9f1ccfd 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -105,50 +105,51 @@ void change_directory(char* new_dir) { if(retcode != 0) { syscall_user(5, (uint32_t) "INVALID DIRECTORY\n", 18, DARK_GREEN); return; - } - - struct FAT32DirectoryTable parent_table; - struct FAT32DriverRequest parent_request = { - .buf = &parent_table, - .ext = "\0\0\0", - .buffer_size = 0, - }; - - for(int i = 0; i < 8; i++) { - parent_request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; } - if(DIR_STACK_LENGTH <= 1) { - parent_request.parent_cluster_number = ROOT_CLUSTER_NUMBER; - } else { - parent_request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; - } - - syscall_user(1, (uint32_t) &parent_request, (uint32_t) &retcode, 0); - if(retcode != 0) { - syscall_user(5, (uint32_t) "SHELL ERROR\n", 100, DARK_GREEN); - return; - } + // struct FAT32DirectoryTable parent_table; + // struct FAT32DriverRequest parent_request = { + // .buf = &parent_table, + // .ext = "\0\0\0", + // .buffer_size = 0, + // }; + + // for(int i = 0; i < 8; i++) { + // parent_request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; + // } + + // if(DIR_STACK_LENGTH <= 1) { + // parent_request.parent_cluster_number = ROOT_CLUSTER_NUMBER; + // } else { + // parent_request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; + // } + + // syscall_user(1, (uint32_t) &parent_request, (uint32_t) &retcode, 0); + // if(retcode != 0) { + // syscall_user(5, (uint32_t) "SHELL ERROR\n", 100, DARK_GREEN); + // return; + // } for(int j = 0; j < 8; j++) { DIR_NAME_STACK[DIR_STACK_LENGTH][j] = request.name[j]; } DIR_NAME_STACK[DIR_STACK_LENGTH][8] = '\0'; + DIR_NUMBER_STACK[DIR_STACK_LENGTH] = req_table.table[0].cluster_high << 16 | req_table.table[0].cluster_low; DIR_STACK_LENGTH++; - for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - char curr_name [9]; - for(int j = 0; j < 8; j++) { - curr_name[j] = parent_table.table[i].name[j]; - } - curr_name[8] = '\0'; + // for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + // char curr_name [9]; + // for(int j = 0; j < 8; j++) { + // curr_name[j] = parent_table.table[i].name[j]; + // } + // curr_name[8] = '\0'; - if(strcmp(curr_name, DIR_NAME_STACK[DIR_STACK_LENGTH - 1]) == 0) { - uint32_t curr_cluster = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; + // if(strcmp(curr_name, DIR_NAME_STACK[DIR_STACK_LENGTH - 1]) == 0) { + // uint32_t curr_cluster = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; - DIR_NUMBER_STACK[DIR_STACK_LENGTH-1] = curr_cluster; - } - } + // DIR_NUMBER_STACK[DIR_STACK_LENGTH-1] = curr_cluster; + // } + // } } } @@ -177,7 +178,7 @@ void list_current_directory() { syscall_user(5, (uint32_t) "SHELL ERROR\n", 6, DARK_GREEN); } - for(uint32_t i = 0; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { if(current_table.table[i].user_attribute == UATTR_NOT_EMPTY) { char filename[9]; for(int j = 0; j < 8; j++) { @@ -207,6 +208,83 @@ void list_current_directory() { } } +// TODO: Not yet checked +void remove(char* target_filename, char* target_extension) { + struct FAT32DirectoryTable current_table; + struct FAT32DriverRequest request = { + .buf = ¤t_table, + .ext = "\0\0\0", + .buffer_size = 0, + }; + + for (int i = 0; i < 8; i++) { + request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; + } + + if(DIR_STACK_LENGTH <= 1) { + request.parent_cluster_number = ROOT_CLUSTER_NUMBER; + } else { + request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; + } + + int8_t retcode; + syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + + if (retcode != 0) { + syscall_user(5, (uint32_t) "SHELL ERROR\n", 15, DARK_GREEN); + return; + } + + + for (uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + if (current_table.table[i].user_attribute == UATTR_NOT_EMPTY) { + char filename[9]; + for (int j = 0; j < 8; j++) { + filename[j] = current_table.table[i].name[j]; + } + filename[8] = '\0'; + + if (current_table.table[i].attribute == ATTR_ARCHIVE) { + char ext_name[4] = "\0\0\0\0"; + if (current_table.table[i].ext[0] != '\0') { + for (int j = 0; j < 3; j++) { + ext_name[j] = current_table.table[i].ext[j]; + } + ext_name[3] = '\0'; + } + + if (strcmp(filename, target_filename) == 0 && strcmp(ext_name, target_extension) == 0) { + + struct FAT32DriverRequest request_delete = { + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + }; + + for (int j = 0; j < 8; j++) { + request_delete.name[j] = filename[j]; + } + + for (int j = 0; j < 3; j++) { + request_delete.ext[j] = ext_name[j]; + } + + int8_t retcode_delete; + syscall_user(3, (uint32_t) &request_delete, (uint32_t) &retcode_delete, 0); + return; + } + } + + + } + } + + +} + +// TODO: Implement where_is +void where_is() { + +} + void clear_screen(){ syscall_user(7, 0, 0, 0); syscall_user(6, 0, 0, 0); @@ -215,6 +293,7 @@ void clear_screen(){ } void execute_cmd(char *input, char* home) { + // TODO : untuk rm, cat, cp extensioonnya dipecah setelah cd cd an // remove space in the first character while (input[0] == ' ') { for (int i = 0; i < 256; i++) { @@ -222,12 +301,12 @@ void execute_cmd(char *input, char* home) { } } // array of string input - char cmd[40][10]; + char cmd[40][15]; // initialize array with \0 for (int i = 0; i < 40; i++) { - for (int j = 0; j < 10; j++) { - cmd[i][j] = '\0'; + for (int j = 0; j < 15; j++) { + cmd[i][j] = '\0'; } } int neff = 0; @@ -292,39 +371,117 @@ void execute_cmd(char *input, char* home) { list_current_directory(); } else if(strcmp(cmd[0], "mkdir") == 0) { // mkdir : Membuat sebuah folder kosong baru + int counterCD = 1; if (cmd[1][0]=='\0'){ syscall_user(5, (uint32_t) "mkdir: missing operand\n", 31, WHITE); } - // else { - // struct FAT32DirectoryTable current_table; - // struct FAT32DriverRequest request = { - // .buf = ¤t_table, - // .ext = "\0\0\0", - // .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], - // .buffer_size = 0, - // }; - // for(int i = 0; i < 8; i++) { - // request.name[i] = cmd[1][i]; - // } - // int8_t retcode; - // syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); - - // if(retcode != 0) { - // syscall_user(5, (uint32_t) "INVALID DIRECTORY", 18, DARK_GREEN); - // } else { - // for(int i = 0; i < 8; i++) { - // dir_name[i] = request.name[i]; - // } - // string_combine(home, request.name, home); - // } + else { + // Change to target directory + uint32_t DIR_NUMBER_STACK_TEMP [256] = {2}; + char DIR_NAME_STACK_TEMP [256][9] = {"ROOT\0\0\0\0\0"}; + uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; + for (int i = 0; i < DIR_STACK_LENGTH+1; i++){ + DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; + for (int j = 0; j < 9; j++){ + DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; + } + } + if (cmd[2][0] != '\0'){ + counterCD = 1; + while (cmd[counterCD+1][0] != '\0') { + if (cmd[counterCD][0] == '.') { + if (cmd[counterCD][1] == '.') { + // cd .. + change_directory(".."); + } else { + // cd . + change_directory("."); + } + } else { + // cd + change_directory(cmd[counterCD]); + } + counterCD++; + } + } + + // mkdir file in relative path + struct FAT32DirectoryTable current_table; + struct FAT32DriverRequest request = { + .buf = ¤t_table, + .name = "\0\0\0\0\0\0\0\0", + .ext = "\0\0\0", + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH-1], + .buffer_size = 0, + }; + // char temp[8]; + for(int i = 0; i < 8; i++) { + request.name[i] = cmd[counterCD][i]; + // temp[i] = cmd[counterCD][i]; + } + int8_t retcode; + // syscall_user(5, (uint32_t) temp, 8, WHITE); + syscall_user(2, (uint32_t) &request, (uint32_t) &retcode, 0); + if(retcode != 0) { + syscall_user(5, (uint32_t) "INVALID DIRECTORY", 18, DARK_GREEN); + } + + // change to previous stack + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++){ + DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; + for (int j = 0; j < 9; j++){ + DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; + } + } + for (int i = DIR_STACK_LENGTH_TEMP; i < (DIR_STACK_LENGTH_TEMP+counterCD); i++){ + DIR_NUMBER_STACK[i] = 0; + for (int j = 0; j < 9; j++){ + DIR_NAME_STACK[i][j] = '\0'; + } + } + DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; + } } else if(strcmp(cmd[0], "cat") == 0) { // cat : Menuliskan sebuah file sebagai text file ke layar (Gunakan format LF newline) + + int counterCD = 1; if (cmd[1][0]=='\0'){ syscall_user(5, (uint32_t) "cat: missing operand\n", 29, WHITE); } else{ + // Change to target directory + uint32_t DIR_NUMBER_STACK_TEMP [256] = {2}; + char DIR_NAME_STACK_TEMP [256][9] = {"ROOT\0\0\0\0\0"}; + uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; + for (int i = 0; i < DIR_STACK_LENGTH+1; i++){ + DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; + for (int j = 0; j < 9; j++){ + DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; + } + } + if (cmd[2][0] != '\0'){ + counterCD = 1; + while (cmd[counterCD+1][0] != '\0') { + if (cmd[counterCD][0] == '.') { + if (cmd[counterCD][1] == '.') { + // cd .. + change_directory(".."); + } else { + // cd . + change_directory("."); + } + } else { + // cd + change_directory(cmd[counterCD]); + } + counterCD++; + } + } + + + // cat file in relative path struct FAT32DirectoryTable current_table; struct FAT32DriverRequest request = { .buf = ¤t_table, @@ -332,56 +489,87 @@ void execute_cmd(char *input, char* home) { .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], .buffer_size = 0, }; + int8_t retcode; for (int i=0;i<8;i++){ - request.name[i]=cmd[1][i]; + request.name[i]=cmd[counterCD][i]; } - int8_t retcode; - syscall_user(0, (uint32_t) &request, (uint32_t) &retcode, 0); - if (retcode!=0){ - char error[50]; - string_combine("cat: ", cmd[1], error); - string_combine(error, ": No such file or directory\n", error); - syscall_user(5, (uint32_t) error, 50, WHITE); + syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + if (retcode != 0 ){ + + } + for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + char curr_name [9]; + for(int j = 0; j < 8; j++) { + curr_name[j] = current_table.table[i].name[j]; + } + curr_name[8] = '\0'; + + if(strcmp(curr_name, request.name) == 0) { + char extension [4]; + for(int j = 0; j < 3; j++) { + extension[j] = current_table.table[i].ext[j]; + } + extension[3] = '\0'; + + uint32_t size = current_table.table[i].filesize; + struct ClusterBuffer cl; + struct FAT32DriverRequest requestBuf = { + .buf = &cl, + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + .buffer_size = size, + }; + for (int j = 0; j < 8; j++) { + requestBuf.name[j] = cmd[counterCD][j]; + } + for (int j = 0; j < 3; j++) { + requestBuf.ext[j] = extension[j]; + } + syscall_user(0, (uint32_t) &requestBuf, (uint32_t) &retcode, 0); + if (retcode!=0){ + char error[50]; + string_combine("cat: ", cmd[1], error); + string_combine(error, ": No such file or directory\n", error); + syscall_user(5, (uint32_t) error, 50, WHITE); + } + else{ + // char fileIn[current_table.filesize]; + for (uint32_t j = 0; j < requestBuf.buffer_size/CLUSTER_SIZE; j++){ + // fileIn[i] = + + syscall_user(5, (uint32_t) ((uint8_t*) requestBuf.buf)[j], 1, WHITE); + } + } + + } + } + + // change to previous stack + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++){ + DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; + for (int j = 0; j < 9; j++){ + DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; + } } - // else{ - // char fileIn[current_table.filesize]; - // for (int i=0;i0){ - // char buffer[CLUSTER_SIZE]; - // struct FAT32DriverRequest request2 = { - // .buf = buffer, - // .ext = "\0\0\0", - // .parent_cluster_number = *parent_cluster_number, - // .buffer_size = 0, - // }; - // for (int i=0;i<8;i++){ - // request2.name[i]=cmd[1][i]; - // } - // syscall_user(1, (uint32_t) &request2, (uint32_t) &retcode, 0); - // string_combine(fileIn, buffer, fileIn); - // // syscall_user(5, (uint32_t) buffer, CLUSTER_SIZE, WHITE); - // current_table.filesize-=CLUSTER_SIZE; - // } - // } - // while (fileIn[i]!='\0'){ - // if (fileIn[i]=='\n'){ - // syscall_user(5, (uint32_t) "\n", 1, WHITE); - // } - // else{ - // syscall_user(5, (uint32_t) &fileIn[i], 1, WHITE); - // } - // i++; - // } + for (int i = DIR_STACK_LENGTH_TEMP; i < (DIR_STACK_LENGTH_TEMP+counterCD); i++){ + DIR_NUMBER_STACK[i] = 0; + for (int j = 0; j < 9; j++){ + DIR_NAME_STACK[i][j] = '\0'; + } + } + DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; } - + + + + + } else if(strcmp(cmd[0], "cp") == 0) { // cp : Mengcopy suatu file (Folder menjadi bonus) + // format : cp // cek apakah ada argumen dan ada tepat 2 argumen if (cmd[1][0]=='\0' || cmd[2][0]=='\0' || cmd[3][0]!='\0'){ - syscall_user(5, (uint32_t) "cp: No such file or directory\n", 31, WHITE); + syscall_user(5, (uint32_t) "cp: No such file or directory1\n", KEYBOARD_BUFFER_SIZE, WHITE); } else { // cek apakah file yang mau dicopy ada // ?: parent cluster yang file @@ -405,9 +593,49 @@ void execute_cmd(char *input, char* home) { if (retcode!=0){ // file - syscall_user(5, (uint32_t) "cp: No such file or directory\n", 31, WHITE); + syscall_user(5, (uint32_t) "cp: No such file or directory2\n", KEYBOARD_BUFFER_SIZE, WHITE); } else { - + // file founded + // check if directory does exist + struct FAT32DirectoryTable current_table2; + struct FAT32DriverRequest request2 = { + .buf = ¤t_table2, + .ext = "\0\0\0", + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + .buffer_size = 0, + }; + + for (int i=0;i<8;i++){ + request2.name[i]=cmd[2][i]; + } + for (int i=0;i<3;i++){ + request2.ext[i]=cmd[2][i+9]; + } + + int8_t retcode2; + syscall_user(1, (uint32_t) &request2, (uint32_t) &retcode2, 0); + + if (retcode2!=0) { + syscall_user(5, (uint32_t) "cp: No such file or directory3\n", KEYBOARD_BUFFER_SIZE, WHITE); + } else { + // folder founded + // copy all content of file to folder + // get file size + + // request.parent_cluster_number = cluster_number; + // request.buffer_size = + + syscall_user(2, (uint32_t) &request, (uint32_t) &retcode, 0); + + // check if error or filename is same + if (retcode!=0){ + syscall_user(5, (uint32_t) "cp: No such file or directory4\n", KEYBOARD_BUFFER_SIZE, WHITE); + } else { + ; + // success + // syscall_user(5, (uint32_t) "cp: Success\n", 13, WHITE); + } + } } } @@ -423,25 +651,70 @@ void execute_cmd(char *input, char* home) { } else if (strcmp(cmd[0], "rm") == 0) { // rm - Menghapus suatu file (Folder menjadi bonus) + int counterCD = 0; if (cmd[0][1]=='\0'){ syscall_user(5, (uint32_t) "rm: missing operand\n", 29, WHITE); } else{ syscall_user(5, (uint32_t) "\nDeleting file: ", KEYBOARD_BUFFER_SIZE, WHITE); - // struct FAT32DirectoryTable current_table; - // struct FAT32DriverRequest request = { - // .buf = ¤t_table, - // .ext = "\0\0\0", - // .parent_cluster_number = *parent_cluster_number, - // .buffer_size = 0, - // }; + + // Change to target directory + uint32_t DIR_NUMBER_STACK_TEMP [256] = {2}; + char DIR_NAME_STACK_TEMP [256][9] = {"ROOT\0\0\0\0\0"}; + uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; + for (int i = 0; i < DIR_STACK_LENGTH+1; i++){ + DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; + for (int j = 0; j < 9; j++){ + DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; + } + } + if (cmd[2][0] != '\0'){ + counterCD = 1; + while (cmd[counterCD+1][0] != '\0') { + if (cmd[counterCD][0] == '.') { + if (cmd[counterCD][1] == '.') { + // cd .. + change_directory(".."); + } else { + // cd . + change_directory("."); + } + } else { + // cd + change_directory(cmd[counterCD]); + } + counterCD++; + } + } + + + + + + // change to previous stack + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++){ + DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; + for (int j = 0; j < 9; j++){ + DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; + } + } + for (int i = DIR_STACK_LENGTH_TEMP; i < (DIR_STACK_LENGTH_TEMP+counterCD); i++){ + DIR_NUMBER_STACK[i] = 0; + for (int j = 0; j < 9; j++){ + DIR_NAME_STACK[i][j] = '\0'; + } + } + DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; + + + // int8_t retcode; // syscall_user(3, (uint32_t) &request, (uint32_t) &retcode, 0); } } else { - syscall_user(5, (uint32_t) "\nCommand not found: ", KEYBOARD_BUFFER_SIZE, WHITE); - syscall_user(5, (uint32_t) input, KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) "\nCommand not found: ", KEYBOARD_BUFFER_SIZE, DARK_RED); + syscall_user(5, (uint32_t) input, KEYBOARD_BUFFER_SIZE, DARK_RED); } syscall_user(6, 1, 0, 0); // syscall_user(5, (uint32_t) "> ", KEYBOARD_BUFFER_SIZE, WHITE); From bdb2976044590ecde79f8061210964b3256b8c57 Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Sat, 29 Apr 2023 03:26:56 +0700 Subject: [PATCH 42/52] feat: update cat, rm, whereis Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: Nigel Sahl <13521043@std.stei.itb.ac.id> Co-authored-by: Rava Maulana --- src/kernel.c | 4 +- src/user-shell.c | 266 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 197 insertions(+), 73 deletions(-) diff --git a/src/kernel.c b/src/kernel.c index 8dbf3ea..4c5831f 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -89,7 +89,7 @@ void kernel_setup(void) { .buf = cbuf, .name = "kano2\0\0\0", .ext = "\0\0\0", - .parent_cluster_number = 0xA, + .parent_cluster_number = 0xB, .buffer_size = 0, }; @@ -97,7 +97,7 @@ void kernel_setup(void) { memcpy(request.name, "daijoubu", 8); memcpy(request.ext, "uwu", 3); - request.parent_cluster_number = 0xB; + request.parent_cluster_number = 0xC; request.buffer_size = 5*CLUSTER_SIZE; write(request); // delete(request); diff --git a/src/user-shell.c b/src/user-shell.c index 9f1ccfd..f1279ac 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -62,7 +62,7 @@ void string_combine(char* s1, char* s2, char* res) { void print_home() { char home [256]; char base [9] = "OSyikkk:\0"; - char path [2] = "\0"; + char path [2] = "/\0"; for(int i = 0; i < DIR_STACK_LENGTH; i++) { if(i == 0) { @@ -107,49 +107,12 @@ void change_directory(char* new_dir) { return; } - // struct FAT32DirectoryTable parent_table; - // struct FAT32DriverRequest parent_request = { - // .buf = &parent_table, - // .ext = "\0\0\0", - // .buffer_size = 0, - // }; - - // for(int i = 0; i < 8; i++) { - // parent_request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; - // } - - // if(DIR_STACK_LENGTH <= 1) { - // parent_request.parent_cluster_number = ROOT_CLUSTER_NUMBER; - // } else { - // parent_request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; - // } - - // syscall_user(1, (uint32_t) &parent_request, (uint32_t) &retcode, 0); - // if(retcode != 0) { - // syscall_user(5, (uint32_t) "SHELL ERROR\n", 100, DARK_GREEN); - // return; - // } - for(int j = 0; j < 8; j++) { DIR_NAME_STACK[DIR_STACK_LENGTH][j] = request.name[j]; } DIR_NAME_STACK[DIR_STACK_LENGTH][8] = '\0'; DIR_NUMBER_STACK[DIR_STACK_LENGTH] = req_table.table[0].cluster_high << 16 | req_table.table[0].cluster_low; DIR_STACK_LENGTH++; - - // for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - // char curr_name [9]; - // for(int j = 0; j < 8; j++) { - // curr_name[j] = parent_table.table[i].name[j]; - // } - // curr_name[8] = '\0'; - - // if(strcmp(curr_name, DIR_NAME_STACK[DIR_STACK_LENGTH - 1]) == 0) { - // uint32_t curr_cluster = parent_table.table[i].cluster_high << 16 | parent_table.table[i].cluster_low; - - // DIR_NUMBER_STACK[DIR_STACK_LENGTH-1] = curr_cluster; - // } - // } } } @@ -281,9 +244,63 @@ void remove(char* target_filename, char* target_extension) { } // TODO: Implement where_is -void where_is() { +// void where_is(uint32_t* path_number_stack, char** path_name_stack, uint8_t stack_length) { +// struct FAT32DirectoryTable req_table; +// struct FAT32DriverRequest request = { +// .buf = &req_table, +// .ext = "\0\0\0", +// .buffer_size = 0, +// }; +// for(int i = 0; i < 8; i++) { +// request.name[i] = path_name_stack[stack_length - 1][i]; +// } -} +// if(stack_length <= 1) { +// request.parent_cluster_number = ROOT_CLUSTER_NUMBER; +// } else { +// request.parent_cluster_number = path_number_stack[stack_length - 2]; +// } + +// int8_t retcode; +// syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + +// if(retcode != 0) { +// syscall_user(5, (uint32_t) "SHELL ERROR\n", 6, DARK_GREEN); +// } + +// for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { +// if(current_table.table[i].user_attribute == UATTR_NOT_EMPTY) { +// char filename[9]; +// for(int j = 0; j < 8; j++) { +// filename[j] = current_table.table[i].name[j]; +// } +// filename[8] = '\0'; + +// if(current_table.table[i].attribute != ATTR_ARCHIVE) { +// if(strcmp(filename, path_name_stack[stack_length - 1]) == 0) { +// char path[256]; + +// for(int i = 0; i < DIR_STACK_LENGTH; i++) { +// if(i == 0) { +// string_combine(path, path, path); +// } else { +// string_combine(path, path, path); +// string_combine(path, DIR_NAME_STACK[i], path); +// } +// } +// syscall_user(5, (uint32_t) home, KEYBOARD_BUFFER_SIZE, WHITE); +// } + +// path_number_stack[stack_length] = req_table.table[i].cluster_high << 16 | req_table.table[i].cluster_low; +// for(int j = 0; j < 8; j++) { +// path_name_stack[stack_length][j] = filename[j]; +// } +// stack_length++; + +// } +// } +// } +// } void clear_screen(){ syscall_user(7, 0, 0, 0); @@ -292,17 +309,62 @@ void clear_screen(){ // syscall_user(5, (uint32_t) "> ", 256, 0xF); } +// nigel.exe +// target_name = nigel\0\0\0 +// target_ext = exe + +void parse_file_cmd(char* file, char* target_name, char* target_ext) { + for (int i = 0; i < 9; i++) { + target_name[i] = '\0'; + } + + for (int i = 0; i < 3; i++) { + target_ext[i] = '\0'; + } + uint8_t ext_found = 0; + int idx_target_name = 0; + int idx_target_ext = 0; + + for (int idx = 0; idx < 12; idx++) { + if (file[idx] == '.') { + ext_found = 1; + // idx++; + continue; + } + if (ext_found == 0) { + if (idx_target_name > 7) { + syscall_user(5, (uint32_t) "File/dir name too long (max 8)", KEYBOARD_BUFFER_SIZE, DARK_GREEN); + return; + } + target_name[idx_target_name] = file[idx]; + idx_target_name++; + } else { + if (idx_target_ext > 3) { + syscall_user(5, (uint32_t) "File extension too long (max 3)", KEYBOARD_BUFFER_SIZE, DARK_GREEN); + return; + } + target_ext[idx_target_ext] = file[idx]; + idx_target_ext++; + } + } +} + void execute_cmd(char *input, char* home) { // TODO : untuk rm, cat, cp extensioonnya dipecah setelah cd cd an // remove space in the first character while (input[0] == ' ') { for (int i = 0; i < 256; i++) { input[i] = input[i+1]; + } } + // array of string input char cmd[40][15]; + // + int cmd_length = 0; + // initialize array with \0 for (int i = 0; i < 40; i++) { for (int j = 0; j < 15; j++) { @@ -318,18 +380,38 @@ void execute_cmd(char *input, char* home) { j++; i++; } + cmd[neff][j] = '\0'; - neff++; - while(input[i] == ' ') { - i++; + if (input[i] == ' ' ) { + cmd[neff][0] = ' '; + neff++; + cmd_length++; + while (input[i] == ' ') { + i++; + } } if (input[i] == '/') { i++; } } + + // for (int i = 0; i < neff; i++) { + // syscall_user(5, (uint32_t) cmd[i], KEYBOARD_BUFFER_SIZE, WHITE); + // syscall_user(5, (uint32_t) "\n", KEYBOARD_BUFFER_SIZE, WHITE); + // } + // cp tep adflasd f /al/sdf/s -> ini dari root + + // syscall_user(5, (uint32_t) "\n", KEYBOARD_BUFFER_SIZE, WHITE); + // syscall_user(5, (uint32_t) target_name, KEYBOARD_BUFFER_SIZE, WHITE); + // syscall_user(5, (uint32_t) target_ext, KEYBOARD_BUFFER_SIZE, WHITE); + + char targetn[9]; + char targetext[3]; + char tergetFull[12] = "test\0\0\0\0.exe"; + parse_file_cmd(tergetFull, targetn, targetext); // execute command from input if (strcmp(cmd[0], "clear") == 0) { @@ -349,7 +431,8 @@ void execute_cmd(char *input, char* home) { // change_directory(home); DIR_STACK_LENGTH = 1; } - int i = 1; + + int i = 2; while (cmd[i][0] != '\0') { if (cmd[i][0] == '.') { if (cmd[i][1] == '.') { @@ -371,8 +454,8 @@ void execute_cmd(char *input, char* home) { list_current_directory(); } else if(strcmp(cmd[0], "mkdir") == 0) { // mkdir : Membuat sebuah folder kosong baru - int counterCD = 1; - if (cmd[1][0]=='\0'){ + int counterCD = 2; + if (cmd[2][0]=='\0'){ syscall_user(5, (uint32_t) "mkdir: missing operand\n", 31, WHITE); } else { @@ -386,8 +469,8 @@ void execute_cmd(char *input, char* home) { DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; } } - if (cmd[2][0] != '\0'){ - counterCD = 1; + if (cmd[3][0] != '\0'){ + counterCD = 2; while (cmd[counterCD+1][0] != '\0') { if (cmd[counterCD][0] == '.') { if (cmd[counterCD][1] == '.') { @@ -446,8 +529,8 @@ void execute_cmd(char *input, char* home) { } else if(strcmp(cmd[0], "cat") == 0) { // cat : Menuliskan sebuah file sebagai text file ke layar (Gunakan format LF newline) - int counterCD = 1; - if (cmd[1][0]=='\0'){ + int counterCD = 2; + if (cmd[2][0]=='\0'){ syscall_user(5, (uint32_t) "cat: missing operand\n", 29, WHITE); } else{ @@ -461,8 +544,8 @@ void execute_cmd(char *input, char* home) { DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; } } - if (cmd[2][0] != '\0'){ - counterCD = 1; + if (cmd[3][0] != '\0'){ + counterCD = 2; while (cmd[counterCD+1][0] != '\0') { if (cmd[counterCD][0] == '.') { if (cmd[counterCD][1] == '.') { @@ -480,19 +563,50 @@ void execute_cmd(char *input, char* home) { } } + // parse file name + char full_name[12]; + for (int i=0;i<12;i++){ + full_name[i] = cmd[counterCD][i]; + } + char file_name[9]; + char file_ext[3]; + parse_file_cmd(full_name, file_name, file_ext); + // if (cmd[counterCD][8] == '.'){ + // for (int i=0;i<3;i++){ + // file_ext[i] = cmd[counterCD][i+9]; + // } + // } + // else{ + // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, WHITE); + // } + // if (cmd[counterCD][12] != '\0' || cmd[counterCD][12] != ' '){ + // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, WHITE); + // } + + // parse_file_cmd(full_name, file_name, file_ext); + // cat file in relative path struct FAT32DirectoryTable current_table; struct FAT32DriverRequest request = { .buf = ¤t_table, .ext = "\0\0\0", - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], .buffer_size = 0, }; + + if(DIR_STACK_LENGTH <= 1) { + request.parent_cluster_number = ROOT_CLUSTER_NUMBER; + } else { + request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; + } + int8_t retcode; for (int i=0;i<8;i++){ - request.name[i]=cmd[counterCD][i]; + request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH-1][i]; } + // for (int i=0; i<3; i++){ + // request.ext[i] = file_ext[i]; + // } syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); if (retcode != 0 ){ @@ -504,15 +618,15 @@ void execute_cmd(char *input, char* home) { } curr_name[8] = '\0'; - if(strcmp(curr_name, request.name) == 0) { - char extension [4]; - for(int j = 0; j < 3; j++) { - extension[j] = current_table.table[i].ext[j]; - } - extension[3] = '\0'; + if(strcmp(curr_name, file_name) == 0) { + // char extension [4]; + // for(int j = 0; j < 3; j++) { + // extension[j] = current_table.table[i].ext[j]; + // } + // extension[3] = '\0'; uint32_t size = current_table.table[i].filesize; - struct ClusterBuffer cl; + struct ClusterBuffer cl[size/CLUSTER_SIZE]; struct FAT32DriverRequest requestBuf = { .buf = &cl, .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], @@ -522,7 +636,7 @@ void execute_cmd(char *input, char* home) { requestBuf.name[j] = cmd[counterCD][j]; } for (int j = 0; j < 3; j++) { - requestBuf.ext[j] = extension[j]; + requestBuf.ext[j] = file_ext[j]; } syscall_user(0, (uint32_t) &requestBuf, (uint32_t) &retcode, 0); if (retcode!=0){ @@ -532,14 +646,21 @@ void execute_cmd(char *input, char* home) { syscall_user(5, (uint32_t) error, 50, WHITE); } else{ + // char fileIn[current_table.filesize]; + // syscall_user(5, (uint32_t) "udah", 5, WHITE); for (uint32_t j = 0; j < requestBuf.buffer_size/CLUSTER_SIZE; j++){ // fileIn[i] = - - syscall_user(5, (uint32_t) ((uint8_t*) requestBuf.buf)[j], 1, WHITE); + char temp[CLUSTER_SIZE + 1]; + for (int k = 0; k < CLUSTER_SIZE; k++) { + temp[k] = ((char*) (((struct ClusterBuffer*) requestBuf.buf) + j))[k]; + } + temp[CLUSTER_SIZE] = '\0'; + syscall_user(5, (uint32_t) temp, 1, WHITE); + // syscall_user(5, (uint32_t) "masuk", 5, WHITE); } } - + break; } } @@ -559,10 +680,6 @@ void execute_cmd(char *input, char* home) { DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; } - - - - } else if(strcmp(cmd[0], "cp") == 0) { // cp : Mengcopy suatu file (Folder menjadi bonus) // format : cp @@ -598,6 +715,7 @@ void execute_cmd(char *input, char* home) { // file founded // check if directory does exist struct FAT32DirectoryTable current_table2; + struct FAT32DriverRequest request2 = { .buf = ¤t_table2, .ext = "\0\0\0", @@ -686,10 +804,16 @@ void execute_cmd(char *input, char* home) { counterCD++; } } - + char full_target[12]; + for (int i = 0; i < 12; i++) { + full_target[i] = cmd[counterCD][i]; + } - + char target_name[9]; + char target_ext[3]; + parse_file_cmd(full_target, target_name, target_ext); + remove(target_name, target_ext); // change to previous stack for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++){ From 5ab5e502a17699cb9fb758d76559f7a7dbb27029 Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Sat, 29 Apr 2023 17:07:20 +0700 Subject: [PATCH 43/52] feat: update rm, whereis Co-authored-by: Rava Maulana Co-authored-by: Nigel Sahl <13521043@std.stei.itb.ac.id> --- src/kernel.c | 4 +- src/user-shell.c | 267 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 194 insertions(+), 77 deletions(-) diff --git a/src/kernel.c b/src/kernel.c index 4c5831f..2f5e31a 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -89,7 +89,7 @@ void kernel_setup(void) { .buf = cbuf, .name = "kano2\0\0\0", .ext = "\0\0\0", - .parent_cluster_number = 0xB, + .parent_cluster_number = 0xC, .buffer_size = 0, }; @@ -97,7 +97,7 @@ void kernel_setup(void) { memcpy(request.name, "daijoubu", 8); memcpy(request.ext, "uwu", 3); - request.parent_cluster_number = 0xC; + request.parent_cluster_number = 0xD; request.buffer_size = 5*CLUSTER_SIZE; write(request); // delete(request); diff --git a/src/user-shell.c b/src/user-shell.c index f1279ac..5dfe5e5 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -234,73 +234,136 @@ void remove(char* target_filename, char* target_extension) { syscall_user(3, (uint32_t) &request_delete, (uint32_t) &retcode_delete, 0); return; } + } else { + + if (strcmp(filename, target_filename) == 0) { + + struct FAT32DriverRequest request_delete = { + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + }; + + for (int j = 0; j < 8; j++) { + request_delete.name[j] = filename[j]; + } + + int8_t retcode_delete; + syscall_user(3, (uint32_t) &request_delete, (uint32_t) &retcode_delete, 0); + } } } - } + } } -// TODO: Implement where_is -// void where_is(uint32_t* path_number_stack, char** path_name_stack, uint8_t stack_length) { -// struct FAT32DirectoryTable req_table; -// struct FAT32DriverRequest request = { -// .buf = &req_table, -// .ext = "\0\0\0", -// .buffer_size = 0, -// }; -// for(int i = 0; i < 8; i++) { -// request.name[i] = path_name_stack[stack_length - 1][i]; -// } +void where_is(char* name, char* ext, uint32_t* path_number_stack, char path_name_stack[][9], uint8_t stack_length) { + struct FAT32DirectoryTable req_table; + struct FAT32DriverRequest request = { + .buf = &req_table, + .ext = "\0\0\0", + .buffer_size = 0, + }; + for(int i = 0; i < 8; i++) { + request.name[i] = path_name_stack[stack_length - 1][i]; + } -// if(stack_length <= 1) { -// request.parent_cluster_number = ROOT_CLUSTER_NUMBER; -// } else { -// request.parent_cluster_number = path_number_stack[stack_length - 2]; -// } - -// int8_t retcode; -// syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); - -// if(retcode != 0) { -// syscall_user(5, (uint32_t) "SHELL ERROR\n", 6, DARK_GREEN); -// } - -// for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { -// if(current_table.table[i].user_attribute == UATTR_NOT_EMPTY) { -// char filename[9]; -// for(int j = 0; j < 8; j++) { -// filename[j] = current_table.table[i].name[j]; -// } -// filename[8] = '\0'; - -// if(current_table.table[i].attribute != ATTR_ARCHIVE) { -// if(strcmp(filename, path_name_stack[stack_length - 1]) == 0) { -// char path[256]; - -// for(int i = 0; i < DIR_STACK_LENGTH; i++) { -// if(i == 0) { -// string_combine(path, path, path); -// } else { -// string_combine(path, path, path); -// string_combine(path, DIR_NAME_STACK[i], path); -// } -// } -// syscall_user(5, (uint32_t) home, KEYBOARD_BUFFER_SIZE, WHITE); -// } + if(stack_length <= 1) { + request.parent_cluster_number = ROOT_CLUSTER_NUMBER; + } else { + request.parent_cluster_number = path_number_stack[stack_length - 2]; + } + + int8_t retcode; + syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + + if(retcode != 0) { + syscall_user(5, (uint32_t) "SHELL ERROR\n", 6, DARK_GREEN); + } + + for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + if(req_table.table[i].user_attribute == UATTR_NOT_EMPTY) { + char curr_name[9]; + for(int j = 0; j < 8; j++) { + curr_name[j] = req_table.table[i].name[j]; + } + curr_name[8] = '\0'; + + if(req_table.table[i].attribute != ATTR_ARCHIVE) { + // path_number_stack[stack_length] = req_table.table[i].cluster_high << 16 | req_table.table[i].cluster_low; + // for(int j = 0; j < 8; j++) { + // path_name_stack[stack_length][j] = curr_name[j]; + // } + stack_length++; + + if(strcmp(curr_name, name) == 0) { + char path[256] = "\0"; + + for(int j = 0; j < stack_length; j++) { + if(j == 0) { + string_combine(path, "/\0", path); + } else { + string_combine(path, "/\0", path); + string_combine(path, path_name_stack[j], path); + } + } + syscall_user(5, (uint32_t) path, KEYBOARD_BUFFER_SIZE, WHITE); + } + + uint32_t new_number_stack [stack_length + 1]; + char new_name_stack [stack_length + 1][9]; + for(int j = 0; j < stack_length - 1; j++) { + new_number_stack[j] = path_number_stack[j]; + for(int k = 0; k < 8; k++) { + new_name_stack[j][k] = path_name_stack[j][k]; + } + new_name_stack[j][8] = '\0'; + } -// path_number_stack[stack_length] = req_table.table[i].cluster_high << 16 | req_table.table[i].cluster_low; -// for(int j = 0; j < 8; j++) { -// path_name_stack[stack_length][j] = filename[j]; -// } -// stack_length++; + new_number_stack[stack_length-1] = req_table.table[i].cluster_high << 16 | req_table.table[i].cluster_low; + for(int j = 0; j < 8; j++) { + new_name_stack[stack_length][j] = curr_name[j]; + } + new_name_stack[stack_length-1][8] = '\0'; + + where_is(name, ext, new_number_stack, new_name_stack, stack_length); + } else { + char curr_ext[4]; + for(int j = 0; j < 3; j++) { + curr_ext[j] = req_table.table[i].ext[j]; + } + curr_ext[3] = '\0'; + + if(strcmp(curr_name, name) == 0 && strcmp(curr_ext, ext)) { + char path[256]; + + for(int j = 0; j < stack_length; j++) { + if(j == 0) { + string_combine(path, "\0", path); + } else { + string_combine(path, "\0", path); + string_combine(path, path_name_stack[j], path); + } + } + + char filename[9]; + for(int j = 0; j < 3; j++) { + filename[j] = curr_name[j]; + } + filename[8] = '\0'; + + string_combine(path, filename, path); + + string_combine(path, ".\0", path); + string_combine(path, ext, path); -// } -// } -// } -// } + syscall_user(5, (uint32_t) path, KEYBOARD_BUFFER_SIZE, WHITE); + } + } + } + } +} void clear_screen(){ syscall_user(7, 0, 0, 0); @@ -318,7 +381,7 @@ void parse_file_cmd(char* file, char* target_name, char* target_ext) { target_name[i] = '\0'; } - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 4; i++) { target_ext[i] = '\0'; } uint8_t ext_found = 0; @@ -358,20 +421,30 @@ void execute_cmd(char *input, char* home) { } } + // remove space in the last character + int k = KEYBOARD_BUFFER_SIZE - 1; + while (input[k] == '\0') { + k--; + } + while (input[k] == ' ') { + input[k] = '\0'; + k--; + } // array of string input char cmd[40][15]; - - // + + int cmd_length = 0; + int neff = 0; // initialize array with \0 for (int i = 0; i < 40; i++) { - for (int j = 0; j < 15; j++) { - cmd[i][j] = '\0'; + for (int j = 0; j < 15; j++) { + cmd[i][j] = '\0'; } - } - int neff = 0; + } + int i = 0; while (input[i] != '\0') { int j = 0; @@ -382,13 +455,13 @@ void execute_cmd(char *input, char* home) { } cmd[neff][j] = '\0'; - neff++; + neff++; if (input[i] == ' ' ) { cmd[neff][0] = ' '; neff++; - cmd_length++; - while (input[i] == ' ') { + cmd_length++; + while (input[i] == ' ') { i++; } } @@ -409,7 +482,7 @@ void execute_cmd(char *input, char* home) { // syscall_user(5, (uint32_t) target_ext, KEYBOARD_BUFFER_SIZE, WHITE); char targetn[9]; - char targetext[3]; + char targetext[4]; char tergetFull[12] = "test\0\0\0\0.exe"; parse_file_cmd(tergetFull, targetn, targetext); @@ -569,7 +642,7 @@ void execute_cmd(char *input, char* home) { full_name[i] = cmd[counterCD][i]; } char file_name[9]; - char file_ext[3]; + char file_ext[4]; parse_file_cmd(full_name, file_name, file_ext); // if (cmd[counterCD][8] == '.'){ // for (int i=0;i<3;i++){ @@ -762,19 +835,59 @@ void execute_cmd(char *input, char* home) { } else if(strcmp(cmd[0], "whereis") == 0) { // whereis - Mencari file/folder dengan nama yang sama diseluruh file system - + uint32_t path_number_stack [2] = {ROOT_CLUSTER_NUMBER}; + char path_name_stack [2][9] = {"ROOT\0\0\0\0"}; + uint8_t stack_length = 1; + + char target[15]; + uint8_t idx; + if(cmd[neff][0] == ' ' || cmd[neff][0] == '\0') { + idx = neff-1; + } else { + idx = neff; + } + + for(int i = 0; i < 15; i++) { + target[i] = cmd[idx][i]; + } + + uint8_t counter = 0; + while(target[counter] != '\0') { + counter++; + } + + if(counter <= 8) { + char dir_name[9]; + for(int i = 0; i < 8; i++) { + dir_name[i] = target[i]; + } + dir_name[8] = '\0'; + + where_is(dir_name, "\0", path_number_stack, path_name_stack, stack_length); + } else { + char full_target[12]; + for(int i = 0; i < 12; i++) { + full_target[i] = target[i]; + } + + char target_name[9]; + char target_ext[4]; + parse_file_cmd(full_target, target_name, target_ext); + + where_is(target_name, target_ext, path_number_stack, path_name_stack, stack_length); + } } else if(strcmp(cmd[0], "echo") == 0) { // echo : Menuliskan string ke layar syscall_user(5, (uint32_t) cmd[1], 1, WHITE); } else if (strcmp(cmd[0], "rm") == 0) { // rm - Menghapus suatu file (Folder menjadi bonus) - int counterCD = 0; - if (cmd[0][1]=='\0'){ + int counterCD = 2; + if (cmd[2][0]=='\0'){ syscall_user(5, (uint32_t) "rm: missing operand\n", 29, WHITE); } else{ - syscall_user(5, (uint32_t) "\nDeleting file: ", KEYBOARD_BUFFER_SIZE, WHITE); + // syscall_user(5, (uint32_t) "\nDeleting file: ", KEYBOARD_BUFFER_SIZE, WHITE); // Change to target directory uint32_t DIR_NUMBER_STACK_TEMP [256] = {2}; @@ -786,8 +899,8 @@ void execute_cmd(char *input, char* home) { DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; } } - if (cmd[2][0] != '\0'){ - counterCD = 1; + if (cmd[3][0] != '\0'){ + counterCD = 2; while (cmd[counterCD+1][0] != '\0') { if (cmd[counterCD][0] == '.') { if (cmd[counterCD][1] == '.') { @@ -811,8 +924,9 @@ void execute_cmd(char *input, char* home) { } char target_name[9]; - char target_ext[3]; + char target_ext[4]; parse_file_cmd(full_target, target_name, target_ext); + remove(target_name, target_ext); // change to previous stack @@ -857,6 +971,9 @@ int main(void) { print_home(home); char buf[KEYBOARD_BUFFER_SIZE]; + for (int i = 0; i < KEYBOARD_BUFFER_SIZE; i++) { + buf[i] = '\0'; + } while (TRUE) { syscall_user(4, (uint32_t) buf, KEYBOARD_BUFFER_SIZE, 0); execute_cmd(buf, home); From 8d09deb30aa3403f5ab6dcca635cf7027c423382 Mon Sep 17 00:00:00 2001 From: Rava Maulana Date: Sat, 29 Apr 2023 20:00:12 +0700 Subject: [PATCH 44/52] feat: whereis --- .vscode/launch.json | 9 +- .vscode/settings.json | 3 + .vscode/tasks.json | 1 - bin/.gitignore | 4 - bin/OSyikkk.iso | Bin 0 -> 542720 bytes bin/disk.o | Bin 0 -> 4896 bytes bin/fat32.o | Bin 0 -> 14236 bytes bin/framebuffer.o | Bin 0 -> 6672 bytes bin/gdt.o | Bin 0 -> 4384 bytes bin/idt.o | Bin 0 -> 4788 bytes bin/inserter | Bin 0 -> 32192 bytes bin/interrupt.o | Bin 0 -> 8852 bytes bin/intsetup.o | Bin 0 -> 5360 bytes bin/iso/boot/grub/grub1 | Bin 0 -> 105522 bytes bin/iso/boot/grub/menu.lst | 5 + bin/iso/boot/kernel | Bin 0 -> 66184 bytes bin/kernel | Bin 0 -> 66184 bytes bin/kernel.o | Bin 0 -> 6724 bytes bin/kernel_loader.o | Bin 0 -> 2400 bytes bin/keyboard.o | Bin 0 -> 8760 bytes bin/paging.o | Bin 0 -> 12936 bytes bin/portio.o | Bin 0 -> 3424 bytes bin/shell | Bin 0 -> 15324 bytes bin/shell_elf | Bin 0 -> 31888 bytes bin/stdmem.o | Bin 0 -> 4288 bytes bin/storage.bin | Bin 0 -> 4194304 bytes src/user-shell.c | 1013 ++++++++++++++++++------------------ 27 files changed, 525 insertions(+), 510 deletions(-) delete mode 100644 bin/.gitignore create mode 100644 bin/OSyikkk.iso create mode 100644 bin/disk.o create mode 100644 bin/fat32.o create mode 100644 bin/framebuffer.o create mode 100644 bin/gdt.o create mode 100644 bin/idt.o create mode 100755 bin/inserter create mode 100644 bin/interrupt.o create mode 100644 bin/intsetup.o create mode 100644 bin/iso/boot/grub/grub1 create mode 100644 bin/iso/boot/grub/menu.lst create mode 100755 bin/iso/boot/kernel create mode 100755 bin/kernel create mode 100644 bin/kernel.o create mode 100644 bin/kernel_loader.o create mode 100644 bin/keyboard.o create mode 100644 bin/paging.o create mode 100644 bin/portio.o create mode 100755 bin/shell create mode 100755 bin/shell_elf create mode 100644 bin/stdmem.o create mode 100644 bin/storage.bin diff --git a/.vscode/launch.json b/.vscode/launch.json index 80ae2ce..f61a7eb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -40,7 +40,6 @@ "description": "Use hexadecimal output" } ], - "avoidWindowsConsoleRedirection": true }, { @@ -48,7 +47,11 @@ "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/bin/inserter", - "args": ["shell", "2", "storage.bin"], + "args": [ + "shell", + "2", + "storage.bin" + ], "stopAtEntry": false, "cwd": "${workspaceFolder}/bin", "environment": [], @@ -100,6 +103,6 @@ "description": "Use hexadecimal output" }, ] - } + } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 70c1ac4..e67c1f6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,4 +17,7 @@ "typeindex": "c", "typeinfo": "c" }, + "githubPullRequests.ignoredPullRequestBranches": [ + "main" + ], } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b7f288c..69b773b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -61,6 +61,5 @@ "isDefault": true } } - ], } \ No newline at end of file diff --git a/bin/.gitignore b/bin/.gitignore deleted file mode 100644 index 6da7fa5..0000000 --- a/bin/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.o -*.iso -* -!.gitignore \ No newline at end of file diff --git a/bin/OSyikkk.iso b/bin/OSyikkk.iso new file mode 100644 index 0000000000000000000000000000000000000000..6eb646a96315994de78daa57f45696bfdafa0f67 GIT binary patch literal 542720 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 z$sS4y@eHFNZdmTGL&mm+E%$yL0Eu`TTbkixKh44Y5!|KR@FVdaiFkz0k0C2)U6=^Vnp0sa=;gx65KL_{U|-F(L3n0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr 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 literal 0 HcmV?d00001 diff --git a/bin/framebuffer.o b/bin/framebuffer.o new file mode 100644 index 0000000000000000000000000000000000000000..b24109fa667a5f6267f52ba8ac0f6c6bfc6d43be GIT binary patch literal 6672 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 literal 0 HcmV?d00001 diff --git a/bin/gdt.o b/bin/gdt.o new file mode 100644 index 0000000000000000000000000000000000000000..453fc32b821e1ebccdd006999194dcd5cc0d7a9e GIT binary patch literal 4384 zcmds4O>7)V6|U}{amJa<*p5SDZxTGt=Fdsy$3N?MLpHHvC$<-htP)$|M5kw_J=1um z$Lj9c#MWwAAXz259Gpl9306Wm!C@~eEe8-BAc_#D%>^VR4rl=ZE2P!3hyw)7_f=Qh zjRA=RC!SRGy}x=@^{V>S%oi>%T+uX5M5GBrY)e8+WJ1fBgp;CQ^htXP^3uKEeEARE zzk9m5xtZjcH#g{XZ|UiqOHY6JLEqhtB9-L3?{1t4`Q>{Xr@(Fe~W$tMB(#NNMwA!B#ad)q_l^$G_#KZdql^NDNXj>0_@_*pQ5F>!u=xI2k1y#d6~*&^5gmp_*0TUs~5nZCZC!R*>`|Av$50@MAYs>NIdsR@<>Gr!0pLY42Z4-ceMLO zbe+e@CJnlE%hbG)38kLTL8aaApoK@Sy|vo~JdkU^Vw4Vb8^>75H?bVEIIxRyCrnFw1&sZ$v#s2gCSiGjq z_tKUc6!X1BMC`;gsdGD~*)2y7YGG_0hn?=xj%9n(#=yaW9&K+&_W|4xwHB0&j0_T6 zd{!(~{Yb zlXLfzrRC&Y4Sg-CeN#vE)K)$FWi#-7QE63e)3e<{U*VTq*!VI=Hh%ERR6Yh_@UA%t%vWy{j+;plU zSX;GCW@alXPLq<{3@50`g_+f@m6_pIIM?tvCL=RC!dYV(k_nvs%u;T8_-t-?xG-8M z&uI)J$h?ZBWzu=5djOX&TTz+}kvuz~StvW76xsLoz?hcaM%9qs0$oHcum05hNpUVdiq@MZoXj?kFE5&lQwKCw* zz*_Jc;U@v!Ic$9|@N}0DgTPKac6o(o6|#_O&@-zV2J@$;hLm!gwwpg6LVSh@=OaWo zpCH1Ize$PA5%G$`Pb>Va!exa`g?L4!|C+)(3LhwZTjBQ<{!HQf3O`i%H-(+NE13Ti z3Xdo(Dm<@nPT>uO4TZNAZYX?9;kOljN8uxdzg74#5&2_oG+ez7BG!>1qV*}6cS}RZ z`?8fsmDOXxe)>%<-QP2x`J@T?*hhgoyxs2M22sN+OUd2}bTly6odt%Pa zG4DI?Ht#!sv#_xa;5bnMLSkKu|B9iFbPx?kEOSnI0q0W!G9HdX#$xt zQ4p{>%AnI|^xyJ}`OqGT@^0+#i`GiN=(iWw5?|jdSYNbX`qPi{3>ste&XQh-5XGPz z&0CG})V~442{ndtvJOV`eieK)2CR4=29J!Q$i}}A@pykiygBF?kFQA-?;&`OVZKR= z`2H^J$P4YVmH{Kb2dJ};I6x77*uJkY@>{|ool!o7$nUY@Bhwdom@%Rs>E~$Vqb1#j z-vSMaxDOKl@x}Lh9!ZPxw-2^xzwuZc_k{C8iT@Y!)ukWDF;;Y!!HOqPtdmg`t&iVi P+;0K%eh@hUBfozDLu_QY literal 0 HcmV?d00001 diff --git a/bin/idt.o b/bin/idt.o new file mode 100644 index 0000000000000000000000000000000000000000..f24a863ea33d4cafb8795b8d2385b5bab8f878f0 GIT binary patch literal 4788 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 B9RvUX literal 0 HcmV?d00001 diff --git a/bin/inserter b/bin/inserter new file mode 100755 index 0000000000000000000000000000000000000000..4e95e5b1f02e91a5a6ea7443b5c401299c9c4365 GIT binary patch literal 32192 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 HLXrJ%%VT?u literal 0 HcmV?d00001 diff --git a/bin/interrupt.o b/bin/interrupt.o new file mode 100644 index 0000000000000000000000000000000000000000..8ab8312dc25c02b3577be8f3da6595ba0c785530 GIT binary patch literal 8852 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 literal 0 HcmV?d00001 diff --git a/bin/intsetup.o b/bin/intsetup.o new file mode 100644 index 0000000000000000000000000000000000000000..85b9b5e573bbde2d6cbbacc2e67bb489877639b8 GIT binary patch literal 5360 zcmb_g3y4%z7(RE#)y>*%eWa$PtEQ>hjPu%jlxd}sRG6BTkJcR>b;otInOQCAv1mvS z$q1CP5)G-4il`8hFzJnjl|+S9XoPgURB9xl=Jx&f|Not99fP2=e)s#nbIv{I{&(*E z|BDY!x@ofKc}AOOqNd`EnIHq{fQOknbAe9%PfTkPI?GvmBF1!$R+gv}*##Wl|MMzi z>MPSa#(JTzQ>gza=JkQE0kCv1Mprrl2(MSC7l3|hIyN?>sh{ReB_U1cO@r+Pb7(z^W#(;aKNCk?-cOhwARH!?B0ohGS2=3cEvGWD6{M*&K`Bb}oxPb_R=! z?Nk}VF3*f@(z?NAc^?Ev-zY%dn|wuZ$(+lj>>d+JQE_h5UR#bwsA7-A2y z7;3*}ak<^cVwnAa#c=yJixKt>7FXCEEJoUGEUvUqv546XEaG+@i&6G|771Hsk+e%$ zq->T&+TO_`W2dobuv1uEWyiA^Z8I#!*pV#8+QBTw*}g2Uw%u402~620KWmJfhs(rwLov69vBWJfpNeMz|Fucpb=OC+zqS-)&lE+jldS* zMc_5yE#O_?6W~kWJK!+zEAR*4b-a3X2iVXX!Y!OsE4f zskk8CTqx&?#nu&NQl(tEbw#XXZkaga=K5qTk%^7U7_Y74LU=h7>?fB(`APQ+@7`tlBYi`2rIwEh&|Y<)$puy~~)z z<6rMP*9YAaF+m*de;AtHQEr|&2|-zn zn-^pq%5drjGM{?NM3e>9XW{3o#eS>c7lu4#C(4xEJhKg+vZnf-@RUK- zQ^s1Y7V8~^ZwvX~;VH{<^GsJfu#|Dt4}hoat3D0CT`ktT5uUO#H_zM!PZ?T$37)dG z`iJ2ubE|(2p0c?5*WnMS#rhw?9}4+z;VIK|^UN`L%KGX%BezloP~Q)ps)71Z@KhPp zPlBf^p?)@eeaJ6|r|O~O55ZFLbY2 zRFTy8hNtSJek43qD)kfLscNa81y9e2`lawx&D6KSQ)N@X5uU1?`W^68@zlQqPt{NT zm+({x)mwOa#?+sIrwXZ_N(NO+^~2z)a;m=uo~o$&>F`uh)h~vx`Pq1s$FLnASMT{If6N=u0~K zQjWf~qc7v=YjE^k<>(vj=o{ncOE?2@MUrrZl5oY6a0QcaMU!xalW@h8a0QfbMU-?! zlypUubVZbOMU-?!lypUubVZbOMU-?!lypUuaz&JKMU-+ylyXIsaz&JKMU-+ylyXIs zaz&JKMU-|$ly*gwc14tSMU-|$ly*gwc14tSMU-|$ly*gwaYdAIMU-(xlyOCraYdAI zMU-(xlyOCraYdAIMbzMmsKFIcgDav2L{us^mddT!Mtm%`beTDlA4-y_KJVH1ooD^;l9xFUv_&VXq!c&EB5uPqQOL(?0eZSCkW`!3C=YGr|#k*U){W@3Bh#Hs&u<-(Z^n literal 0 HcmV?d00001 diff --git a/bin/iso/boot/grub/grub1 b/bin/iso/boot/grub/grub1 new file mode 100644 index 0000000000000000000000000000000000000000..9e1617cee78bb8f1ab6838837c9a1f36d729c9fd GIT binary patch literal 105522 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 I8%}5UKb>zEqW}N^ literal 0 HcmV?d00001 diff --git a/bin/iso/boot/grub/menu.lst b/bin/iso/boot/grub/menu.lst new file mode 100644 index 0000000..7c06fd8 --- /dev/null +++ b/bin/iso/boot/grub/menu.lst @@ -0,0 +1,5 @@ +default 0 +timeout 0 + +title os +kernel /boot/kernel diff --git a/bin/iso/boot/kernel b/bin/iso/boot/kernel new file mode 100755 index 0000000000000000000000000000000000000000..749c36f1f7bc184d7b2cc2c95812b43ca98b1f01 GIT binary patch literal 66184 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 literal 0 HcmV?d00001 diff --git a/bin/kernel b/bin/kernel new file mode 100755 index 0000000000000000000000000000000000000000..749c36f1f7bc184d7b2cc2c95812b43ca98b1f01 GIT binary patch literal 66184 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 literal 0 HcmV?d00001 diff --git a/bin/kernel.o b/bin/kernel.o new file mode 100644 index 0000000000000000000000000000000000000000..547dc2b4e43655bd5b70ac89db24701a03e32727 GIT binary patch literal 6724 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 literal 0 HcmV?d00001 diff --git a/bin/kernel_loader.o b/bin/kernel_loader.o new file mode 100644 index 0000000000000000000000000000000000000000..e5c74d3d596132dc91a1024847517bd90c314655 GIT binary patch literal 2400 zcmb7FO>7%Q6n^WU5-7zEEeMGp*=~X&4(m`+I6xG;!D%Bq!HSbgK|+gv)~;2@YwfN} zb0W|as%kiKLWm<0LgEBIaIn+^kvO!+9DP8dDyLlAsOEd~W3QtMiLqwi_r3Scn>VvF z>(6tARl_hyH-o}7VMG}fct_X;I!7@r{a==I&Y*|Ys80n*C`J@f=oBn#LyEKRd4<_% z-7IiW*MTk@&~c30>*$O*FSh}7MG3_-uJCzSJ%(LzOq5`tnvPun*EMix5#$wE&J#uF z7}vXsuEzjUF*qm?yBfe+mt&xs&QSpmruYHm1NeAe-d8=Z2du{c(hz}H8K|aXUw~^Y zLMXnhMS<8I;NZL+=y-iR?`aid{ylmy-#%|fpmB}denS5*osRtq9bVPAalVIRHpwx7 zPoEVIckrUWK@vV6XVJNKuJr&tn2$H@Wjz^U8r}zf7G1Xl$N~uG;PIS~*D^#8>WU!j zXB}&_F7(UKV*8(bwD}j@)JM<6Qirj`@YX>*mKw$qgER2$$5Mkk(K(>e@Q?A{#f&9* z=y#$7JvbhXM*lov9%y|&xV^*Ppf~J2xLufw6u&x%9*>r(_+6hT1})xubo;}(NX&?1 zu3HBNW_rkJXQ8GyuhkgWDn(fsHC}9~ z9_zW)^mW^9+byf*lxwzYdbE)(ttT#+Z(lSQFOsnze2p?0o?^adB;|9a=L^n#j{Us( zXnZHMhPg3|--|~4RCs27<}8X0nKWx;)C!0`fhI(6vC??_!Wlfp#}Ks}^t75eCq+f6 z;bmhdolY<2lj)sgawnNi%_sAjrKQYgd3ev~mqG^8i2;+xdw{|E$gFrCnY-PV->f)} zPo`)4-Hz$oH-Y7|W>>lmtL&EB4I5h5Zk5dwK1n;>O1r$v8p)gOx-+Gh4mGw?aqSyg z+G@7#DXDZwY28KEvC^rc$ZMYO`sE7H-mbKYqq?ajFc-_1>_HF(_a{O}qhM_MpNQ|@ z^brY9-)OMOndLlTaU&-g!}Q#Qi1=yBNY6l;qtTWC2}w~;9Z26{0_X4Rq#cd+K=j?_ z_?Ifk>%!}&Zzp1)F#U)p-*VZ)^c%pkridQ{i#soT5qfDHli!xq3I4U@88&f@|5I}D zjAK9k2AWqs=1~-OCBUD0$kjy^Cy}g9xzTJlEae2Vtym8?EQ8*Uf6o0{*rZ(H3^@^Bx14VXK2jb&GihOg&-reK`8 zJ6hj1y9^Z$vA2))`Eccxf!k|F!`Fn@71&w6I~}f*bTUBRB7pHyB8YH85Rylr3&UzL zH0;&^jNtgOo2A)OQQ%u!Enf2t&lc3xExzGOPz6_v{N{-lgu5O8tc*Xp=laN%b2nTH zZtjuuNM5-z626f;cD*_~6J^+zt;Dmum%U*>OelSCAGKxoG*lHI5%>6_p92G%MDPWw4wxiAF*o`_=NHAjhs7+hx3Pn zYx%epw-;klY{}MRP%d-MA9_kOkw(NsL78V~{bK01Y5Gg?ZRHf@t=Kg=byEN6u_q$p z-NGy67INzq0+*mSvX7n2(c`m+Ss~w+Q=tBs83d$V1H zK$!c+#Opm(0+tshFjBB1(0ult%(Wx0d_H!MpGY*HVeU>vp~vrs@6u$vJ!^E&-O;e7 z;91flQ2eHluF8e)Dg{l&=vEkcm5VTHJ8!;Q&@kWIXGyuot6cVtRr%ijVZ)V6S}XbVA>A;KWRvNk#}1UTXr?FLo9OEwcwo_!?D3}##}0O<2A72z z*%B6@{+%rdW~o!~|AU@gw{G721y3wovbZ6F^mptH@7%qA&!%nLA3LyiZO5(+8#^D` zyrrwX{u%c8qx;rB^5DZ;SFKq~Lt7JIqwe|RqjTnuU%*nync^m~e-!K8eMD8$4egAO zGp4$WH)0|b&4q!#x(yoDReTvxGz4+=%*`ZGw6_4$+&q~wq36GWN>z#m;ZxKB&9X}B zeENsDxztMSPl07d`VgxUIhDoI1I$mP!iv8G_zEs9s{owd%4(aM8V>rLm<_azE6YxJjhxW_#BPmMbYn8 z6l)L~<>kMJ&XZ|oFIG`ST&Kabuovf8jN6-JFBMaMuG1!~qr;B!e9=dED*J_VeI127 zRYcGBN081C(u(DXIr4Szt%9Eg%F~^Vvyp)8gUzF(mGW#+1}d8I%F7jt;M5cX9fnMK z`D?hlSHP;Jl7`?qz12(exm9Mf+e=frwZ_VAd*xBs(7DHb5=46$)xvdpn>z{IR7zEJ zosLv882Av<{Sj_SpXIv`%Bn9w5Sj^!F6HH_UEr62lsr>7+vG=(GNzWd*SR0?tgI{B zp zcoB)xt5m9r)>R)BNmhCkVo{qA6JwZU^;8X3t4d`-x!6H1LYMqykJwkoOSvwmI_!i(S<~_WIiGel@u=}Muw`*p*QqC zF(}OnQK;EmXDRJM)*>7WI5p&8RpBX7g*s^*>_E+{#4+d<>g<2U7H1el0ew27)!_kyMs_RF|jO0CPMy-nInbo)n={fKUN==Mvx zk)z8?H$ES0FGNgTs_@Q>yvyVZ*YHgj`NJl!S;J>(Yq>*PA@BkJt3^Hj`^6|5O+EZ|1?jrF+%4xdxiK+{&}O3*dT% zPkWV@zQVmZ-a_J6cpZVs%X55jFE5Mpd77Ufs0|Y=*7g(BXy4=uP2P5zcfP{?m$;{o z`(NdYtq5(elC(xx%z2*seoDa>kS@+=^m30jo!`Hidt8gF`22lfO}>E)$rWFN0k7luGRdWNg&(g0U3@%rbvs@3sY zEytBCU(g56&lRLwhk}U8PH!TM{U2znA2L6g zH@J5@pY}elc%Lhcm;j2>uOI17M~$9H#*CUeGxTUrL_cimsUE#2GMMhsS=@*Y>R3+W zx)C{KBw-XA(4$5q6Ek!@#k$j27@am5-HgPtMkZ~r=ztj+GW1MF?_q<{p&^8fC87o! zO2#75SU<~mZiQ!=STq%*K4YdakoUBukyvynX+{Sk1G;JIMuufmnPhKD?+J*YnMB$! z19=%8TqdLyGtvX_O|kV>a)bIHQpJcV;mMhxt4t)8PW7;4#-SWY$NH10UM9z9>Vre; zDc=db=3|MHVxVU7Ak|i6nMGc05tD6^b>Cnl59-D)QT&W)#0G~7Rk8eg^n3*8c*H|+ z*visHnj>a963xVt$p}i$Fv{4_5hkBs5|8yTOUL6ha;spI>m(jAW`jZFq->j4Al>|! zk{N^bPEjytYNVGH$|REU!W?iuu0q7rP}Yp3Q>^phJ%RPj_XnDrL#?5vKya|N1vfLB z(pUHDDcwlMz($Q&Vs&yw+loN2HxP`+F$YnmR1dNRD~1$2M3n}YN(Z~M$pJH&vKSb~ z`h!D8+SFrank=H3BdJ)zNT<@-OfZ`|g!%-{X!n4gpu(3Zokc=i8lkt|8)}~-fQ%h?w zojH>1@9+P=x;wL)m|`f@IFRfPCh%tKM*cw}VS5lDGiHx@WJnJsoUaVHyh(ygFqa_1 zl+X<$J7j_$!Yh|dgJ7NO*S7~2@9EB_%&dHZ!RD-Bo1bZE2{kQmkYYBCHU>21shvzY zswuO*Zubf78nj{2MUBoytfO3e&h=m!-}MeGEZr)pn@UdysK;^Hhq}D~#y?j7$erVA zn>yZcq2v16bQ?Gi`_Vi8ODs)9NSy z-A8och)L(zydM3yc343uMm1_+AC&VpuA~T30eu_v)a>n?WL=YF!oYEpY9Vp6|GMW; z;TJ|E>=Dhi#T>IeBe6wGslU>DYgznP6>`g@{BRzh-4pkrao%ObI4Sis@%M% zJc2m;wax-g$%I{8o-62`b>Wqw{|ZX0@L2{d!6T-3n&bf~r_UOaw~>50eqoaG%|JIk zxv|;{y&>^glH&z>LGrIj{FcNs63~E#2r94KABR4`0S$J z6Nvw&d!a7fgel*xpKP?j10?B_f zE*FaRh~#5HHx}-vNsc;w7D#nHE%kp+`nimKQSyHYq&mL|Bs==Kj^h3Rmpj1NPk^Mq z2_*f00!e?DL}3yFu%I^xm)ZXm_o4?Blo*t_Q{pj+ewNWFQ>eZ>kyu&~;rP~h3(I!bu-&iaZOApd#0t=<^RT9D{P*8k!STXcO zL@eqo)N7=(Lm2x1wtWagyz==*&yq?<=Nyt_DG=9WyH$|ec35)qcD0~U{``q=18C=u zNu;AjdkO6?>u^!)!TO}(8B)KPej*;lUx15zNJi~hH_O=~1?m)+1g?K*ysohj`o7PTfAe$ZI^WZ1+i~XtsGI}0( zmGk|61+?u0l4TIjt`YIJ?lsWPct;WMNysQ3ohR&Zd>-OC)&07e literal 0 HcmV?d00001 diff --git a/bin/paging.o b/bin/paging.o new file mode 100644 index 0000000000000000000000000000000000000000..a2e827c42e75ad19bf231678378c093d6f434879 GIT binary patch literal 12936 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 literal 0 HcmV?d00001 diff --git a/bin/portio.o b/bin/portio.o new file mode 100644 index 0000000000000000000000000000000000000000..37c98e6a683cfdf8cfbe7b97565f3228709dce27 GIT binary patch literal 3424 zcmbtWU2GIp6uz^&({BH1%TG(uE~d4B?zG)DrGP*wE_$5#7N?c5kdlqi9D%^Pnu|mR>dbtY^dKibMJObOfc~z_nyD+ zoO{o`bI8qDK=<+2{;N}`G#OF?3oI4p`0C?dJu<_pzatk51 zB(Qwx(eO+hOOMV2gH`>N$N#|6Gog-4F>Fs4T8KsHn=#!*sg4uqoX!0w|s`!?r#;5%1DAqLL4 zS~Il{Uz-x)Ux{d|rhWipJ=NK3oEg^E;seJ*H(***?`wxdm1PU9v1&VvT!?$rW-Acj zMK#LB{HUfn!UAUwi|TyQ6JK_2E-Uri)5ZF?)y*Dyg> zD-lC2M_aXKA;L>tdz;IhRh~1f;+8tAmpWA(>RrP#VBy012s}yMJC$h+9lnj=cn^Qq z*OB2wdD{@$gCmeDVn<`=&Ot=D<)jJ@hBb`k;qL9ryZe$5~_oJhc zoxK|)y}d@C(G!VI_Qf!{Mcdq(F>TYa(omC5I=j^x7#xU1Gm+@{xMP|HH))SqHdQZc z^e|U|X6K`+qLp(k+gAWgPeiAjylbZ2JQ2ylw4Kg6c{^V$M2q%e%N~om$yClPFqsEW z=aPl23bOLH+&QbWY!u~~g<{UHw08idmP+!wYkwE>4=ZTj<{Je@^+%VMNjR0Ar>=w`qxXd z5X0=8h`O}O3r(jl% zrQ|mk{m0-}{i@`b`Io^ri$Pj^HNLK9z7Rzr@#lh)$%FF6DO*r@tw*WwSC(W7K_P!^ zq%|mNZ}X$?lu=MLh-#_xe44OcIie|0Av((NtcT5CwM^o-LRaTQeHgkr59*7z%;uR$ z92p!iGNzlDN+;ZG(Vj4pM?_-x$iCqnBZ+;xc0Cq9m^ip&cqE>{m+pV;{yS!+0c@5P zRo4g+lREni^(UnMwA5dg`fE~0$7DbDezPbzE*PVr%Q5v5&T`| z-IVr^y6&Y8zCm*S|De7}@@q>OOcLFJY({h$4&w;@ecDe6*G4JB?uelVv{k z9-a~Ny^g^co+Ig1%fJ&s;=zXC}g{hvr#xlRCA zPQ;KdV^EgYD_#NaBRV9hyZ#b-dA(Oq+&b7;FMFh_w>%fjq6dxV1~~t+GoH9*QdZv{ aR1v}D@1Y<`?T>G$9CrijexpF(3il6Vuj`5c literal 0 HcmV?d00001 diff --git a/bin/shell b/bin/shell new file mode 100755 index 0000000000000000000000000000000000000000..317411220824137759cc58abdd704763acda934d GIT binary patch literal 15324 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 C<0z>B literal 0 HcmV?d00001 diff --git a/bin/shell_elf b/bin/shell_elf new file mode 100755 index 0000000000000000000000000000000000000000..c5cf9397b725f99590f5b26d38a93a0f4f5b448b GIT binary patch literal 31888 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 literal 0 HcmV?d00001 diff --git a/bin/stdmem.o b/bin/stdmem.o new file mode 100644 index 0000000000000000000000000000000000000000..249b176f9e0f6b24895ae15d85b0f1961e6148f6 GIT binary patch literal 4288 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 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 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 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&UAn^a%p#u^C0000%|6jKw&YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA uz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*`+)$$U@Nx( literal 0 HcmV?d00001 diff --git a/src/user-shell.c b/src/user-shell.c index 5dfe5e5..fe389dc 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -1,5 +1,5 @@ -#include "lib-header/stdtype.h" #include "lib-header/fat32.h" +#include "lib-header/stdtype.h" #define BLACK 0x00 #define DARK_BLUE 0x01 @@ -20,21 +20,21 @@ #define KEYBOARD_BUFFER_SIZE 256 // TODO: Change max stack -uint32_t DIR_NUMBER_STACK [256] = {2}; -char DIR_NAME_STACK [256][9] = {"ROOT\0\0\0\0\0"}; +uint32_t DIR_NUMBER_STACK[256] = {2}; +char DIR_NAME_STACK[256][9] = {"ROOT\0\0\0\0\0"}; uint8_t DIR_STACK_LENGTH = 1; void syscall_user(uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx) { - __asm__ volatile("mov %0, %%ebx" : /* */ : "r"(ebx)); - __asm__ volatile("mov %0, %%ecx" : /* */ : "r"(ecx)); - __asm__ volatile("mov %0, %%edx" : /* */ : "r"(edx)); - __asm__ volatile("mov %0, %%eax" : /* */ : "r"(eax)); - // Note : gcc usually use %eax as intermediate register, - // so it need to be the last one to mov - __asm__ volatile("int $0x30"); + __asm__ volatile("mov %0, %%ebx" : /* */ : "r"(ebx)); + __asm__ volatile("mov %0, %%ecx" : /* */ : "r"(ecx)); + __asm__ volatile("mov %0, %%edx" : /* */ : "r"(edx)); + __asm__ volatile("mov %0, %%eax" : /* */ : "r"(eax)); + // Note : gcc usually use %eax as intermediate register, + // so it need to be the last one to mov + __asm__ volatile("int $0x30"); } -int strcmp(char *s1, char *s2) { +int strcmp(char* s1, char* s2) { int i = 0; while (s1[i] == s2[i]) { if (s1[i] == '\0') { @@ -46,26 +46,26 @@ int strcmp(char *s1, char *s2) { } void string_combine(char* s1, char* s2, char* res) { - int i = 0; - while (s1[i] != '\0') { - res[i] = s1[i]; - i++; - } - int j = 0; - while (s2[j] != '\0') { - res[i+j] = s2[j]; - j++; - } - res[i+j] = '\0'; + int i = 0; + while (s1[i] != '\0') { + res[i] = s1[i]; + i++; + } + int j = 0; + while (s2[j] != '\0') { + res[i + j] = s2[j]; + j++; + } + res[i + j] = '\0'; } void print_home() { - char home [256]; - char base [9] = "OSyikkk:\0"; - char path [2] = "/\0"; + char home[256]; + char base[9] = "OSyikkk:\0"; + char path[2] = "/\0"; - for(int i = 0; i < DIR_STACK_LENGTH; i++) { - if(i == 0) { + for (int i = 0; i < DIR_STACK_LENGTH; i++) { + if (i == 0) { string_combine(base, path, home); } else { string_combine(home, path, home); @@ -73,45 +73,46 @@ void print_home() { } } - syscall_user(5, (uint32_t) home, KEYBOARD_BUFFER_SIZE, BLUE); - syscall_user(5, (uint32_t) "> ", KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t)home, KEYBOARD_BUFFER_SIZE, BLUE); + syscall_user(5, (uint32_t) "> ", KEYBOARD_BUFFER_SIZE, WHITE); } void change_directory(char* new_dir) { struct FAT32DirectoryTable req_table; struct FAT32DriverRequest request = { - .buf = &req_table, + .buf = &req_table, .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], - .ext = "\0\0\0", - .buffer_size = 0, + .ext = "\0\0\0", + .buffer_size = 0, }; - if(strcmp(new_dir, "..") == 0) { - if(DIR_STACK_LENGTH <= 1) { + if (strcmp(new_dir, "..") == 0) { + if (DIR_STACK_LENGTH <= 1) { return; } else { DIR_STACK_LENGTH--; } } else if (strcmp(new_dir, ".") == 0) { return; - } else { - for(int i = 0; i < 8; i++) { + } else { + for (int i = 0; i < 8; i++) { request.name[i] = new_dir[i]; } int8_t retcode; - syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); - - if(retcode != 0) { + syscall_user(1, (uint32_t)&request, (uint32_t)&retcode, 0); + + if (retcode != 0) { syscall_user(5, (uint32_t) "INVALID DIRECTORY\n", 18, DARK_GREEN); return; } - for(int j = 0; j < 8; j++) { + for (int j = 0; j < 8; j++) { DIR_NAME_STACK[DIR_STACK_LENGTH][j] = request.name[j]; } DIR_NAME_STACK[DIR_STACK_LENGTH][8] = '\0'; - DIR_NUMBER_STACK[DIR_STACK_LENGTH] = req_table.table[0].cluster_high << 16 | req_table.table[0].cluster_low; + DIR_NUMBER_STACK[DIR_STACK_LENGTH] = + req_table.table[0].cluster_high << 16 | req_table.table[0].cluster_low; DIR_STACK_LENGTH++; } } @@ -119,42 +120,43 @@ void change_directory(char* new_dir) { void list_current_directory() { struct FAT32DirectoryTable current_table; struct FAT32DriverRequest request = { - .buf = ¤t_table, - .ext = "\0\0\0", - .buffer_size = 0, + .buf = ¤t_table, + .ext = "\0\0\0", + .buffer_size = 0, }; - for(int i = 0; i < 8; i++) { + for (int i = 0; i < 8; i++) { request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; } - if(DIR_STACK_LENGTH <= 1) { + if (DIR_STACK_LENGTH <= 1) { request.parent_cluster_number = ROOT_CLUSTER_NUMBER; } else { request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; } - + int8_t retcode; - syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + syscall_user(1, (uint32_t)&request, (uint32_t)&retcode, 0); - if(retcode != 0) { + if (retcode != 0) { syscall_user(5, (uint32_t) "SHELL ERROR\n", 6, DARK_GREEN); } - for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - if(current_table.table[i].user_attribute == UATTR_NOT_EMPTY) { + for (uint32_t i = 1; i < CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry); + i++) { + if (current_table.table[i].user_attribute == UATTR_NOT_EMPTY) { char filename[9]; - for(int j = 0; j < 8; j++) { + for (int j = 0; j < 8; j++) { filename[j] = current_table.table[i].name[j]; } filename[8] = '\0'; - if(current_table.table[i].attribute == ATTR_ARCHIVE) { - syscall_user(5, (uint32_t) filename, 8, WHITE); + if (current_table.table[i].attribute == ATTR_ARCHIVE) { + syscall_user(5, (uint32_t)filename, 8, WHITE); - if(current_table.table[i].ext[0] != '\0') { + if (current_table.table[i].ext[0] != '\0') { char ext_name[4]; - for(int j = 0; j < 3; j++) { + for (int j = 0; j < 3; j++) { ext_name[j] = current_table.table[i].ext[j]; } ext_name[3] = '\0'; @@ -163,7 +165,7 @@ void list_current_directory() { syscall_user(5, (uint32_t)ext_name, 3, WHITE); } } else { - syscall_user(5, (uint32_t) filename, 8, AQUA); + syscall_user(5, (uint32_t)filename, 8, AQUA); } syscall_user(5, (uint32_t) " ", 1, WHITE); @@ -175,31 +177,31 @@ void list_current_directory() { void remove(char* target_filename, char* target_extension) { struct FAT32DirectoryTable current_table; struct FAT32DriverRequest request = { - .buf = ¤t_table, - .ext = "\0\0\0", - .buffer_size = 0, + .buf = ¤t_table, + .ext = "\0\0\0", + .buffer_size = 0, }; for (int i = 0; i < 8; i++) { request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; } - if(DIR_STACK_LENGTH <= 1) { + if (DIR_STACK_LENGTH <= 1) { request.parent_cluster_number = ROOT_CLUSTER_NUMBER; } else { request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; } - + int8_t retcode; - syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + syscall_user(1, (uint32_t)&request, (uint32_t)&retcode, 0); if (retcode != 0) { syscall_user(5, (uint32_t) "SHELL ERROR\n", 15, DARK_GREEN); return; } - - for (uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + for (uint32_t i = 1; i < CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry); + i++) { if (current_table.table[i].user_attribute == UATTR_NOT_EMPTY) { char filename[9]; for (int j = 0; j < 8; j++) { @@ -216,12 +218,12 @@ void remove(char* target_filename, char* target_extension) { ext_name[3] = '\0'; } - if (strcmp(filename, target_filename) == 0 && strcmp(ext_name, target_extension) == 0) { - + if (strcmp(filename, target_filename) == 0 && + strcmp(ext_name, target_extension) == 0) { struct FAT32DriverRequest request_delete = { - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], }; - + for (int j = 0; j < 8; j++) { request_delete.name[j] = filename[j]; } @@ -229,126 +231,123 @@ void remove(char* target_filename, char* target_extension) { for (int j = 0; j < 3; j++) { request_delete.ext[j] = ext_name[j]; } - + int8_t retcode_delete; - syscall_user(3, (uint32_t) &request_delete, (uint32_t) &retcode_delete, 0); + syscall_user(3, (uint32_t)&request_delete, (uint32_t)&retcode_delete, + 0); return; } } else { - if (strcmp(filename, target_filename) == 0) { - struct FAT32DriverRequest request_delete = { - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], }; - + for (int j = 0; j < 8; j++) { request_delete.name[j] = filename[j]; } - + int8_t retcode_delete; - syscall_user(3, (uint32_t) &request_delete, (uint32_t) &retcode_delete, 0); + syscall_user(3, (uint32_t)&request_delete, (uint32_t)&retcode_delete, + 0); } } - - } - } - - + } } -void where_is(char* name, char* ext, uint32_t* path_number_stack, char path_name_stack[][9], uint8_t stack_length) { +void where_is(char* name, char* ext, uint32_t* path_number_stack, + char path_name_stack[][9], uint8_t stack_length) { struct FAT32DirectoryTable req_table; struct FAT32DriverRequest request = { - .buf = &req_table, - .ext = "\0\0\0", - .buffer_size = 0, + .buf = &req_table, + .ext = "\0\0\0", + .buffer_size = 0, }; - for(int i = 0; i < 8; i++) { + for (int i = 0; i < 8; i++) { request.name[i] = path_name_stack[stack_length - 1][i]; } - - if(stack_length <= 1) { + + if (stack_length <= 1) { request.parent_cluster_number = ROOT_CLUSTER_NUMBER; } else { request.parent_cluster_number = path_number_stack[stack_length - 2]; } int8_t retcode; - syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + syscall_user(1, (uint32_t)&request, (uint32_t)&retcode, 0); - if(retcode != 0) { + if (retcode != 0) { syscall_user(5, (uint32_t) "SHELL ERROR\n", 6, DARK_GREEN); } - for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - if(req_table.table[i].user_attribute == UATTR_NOT_EMPTY) { + for (uint32_t i = 1; i < CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry); + i++) { + if (req_table.table[i].user_attribute == UATTR_NOT_EMPTY) { char curr_name[9]; - for(int j = 0; j < 8; j++) { + for (int j = 0; j < 8; j++) { curr_name[j] = req_table.table[i].name[j]; } curr_name[8] = '\0'; - if(req_table.table[i].attribute != ATTR_ARCHIVE) { - // path_number_stack[stack_length] = req_table.table[i].cluster_high << 16 | req_table.table[i].cluster_low; - // for(int j = 0; j < 8; j++) { - // path_name_stack[stack_length][j] = curr_name[j]; - // } - stack_length++; - - if(strcmp(curr_name, name) == 0) { + if (req_table.table[i].attribute != ATTR_ARCHIVE) { + if (strcmp(curr_name, name) == 0) { char path[256] = "\0"; - for(int j = 0; j < stack_length; j++) { - if(j == 0) { + for (int j = 0; j < stack_length; j++) { + if (j == 0) { string_combine(path, "/\0", path); - } else { + } else if (j < stack_length - 1) { + string_combine(path, path_name_stack[j], path); string_combine(path, "/\0", path); + } else { string_combine(path, path_name_stack[j], path); } } - syscall_user(5, (uint32_t) path, KEYBOARD_BUFFER_SIZE, WHITE); + + string_combine(path, " \0", path); + syscall_user(5, (uint32_t)path, KEYBOARD_BUFFER_SIZE, WHITE); } - uint32_t new_number_stack [stack_length + 1]; - char new_name_stack [stack_length + 1][9]; - for(int j = 0; j < stack_length - 1; j++) { + uint32_t new_number_stack[stack_length + 1]; + char new_name_stack[stack_length + 1][9]; + for (int j = 0; j < stack_length; j++) { new_number_stack[j] = path_number_stack[j]; - for(int k = 0; k < 8; k++) { + for (int k = 0; k < 8; k++) { new_name_stack[j][k] = path_name_stack[j][k]; } new_name_stack[j][8] = '\0'; } - - new_number_stack[stack_length-1] = req_table.table[i].cluster_high << 16 | req_table.table[i].cluster_low; - for(int j = 0; j < 8; j++) { + + new_number_stack[stack_length] = req_table.table[i].cluster_high << 16 | + req_table.table[i].cluster_low; + for (int j = 0; j < 8; j++) { new_name_stack[stack_length][j] = curr_name[j]; } - new_name_stack[stack_length-1][8] = '\0'; + new_name_stack[stack_length][8] = '\0'; - where_is(name, ext, new_number_stack, new_name_stack, stack_length); + where_is(name, ext, new_number_stack, new_name_stack, stack_length + 1); } else { char curr_ext[4]; - for(int j = 0; j < 3; j++) { + for (int j = 0; j < 3; j++) { curr_ext[j] = req_table.table[i].ext[j]; } curr_ext[3] = '\0'; - if(strcmp(curr_name, name) == 0 && strcmp(curr_ext, ext)) { + if (strcmp(curr_name, name) == 0 && strcmp(curr_ext, ext) == 0) { char path[256]; - for(int j = 0; j < stack_length; j++) { - if(j == 0) { - string_combine(path, "\0", path); + for (int j = 0; j < stack_length; j++) { + if (j == 0) { + string_combine(path, "/\0", path); } else { - string_combine(path, "\0", path); string_combine(path, path_name_stack[j], path); + string_combine(path, "/\0", path); } } char filename[9]; - for(int j = 0; j < 3; j++) { + for (int j = 0; j < 8; j++) { filename[j] = curr_name[j]; } filename[8] = '\0'; @@ -356,16 +355,17 @@ void where_is(char* name, char* ext, uint32_t* path_number_stack, char path_name string_combine(path, filename, path); string_combine(path, ".\0", path); - string_combine(path, ext, path); + string_combine(path, curr_ext, path); - syscall_user(5, (uint32_t) path, KEYBOARD_BUFFER_SIZE, WHITE); + string_combine(path, " \0", path); + syscall_user(5, (uint32_t)path, KEYBOARD_BUFFER_SIZE, WHITE); } } } } } -void clear_screen(){ +void clear_screen() { syscall_user(7, 0, 0, 0); syscall_user(6, 0, 0, 0); print_home(); @@ -380,14 +380,14 @@ void parse_file_cmd(char* file, char* target_name, char* target_ext) { for (int i = 0; i < 9; i++) { target_name[i] = '\0'; } - + for (int i = 0; i < 4; i++) { target_ext[i] = '\0'; } uint8_t ext_found = 0; int idx_target_name = 0; int idx_target_ext = 0; - + for (int idx = 0; idx < 12; idx++) { if (file[idx] == '.') { ext_found = 1; @@ -396,14 +396,16 @@ void parse_file_cmd(char* file, char* target_name, char* target_ext) { } if (ext_found == 0) { if (idx_target_name > 7) { - syscall_user(5, (uint32_t) "File/dir name too long (max 8)", KEYBOARD_BUFFER_SIZE, DARK_GREEN); + syscall_user(5, (uint32_t) "File/dir name too long (max 8)", + KEYBOARD_BUFFER_SIZE, DARK_GREEN); return; } - target_name[idx_target_name] = file[idx]; + target_name[idx_target_name] = file[idx]; idx_target_name++; } else { if (idx_target_ext > 3) { - syscall_user(5, (uint32_t) "File extension too long (max 3)", KEYBOARD_BUFFER_SIZE, DARK_GREEN); + syscall_user(5, (uint32_t) "File extension too long (max 3)", + KEYBOARD_BUFFER_SIZE, DARK_GREEN); return; } target_ext[idx_target_ext] = file[idx]; @@ -412,20 +414,19 @@ void parse_file_cmd(char* file, char* target_name, char* target_ext) { } } -void execute_cmd(char *input, char* home) { +void execute_cmd(char* input, char* home) { // TODO : untuk rm, cat, cp extensioonnya dipecah setelah cd cd an // remove space in the first character while (input[0] == ' ') { for (int i = 0; i < 256; i++) { - input[i] = input[i+1]; - + input[i] = input[i + 1]; } } // remove space in the last character int k = KEYBOARD_BUFFER_SIZE - 1; while (input[k] == '\0') { k--; - } + } while (input[k] == ' ') { input[k] = '\0'; k--; @@ -433,17 +434,16 @@ void execute_cmd(char *input, char* home) { // array of string input char cmd[40][15]; - - + int cmd_length = 0; int neff = 0; // initialize array with \0 for (int i = 0; i < 40; i++) { - for (int j = 0; j < 15; j++) { - cmd[i][j] = '\0'; + for (int j = 0; j < 15; j++) { + cmd[i][j] = '\0'; } - } + } int i = 0; while (input[i] != '\0') { @@ -453,19 +453,19 @@ void execute_cmd(char *input, char* home) { j++; i++; } - + cmd[neff][j] = '\0'; - neff++; + neff++; - if (input[i] == ' ' ) { + if (input[i] == ' ') { cmd[neff][0] = ' '; neff++; - cmd_length++; - while (input[i] == ' ') { + cmd_length++; + while (input[i] == ' ') { i++; } } - + if (input[i] == '/') { i++; } @@ -480,24 +480,27 @@ void execute_cmd(char *input, char* home) { // syscall_user(5, (uint32_t) "\n", KEYBOARD_BUFFER_SIZE, WHITE); // syscall_user(5, (uint32_t) target_name, KEYBOARD_BUFFER_SIZE, WHITE); // syscall_user(5, (uint32_t) target_ext, KEYBOARD_BUFFER_SIZE, WHITE); - + char targetn[9]; char targetext[4]; char tergetFull[12] = "test\0\0\0\0.exe"; parse_file_cmd(tergetFull, targetn, targetext); - - // execute command from input - if (strcmp(cmd[0], "clear") == 0) { + + // execute command from input + if (strcmp(cmd[0], "clear") == 0) { clear_screen(); } else { syscall_user(5, (uint32_t) "\n", 1, WHITE); if (strcmp(cmd[0], "help") == 0) { // list of available commands - syscall_user(5, (uint32_t) "Available commands:\n", KEYBOARD_BUFFER_SIZE, WHITE); - syscall_user(5, (uint32_t) "clear - Clear the screen\n", KEYBOARD_BUFFER_SIZE, WHITE); - syscall_user(5, (uint32_t) "help - List of available commands", KEYBOARD_BUFFER_SIZE, WHITE); - } else if(strcmp(cmd[0], "cd") == 0) { + syscall_user(5, (uint32_t) "Available commands:\n", KEYBOARD_BUFFER_SIZE, + WHITE); + syscall_user(5, (uint32_t) "clear - Clear the screen\n", + KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) "help - List of available commands", + KEYBOARD_BUFFER_SIZE, WHITE); + } else if (strcmp(cmd[0], "cd") == 0) { // cd : Pindah ke folder yang dituju if (cmd[1][0] == '\0') { // cd @@ -522,386 +525,393 @@ void execute_cmd(char *input, char* home) { i++; } // change_directory(cmd[1]); - - } else if(strcmp(cmd[0], "ls") == 0 ) { + + } else if (strcmp(cmd[0], "ls") == 0) { list_current_directory(); - } else if(strcmp(cmd[0], "mkdir") == 0) { - // mkdir : Membuat sebuah folder kosong baru - int counterCD = 2; - if (cmd[2][0]=='\0'){ - syscall_user(5, (uint32_t) "mkdir: missing operand\n", 31, WHITE); - } - else { - // Change to target directory - uint32_t DIR_NUMBER_STACK_TEMP [256] = {2}; - char DIR_NAME_STACK_TEMP [256][9] = {"ROOT\0\0\0\0\0"}; - uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; - for (int i = 0; i < DIR_STACK_LENGTH+1; i++){ - DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; - for (int j = 0; j < 9; j++){ - DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; - } + } else if (strcmp(cmd[0], "mkdir") == 0) { + // mkdir : Membuat sebuah folder kosong baru + int counterCD = 2; + if (cmd[2][0] == '\0') { + syscall_user(5, (uint32_t) "mkdir: missing operand\n", 31, WHITE); + } else { + // Change to target directory + uint32_t DIR_NUMBER_STACK_TEMP[256] = {2}; + char DIR_NAME_STACK_TEMP[256][9] = {"ROOT\0\0\0\0\0"}; + uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; + for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { + DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; } - if (cmd[3][0] != '\0'){ - counterCD = 2; - while (cmd[counterCD+1][0] != '\0') { - if (cmd[counterCD][0] == '.') { - if (cmd[counterCD][1] == '.') { - // cd .. - change_directory(".."); - } else { - // cd . - change_directory("."); - } + } + if (cmd[3][0] != '\0') { + counterCD = 2; + while (cmd[counterCD + 1][0] != '\0') { + if (cmd[counterCD][0] == '.') { + if (cmd[counterCD][1] == '.') { + // cd .. + change_directory(".."); } else { - // cd - change_directory(cmd[counterCD]); + // cd . + change_directory("."); } - counterCD++; + } else { + // cd + change_directory(cmd[counterCD]); } + counterCD++; } + } - // mkdir file in relative path - struct FAT32DirectoryTable current_table; - struct FAT32DriverRequest request = { - .buf = ¤t_table, - .name = "\0\0\0\0\0\0\0\0", - .ext = "\0\0\0", - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH-1], - .buffer_size = 0, - }; - // char temp[8]; - for(int i = 0; i < 8; i++) { - request.name[i] = cmd[counterCD][i]; - // temp[i] = cmd[counterCD][i]; - } - int8_t retcode; - // syscall_user(5, (uint32_t) temp, 8, WHITE); - syscall_user(2, (uint32_t) &request, (uint32_t) &retcode, 0); - if(retcode != 0) { - syscall_user(5, (uint32_t) "INVALID DIRECTORY", 18, DARK_GREEN); - } - - // change to previous stack - for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++){ - DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; - for (int j = 0; j < 9; j++){ - DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; - } + // mkdir file in relative path + struct FAT32DirectoryTable current_table; + struct FAT32DriverRequest request = { + .buf = ¤t_table, + .name = "\0\0\0\0\0\0\0\0", + .ext = "\0\0\0", + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + .buffer_size = 0, + }; + // char temp[8]; + for (int i = 0; i < 8; i++) { + request.name[i] = cmd[counterCD][i]; + // temp[i] = cmd[counterCD][i]; + } + int8_t retcode; + // syscall_user(5, (uint32_t) temp, 8, WHITE); + syscall_user(2, (uint32_t)&request, (uint32_t)&retcode, 0); + if (retcode != 0) { + syscall_user(5, (uint32_t) "INVALID DIRECTORY", 18, DARK_GREEN); + } + + // change to previous stack + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { + DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; } - for (int i = DIR_STACK_LENGTH_TEMP; i < (DIR_STACK_LENGTH_TEMP+counterCD); i++){ - DIR_NUMBER_STACK[i] = 0; - for (int j = 0; j < 9; j++){ - DIR_NAME_STACK[i][j] = '\0'; - } + } + for (int i = DIR_STACK_LENGTH_TEMP; + i < (DIR_STACK_LENGTH_TEMP + counterCD); i++) { + DIR_NUMBER_STACK[i] = 0; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = '\0'; } - DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; - } - - - } else if(strcmp(cmd[0], "cat") == 0) { - // cat : Menuliskan sebuah file sebagai text file ke layar (Gunakan format LF newline) - - int counterCD = 2; - if (cmd[2][0]=='\0'){ - syscall_user(5, (uint32_t) "cat: missing operand\n", 29, WHITE); - } - else{ - // Change to target directory - uint32_t DIR_NUMBER_STACK_TEMP [256] = {2}; - char DIR_NAME_STACK_TEMP [256][9] = {"ROOT\0\0\0\0\0"}; - uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; - for (int i = 0; i < DIR_STACK_LENGTH+1; i++){ - DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; - for (int j = 0; j < 9; j++){ - DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; - } + } + DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; + } + + } else if (strcmp(cmd[0], "cat") == 0) { + // cat : Menuliskan sebuah file sebagai text file ke layar (Gunakan format + // LF newline) + + int counterCD = 2; + if (cmd[2][0] == '\0') { + syscall_user(5, (uint32_t) "cat: missing operand\n", 29, WHITE); + } else { + // Change to target directory + uint32_t DIR_NUMBER_STACK_TEMP[256] = {2}; + char DIR_NAME_STACK_TEMP[256][9] = {"ROOT\0\0\0\0\0"}; + uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; + for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { + DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; } - if (cmd[3][0] != '\0'){ - counterCD = 2; - while (cmd[counterCD+1][0] != '\0') { - if (cmd[counterCD][0] == '.') { - if (cmd[counterCD][1] == '.') { - // cd .. - change_directory(".."); - } else { - // cd . - change_directory("."); - } + } + if (cmd[3][0] != '\0') { + counterCD = 2; + while (cmd[counterCD + 1][0] != '\0') { + if (cmd[counterCD][0] == '.') { + if (cmd[counterCD][1] == '.') { + // cd .. + change_directory(".."); } else { - // cd - change_directory(cmd[counterCD]); + // cd . + change_directory("."); } - counterCD++; + } else { + // cd + change_directory(cmd[counterCD]); } + counterCD++; } + } - // parse file name - char full_name[12]; - for (int i=0;i<12;i++){ - full_name[i] = cmd[counterCD][i]; - } - char file_name[9]; - char file_ext[4]; - parse_file_cmd(full_name, file_name, file_ext); - // if (cmd[counterCD][8] == '.'){ - // for (int i=0;i<3;i++){ - // file_ext[i] = cmd[counterCD][i+9]; - // } - // } - // else{ - // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, WHITE); - // } - // if (cmd[counterCD][12] != '\0' || cmd[counterCD][12] != ' '){ - // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, WHITE); - // } - - - // parse_file_cmd(full_name, file_name, file_ext); - - // cat file in relative path - struct FAT32DirectoryTable current_table; - struct FAT32DriverRequest request = { - .buf = ¤t_table, - .ext = "\0\0\0", - .buffer_size = 0, - }; - - if(DIR_STACK_LENGTH <= 1) { - request.parent_cluster_number = ROOT_CLUSTER_NUMBER; - } else { - request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; - } - - int8_t retcode; - for (int i=0;i<8;i++){ - request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH-1][i]; - } - // for (int i=0; i<3; i++){ - // request.ext[i] = file_ext[i]; - // } - syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); - if (retcode != 0 ){ - + // parse file name + char full_name[12]; + for (int i = 0; i < 12; i++) { + full_name[i] = cmd[counterCD][i]; + } + char file_name[9]; + char file_ext[4]; + parse_file_cmd(full_name, file_name, file_ext); + // if (cmd[counterCD][8] == '.'){ + // for (int i=0;i<3;i++){ + // file_ext[i] = cmd[counterCD][i+9]; + // } + // } + // else{ + // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, WHITE); + // } + // if (cmd[counterCD][12] != '\0' || cmd[counterCD][12] != ' '){ + // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, WHITE); + // } + + // parse_file_cmd(full_name, file_name, file_ext); + + // cat file in relative path + struct FAT32DirectoryTable current_table; + struct FAT32DriverRequest request = { + .buf = ¤t_table, + .ext = "\0\0\0", + .buffer_size = 0, + }; + + if (DIR_STACK_LENGTH <= 1) { + request.parent_cluster_number = ROOT_CLUSTER_NUMBER; + } else { + request.parent_cluster_number = + DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; + } + + int8_t retcode; + for (int i = 0; i < 8; i++) { + request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; + } + // for (int i=0; i<3; i++){ + // request.ext[i] = file_ext[i]; + // } + syscall_user(1, (uint32_t)&request, (uint32_t)&retcode, 0); + if (retcode != 0) { + } + for (uint32_t i = 1; + i < CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry); i++) { + char curr_name[9]; + for (int j = 0; j < 8; j++) { + curr_name[j] = current_table.table[i].name[j]; } - for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - char curr_name [9]; - for(int j = 0; j < 8; j++) { - curr_name[j] = current_table.table[i].name[j]; + curr_name[8] = '\0'; + + if (strcmp(curr_name, file_name) == 0) { + // char extension [4]; + // for(int j = 0; j < 3; j++) { + // extension[j] = current_table.table[i].ext[j]; + // } + // extension[3] = '\0'; + + uint32_t size = current_table.table[i].filesize; + struct ClusterBuffer cl[size / CLUSTER_SIZE]; + struct FAT32DriverRequest requestBuf = { + .buf = &cl, + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + .buffer_size = size, + }; + for (int j = 0; j < 8; j++) { + requestBuf.name[j] = cmd[counterCD][j]; } - curr_name[8] = '\0'; - - if(strcmp(curr_name, file_name) == 0) { - // char extension [4]; - // for(int j = 0; j < 3; j++) { - // extension[j] = current_table.table[i].ext[j]; - // } - // extension[3] = '\0'; - - uint32_t size = current_table.table[i].filesize; - struct ClusterBuffer cl[size/CLUSTER_SIZE]; - struct FAT32DriverRequest requestBuf = { - .buf = &cl, - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], - .buffer_size = size, - }; - for (int j = 0; j < 8; j++) { - requestBuf.name[j] = cmd[counterCD][j]; - } - for (int j = 0; j < 3; j++) { - requestBuf.ext[j] = file_ext[j]; - } - syscall_user(0, (uint32_t) &requestBuf, (uint32_t) &retcode, 0); - if (retcode!=0){ - char error[50]; - string_combine("cat: ", cmd[1], error); - string_combine(error, ": No such file or directory\n", error); - syscall_user(5, (uint32_t) error, 50, WHITE); - } - else{ - - // char fileIn[current_table.filesize]; - // syscall_user(5, (uint32_t) "udah", 5, WHITE); - for (uint32_t j = 0; j < requestBuf.buffer_size/CLUSTER_SIZE; j++){ - // fileIn[i] = - char temp[CLUSTER_SIZE + 1]; - for (int k = 0; k < CLUSTER_SIZE; k++) { - temp[k] = ((char*) (((struct ClusterBuffer*) requestBuf.buf) + j))[k]; - } - temp[CLUSTER_SIZE] = '\0'; - syscall_user(5, (uint32_t) temp, 1, WHITE); - // syscall_user(5, (uint32_t) "masuk", 5, WHITE); + for (int j = 0; j < 3; j++) { + requestBuf.ext[j] = file_ext[j]; + } + syscall_user(0, (uint32_t)&requestBuf, (uint32_t)&retcode, 0); + if (retcode != 0) { + char error[50]; + string_combine("cat: ", cmd[1], error); + string_combine(error, ": No such file or directory\n", error); + syscall_user(5, (uint32_t)error, 50, WHITE); + } else { + // char fileIn[current_table.filesize]; + // syscall_user(5, (uint32_t) "udah", 5, WHITE); + for (uint32_t j = 0; j < requestBuf.buffer_size / CLUSTER_SIZE; + j++) { + // fileIn[i] = + char temp[CLUSTER_SIZE + 1]; + for (int k = 0; k < CLUSTER_SIZE; k++) { + temp[k] = + ((char*)(((struct ClusterBuffer*)requestBuf.buf) + j))[k]; } + temp[CLUSTER_SIZE] = '\0'; + syscall_user(5, (uint32_t)temp, 1, WHITE); + // syscall_user(5, (uint32_t) "masuk", 5, WHITE); } - break; } - } + break; + } + } - // change to previous stack - for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++){ - DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; - for (int j = 0; j < 9; j++){ - DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; - } + // change to previous stack + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { + DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; } - for (int i = DIR_STACK_LENGTH_TEMP; i < (DIR_STACK_LENGTH_TEMP+counterCD); i++){ - DIR_NUMBER_STACK[i] = 0; - for (int j = 0; j < 9; j++){ - DIR_NAME_STACK[i][j] = '\0'; - } + } + for (int i = DIR_STACK_LENGTH_TEMP; + i < (DIR_STACK_LENGTH_TEMP + counterCD); i++) { + DIR_NUMBER_STACK[i] = 0; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = '\0'; } - DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; + } + DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; + } + + } else if (strcmp(cmd[0], "cp") == 0) { + // cp : Mengcopy suatu file (Folder menjadi bonus) + // format : cp + + // cek apakah ada argumen dan ada tepat 2 argumen + if (cmd[1][0] == '\0' || cmd[2][0] == '\0' || cmd[3][0] != '\0') { + syscall_user(5, (uint32_t) "cp: No such file or directory1\n", + KEYBOARD_BUFFER_SIZE, WHITE); + } else { + // cek apakah file yang mau dicopy ada + // ?: parent cluster yang file + struct FAT32DirectoryTable current_table; + struct FAT32DriverRequest request = { + .buf = ¤t_table, + .ext = "\0\0\0", + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + .buffer_size = 0, + }; + + for (int i = 0; i < 8; i++) { + request.name[i] = cmd[1][i]; + } + for (int i = 0; i < 3; i++) { + request.ext[i] = cmd[1][i + 9]; } - } else if(strcmp(cmd[0], "cp") == 0) { - // cp : Mengcopy suatu file (Folder menjadi bonus) - // format : cp + int8_t retcode; + syscall_user(1, (uint32_t)&request, (uint32_t)&retcode, 0); - // cek apakah ada argumen dan ada tepat 2 argumen - if (cmd[1][0]=='\0' || cmd[2][0]=='\0' || cmd[3][0]!='\0'){ - syscall_user(5, (uint32_t) "cp: No such file or directory1\n", KEYBOARD_BUFFER_SIZE, WHITE); + if (retcode != 0) { + // file + syscall_user(5, (uint32_t) "cp: No such file or directory2\n", + KEYBOARD_BUFFER_SIZE, WHITE); } else { - // cek apakah file yang mau dicopy ada - // ?: parent cluster yang file - struct FAT32DirectoryTable current_table; - struct FAT32DriverRequest request = { - .buf = ¤t_table, - .ext = "\0\0\0", + // file founded + // check if directory does exist + struct FAT32DirectoryTable current_table2; + + struct FAT32DriverRequest request2 = { + .buf = ¤t_table2, + .ext = "\0\0\0", .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], - .buffer_size = 0, + .buffer_size = 0, }; - for (int i=0;i<8;i++){ - request.name[i]=cmd[1][i]; + for (int i = 0; i < 8; i++) { + request2.name[i] = cmd[2][i]; } - for (int i=0;i<3;i++){ - request.ext[i]=cmd[1][i+9]; + for (int i = 0; i < 3; i++) { + request2.ext[i] = cmd[2][i + 9]; } - int8_t retcode; - syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + int8_t retcode2; + syscall_user(1, (uint32_t)&request2, (uint32_t)&retcode2, 0); - if (retcode!=0){ - // file - syscall_user(5, (uint32_t) "cp: No such file or directory2\n", KEYBOARD_BUFFER_SIZE, WHITE); + if (retcode2 != 0) { + syscall_user(5, (uint32_t) "cp: No such file or directory3\n", + KEYBOARD_BUFFER_SIZE, WHITE); } else { - // file founded - // check if directory does exist - struct FAT32DirectoryTable current_table2; - - struct FAT32DriverRequest request2 = { - .buf = ¤t_table2, - .ext = "\0\0\0", - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], - .buffer_size = 0, - }; + // folder founded + // copy all content of file to folder + // get file size - for (int i=0;i<8;i++){ - request2.name[i]=cmd[2][i]; - } - for (int i=0;i<3;i++){ - request2.ext[i]=cmd[2][i+9]; - } + // request.parent_cluster_number = cluster_number; + // request.buffer_size = - int8_t retcode2; - syscall_user(1, (uint32_t) &request2, (uint32_t) &retcode2, 0); + syscall_user(2, (uint32_t)&request, (uint32_t)&retcode, 0); - if (retcode2!=0) { - syscall_user(5, (uint32_t) "cp: No such file or directory3\n", KEYBOARD_BUFFER_SIZE, WHITE); + // check if error or filename is same + if (retcode != 0) { + syscall_user(5, (uint32_t) "cp: No such file or directory4\n", + KEYBOARD_BUFFER_SIZE, WHITE); } else { - // folder founded - // copy all content of file to folder - // get file size - - // request.parent_cluster_number = cluster_number; - // request.buffer_size = - - syscall_user(2, (uint32_t) &request, (uint32_t) &retcode, 0); - - // check if error or filename is same - if (retcode!=0){ - syscall_user(5, (uint32_t) "cp: No such file or directory4\n", KEYBOARD_BUFFER_SIZE, WHITE); - } else { - ; - // success - // syscall_user(5, (uint32_t) "cp: Success\n", 13, WHITE); - } + ; + // success + // syscall_user(5, (uint32_t) "cp: Success\n", 13, WHITE); } } } - - } else if(strcmp(cmd[0], "mv") == 0) { - // mv : Memindah dan merename lokasi file/folder + } - } else if(strcmp(cmd[0], "whereis") == 0) { - // whereis - Mencari file/folder dengan nama yang sama diseluruh file system - uint32_t path_number_stack [2] = {ROOT_CLUSTER_NUMBER}; - char path_name_stack [2][9] = {"ROOT\0\0\0\0"}; - uint8_t stack_length = 1; + } else if (strcmp(cmd[0], "mv") == 0) { + // mv : Memindah dan merename lokasi file/folder - char target[15]; - uint8_t idx; - if(cmd[neff][0] == ' ' || cmd[neff][0] == '\0') { - idx = neff-1; - } else { - idx = neff; - } - - for(int i = 0; i < 15; i++) { - target[i] = cmd[idx][i]; - } + } else if (strcmp(cmd[0], "whereis") == 0) { + // whereis - Mencari file/folder dengan nama yang sama diseluruh + // file system + uint32_t path_number_stack[2] = {ROOT_CLUSTER_NUMBER}; + char path_name_stack[2][9] = {"ROOT\0\0\0\0"}; + uint8_t stack_length = 1; - uint8_t counter = 0; - while(target[counter] != '\0') { - counter++; - } + char target[15]; + uint8_t idx; + if (cmd[neff][0] == ' ' || cmd[neff][0] == '\0') { + idx = neff - 1; + } else { + idx = neff; + } - if(counter <= 8) { - char dir_name[9]; - for(int i = 0; i < 8; i++) { - dir_name[i] = target[i]; - } - dir_name[8] = '\0'; + for (int i = 0; i < 15; i++) { + target[i] = cmd[idx][i]; + } - where_is(dir_name, "\0", path_number_stack, path_name_stack, stack_length); - } else { - char full_target[12]; - for(int i = 0; i < 12; i++) { - full_target[i] = target[i]; - } + uint8_t counter = 0; + while (target[counter] != '\0') { + counter++; + } - char target_name[9]; - char target_ext[4]; - parse_file_cmd(full_target, target_name, target_ext); + if (counter <= 8) { + char dir_name[9]; + for (int i = 0; i < 8; i++) { + dir_name[i] = target[i]; + } + dir_name[8] = '\0'; - where_is(target_name, target_ext, path_number_stack, path_name_stack, stack_length); + where_is(dir_name, "\0", path_number_stack, path_name_stack, + stack_length); + } else { + char full_target[12]; + for (int i = 0; i < 12; i++) { + full_target[i] = target[i]; } - } else if(strcmp(cmd[0], "echo") == 0) { - // echo : Menuliskan string ke layar - syscall_user(5, (uint32_t) cmd[1], 1, WHITE); - + + char target_name[9]; + char target_ext[4]; + parse_file_cmd(full_target, target_name, target_ext); + + where_is(target_name, target_ext, path_number_stack, path_name_stack, + stack_length); + } + } else if (strcmp(cmd[0], "echo") == 0) { + // echo : Menuliskan string ke layar + syscall_user(5, (uint32_t)cmd[1], 1, WHITE); + } else if (strcmp(cmd[0], "rm") == 0) { - // rm - Menghapus suatu file (Folder menjadi bonus) + // rm - Menghapus suatu file (Folder menjadi bonus) int counterCD = 2; - if (cmd[2][0]=='\0'){ + if (cmd[2][0] == '\0') { syscall_user(5, (uint32_t) "rm: missing operand\n", 29, WHITE); - } - else{ - // syscall_user(5, (uint32_t) "\nDeleting file: ", KEYBOARD_BUFFER_SIZE, WHITE); - + } else { + // syscall_user(5, (uint32_t) "\nDeleting file: ", KEYBOARD_BUFFER_SIZE, + // WHITE); + // Change to target directory - uint32_t DIR_NUMBER_STACK_TEMP [256] = {2}; - char DIR_NAME_STACK_TEMP [256][9] = {"ROOT\0\0\0\0\0"}; + uint32_t DIR_NUMBER_STACK_TEMP[256] = {2}; + char DIR_NAME_STACK_TEMP[256][9] = {"ROOT\0\0\0\0\0"}; uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; - for (int i = 0; i < DIR_STACK_LENGTH+1; i++){ + for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; - for (int j = 0; j < 9; j++){ + for (int j = 0; j < 9; j++) { DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; } } - if (cmd[3][0] != '\0'){ + if (cmd[3][0] != '\0') { counterCD = 2; - while (cmd[counterCD+1][0] != '\0') { + while (cmd[counterCD + 1][0] != '\0') { if (cmd[counterCD][0] == '.') { if (cmd[counterCD][1] == '.') { // cd .. @@ -917,7 +927,7 @@ void execute_cmd(char *input, char* home) { counterCD++; } } - + char full_target[12]; for (int i = 0; i < 12; i++) { full_target[i] = cmd[counterCD][i]; @@ -926,58 +936,57 @@ void execute_cmd(char *input, char* home) { char target_name[9]; char target_ext[4]; parse_file_cmd(full_target, target_name, target_ext); - + remove(target_name, target_ext); // change to previous stack - for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++){ + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; - for (int j = 0; j < 9; j++){ + for (int j = 0; j < 9; j++) { DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; } } - for (int i = DIR_STACK_LENGTH_TEMP; i < (DIR_STACK_LENGTH_TEMP+counterCD); i++){ + for (int i = DIR_STACK_LENGTH_TEMP; + i < (DIR_STACK_LENGTH_TEMP + counterCD); i++) { DIR_NUMBER_STACK[i] = 0; - for (int j = 0; j < 9; j++){ + for (int j = 0; j < 9; j++) { DIR_NAME_STACK[i][j] = '\0'; } } DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; - - - + // int8_t retcode; // syscall_user(3, (uint32_t) &request, (uint32_t) &retcode, 0); - } } else { - syscall_user(5, (uint32_t) "\nCommand not found: ", KEYBOARD_BUFFER_SIZE, DARK_RED); - syscall_user(5, (uint32_t) input, KEYBOARD_BUFFER_SIZE, DARK_RED); + syscall_user(5, (uint32_t) "\nCommand not found: ", KEYBOARD_BUFFER_SIZE, + DARK_RED); + syscall_user(5, (uint32_t)input, KEYBOARD_BUFFER_SIZE, DARK_RED); } syscall_user(6, 1, 0, 0); // syscall_user(5, (uint32_t) "> ", KEYBOARD_BUFFER_SIZE, WHITE); print_home(home); - } + } } -int main(void) { - char base [9] = "OSyikkk:\0"; - char path [256] = "/\0"; - // char curr_dir [8] = "ROOT\0\0\0\0"; +int main(void) { + char base[9] = "OSyikkk:\0"; + char path[256] = "/\0"; + // char curr_dir [8] = "ROOT\0\0\0\0"; - char home [264]; - string_combine(base, path, home); + char home[264]; + string_combine(base, path, home); - print_home(home); + print_home(home); - char buf[KEYBOARD_BUFFER_SIZE]; - for (int i = 0; i < KEYBOARD_BUFFER_SIZE; i++) { - buf[i] = '\0'; - } - while (TRUE) { - syscall_user(4, (uint32_t) buf, KEYBOARD_BUFFER_SIZE, 0); - execute_cmd(buf, home); - } + char buf[KEYBOARD_BUFFER_SIZE]; + for (int i = 0; i < KEYBOARD_BUFFER_SIZE; i++) { + buf[i] = '\0'; + } + while (TRUE) { + syscall_user(4, (uint32_t)buf, KEYBOARD_BUFFER_SIZE, 0); + execute_cmd(buf, home); + } - return 0; + return 0; } From 48ce9dc1c78467929d05bcbe6a9b5c9842134a37 Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Sat, 29 Apr 2023 20:02:33 +0700 Subject: [PATCH 45/52] feat: update number of arguments validation Co-authored-by: Nigel Sahl <13521043@std.stei.itb.ac.id> Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: RMA1403 --- src/user-shell.c | 428 ++++++++++++++++++++++++++--------------------- 1 file changed, 240 insertions(+), 188 deletions(-) diff --git a/src/user-shell.c b/src/user-shell.c index 5dfe5e5..d27c593 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -171,7 +171,6 @@ void list_current_directory() { } } -// TODO: Not yet checked void remove(char* target_filename, char* target_extension) { struct FAT32DirectoryTable current_table; struct FAT32DriverRequest request = { @@ -395,16 +394,20 @@ void parse_file_cmd(char* file, char* target_name, char* target_ext) { continue; } if (ext_found == 0) { - if (idx_target_name > 7) { - syscall_user(5, (uint32_t) "File/dir name too long (max 8)", KEYBOARD_BUFFER_SIZE, DARK_GREEN); - return; + if (idx_target_name == 8) { + if(file[idx] != '\0') { + syscall_user(5, (uint32_t) "File/dir name too long (max 8)", KEYBOARD_BUFFER_SIZE, DARK_GREEN); + return; + } } target_name[idx_target_name] = file[idx]; idx_target_name++; } else { - if (idx_target_ext > 3) { - syscall_user(5, (uint32_t) "File extension too long (max 3)", KEYBOARD_BUFFER_SIZE, DARK_GREEN); - return; + if (idx_target_ext == 4) { + if (file[idx] != '\0') { + syscall_user(5, (uint32_t) "File extension too long (max 3)", KEYBOARD_BUFFER_SIZE, DARK_GREEN); + return; + } } target_ext[idx_target_ext] = file[idx]; idx_target_ext++; @@ -470,6 +473,7 @@ void execute_cmd(char *input, char* home) { i++; } } + // check for command length must be valid // for (int i = 0; i < neff; i++) { // syscall_user(5, (uint32_t) cmd[i], KEYBOARD_BUFFER_SIZE, WHITE); @@ -480,25 +484,34 @@ void execute_cmd(char *input, char* home) { // syscall_user(5, (uint32_t) "\n", KEYBOARD_BUFFER_SIZE, WHITE); // syscall_user(5, (uint32_t) target_name, KEYBOARD_BUFFER_SIZE, WHITE); // syscall_user(5, (uint32_t) target_ext, KEYBOARD_BUFFER_SIZE, WHITE); - - char targetn[9]; - char targetext[4]; - char tergetFull[12] = "test\0\0\0\0.exe"; - parse_file_cmd(tergetFull, targetn, targetext); - + // daijoubu.uwuuuuuu // execute command from input if (strcmp(cmd[0], "clear") == 0) { + if (cmd_length > 0) { + syscall_user(5, (uint32_t) "clear: too many arguments", KEYBOARD_BUFFER_SIZE, WHITE); + return; + } clear_screen(); } else { syscall_user(5, (uint32_t) "\n", 1, WHITE); if (strcmp(cmd[0], "help") == 0) { // list of available commands + if (cmd_length > 0) { + syscall_user(5, (uint32_t) "help: too many arguments", KEYBOARD_BUFFER_SIZE, WHITE); + return; + } syscall_user(5, (uint32_t) "Available commands:\n", KEYBOARD_BUFFER_SIZE, WHITE); syscall_user(5, (uint32_t) "clear - Clear the screen\n", KEYBOARD_BUFFER_SIZE, WHITE); syscall_user(5, (uint32_t) "help - List of available commands", KEYBOARD_BUFFER_SIZE, WHITE); } else if(strcmp(cmd[0], "cd") == 0) { // cd : Pindah ke folder yang dituju + + if (cmd_length > 1) { + syscall_user(5, (uint32_t) "cd: too many arguments", KEYBOARD_BUFFER_SIZE, WHITE); + return; + } + if (cmd[1][0] == '\0') { // cd // change_directory(home); @@ -524,12 +537,19 @@ void execute_cmd(char *input, char* home) { // change_directory(cmd[1]); } else if(strcmp(cmd[0], "ls") == 0 ) { - list_current_directory(); + if (cmd_length > 0) { + syscall_user(5, (uint32_t) "ls: too many arguments\n", KEYBOARD_BUFFER_SIZE, WHITE); + } else { + list_current_directory(); + } } else if(strcmp(cmd[0], "mkdir") == 0) { // mkdir : Membuat sebuah folder kosong baru int counterCD = 2; - if (cmd[2][0]=='\0'){ - syscall_user(5, (uint32_t) "mkdir: missing operand\n", 31, WHITE); + if (cmd_length == 0){ + syscall_user(5, (uint32_t) "mkdir: missing operand\n", KEYBOARD_BUFFER_SIZE, WHITE); + } + else if (cmd_length > 1){ + syscall_user(5, (uint32_t) "mkdir: too many arguments\n", KEYBOARD_BUFFER_SIZE, WHITE); } else { // Change to target directory @@ -601,156 +621,163 @@ void execute_cmd(char *input, char* home) { } else if(strcmp(cmd[0], "cat") == 0) { // cat : Menuliskan sebuah file sebagai text file ke layar (Gunakan format LF newline) - - int counterCD = 2; - if (cmd[2][0]=='\0'){ + if (cmd_length == 0){ syscall_user(5, (uint32_t) "cat: missing operand\n", 29, WHITE); } - else{ - // Change to target directory - uint32_t DIR_NUMBER_STACK_TEMP [256] = {2}; - char DIR_NAME_STACK_TEMP [256][9] = {"ROOT\0\0\0\0\0"}; - uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; - for (int i = 0; i < DIR_STACK_LENGTH+1; i++){ - DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; - for (int j = 0; j < 9; j++){ - DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; - } + else if (cmd_length > 1){ + syscall_user(5, (uint32_t) "cat: too many arguments\n", 29, WHITE); + } + else { + int counterCD = 2; + if (cmd[2][0]=='\0'){ + syscall_user(5, (uint32_t) "cat: missing operand\n", 29, WHITE); } - if (cmd[3][0] != '\0'){ - counterCD = 2; - while (cmd[counterCD+1][0] != '\0') { - if (cmd[counterCD][0] == '.') { - if (cmd[counterCD][1] == '.') { - // cd .. - change_directory(".."); + else{ + // Change to target directory + uint32_t DIR_NUMBER_STACK_TEMP [256] = {2}; + char DIR_NAME_STACK_TEMP [256][9] = {"ROOT\0\0\0\0\0"}; + uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; + for (int i = 0; i < DIR_STACK_LENGTH+1; i++){ + DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; + for (int j = 0; j < 9; j++){ + DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; + } + } + if (cmd[3][0] != '\0'){ + counterCD = 2; + while (cmd[counterCD+1][0] != '\0') { + if (cmd[counterCD][0] == '.') { + if (cmd[counterCD][1] == '.') { + // cd .. + change_directory(".."); + } else { + // cd . + change_directory("."); + } } else { - // cd . - change_directory("."); + // cd + change_directory(cmd[counterCD]); } - } else { - // cd - change_directory(cmd[counterCD]); + counterCD++; } - counterCD++; } - } - // parse file name - char full_name[12]; - for (int i=0;i<12;i++){ - full_name[i] = cmd[counterCD][i]; - } - char file_name[9]; - char file_ext[4]; - parse_file_cmd(full_name, file_name, file_ext); - // if (cmd[counterCD][8] == '.'){ - // for (int i=0;i<3;i++){ - // file_ext[i] = cmd[counterCD][i+9]; - // } - // } - // else{ - // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, WHITE); - // } - // if (cmd[counterCD][12] != '\0' || cmd[counterCD][12] != ' '){ - // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, WHITE); - // } + // parse file name + char full_name[12]; + for (int i=0;i<12;i++){ + full_name[i] = cmd[counterCD][i]; + } + char file_name[9]; + char file_ext[4]; + parse_file_cmd(full_name, file_name, file_ext); + // if (cmd[counterCD][8] == '.'){ + // for (int i=0;i<3;i++){ + // file_ext[i] = cmd[counterCD][i+9]; + // } + // } + // else{ + // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, WHITE); + // } + // if (cmd[counterCD][12] != '\0' || cmd[counterCD][12] != ' '){ + // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, WHITE); + // } - - // parse_file_cmd(full_name, file_name, file_ext); - - // cat file in relative path - struct FAT32DirectoryTable current_table; - struct FAT32DriverRequest request = { - .buf = ¤t_table, - .ext = "\0\0\0", - .buffer_size = 0, - }; - - if(DIR_STACK_LENGTH <= 1) { - request.parent_cluster_number = ROOT_CLUSTER_NUMBER; - } else { - request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; - } - - int8_t retcode; - for (int i=0;i<8;i++){ - request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH-1][i]; - } - // for (int i=0; i<3; i++){ - // request.ext[i] = file_ext[i]; - // } - syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); - if (retcode != 0 ){ - } - for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { - char curr_name [9]; - for(int j = 0; j < 8; j++) { - curr_name[j] = current_table.table[i].name[j]; + // parse_file_cmd(full_name, file_name, file_ext); + + // cat file in relative path + struct FAT32DirectoryTable current_table; + struct FAT32DriverRequest request = { + .buf = ¤t_table, + .ext = "\0\0\0", + .buffer_size = 0, + }; + + if(DIR_STACK_LENGTH <= 1) { + request.parent_cluster_number = ROOT_CLUSTER_NUMBER; + } else { + request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; } - curr_name[8] = '\0'; - - if(strcmp(curr_name, file_name) == 0) { - // char extension [4]; - // for(int j = 0; j < 3; j++) { - // extension[j] = current_table.table[i].ext[j]; - // } - // extension[3] = '\0'; - - uint32_t size = current_table.table[i].filesize; - struct ClusterBuffer cl[size/CLUSTER_SIZE]; - struct FAT32DriverRequest requestBuf = { - .buf = &cl, - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], - .buffer_size = size, - }; - for (int j = 0; j < 8; j++) { - requestBuf.name[j] = cmd[counterCD][j]; - } - for (int j = 0; j < 3; j++) { - requestBuf.ext[j] = file_ext[j]; - } - syscall_user(0, (uint32_t) &requestBuf, (uint32_t) &retcode, 0); - if (retcode!=0){ - char error[50]; - string_combine("cat: ", cmd[1], error); - string_combine(error, ": No such file or directory\n", error); - syscall_user(5, (uint32_t) error, 50, WHITE); + + int8_t retcode; + for (int i=0;i<8;i++){ + request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH-1][i]; + } + // for (int i=0; i<3; i++){ + // request.ext[i] = file_ext[i]; + // } + syscall_user(1, (uint32_t) &request, (uint32_t) &retcode, 0); + if (retcode != 0 ){ + + } + for(uint32_t i = 1; i < CLUSTER_SIZE/sizeof(struct FAT32DirectoryEntry); i++) { + char curr_name [9]; + for(int j = 0; j < 8; j++) { + curr_name[j] = current_table.table[i].name[j]; } - else{ - - // char fileIn[current_table.filesize]; - // syscall_user(5, (uint32_t) "udah", 5, WHITE); - for (uint32_t j = 0; j < requestBuf.buffer_size/CLUSTER_SIZE; j++){ - // fileIn[i] = - char temp[CLUSTER_SIZE + 1]; - for (int k = 0; k < CLUSTER_SIZE; k++) { - temp[k] = ((char*) (((struct ClusterBuffer*) requestBuf.buf) + j))[k]; + curr_name[8] = '\0'; + + if(strcmp(curr_name, file_name) == 0) { + // char extension [4]; + // for(int j = 0; j < 3; j++) { + // extension[j] = current_table.table[i].ext[j]; + // } + // extension[3] = '\0'; + + uint32_t size = current_table.table[i].filesize; + struct ClusterBuffer cl[size/CLUSTER_SIZE]; + struct FAT32DriverRequest requestBuf = { + .buf = &cl, + .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], + .buffer_size = size, + }; + for (int j = 0; j < 8; j++) { + requestBuf.name[j] = cmd[counterCD][j]; + } + for (int j = 0; j < 3; j++) { + requestBuf.ext[j] = file_ext[j]; + } + syscall_user(0, (uint32_t) &requestBuf, (uint32_t) &retcode, 0); + if (retcode!=0){ + char error[50]; + string_combine("cat: ", cmd[1], error); + string_combine(error, ": No such file or directory\n", error); + syscall_user(5, (uint32_t) error, 50, WHITE); + } + else{ + + // char fileIn[current_table.filesize]; + // syscall_user(5, (uint32_t) "udah", 5, WHITE); + for (uint32_t j = 0; j < requestBuf.buffer_size/CLUSTER_SIZE; j++){ + // fileIn[i] = + char temp[CLUSTER_SIZE + 1]; + for (int k = 0; k < CLUSTER_SIZE; k++) { + temp[k] = ((char*) (((struct ClusterBuffer*) requestBuf.buf) + j))[k]; + } + temp[CLUSTER_SIZE] = '\0'; + syscall_user(5, (uint32_t) temp, 1, WHITE); + // syscall_user(5, (uint32_t) "masuk", 5, WHITE); } - temp[CLUSTER_SIZE] = '\0'; - syscall_user(5, (uint32_t) temp, 1, WHITE); - // syscall_user(5, (uint32_t) "masuk", 5, WHITE); } + break; } - break; - } - } + } - // change to previous stack - for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++){ - DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; - for (int j = 0; j < 9; j++){ - DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; + // change to previous stack + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++){ + DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; + for (int j = 0; j < 9; j++){ + DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; + } } - } - for (int i = DIR_STACK_LENGTH_TEMP; i < (DIR_STACK_LENGTH_TEMP+counterCD); i++){ - DIR_NUMBER_STACK[i] = 0; - for (int j = 0; j < 9; j++){ - DIR_NAME_STACK[i][j] = '\0'; + for (int i = DIR_STACK_LENGTH_TEMP; i < (DIR_STACK_LENGTH_TEMP+counterCD); i++){ + DIR_NUMBER_STACK[i] = 0; + for (int j = 0; j < 9; j++){ + DIR_NAME_STACK[i][j] = '\0'; + } } + DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; } - DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; } } else if(strcmp(cmd[0], "cp") == 0) { @@ -758,8 +785,11 @@ void execute_cmd(char *input, char* home) { // format : cp // cek apakah ada argumen dan ada tepat 2 argumen - if (cmd[1][0]=='\0' || cmd[2][0]=='\0' || cmd[3][0]!='\0'){ - syscall_user(5, (uint32_t) "cp: No such file or directory1\n", KEYBOARD_BUFFER_SIZE, WHITE); + if (cmd_length <= 1){ + syscall_user(5, (uint32_t) "cp: missing file operand\n", 26, WHITE); + } + else if (cmd_length > 2){ + syscall_user(5, (uint32_t) "cp: too many arguments\n", 24, WHITE); } else { // cek apakah file yang mau dicopy ada // ?: parent cluster yang file @@ -832,61 +862,83 @@ void execute_cmd(char *input, char* home) { } else if(strcmp(cmd[0], "mv") == 0) { // mv : Memindah dan merename lokasi file/folder + if (cmd_length <= 1){ + syscall_user(5, (uint32_t) "mv: missing file operand\n", 26, WHITE); + } + else if (cmd_length > 2){ + syscall_user(5, (uint32_t) "mv: too many arguments\n", 24, WHITE); + } + else{ + // code disinisjs + + + + } } else if(strcmp(cmd[0], "whereis") == 0) { // whereis - Mencari file/folder dengan nama yang sama diseluruh file system - uint32_t path_number_stack [2] = {ROOT_CLUSTER_NUMBER}; - char path_name_stack [2][9] = {"ROOT\0\0\0\0"}; - uint8_t stack_length = 1; - - char target[15]; - uint8_t idx; - if(cmd[neff][0] == ' ' || cmd[neff][0] == '\0') { - idx = neff-1; - } else { - idx = neff; - } - - for(int i = 0; i < 15; i++) { - target[i] = cmd[idx][i]; + // format : whereis + if (cmd_length == 0){ + syscall_user(5, (uint32_t) "whereis: missing file operand\n", 26, WHITE); } - - uint8_t counter = 0; - while(target[counter] != '\0') { - counter++; + else if (cmd_length > 1){ + syscall_user(5, (uint32_t) "whereis: too many arguments\n", 24, WHITE); } - - if(counter <= 8) { - char dir_name[9]; - for(int i = 0; i < 8; i++) { - dir_name[i] = target[i]; + else{ + uint32_t path_number_stack [2] = {ROOT_CLUSTER_NUMBER}; + char path_name_stack [2][9] = {"ROOT\0\0\0\0"}; + uint8_t stack_length = 1; + + char target[15]; + uint8_t idx; + if(cmd[neff][0] == ' ' || cmd[neff][0] == '\0') { + idx = neff-1; + } else { + idx = neff; + } + + for(int i = 0; i < 15; i++) { + target[i] = cmd[idx][i]; } - dir_name[8] = '\0'; - where_is(dir_name, "\0", path_number_stack, path_name_stack, stack_length); - } else { - char full_target[12]; - for(int i = 0; i < 12; i++) { - full_target[i] = target[i]; + uint8_t counter = 0; + while(target[counter] != '\0') { + counter++; } - char target_name[9]; - char target_ext[4]; - parse_file_cmd(full_target, target_name, target_ext); + if(counter <= 8) { + char dir_name[9]; + for(int i = 0; i < 8; i++) { + dir_name[i] = target[i]; + } + dir_name[8] = '\0'; + + where_is(dir_name, "\0", path_number_stack, path_name_stack, stack_length); + } else { + char full_target[12]; + for(int i = 0; i < 12; i++) { + full_target[i] = target[i]; + } + + char target_name[9]; + char target_ext[4]; + parse_file_cmd(full_target, target_name, target_ext); - where_is(target_name, target_ext, path_number_stack, path_name_stack, stack_length); + where_is(target_name, target_ext, path_number_stack, path_name_stack, stack_length); + } } } else if(strcmp(cmd[0], "echo") == 0) { // echo : Menuliskan string ke layar - syscall_user(5, (uint32_t) cmd[1], 1, WHITE); - + syscall_user(5, (uint32_t) cmd[2], 1, WHITE); } else if (strcmp(cmd[0], "rm") == 0) { - // rm - Menghapus suatu file (Folder menjadi bonus) + // rm - Menghapus suatu file (Folder menjadi bonus) int counterCD = 2; - if (cmd[2][0]=='\0'){ - syscall_user(5, (uint32_t) "rm: missing operand\n", 29, WHITE); + if (cmd_length == 0){ + syscall_user(5, (uint32_t) "rm: missing file operand\n", 26, WHITE); } - else{ + else if (cmd_length > 1){ + syscall_user(5, (uint32_t) "rm: too many arguments\n", 24, WHITE); + } else{ // syscall_user(5, (uint32_t) "\nDeleting file: ", KEYBOARD_BUFFER_SIZE, WHITE); // Change to target directory From bd50559939ce159fbc978a9ef52ebdfde999398a Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Sat, 29 Apr 2023 20:37:20 +0700 Subject: [PATCH 46/52] add gitignore --- bin/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 bin/.gitignore diff --git a/bin/.gitignore b/bin/.gitignore new file mode 100644 index 0000000..40b71b3 --- /dev/null +++ b/bin/.gitignore @@ -0,0 +1,4 @@ +*.o +*.iso +* +!.gitignore \ No newline at end of file From cba923e22a5015cdfa7573da217c6f2dbf4bb09b Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Sat, 29 Apr 2023 20:53:40 +0700 Subject: [PATCH 47/52] fix: duplicate command Co-authored-by: Nigel Sahl <13521043@std.stei.itb.ac.id> Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: RMA1403 --- .gitignore | 3 +- src/kernel.c | 4 +- src/user-shell.c | 474 +++++++++++------------------------------------ 3 files changed, 112 insertions(+), 369 deletions(-) diff --git a/.gitignore b/.gitignore index b40125b..dcdf481 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /test -*.json \ No newline at end of file +*.json +./bin/* \ No newline at end of file diff --git a/src/kernel.c b/src/kernel.c index 2f5e31a..22256d0 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -89,7 +89,7 @@ void kernel_setup(void) { .buf = cbuf, .name = "kano2\0\0\0", .ext = "\0\0\0", - .parent_cluster_number = 0xC, + .parent_cluster_number = 0xD, .buffer_size = 0, }; @@ -97,7 +97,7 @@ void kernel_setup(void) { memcpy(request.name, "daijoubu", 8); memcpy(request.ext, "uwu", 3); - request.parent_cluster_number = 0xD; + request.parent_cluster_number = 0xE; request.buffer_size = 5*CLUSTER_SIZE; write(request); // delete(request); diff --git a/src/user-shell.c b/src/user-shell.c index 95bbc12..489194d 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -67,8 +67,10 @@ void print_home() { for (int i = 0; i < DIR_STACK_LENGTH; i++) { if (i == 0) { string_combine(base, path, home); - } else { + } else if (i < DIR_STACK_LENGTH -1 ){ + string_combine(home, DIR_NAME_STACK[i], home); string_combine(home, path, home); + } else { string_combine(home, DIR_NAME_STACK[i], home); } } @@ -872,15 +874,111 @@ void execute_cmd(char* input, char* home) { } } else if (strcmp(cmd[0], "mv") == 0) { - // mv : Memindah dan merename lokasi file/folder - if (cmd_length <= 1) { - syscall_user(5, (uint32_t) "mv: missing file operand\n", 26, WHITE); - } else if (cmd_length > 2) { - syscall_user(5, (uint32_t) "mv: too many arguments\n", 24, WHITE); - } else { - // code disinisjs - } - } else if (strcmp(cmd[0], "whereis") == 0) { + // mv : Memindah dan merename lokasi file/folder + if (cmd_length <= 1) { + syscall_user(5, (uint32_t) "mv: missing file operand\n", 26, WHITE); + } else if (cmd_length > 2) { + syscall_user(5, (uint32_t) "mv: too many arguments\n", 24, WHITE); + } else { + int counterCD = 2; + // Change to target directory + // read source directory relative path + uint32_t DIR_NUMBER_STACK_TEMP[256] = {2}; + char DIR_NAME_STACK_TEMP[256][9] = {"ROOT\0\0\0\0\0"}; + uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; + for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { + DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; + } + } + if (cmd[3][0] != '\0') { + counterCD = 2; + while (cmd[counterCD + 1][0] != ' ') { + if (cmd[counterCD][0] == '.') { + if (cmd[counterCD][1] == '.') { + // cd .. + change_directory(".."); + } else { + // cd . + change_directory("."); + } + } else { + // cd + change_directory(cmd[counterCD]); + } + counterCD++; + } + } + + // MV + // parse file name + char full_name[12]; + for (int i = 0; i < 12; i++) { + full_name[i] = cmd[counterCD][i]; + } + syscall_user(5, (uint32_t) full_name, 12, WHITE); + // char file_name[9]; + // char file_ext[4]; + // parse_file_cmd(full_name, file_name, file_ext); + + + //.... + + + + // change to previous stack + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { + DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; + } + } + for (int i = DIR_STACK_LENGTH_TEMP; + i < (DIR_STACK_LENGTH_TEMP + counterCD); i++) { + DIR_NUMBER_STACK[i] = 0; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = '\0'; + } + } + DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; + + + // read destination directory relative path + DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; + for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { + DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; + } + } + if (cmd[3][0] != '\0') { + counterCD++; + while (cmd[counterCD + 1][0] != '\0') { + if (cmd[counterCD][0] == '.') { + if (cmd[counterCD][1] == '.') { + // cd .. + change_directory(".."); + } else { + // cd . + change_directory("."); + } + } else { + // cd + change_directory(cmd[counterCD]); + } + counterCD++; + } + } + + for (int i = 0; i < 12; i++) { + full_name[i] = cmd[counterCD][i]; + } + syscall_user(5, (uint32_t) full_name, 12, WHITE); + } + + + } else if (strcmp(cmd[0], "whereis") == 0) { // whereis - Mencari file/folder dengan nama yang sama diseluruh // file system format : whereis if (cmd_length == 0) { @@ -1014,363 +1112,7 @@ void execute_cmd(char* input, char* home) { DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; } - } else if (strcmp(cmd[0], "cat") == 0) { - // cat : Menuliskan sebuah file sebagai text file ke layar (Gunakan format - // LF newline) - - int counterCD = 2; - if (cmd[2][0] == '\0') { - syscall_user(5, (uint32_t) "cat: missing operand\n", 29, WHITE); - } else { - // Change to target directory - uint32_t DIR_NUMBER_STACK_TEMP[256] = {2}; - char DIR_NAME_STACK_TEMP[256][9] = {"ROOT\0\0\0\0\0"}; - uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; - for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { - DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; - for (int j = 0; j < 9; j++) { - DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; - } - } - if (cmd[3][0] != '\0') { - counterCD = 2; - while (cmd[counterCD + 1][0] != '\0') { - if (cmd[counterCD][0] == '.') { - if (cmd[counterCD][1] == '.') { - // cd .. - change_directory(".."); - } else { - // cd . - change_directory("."); - } - } else { - // cd - change_directory(cmd[counterCD]); - } - counterCD++; - } - } - - // parse file name - char full_name[12]; - for (int i = 0; i < 12; i++) { - full_name[i] = cmd[counterCD][i]; - } - char file_name[9]; - char file_ext[4]; - parse_file_cmd(full_name, file_name, file_ext); - // if (cmd[counterCD][8] == '.'){ - // for (int i=0;i<3;i++){ - // file_ext[i] = cmd[counterCD][i+9]; - // } - // } - // else{ - // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, WHITE); - // } - // if (cmd[counterCD][12] != '\0' || cmd[counterCD][12] != ' '){ - // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, WHITE); - // } - - // parse_file_cmd(full_name, file_name, file_ext); - - // cat file in relative path - struct FAT32DirectoryTable current_table; - struct FAT32DriverRequest request = { - .buf = ¤t_table, - .ext = "\0\0\0", - .buffer_size = 0, - }; - - if (DIR_STACK_LENGTH <= 1) { - request.parent_cluster_number = ROOT_CLUSTER_NUMBER; - } else { - request.parent_cluster_number = - DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; - } - int8_t retcode; - for (int i = 0; i < 8; i++) { - request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; - } - // for (int i=0; i<3; i++){ - // request.ext[i] = file_ext[i]; - // } - syscall_user(1, (uint32_t)&request, (uint32_t)&retcode, 0); - if (retcode != 0) { - } - for (uint32_t i = 1; - i < CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry); i++) { - char curr_name[9]; - for (int j = 0; j < 8; j++) { - curr_name[j] = current_table.table[i].name[j]; - } - curr_name[8] = '\0'; - - if (strcmp(curr_name, file_name) == 0) { - // char extension [4]; - // for(int j = 0; j < 3; j++) { - // extension[j] = current_table.table[i].ext[j]; - // } - // extension[3] = '\0'; - - uint32_t size = current_table.table[i].filesize; - struct ClusterBuffer cl[size / CLUSTER_SIZE]; - struct FAT32DriverRequest requestBuf = { - .buf = &cl, - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], - .buffer_size = size, - }; - for (int j = 0; j < 8; j++) { - requestBuf.name[j] = cmd[counterCD][j]; - } - for (int j = 0; j < 3; j++) { - requestBuf.ext[j] = file_ext[j]; - } - syscall_user(0, (uint32_t)&requestBuf, (uint32_t)&retcode, 0); - if (retcode != 0) { - char error[50]; - string_combine("cat: ", cmd[1], error); - string_combine(error, ": No such file or directory\n", error); - syscall_user(5, (uint32_t)error, 50, WHITE); - } else { - // char fileIn[current_table.filesize]; - // syscall_user(5, (uint32_t) "udah", 5, WHITE); - for (uint32_t j = 0; j < requestBuf.buffer_size / CLUSTER_SIZE; - j++) { - // fileIn[i] = - char temp[CLUSTER_SIZE + 1]; - for (int k = 0; k < CLUSTER_SIZE; k++) { - temp[k] = - ((char*)(((struct ClusterBuffer*)requestBuf.buf) + j))[k]; - } - temp[CLUSTER_SIZE] = '\0'; - syscall_user(5, (uint32_t)temp, 1, WHITE); - // syscall_user(5, (uint32_t) "masuk", 5, WHITE); - } - } - break; - } - } - - // change to previous stack - for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { - DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; - for (int j = 0; j < 9; j++) { - DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; - } - } - for (int i = DIR_STACK_LENGTH_TEMP; - i < (DIR_STACK_LENGTH_TEMP + counterCD); i++) { - DIR_NUMBER_STACK[i] = 0; - for (int j = 0; j < 9; j++) { - DIR_NAME_STACK[i][j] = '\0'; - } - } - DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; - } - - } else if (strcmp(cmd[0], "cp") == 0) { - // cp : Mengcopy suatu file (Folder menjadi bonus) - // format : cp - - // cek apakah ada argumen dan ada tepat 2 argumen - if (cmd[1][0] == '\0' || cmd[2][0] == '\0' || cmd[3][0] != '\0') { - syscall_user(5, (uint32_t) "cp: No such file or directory1\n", - KEYBOARD_BUFFER_SIZE, WHITE); - } else { - // cek apakah file yang mau dicopy ada - // ?: parent cluster yang file - struct FAT32DirectoryTable current_table; - struct FAT32DriverRequest request = { - .buf = ¤t_table, - .ext = "\0\0\0", - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], - .buffer_size = 0, - }; - - for (int i = 0; i < 8; i++) { - request.name[i] = cmd[1][i]; - } - for (int i = 0; i < 3; i++) { - request.ext[i] = cmd[1][i + 9]; - } - - int8_t retcode; - syscall_user(1, (uint32_t)&request, (uint32_t)&retcode, 0); - - if (retcode != 0) { - // file - syscall_user(5, (uint32_t) "cp: No such file or directory2\n", - KEYBOARD_BUFFER_SIZE, WHITE); - } else { - // file founded - // check if directory does exist - struct FAT32DirectoryTable current_table2; - - struct FAT32DriverRequest request2 = { - .buf = ¤t_table2, - .ext = "\0\0\0", - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], - .buffer_size = 0, - }; - - for (int i = 0; i < 8; i++) { - request2.name[i] = cmd[2][i]; - } - for (int i = 0; i < 3; i++) { - request2.ext[i] = cmd[2][i + 9]; - } - - int8_t retcode2; - syscall_user(1, (uint32_t)&request2, (uint32_t)&retcode2, 0); - - if (retcode2 != 0) { - syscall_user(5, (uint32_t) "cp: No such file or directory3\n", - KEYBOARD_BUFFER_SIZE, WHITE); - } else { - // folder founded - // copy all content of file to folder - // get file size - - // request.parent_cluster_number = cluster_number; - // request.buffer_size = - - syscall_user(2, (uint32_t)&request, (uint32_t)&retcode, 0); - - // check if error or filename is same - if (retcode != 0) { - syscall_user(5, (uint32_t) "cp: No such file or directory4\n", - KEYBOARD_BUFFER_SIZE, WHITE); - } else { - ; - // success - // syscall_user(5, (uint32_t) "cp: Success\n", 13, WHITE); - } - } - } - } - - } else if (strcmp(cmd[0], "mv") == 0) { - // mv : Memindah dan merename lokasi file/folder - - } else if (strcmp(cmd[0], "whereis") == 0) { - // whereis - Mencari file/folder dengan nama yang sama diseluruh - // file system - uint32_t path_number_stack[2] = {ROOT_CLUSTER_NUMBER}; - char path_name_stack[2][9] = {"ROOT\0\0\0\0"}; - uint8_t stack_length = 1; - - char target[15]; - uint8_t idx; - if (cmd[neff][0] == ' ' || cmd[neff][0] == '\0') { - idx = neff - 1; - } else { - idx = neff; - } - - for (int i = 0; i < 15; i++) { - target[i] = cmd[idx][i]; - } - - uint8_t counter = 0; - while (target[counter] != '\0') { - counter++; - } - - if (counter <= 8) { - char dir_name[9]; - for (int i = 0; i < 8; i++) { - dir_name[i] = target[i]; - } - dir_name[8] = '\0'; - - where_is(dir_name, "\0", path_number_stack, path_name_stack, - stack_length); - } else { - char full_target[12]; - for (int i = 0; i < 12; i++) { - full_target[i] = target[i]; - } - - char target_name[9]; - char target_ext[4]; - parse_file_cmd(full_target, target_name, target_ext); - - where_is(target_name, target_ext, path_number_stack, path_name_stack, - stack_length); - } - } else if (strcmp(cmd[0], "echo") == 0) { - // echo : Menuliskan string ke layar - syscall_user(5, (uint32_t)cmd[1], 1, WHITE); - - } else if (strcmp(cmd[0], "rm") == 0) { - // rm - Menghapus suatu file (Folder menjadi bonus) - int counterCD = 2; - if (cmd[2][0] == '\0') { - syscall_user(5, (uint32_t) "rm: missing operand\n", 29, WHITE); - } else { - // syscall_user(5, (uint32_t) "\nDeleting file: ", KEYBOARD_BUFFER_SIZE, - // WHITE); - - // Change to target directory - uint32_t DIR_NUMBER_STACK_TEMP[256] = {2}; - char DIR_NAME_STACK_TEMP[256][9] = {"ROOT\0\0\0\0\0"}; - uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; - for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { - DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; - for (int j = 0; j < 9; j++) { - DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; - } - } - if (cmd[3][0] != '\0') { - counterCD = 2; - while (cmd[counterCD + 1][0] != '\0') { - if (cmd[counterCD][0] == '.') { - if (cmd[counterCD][1] == '.') { - // cd .. - change_directory(".."); - } else { - // cd . - change_directory("."); - } - } else { - // cd - change_directory(cmd[counterCD]); - } - counterCD++; - } - } - - char full_target[12]; - for (int i = 0; i < 12; i++) { - full_target[i] = cmd[counterCD][i]; - } - - char target_name[9]; - char target_ext[4]; - parse_file_cmd(full_target, target_name, target_ext); - - remove(target_name, target_ext); - - // change to previous stack - for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { - DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; - for (int j = 0; j < 9; j++) { - DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; - } - } - for (int i = DIR_STACK_LENGTH_TEMP; - i < (DIR_STACK_LENGTH_TEMP + counterCD); i++) { - DIR_NUMBER_STACK[i] = 0; - for (int j = 0; j < 9; j++) { - DIR_NAME_STACK[i][j] = '\0'; - } - } - DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; - - // int8_t retcode; - // syscall_user(3, (uint32_t) &request, (uint32_t) &retcode, 0); - } } else { syscall_user(5, (uint32_t) "\nCommand not found: ", KEYBOARD_BUFFER_SIZE, DARK_RED); From 0f6cacf15fa23cefced1d023a78f427f35b0a948 Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Sat, 29 Apr 2023 21:16:01 +0700 Subject: [PATCH 48/52] feat: handle 2 path arguments Co-authored-by: Nigel Sahl <13521043@std.stei.itb.ac.id> Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: RMA1403 --- src/user-shell.c | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/user-shell.c b/src/user-shell.c index 489194d..35d369c 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -918,9 +918,9 @@ void execute_cmd(char* input, char* home) { full_name[i] = cmd[counterCD][i]; } syscall_user(5, (uint32_t) full_name, 12, WHITE); - // char file_name[9]; - // char file_ext[4]; - // parse_file_cmd(full_name, file_name, file_ext); + char file_name[9]; + char file_ext[4]; + parse_file_cmd(full_name, file_name, file_ext); //.... @@ -953,28 +953,49 @@ void execute_cmd(char* input, char* home) { } } if (cmd[3][0] != '\0') { - counterCD++; - while (cmd[counterCD + 1][0] != '\0') { + counterCD +=2; + while (cmd[counterCD + 1][0] != '\0' && cmd[counterCD][0] != ' ') { if (cmd[counterCD][0] == '.') { if (cmd[counterCD][1] == '.') { - // cd .. change_directory(".."); } else { - // cd . change_directory("."); } } else { - // cd change_directory(cmd[counterCD]); } counterCD++; } } + char full_name_2[12]; for (int i = 0; i < 12; i++) { - full_name[i] = cmd[counterCD][i]; + full_name_2[i] = cmd[counterCD][i]; } - syscall_user(5, (uint32_t) full_name, 12, WHITE); + // enter + syscall_user(5, (uint32_t) "\n", 1, WHITE); + syscall_user(5, (uint32_t) full_name_2, 12, WHITE); + + // MV + // move file to new directory + + + + // change to previous stack + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { + DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; + } + } + for (int i = DIR_STACK_LENGTH_TEMP; + i < (DIR_STACK_LENGTH_TEMP + counterCD); i++) { + DIR_NUMBER_STACK[i] = 0; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = '\0'; + } + } + DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; } From a8309576886192e6a3fa1ea75ee79bcc00406573 Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Sat, 29 Apr 2023 23:22:39 +0700 Subject: [PATCH 49/52] feat: update mv Co-authored-by: Nigel Sahl <13521043@std.stei.itb.ac.id> Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: RMA1403 --- src/interrupt/interrupt.c | 49 +++++++- src/kernel.c | 4 +- src/user-shell.c | 230 +++++++++++++++++++++++++------------- 3 files changed, 202 insertions(+), 81 deletions(-) diff --git a/src/interrupt/interrupt.c b/src/interrupt/interrupt.c index 6d6cd40..9f12af2 100644 --- a/src/interrupt/interrupt.c +++ b/src/interrupt/interrupt.c @@ -91,11 +91,52 @@ void syscall(struct CPURegister cpu, __attribute__((unused)) struct InterruptSta // Clear screen clear_screen(); } else if (cpu.eax == 8){ - // - } else if (cpu.eax == 9){ - // - } + // Set parent cluster buffer for move command + struct FAT32DirectoryTable src_table = *(struct FAT32DirectoryTable*) cpu.ebx; + struct FAT32DirectoryTable dest_table = *(struct FAT32DirectoryTable*) cpu.ecx; + uint32_t src_idx = (uint32_t) cpu.edx; + + + for (uint32_t i = 1; i < CLUSTER_SIZE/(sizeof(struct FAT32DirectoryEntry)); i++) { + if (dest_table.table[i].user_attribute != UATTR_NOT_EMPTY) { + memcpy(dest_table.table[i].name, src_table.table[src_idx].name, 8); + memcpy(dest_table.table[i].ext, src_table.table[src_idx].ext, 8); + dest_table.table[i].attribute = src_table.table[src_idx].attribute; + dest_table.table[i].user_attribute = src_table.table[src_idx].user_attribute; + dest_table.table[i].undelete = src_table.table[src_idx].undelete; + dest_table.table[i].create_time = src_table.table[src_idx].create_time; + dest_table.table[i].create_date = src_table.table[src_idx].create_date; + dest_table.table[i].access_date = src_table.table[src_idx].access_date; + dest_table.table[i].modified_time = src_table.table[src_idx].modified_time; + dest_table.table[i].modified_date = src_table.table[src_idx].modified_date; + dest_table.table[i].filesize = src_table.table[src_idx].filesize; + dest_table.table[i].cluster_high = src_table.table[src_idx].cluster_high; + dest_table.table[i].cluster_low = src_table.table[src_idx].cluster_low; + memcpy(src_table.table[src_idx].name, "\0\0\0\0\0\0\0\0", 8); + memcpy(src_table.table[src_idx].ext, "\0\0\0", 3); + src_table.table[src_idx].attribute = 0; + src_table.table[src_idx].user_attribute = 0; + src_table.table[src_idx].undelete = 0; + src_table.table[src_idx].create_time = 0; + src_table.table[src_idx].create_date = 0; + src_table.table[src_idx].access_date = 0; + src_table.table[src_idx].modified_time = 0; + src_table.table[src_idx].modified_date = 0; + src_table.table[src_idx].filesize = 0; + src_table.table[src_idx].cluster_high = 0; + src_table.table[src_idx].cluster_low = 0; + break; + } + } + + uint32_t src_cluster_number = src_table.table[0].cluster_high << 16 | src_table.table[0].cluster_low; + uint32_t dest_cluster_number = dest_table.table[0].cluster_high << 16 | dest_table.table[0].cluster_low; + + write_clusters(&(src_table), src_cluster_number, 1); + write_clusters(&(dest_table), dest_cluster_number, 1); + + } } void main_interrupt_handler ( diff --git a/src/kernel.c b/src/kernel.c index 22256d0..2c875f3 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -89,7 +89,7 @@ void kernel_setup(void) { .buf = cbuf, .name = "kano2\0\0\0", .ext = "\0\0\0", - .parent_cluster_number = 0xD, + .parent_cluster_number = 0xE, .buffer_size = 0, }; @@ -97,7 +97,7 @@ void kernel_setup(void) { memcpy(request.name, "daijoubu", 8); memcpy(request.ext, "uwu", 3); - request.parent_cluster_number = 0xE; + request.parent_cluster_number = 0xF; request.buffer_size = 5*CLUSTER_SIZE; write(request); // delete(request); diff --git a/src/user-shell.c b/src/user-shell.c index 35d369c..75cc60b 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -175,6 +175,10 @@ void list_current_directory() { } } +// void get_src_request(char* target_filename, char* target_extension) { + +// } + void remove(char* target_filename, char* target_extension) { struct FAT32DirectoryTable current_table; struct FAT32DriverRequest request = { @@ -251,6 +255,10 @@ void remove(char* target_filename, char* target_extension) { int8_t retcode_delete; syscall_user(3, (uint32_t)&request_delete, (uint32_t)&retcode_delete, 0); + if (retcode_delete == 2) { + syscall_user(5, (uint32_t) "rm: Cannot delete filled directory.\n", KEYBOARD_BUFFER_SIZE, WHITE); + } + return; } } } @@ -677,21 +685,6 @@ void execute_cmd(char* input, char* home) { char file_name[9]; char file_ext[4]; parse_file_cmd(full_name, file_name, file_ext); - // if (cmd[counterCD][8] == '.'){ - // for (int i=0;i<3;i++){ - // file_ext[i] = cmd[counterCD][i+9]; - // } - // } - // else{ - // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, - // WHITE); - // } - // if (cmd[counterCD][12] != '\0' || cmd[counterCD][12] != ' '){ - // syscall_user(5, (uint32_t) "cat: invalid file name\n", 24, - // WHITE); - // } - - // parse_file_cmd(full_name, file_name, file_ext); // cat file in relative path struct FAT32DirectoryTable current_table; @@ -880,72 +873,131 @@ void execute_cmd(char* input, char* home) { } else if (cmd_length > 2) { syscall_user(5, (uint32_t) "mv: too many arguments\n", 24, WHITE); } else { - int counterCD = 2; - // Change to target directory - // read source directory relative path - uint32_t DIR_NUMBER_STACK_TEMP[256] = {2}; - char DIR_NAME_STACK_TEMP[256][9] = {"ROOT\0\0\0\0\0"}; - uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; - for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { - DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; - for (int j = 0; j < 9; j++) { - DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; + int counterCD = 2; + // Change to target directory + // read source directory relative path + uint32_t DIR_NUMBER_STACK_TEMP[256] = {2}; + char DIR_NAME_STACK_TEMP[256][9] = {"ROOT\0\0\0\0\0"}; + uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; + for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { + DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; + } } - } - if (cmd[3][0] != '\0') { - counterCD = 2; - while (cmd[counterCD + 1][0] != ' ') { - if (cmd[counterCD][0] == '.') { - if (cmd[counterCD][1] == '.') { - // cd .. - change_directory(".."); + if (cmd[3][0] != '\0') { + counterCD = 2; + while (cmd[counterCD + 1][0] != ' ') { + if (cmd[counterCD][0] == '.') { + if (cmd[counterCD][1] == '.') { + // cd .. + change_directory(".."); + } else { + // cd . + change_directory("."); + } } else { - // cd . - change_directory("."); + // cd + change_directory(cmd[counterCD]); } - } else { - // cd - change_directory(cmd[counterCD]); + counterCD++; } - counterCD++; } - } - // MV - // parse file name - char full_name[12]; - for (int i = 0; i < 12; i++) { - full_name[i] = cmd[counterCD][i]; - } - syscall_user(5, (uint32_t) full_name, 12, WHITE); - char file_name[9]; - char file_ext[4]; - parse_file_cmd(full_name, file_name, file_ext); - + // MV + // parse file name + char full_name[12]; + for (int i = 0; i < 12; i++) { + full_name[i] = cmd[counterCD][i]; + } + syscall_user(5, (uint32_t) full_name, 12, WHITE); + + // Search source request + char target_filename_src[9]; + char target_ext_src[4]; + parse_file_cmd(full_name, target_filename_src, target_ext_src); + + uint32_t src_idx = 0x800; // if unchanged, file not found + struct FAT32DirectoryTable src_table; + struct FAT32DriverRequest src_request = { + .buf = &src_table, + .ext = "\0\0\0", + .buffer_size = 0, + }; + + for (int i = 0; i < 8; i++) { + src_request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; + } - //.... + if (DIR_STACK_LENGTH <= 1) { + src_request.parent_cluster_number = ROOT_CLUSTER_NUMBER; + } else { + src_request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; + } + int8_t src_retcode; + syscall_user(1, (uint32_t)&src_request, (uint32_t)&src_retcode, 0); + if (src_retcode != 0) { + syscall_user(5, (uint32_t) "SHELL ERROR\n", 15, DARK_GREEN); + return; + } - // change to previous stack - for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { - DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; - for (int j = 0; j < 9; j++) { - DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; + for (uint32_t i = 1; i < CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry); + i++) { + if (src_table.table[i].user_attribute == UATTR_NOT_EMPTY) { + char filename[9]; + for (int j = 0; j < 8; j++) { + filename[j] = src_table.table[i].name[j]; + } + filename[8] = '\0'; + + if (src_table.table[i].attribute == ATTR_ARCHIVE) { + char ext_name[4] = "\0\0\0\0"; + if (src_table.table[i].ext[0] != '\0') { + for (int j = 0; j < 3; j++) { + ext_name[j] = src_table.table[i].ext[j]; + } + ext_name[3] = '\0'; + } + + if (strcmp(filename, target_filename_src) == 0 && + strcmp(ext_name, target_ext_src) == 0) { + src_idx = i; + } + } else { + if (strcmp(filename, target_filename_src) == 0) { + src_idx = i; + } + } + } } - } - for (int i = DIR_STACK_LENGTH_TEMP; - i < (DIR_STACK_LENGTH_TEMP + counterCD); i++) { - DIR_NUMBER_STACK[i] = 0; - for (int j = 0; j < 9; j++) { - DIR_NAME_STACK[i][j] = '\0'; + + if (src_idx == 0x800) { + syscall_user(5, (uint32_t) "mv: source not found\n", KEYBOARD_BUFFER_SIZE, WHITE); + return; } - } - DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; + + + // change to previous stack + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { + DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; + } + } + for (int i = DIR_STACK_LENGTH_TEMP; + i < (DIR_STACK_LENGTH_TEMP + counterCD); i++) { + DIR_NUMBER_STACK[i] = 0; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = '\0'; + } + } + DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; - // read destination directory relative path - DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; + // read destination directory relative path + DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; for (int j = 0; j < 9; j++) { @@ -953,8 +1005,8 @@ void execute_cmd(char* input, char* home) { } } if (cmd[3][0] != '\0') { - counterCD +=2; - while (cmd[counterCD + 1][0] != '\0' && cmd[counterCD][0] != ' ') { + counterCD += 2; + while (cmd[counterCD][0] != '\0' && cmd[counterCD][0] != ' ') { if (cmd[counterCD][0] == '.') { if (cmd[counterCD][1] == '.') { change_directory(".."); @@ -968,18 +1020,46 @@ void execute_cmd(char* input, char* home) { } } - char full_name_2[12]; - for (int i = 0; i < 12; i++) { - full_name_2[i] = cmd[counterCD][i]; + char full_name_2[8]; + for (int i = 0; i < 8; i++) { + full_name_2[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; } // enter syscall_user(5, (uint32_t) "\n", 1, WHITE); syscall_user(5, (uint32_t) full_name_2, 12, WHITE); - // MV - // move file to new directory + // Search destination request + char target_filename_dest[9]; + char target_ext_dest[4]; + parse_file_cmd(full_name_2, target_filename_dest, target_ext_dest); - + struct FAT32DirectoryTable dest_table; + struct FAT32DriverRequest dest_request = { + .buf = &dest_table, + .ext = "\0\0\0", + .buffer_size = 0, + }; + + for (int i = 0; i < 8; i++) { + dest_request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; + } + + if (DIR_STACK_LENGTH <= 1) { + dest_request.parent_cluster_number = ROOT_CLUSTER_NUMBER; + } else { + dest_request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; + } + + int8_t dest_retcode; + syscall_user(1, (uint32_t)&dest_request, (uint32_t)&dest_retcode, 0); + + if (dest_retcode != 0) { + syscall_user(5, (uint32_t) "SHELL ERROR\n", 15, DARK_GREEN); + return; + } + + // Move + syscall_user(8, (uint32_t)&src_table, (uint32_t)&dest_table, (uint32_t) src_idx); // change to previous stack for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { From 53859ed53a9b15d9692214fc6ee4348762712bdb Mon Sep 17 00:00:00 2001 From: hanifmz07 <13521157@std.stei.itb.ac.id> Date: Sat, 29 Apr 2023 23:48:40 +0700 Subject: [PATCH 50/52] feat: finalize all commands :) Co-authored-by: Nigel Sahl <13521043@std.stei.itb.ac.id> Co-authored-by: razzanYoni <13521087@mahasiswa.itb.ac.id> Co-authored-by: RMA1403 --- bin/OSyikkk.iso | Bin 542720 -> 542720 bytes bin/disk.o | Bin 4896 -> 4700 bytes bin/fat32.o | Bin 14236 -> 14032 bytes bin/framebuffer.o | Bin 6672 -> 6456 bytes bin/gdt.o | Bin 4384 -> 4500 bytes bin/idt.o | Bin 4788 -> 4824 bytes bin/inserter | Bin 32192 -> 32304 bytes bin/interrupt.o | Bin 8852 -> 11172 bytes bin/intsetup.o | Bin 5360 -> 5264 bytes bin/iso/boot/kernel | Bin 66184 -> 67544 bytes bin/kernel | Bin 66184 -> 67544 bytes bin/kernel.o | Bin 6724 -> 7896 bytes bin/kernel_loader.o | Bin 2400 -> 2464 bytes bin/keyboard.o | Bin 8760 -> 8324 bytes bin/paging.o | Bin 12936 -> 12996 bytes bin/portio.o | Bin 3424 -> 3436 bytes bin/shell | Bin 15324 -> 21224 bytes bin/stdmem.o | Bin 4288 -> 4316 bytes bin/storage.bin | Bin 4194304 -> 4194304 bytes src/kernel.c | 4 +- src/user-shell.c | 312 ++++++++++++++++++++++++++++++-------------- 21 files changed, 213 insertions(+), 103 deletions(-) diff --git a/bin/OSyikkk.iso b/bin/OSyikkk.iso index 6eb646a96315994de78daa57f45696bfdafa0f67..874f613694967d2077bb94366a12d2d1161fafe7 100644 GIT binary patch delta 31492 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 T^WE*X-R;`$cKbu^j^F%0LjNhA delta 28780 zcmb7t31C#!)&G6(zBe;TCYi~;l1xG%VadwA$R=SEFd|_S3?U0i2qb0$Q5<3rNnmP#fP_b-Zzt@@-0dI^P)+}RoaNa$1Pj>i_?QdE6R!$y3$d)CR^D$m*H>j z)?1uMJaK^+0^4PNT@n6lFC4ci2`8mbpU;mdC=TUz^nPa>5 z_#?-a%!gtIWrc~%!*rI*5?H)3VV_!L+c}o8onwvqeQJuH8^Kua!LEJkZ!+wxx3J$A zQE>2r`_)*tx2)@fOFK_%z65Z04BTJNSa0WPXYDsI+Q)Ml>wLz&r+>%k-rnvF-yIpS zxP4=YduIsuYdM13^+)CgaqlZR>DxUxXuSETnv#E+ zRkh?~=V?c2?U$r|fkbwfI7uX-zan%&jxqOfHA#)nF)nyaO)C6$v``C%dXF^wV~Oc5 zadefOeXisT`RCLU-y7c#4*K|@aouC8NB#F`@uQ__-;NTdjd^dB-?Tz$+MgxnxstQOs8ZkIf5WKHjlRd# zB2^u2{P}TpbT(}Ab)oTk;s!Fuap;7)!SsBbuK|RPf$xpXC)9)y4$F?SJ*5uxVmhJ4}c0C&eg?0OglR;8ZxTz3xYOMUl$H#mrX2DX-g z5i6ASJVt(IRIyx+l)}`)q2+0^^H6A=d90X+%7! z&hRcmL&&i*d_MjIjP1GY^f!Zp-6dxaozRtI)J8{XPl>bCS2$9*2iFEp;JxLa=&N41}3azlo?nEmvp4rFCaPPV8#Q9tC zS*E`gnZx<|WzeI+aizY20PEMH!KsIQ5nUxGg4?0N%!Ko|(a7vqN2=esj0OE_QJL@) zai5<#?|Z+m!j5&7oH>=(UFPU6@f|u5Av-}CY&%oxdrgK$cgdOVJqUB-XumpvQqHQ7 z&Mx%z!t{=Ttkp*PAvMw4<}usc7KXI*?#KBWbzfrVIc0m~;gWY%flM}$G?Fn?UxoWh zeK}H}86Q^a8wvf04%zofX5T;13VY8WL7oclEIFfe?m9EO{q>&PASc0!rl(~OqU%=p zj11$2Lu!hb!b}QEhM;}!VCRzP!|Z`zRH8tI1YZJBHO~!+$hh+%V}>IOe0fpBvA#TK z0GZldcJ|Oe^q%H(J#qQQ{-@P=?`b48gybo9D!aSnwCD;CyRN@22GDcVF)}rQL+LCz zt#s@Ff|+Z z#$|`qu**eKu=(*c@=pg>i6;`}Ro8V7!AjwQ!M4*q*B$?eg5%uRrVmaj&y}2{cAU!W zE;(t9DC6nFYUP+h$rOl8upxH8VILnrAxN9C{5f^Lci^8^P<53YMs5M-)|;|3{~^oF?EObIfnukfw(CJR@G@C1U*T8rdMt{~&{$W@0!+LK2) zU;^IUUer=A-~8Wq)D}wUW9&!0Ive`}2=* zWW9e;lVcPdQ4>|yS!2-=HOpnKHmAWM9Rn{KyN;+63tvqTK6D){`MTzi&tWZgUqd|l z9+fm&+`4LCCCt4E#(PK91n-TK61@6}^Zg=;nvFE6dk~OL<8O~ZE(`)K`1k*gN zkb{Iz8^XHM&#IK1?Ot~F@y#_gHC@kt^GR3Eu|wzdL;uR?dST45eI;Lp(1_Ia;-POd z#vJc5e)@u%djYLzKS{)jcE>UrAtjjC`o&7g_bOpGNh~#8U{})uD%Ha>_o(Vg39csk(pk@7N~tg1IB`@RF(L=!uY1|afGb1XMIt5) z6X8JhasFlIS*y|TQk-vnJlgk(vGpZn%dcXM>t9kcl``X3FR7`Ka02<|TpT#|8n3;i z7Ma_lj$Lgo3&AyBth__MXnPTo3E&{2mYu{8OuT@A{^oJqP#M+cRB{lzsDD z|B_km{_eP5bm2Gkyxy^*)He?fL}EXcF`R)31}trefvR-&k5=}Td_w};f9XnC9*huXwIlFt;S$lho+$`RMdcId_%}skBY#-}AzGo#ED&&nAFG*<-8lCl-z857I z+CqEfpOKtLE!5W}DjK4$k{I7YTC@|cRw%48(S;`319_z;F3rT{N}1D4v_9ySA~f)h zGLc`CF5)BMBp;Yo7lt^!| zLp0w}xVmHD@`a)YYX6P)qkfNMMcvv#U}XIr66vo_E8mTn`g~nNL@Gp}W3U$6j^Wwu z?kan)yHx8c`=Gnj(N*?wcd4_h?9=X2x3{b83)jKYxSFoAf4Ux-lK{`-458~Vly#m? z>N?R~_8!(wWgm2vYTacYca>t1@@ZG8v%Bnzu2Q$_VA(%wx=Q0HLid#>_-_0ZE_%kZ z&-U>ov5<3MW$oAhg3$l6(Ep0i|2L`s4O9O+QvZn|`d=XZFAM#z2>pK(`rnZH-!b){ zApM6=S^6J=C(!)PbE;qe!$SW-q5lb?|0$_|z|{Yo)PH1%{)b8bL81Q%q5mnNe?aPg z&eVT|^w)o8>7Qch=YIWtLjOHN|1X99`=$N|P5lo^{l6QczmN3aBlQ1L=)Yg+e^Baw z$khKk(qHqXrTJ%sD`xE{yVkLw7oS8%m;sEaea+z5SQZucYidl6kM4X zxV`;jQJ8>hCaw}(%WFp$2EZKD6ZFVy@Tr` zTxW3o3zv#%CjwU-t`WFK<0`;471!KZs=MFFe@o5q>Ij{V0b>rOCFgVf?%nSGQr|C5 zP_(&yDuTe5g%0=G{)OAmz5*I~lGQWczAq$8>IWbkLZ|!oQ>D|GxqY+>MD@cZpDVj} zeXh8!D@4w)&hMsLTkI@sdV7jWE5zP60vop;XD*hz%JX^4b7`AQTkMWqX9f}C?XPv> z;G=WbS*`tPgLT|CxtlQ-Eflhz}~kB<39C0t>QjMJ$=^&SgG$GQRrbxuUy6*Cv=S;%_Q1ffgJ>~ zV_-3;JteP+argCS#^dj*<0IaKFGl|by?_4-)Xo+Yw0-L+>9N=V(W zcXwtNdDyh4U3;b*Jtx#+b?v7HmSm~wQ=b{{oKVx$^iPfNPT*|U`V)vHQ-W`h zBm29TA^AC#7hI3TxSo7v%rWt`gFI6$B>pF-8cR;9TQlez6SOom4afO@20a}E6Q&w( zoK&YS&ZQ+4O&w=-#^zvoi_AMn^A7VbDSdNML;v#>F&A|WxRe=w7oZ`&kwiT(#i;l| z9T6p#)N#IZaJjyBiqZRlnrz|zLb&oN#uFc?sS@Y#-A}m5Qw*QKuG_bZa1m3C#1GX1 z$>;XXA>7N8jWr*t7g)G|!bZJwvT@&sYJ-Klig00*jZq(|Zys>x=#Ti+1l1V%iJHDY zJo4b&Pt;1qmQRhFYdmmDO$mSe=-}W)3BOG63xFy3LI3iUnic-oOM`<+5>7v@rfiyy z{D?jme>fy!&*Qq|<-tMhF;FMUah*CZQorO`rv@euP(EBYf=-`)`ut9=o_KbEm^?r` zA%6wFP`Fg^7W`c{4;=83fR;^GL}ji*}(6B49~H_!3DBT{-dr(U?0tQ3@FN5v9QXq2dT%}(a2WBlf@jj3gM+uD?3YK4xiR37AA{#U z@EjV-b5Qb- z1dJVDK%lxnNE{E;A~fnz;unrFdtt%#e6QB2^IZzL;<;dF>;QkvJA;GtHA+ZBHv}5d z2cAp8GfVP#W%%q2gwG$ra0M8Qp+as7c=p+(Z%;-EiijXo$Y2F^7O5!a|k?tfye!xSR_Jd zKphf7{ot*6|37=}BzW!vPqx%)dTpch8V-1vhD`P})(n&28Nb5IIyuFK2edao9vs9` zNPu=}Aa3S>{>l%~DGWA(egyOqzicl?ycGvi`LHg@{r-j!J}Y89TxCw;SLWN&Vk_@2w6Ksh?yUmuZQwQs*P#(xF6DZF-v=HKc1@(i@A0{ve05L*9t2MkctT=ubHMXQ zK_3ZvPOyy1XT2hqlHk)|7z+k`OCC^C9T4EeGHSwqP%s|!S)gN`6{vq@p#D&E_KN4oq^NQU43nZ&xk@V-C7QJPXyI*7xQ-Gv@4vDhTfl1hf zY@tCL!r+d!7}BP=`x6mC)}IuRhiH=m=3rYBPBCx(N&0p_eaUQhzZh>J^r$bImt}g*yRcsa5jI&6@0^`}XefUsl?28+V8Ur4l7EYME{8(3G zTPx#E+aD~9B4#0X-8RPK!l?X$MPYViX_om?rn{fkB}(5U+#OA=%`7v6it2vGz3@8A z%o~lnx?kfI&zVa}in@Qjb~Pex6UurW8yhQG8nl~$oJYudR+uP}wE)AWLt?Z&4tMA8 z;3!%dDSdyyU1)SBQIRx~Lg#2pp_eo|OH7S10GJZ*Iw*J>v|5uIS5~2JV<<+9>R5qf#HmZZzl{%EyMNvmYDZ3!&GEsIb^d<0mQI1jm1^fn4 zj#IqvphCF-5)?ZK8$~%uc^vo(QBF}>f!`EGHm5QBi;zTDG+Azk&utWb0{ssQtuki^ z>N7{pw?HY4HpVu4s0c>UY!F5L*%nQrxmHv3r#ylkq=qI3Lvf|k<5``LpIv^ z6rtTLz5}?#y?a0w?!VqeMd`MEE^?bD+}7nHw>g#fA@fHrvL46uz;ugpEX&Xm_o#?J zb!|Az@b*#JpS!nu~G%VS2YLzw{wNez3VaPo+Y2_-} z(tAhCnQh8A97sZ~qt45Ut!nFW>`<&EOX+L0c*vNb#E`{&|56)5lqhonW>aMF{iQbM zj)CeYL>s>t?e3?Ipwjo2)y5}s%*YT;{G-{#a?!-+%_e$}H`>Z`-2IiP8dJw!G~=Sd z_9T#BLyvkNC@+~&u|}mJdD(<&)l0ze7b{|7>e*Xnc_Z-dYBb4XiuT}_u3R1Zx}PWml||!t`wo^EmUES4C@9Ky9)B#)BN?{ zvg)^I(%7c@RK2|*xOzK%5eZrYOMM^Gs-Mqx_tSBJ(w9ol?vzdfOveREpZgWgyllR$ z1JFN4y8AB`*io1t)g@58Ad;5$eE*rNSS%%{od3bh?-M-DG>6_zxG7X5Luk^-S+re1 zn(iUmYH5~3rNv%u=GB7Mz@jJ)qbP+(#VQzoqh3W(New4%nt|hm3Z0I)QnRE8V7>)N z(>9HdNUs7jy*)s!XrDxjmVV6Vof(l;KS3jY9tGEa{50Osut z;7TK|Jupr(^-88RKT~oym=u?bDh*)Ubx6p1mO_<07;Sp30ltkheM^g0s${oO+}=*} zkJ6_!GyACdC}PzOE9`}`yhdwkY+(+|nV51(G6q2%QmovYP(+Y%)}}6*wt<1AE0^Oz z3oKo^T)JxJ-h}k?kj)8~%T|ufiJ*y|)*pw)T3-7YP|9i8&+EjTgvB}tyj}oWWJ^mS z<68vaP-=*>HR@SZaWdNt1kFj>0Z2?Fuc6>5LjHmII-SDUwAtU0^l>O_bFwHpn39W| zHh1I+iWz2GPo&5=s;YmBvO`Ou46B3@K20nZnR0-D5du(^0RlXc6rP%T6hNjZ=Bk&V zoQ1NNDdWScnh;?`Q1jHEfiQ~7N`d+s%A-Yv!mx_=29_i66V-ph0lA_)NzDe&7*U?A z7NR^>l&9c#0vH-pc1&ea?Euhi6CKCM+(sL$nUqdt=(juYNezTIuo7b~joK9cKB|O` zq9T@o8{PeG;hY~u>YToci=GLxvfpNPJN7wCfw@_@Ufth{O^q2UJE2U5%2LRpP{ICQ zZ11}fD!O-AtmwmHMGx#_|5K=ld87jh?9`CiEK+mSKzUr$6#&C3P_!-k9g3odmpf$# zd#Q&n&fW&wROWaCObKx}5`Q|1j?mQeho*H3~pNstN@1@p*cp~A*ohd^KsKG$(M zSwcNGFNTU}%%5;0yG3&cSvcA177f(UOYHu_{^$kZSM*Gx=-dNL%x8qjuq~SG?tc~a z6>r}cAZm03kRf7I-M>-09!wb%IzY#sT#E$c5#xdOyt!uYu5<_%_@%nObz(27Bc`7$}-h!mr1T&N}1{ zmf6gS_*tM-niY7A7|TqhNrA@-Kw&nKowQmT1aitwnwgzAWhc$dPAX+5&CE`mvXizo z%KIDCak3;zPuiR$Qe;~JXC!Hhw}PTpSL|Tg5;3d*YyxnR05r9_62MXcIMnJY0Luj6 z1X~l+mJ7hGR<{7SSO9U%M$cBdwaZB%Um?a z@uGSF;(SUTQl-&!$)~xfhp(1=URtQ|g*OwUxKVJjxa)C?s)NLZc3NlIGwfvUpNQ)z z()~1*D9y0A=TM9x$_Up7WWwCP6OClCbT1+1+pZ>Q8F2j!J8ft6Z?Mw=4+JY-@$9gj z_G%_1%|iMN9I z<#epS=0fGopo+7U}=qaKIarP zBkl&ANk>s+^nFaSDvDUVNDerNh^#1P@#bE58*{#f>QM{9UP9Gp_Bk=~-F3}ZAi>GR z*udid1)gxqfa8P;wg4Y0Q;-TZg-*BW1iQ$|jAgrdit$+ycleKyi3(=KDjZ0eH4T;G zL}sM}P|B><@%O4o1a=^^>LFW~3&E(%%xdmh$_OJfYrM>?y3DM0WLB@4T6HtEn(K41 zuHv&FKvk92Roq-xsky-Lxg&e1e^~;pt9WEwCy)_hUBy#25rx)O++0_ww65ajx=N*W z6*t#a9P28twXovW!V2puchI_u2d%4k(7MVUw65Yo>na|!uHr%KDjvM9ik9mtK5>_q z%x%QH$0w!lLKzYK2yWEAFuAO9Qi~F`n49qtEl*Ij@eczqk5H+IS`-453zMi89x-xI z?x1`q#F#J*3jwPvLS$CWyO$niGLHjE1yd=pYGz_J&jUqXRVlA(W?oe(uWD9ajd}>7 z?1+h+M+JGV3M|k<7v= zZD}%X(Ni5JwjLU7SBPYGkg!tg-DF2%q(-YuUSiYUCo2!yqA06}71=%{PV$t@XWDNy zjLWb&HjRd+z0Ic43M71!O{1Y{zs45zBa-KdOhO|Rx-n-bZYdjpR$Mk~Kq!5`2hDaA z*bH;pL<2U zg-{t=$3~jp88o=wgs_Q(tY_J&GKpXLeFN4Mo$c zBpQQ?-Kq0AE8P9U=G7K;8Vg&9a&i_#DIO=`!FH*;UwZ{Ab4EUms#r5WRM@XA1B={WvfcYz!=u%#z+q-ld4K|jlxd|Nk zWJ%Q3<@#uI2ooEDMT9*rj5ug{q~wvjbaT;k1-1j zegdx)M^huz{mYyb)e}r}RyrxLCnQVbN~6C;#rMz_&534Ck0fuX`!CiQpvB?jM|FRB zw0FMz3Rt6amO7VLR;E2hOcNOMZ-E&RsQV3jayf@~Vmr!fCI#E4D>h8k>r zuUzYf_Q1?+9x4VMqL}B2)*%Y{-#kzZlY`s*hF2ql;(|MB^-A*bMpm`AETtx zq_rqR%vlkWLF3^gXv(n-QXi*^?``h^I?debC_doRC7>w32Subx^uMY^AVtYJS>Z|o zNSTsPQ_cX$l7Oz{0vIU)n{pgLwgl{0Dno)+OvdOnAWRtfOPHb@MRB4yP*EJp2(U~R z01s0h1Z4^V(C<|K1Zb)tIU<#Iv}>BI6tDanl<5KpPhd`J)Qz|ziy(cFGuv^rNh|#? zQkw(umqbw!U;He=-9=tf_!R6lnvg?QLQ-4?)dpfWjzw0Zf>P|YjmLNTX~f$1If(pA zh%6pKMGOQMt;Ntpr+CCf_v}J<|7O9Oi;fqpo_v$l{s|OhnTv3w+dTl4BpXErorWuY zd@>X!<(llGa?%B#i}eS-KPRsx)sffK{SNz;fRg>P@Z}1uM&ONi;Zp?OrDot+4=hAR zqoP~~a(N5`-&&{FTpq(LA&guH+Wf)hT|zCDA!f(uW6CJo06a2|ihXy`y%BbgiqE6k z`&*S(g0dyl#KgRSBY1-EM71%Z+A*tIYLaBE(rQsH%{tI!VaQiXH03KVYeBUfQOyQZ zgtll?o2(XNj^ynuoVGfP-q1$P3p~n;!UVQzZnwAZ!jkz+A@;aXz>{#yfY3A z7YUVg2f zCbUvF(|0*ID96D_oYfnZOiLv{K-m~*(?*%vxH#dkwCNh;XG!_C=+Q>_JU+^+v&FHf zn9U+0W<_u;;JD6s)bvbcM#Qv;#j$D<;w3_fR$^=zsuLM^gph0rf?1W!=$L$EcCuAH z)0UG=73U;fovc8K1Cm3lhq=5gB2yy^!t?!VGb629aCWpZDk?UDXQXBj(v6u5ud)!f zB8^3w>LN&gPV!8lCMp!UI98%v0_~U{!t3^iVl8Ij{TMViO7crb#JZrFydNoS<+9aN zfl>u)u+!j1YL;dZbjzE1h=0B0V4LM&yP_8{%u--#Tk3+J*;LV7yxcEG)8Agmb8# z@L`?&zvM;H(WdoA?L|BT60G<&8W!>7)iUH{qk^)78o*>C{SR;EusG2}8g6(N^AxX@ zZ{!dP8$SHQ{W1E#G(>iNRB(HwO)eo+mqSuyus{pNtQ8&VZlhu$Pu3_rjh`&yxyseX zvx_)>+$W-vT5ZOZ92Q)-A*lPcKu?D@7}l#lWF=#LsJ4>N0}!z$mR$+l+%Jy)}R-Uf2EuD1o;|CJ2V|w z6+EG6h}PivOfl>uW#-C~9b&lWg#?Fx89=E(28Wzv{i#3;wS$_c2VzE!{owV%u-t5Q zf0&gD!sXh;R2>MTh+*;O^!JWA-Bb2Jzml;-RzuRAH1mrUom&L{kfubNL#v*ADwh#f zEDQ^}xZ!;sAL#Rh;eDPcM|6^CB_@6Gl$}5G(m+ThPssU@KFy3|B}CBy0mu5+SN=fO zgF+<`@xvCXmb)F6yTkAVBh_Hr?UlI0FLq=fGV#}IegH~H|IE5^<0@Er@ zN0^4At&WK~|1hJhjg{HZ$u%zEa%-(6#sj(Ye0?x9cVcW}cGxQF&F7Ff*axR}R@_0h;+p1{U}0!1Wi zYtE6=L+H|J$O>uLTpc1{Y9?e_NnJ?w%0Ymw=MX`T;ay>^)3G}Vmu}vt1kF+rp`N8Q z;j}_A(i#&KG#I|f-ZC+U(Qu|taEnwGC7l*6-5(C-7I7sevXy+N~4YBS;7Y%P`gm8`+3e*MoQkgy?1Fese>Zy?~*#&MI z0nw&0##5!-86js$vpj(BVLbiWa>_wl_?&4CwhUN)-Etn5|cz9UrKrn z=ojfL+I8L0X+Zf=*Y9zxbKH&Qt<`gQc#R&0Du20@ zr;O;-6?IgsjxE-;)M^P$NkdCC10VW8keo)$+nAh$5P#HM51g=T+K6$->>AE zN|y1rl{|l(p0!ZVxJ|!Uc%$|%Jym+c$u`=iB$&>R@6^)RC?ZiLMmkJJzO}t0t-5VV$Swg0>n-9RXPZqx`|WtaeV^%iXPfb zLhl?g+-rH}yi7d?{SoHK*Y(Z1i((SR#MyeRqgi~Yxh~zgL09h9$KIny9?_LHeQcpw zSW&2_s*{YiwLGuzD?Or4ced$zjqXPKc!5r#%s8Q4u0aU2>dIGo4h4Fw9_2Wo>#cg2 z)RIjfj+^vElD|ZcuSWo?IHSgKnsnh@a>);b`ZRnK02h%Dh5z(IK#I6^5RQHkrY@ix zMv|`5vx>Y|L7abOytj_WdmlA*{O7c=5dyZ65Rn$vp`?gg2dNNG0wXeYm7aFvP6hpu zrN{5r-G9{i65UatM^npcbo*w@q&a zYeRcKgcb@QN=ZM|di+!gTz<>K?jp;?O;|?3BRwF7`~w7vAulso989JMOzM#55f_?O_=E;z#0=}I}EodQzk>3Mt~ zV$beqZ!63!RL8{XN|rvRUf2Jrk6Wi_AV+UjIXs<7USb9QsY5pk%XylY9;)GyN+$hf zUnV|AUYMCvP%!>{|0Wdh9jdAn6ig_{GygLYVD}Sc)l~%rKlE<{iP0fumG_64eyH;Q z3C~}^2vP8rhJx?cWZ;zt2VTvvsWN2PMLdQ|inw*qm=GgQ1f7Vtr^%;cDAG?ZR1ueQ z*d!wc>Ddb&g&0)F7>hRY;^-Fa-$X7LO@rko-D7lb%JWEeOU>Y}6@V`bQ zpJlJXMBl2<#GJlR%{7`f@x|UuJ%eVsHhomR&Jkvc)T1uaZSA^qfgb;$?tV~rmg;eT)7^hVx1+&|@-x^?t^SW0Y62@j zlc!tF)<&bGh?{7(@W0-OCeoLPJWG{U%et5 z`+w11McTzpYR`ps`nD#XlKda4_{f+P8BNYTx^jWOu#oVls(4-~esZB6_mwfOn&KdwB>ULJM%I4~d*6Q*q+!$$Sx{}pYv`S$|>v*xfx%!H>>Xud` zrk+oVsBCC!X@y99d1aK>^D)M!^*r3Tx}ML~x7SuTR~vo%c+!}bri#jHc41p{OJg&u zX|C8>4gMNbt8A#QXlAW=g0~!xQ#3Xkd0TkO$i|wQ7I>$rxw^f)wWWnsHda-aw^VN~ zpFD9>T`OBSf8`3}+AVyg@#YrZLJsk$6^V@m^H=d7!0Eyu6b%QwS*%oIJ$aU zm08>PdlO%jggz!`S5`E&pbxfHp*OZRUD#MQHmX?XYD-X~pL1 z`OS6h)y-%aWN&Y-L ywAVGawpBEgS5#FsGr?wsW68GG<|`L8RBVQ-%31_iT?^t< z)~aZ)sB5qw!`{d<3Ywszyh`XOmkp?)x*;N&U=n5-o@PG6i->7~pM)h%2wJj8L_8I0 z>oy}Ig=N(Us?80Jn_wu3ARbYm0Z=}%eCsCa$mUj7QCSIAxQpsHG;R;7E}Pt1+uYc; zxmM6C+U;g5jQg5-x;w;FwBAsz$MFa(Y->PlH$ptdSOtx-t&Q!~tO{d_RkgIz*TA@n z=vK_|-8~`jQ6NU>l?c_X1@PUL{7P0(+qkv5pm}RW(Zs?D1@jv#+qR}~p;54Ey<70Iy*BUo$6MTc z{EUKUV0H%~j)Q^T6=^Y?22Agbv|w5$(K{q9I36&)Ptt%< zzXYbYW?HcKF(B{?O+Vsg!1Ojvi-8`zrZ;O^@HxQrmQ4$e)EJ{TZdz~_V0!ze1y2D? zZ{oDzrGV+JoF?pL6+qA{IxU8F!1TIK3#KPI>D8SUd=FrHji&|w0WiJN(}G_BOt1H} z;CBJjt3ECGEMR)=r-b2udSIX40ctU%1E%+ZTJQwG^lnfKUIdul6KcU50Mk1|Ew~vl zy+71~uLn%;61Cty!1P{G0edmb=tlzdl2MD{FkpJ&s0F_Xm|i|=!Dj%|i%2b4x8YDd z2u=Y^FDSL>1%T;gr4~FNFuk~xU_5Nc*;*jx&N9aCM}UzZTP6R`E5 z8^PBAw%(6J@Q(nuFEJjtile5jb&H!)KM?y%C}PxiJrEn-zhSDMzaXKbzD9bdxOyf zR7z_&UJW&-6(OJ)USiuTAi80E?fb9=yQIcR7t0y7_1q zy(}QV3j2%Axwt1cL!VNsj){b)fI^)Y5aIk#8QPRUd z_x^h%G@E~z_{1N*8^J0Q;^99q_K91N#Lc1|0c4el@YBP1VO@-15jTtW8XzhcnxxzQ zj^B==A^2M;?nI>Pg!ySPPWXVat(d*6Ka20o5WagpGM3!LM@Jt02varu9tZj+W7kbs qa;*Q-xD%xCwC|DcgdF3GtJT;8kuCb(UcR?i+uMtR?cQGdqyG=#5dlj8 diff --git a/bin/disk.o b/bin/disk.o index 22f0a987cf007f175fa13f9987b7af7a0c33406b..5119dc48f96a31df4ebdd09e90a61d42d8a5d021 100644 GIT binary patch literal 4700 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 G>-`s?HAtfX literal 14236 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 diff --git a/bin/framebuffer.o b/bin/framebuffer.o index b24109fa667a5f6267f52ba8ac0f6c6bfc6d43be..412bed94710e1e6b274165dc9179ad82b35b82cf 100644 GIT binary patch literal 6456 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 BGLQfO literal 6672 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 diff --git a/bin/gdt.o b/bin/gdt.o index 453fc32b821e1ebccdd006999194dcd5cc0d7a9e..588415d7f81b5f2e9e7c02ba43b7982f1e1ce974 100644 GIT binary patch literal 4500 zcmds4TZ|k>6|J70o}Jx!cpt{ujYB+k{ER)bPy9-Z*XzgHuwcpd3dwwQdS-fO+B?%T z>F$ZWt0*8Mkew*7wm^{(D2W8($KnHtABY4fiXy%uAtCtSgNJyEhcHM;#9}$8x@xzz z1c`61RDJHbb?a8ut=G(_&M#ar3`1x!gef+e5cdml&5VSDVoZ$6`Yib0wmtsd;+!yo)%^!C~~jnoHkuN{f?r8{d+LR4Kx z!Oe8v*OBtn4Vp$q&(W0XJ4Vy@_!)iV6Eqpw?O^G&<@pgDtc!3rjm+}^>kDvr5hcTT z>!L4|4Km+BgTC9)vk%yG*9d+3e-6EAHnYD389))HSKLP&_!G$uNz%D6oDk-+B-w!* zXk)gEe}+R|WY@@MM_&iC*z#W}vg;k9|C6Z8Sf{i5fdjWWxY<3&Q7;C*1&ej&iD}qU zBKr(kVLO}lzXDU%dNkVy9QXw+)?>xE%}?m{K}k{rzb7dbJ_?6){-<=?Es24o-t5EN@gta&3DZ|RY!}@)yURe1N4sE|~pD8%c!T*46pUL+#{z1i4nfycYLy8-j ze3tQ^R9s~84_JRV=2_7vimPxChBcFYg0otr`-dK<4rL0Ad5}rlCAOtG^T8|l82$UG zTPFR6m_*~HLd=HukloF&W)8*p${^zy_v9L-?ncmujD5wCoLSsi95RM8gO4E9JA_$0 znR_aCB6n#7%eGhKavz2Va;Q{X0hVE08Zl6v*)1-OjOVaz_Cvv{%p_W$9(#HWt2JVn z#au29$uUDA+GdgOA2Ts}v0A?deiF3Jw9%?`G0!A$FF-nY1r%p zQTb-A=>%YYr(ErMt;q9Z0>|2Nxf^sMw-$Bi;e;!Gtr>LuPA@F?{F|O%FGo(b<%SF< z-D@o;Z0bf{hoNdq2`-o}TTaAnv|T^4Tb-N24Z9Pf>V&S{^cqc3cbDu=*ABf^S2P00 z@3ox3i&jL>550!()=L~Q7s2*?blYmlM6tb)Qz~sZ^0M?hz8$WFk=qt>uwwq)wJXAA zbCdv4d!bvm1GnM9DG>9mPSt6hbHiHTbusa4oS{HhYIWI}jgbe50kI#aUJqO@7zfvb z<+zRIQnSQzhG-K{l2g8R_3C*ULr%Nx)rH2Abod2MWj z@n)y(jyE0OTWYUP92>uUb=+H;nwp*{PfbismpkE#x4gWp62kE{DJ69USW9 zQH+(!^b`l3$g*67tMM#0b0Wn2*|R502N$b7KkCI>usqq5Z1RPviOR%GW%5vL=NaRv z%cEE**ta|_-=~fD+;$X-v3f-q&u5LD`E>d=&KA!s3uTU<^Tq6EQ*#At^=)|II)LeE z+98{#I_CO|FdLEX#d7i$m9g}O#eoZ!s2yM?Qp@V{-X%|o{Ju-f*m<3}qEH?q4mJ8Jg)<%)Al*z*gQ=gutH zmoHqndj6Vy?abW5dHem39^M&@C56qyaSePxh#kPg`0yu5KQ2#I_z3^?y5qLcl~&fm`TV~&XP3KtY!SIFPEtnVm%S>cxyzM=4&3csuH z#|nR?keiip^4xpCF@*;dPAi;M_%Vgo6*d&!RQRgGZzz06A%A2s{%;ljLtz#$Lf5iHeVBv4X3tEZ;rF8-A~X2ea!2c&%i=Zqr^0oHl|qf=;iC+5XS+ z8+ay9F!vvC818f4nY{B@c&4#ra$f`Foj6BD3F|03QFtTGqEP;hU;h(8#=~;RSd7o} z!!}rADWB$72Z355ha5(s|At?}hxMAubF;;ddqa!9yE&HR`1a!L>G{&1e&pv+7(2s3 zLp}$hW0329>j|Cqmr%jKOIozwGcY9kJ%WClsAOJf*Zp$wbQ`E5gNjEJ2CaT(i(d(k zUWpBqXulW0ll^`XXKfHR_6v2RUwLnUYnes7dzt`syt~ko@xF#2I)8jw^gP~BZJ2M$ zj}Y&*EqP(g`zS>1_bh@P0oTGhmTxQ6_OG(jgwTibHVTis?&@{;>Gz%W93SOpC~Tws z-h|%*E0kzI{sm0V_de#U^QX@ThmwrN5Lq4m!XPVI3KH2l<0Zz QOeX!_M8Ev&tVR3%8^Y9F2mk;8 literal 4384 zcmds4O>7)V6|U}{amJa<*p5SDZxTGt=Fdsy$3N?MLpHHvC$<-htP)$|M5kw_J=1um z$Lj9c#MWwAAXz259Gpl9306Wm!C@~eEe8-BAc_#D%>^VR4rl=ZE2P!3hyw)7_f=Qh zjRA=RC!SRGy}x=@^{V>S%oi>%T+uX5M5GBrY)e8+WJ1fBgp;CQ^htXP^3uKEeEARE zzk9m5xtZjcH#g{XZ|UiqOHY6JLEqhtB9-L3?{1t4`Q>{Xr@(Fe~W$tMB(#NNMwA!B#ad)q_l^$G_#KZdql^NDNXj>0_@_*pQ5F>!u=xI2k1y#d6~*&^5gmp_*0TUs~5nZCZC!R*>`|Av$50@MAYs>NIdsR@<>Gr!0pLY42Z4-ceMLO zbe+e@CJnlE%hbG)38kLTL8aaApoK@Sy|vo~JdkU^Vw4Vb8^>75H?bVEIIxRyCrnFw1&sZ$v#s2gCSiGjq z_tKUc6!X1BMC`;gsdGD~*)2y7YGG_0hn?=xj%9n(#=yaW9&K+&_W|4xwHB0&j0_T6 zd{!(~{Yb zlXLfzrRC&Y4Sg-CeN#vE)K)$FWi#-7QE63e)3e<{U*VTq*!VI=Hh%ERR6Yh_@UA%t%vWy{j+;plU zSX;GCW@alXPLq<{3@50`g_+f@m6_pIIM?tvCL=RC!dYV(k_nvs%u;T8_-t-?xG-8M z&uI)J$h?ZBWzu=5djOX&TTz+}kvuz~StvW76xsLoz?hcaM%9qs0$oHcum05hNpUVdiq@MZoXj?kFE5&lQwKCw* zz*_Jc;U@v!Ic$9|@N}0DgTPKac6o(o6|#_O&@-zV2J@$;hLm!gwwpg6LVSh@=OaWo zpCH1Ize$PA5%G$`Pb>Va!exa`g?L4!|C+)(3LhwZTjBQ<{!HQf3O`i%H-(+NE13Ti z3Xdo(Dm<@nPT>uO4TZNAZYX?9;kOljN8uxdzg74#5&2_oG+ez7BG!>1qV*}6cS}RZ z`?8fsmDOXxe)>%<-QP2x`J@T?*hhgoyxs2M22sN+OUd2}bTly6odt%Pa zG4DI?Ht#!sv#_xa;5bnMLSkKu|B9iFbPx?kEOSnI0q0W!G9HdX#$xt zQ4p{>%AnI|^xyJ}`OqGT@^0+#i`GiN=(iWw5?|jdSYNbX`qPi{3>ste&XQh-5XGPz z&0CG})V~442{ndtvJOV`eieK)2CR4=29J!Q$i}}A@pykiygBF?kFQA-?;&`OVZKR= z`2H^J$P4YVmH{Kb2dJ};I6x77*uJkY@>{|ool!o7$nUY@Bhwdom@%Rs>E~$Vqb1#j z-vSMaxDOKl@x}Lh9!ZPxw-2^xzwuZc_k{C8iT@Y!)ukWDF;;Y!!HOqPtdmg`t&iVi P+;0K%eh@hUBfozDLu_QY diff --git a/bin/idt.o b/bin/idt.o index f24a863ea33d4cafb8795b8d2385b5bab8f878f0..94a1f3addeccdc541a81867eadec9f014260ed66 100644 GIT binary patch delta 2392 zcmZ8iYiv|S6rQ>F&hB>azVCM1rUiCw>q@1&Td0;ILTSO;K&c67C0FMj~h=kSvvuXo5iv#>5Z<1QR7bVnQsWe&_Dg7AKkeo$tKn z%$b?pQ`J|iUybtLbFs?QU;c&Dori90n*23<>+a0V%u9KVF_xXghxK1%GtPpTm83ZI zl$P7u6E3Lyymb22?Do5t@{}X@V=d3hBK}@x@oX9ylj1gdmP2N4le{{r4g)mi`iWR1 zoTHd^__*{M*B&N56-n(N`tg>IN{ku_D|Iy21oiPtGEgxL;9 zeQAMnLzpMXzD#jlcy^6G!}R(B*SIjFRPH0n&V+jd@ki~|9n3q0=nU#y<2ne*{RA1C zLnNaqQ~1!%?54~xpFw-^Qxmx16*~C@7DB~Ej4TCBum(B!BFAx9e<0VR3!w=0*xDFv zQ_JlPWhrdYGHSV&$r+(~@{B$}P1SPSLPO*oU0Lw%vWCbzW>F$7H;O{2vhb&*^*SK3 zF%6&l|as*0{C&F~!3iC0u(af}2*mV^%%~f#0 z6;YxKTjIRU;xId6tj$WAj5+=A!^zhF7v5&AE?ZfK6SuhTGtELyigA1A`7B3}IbUI# zUejZeI^%)fRfub|=JZ}Wp_ohPqqV76e?4op3VN*+hj4!}qzWeZ>ay7l#>;5eO{H%% zmr-3IF|RH-OCjRg;bvJYmd0n_7n;Vt#njn0>K~u>dGY1wIHRu1o35G$Hn=OD=q0`{ zy(>L2+Lsu>Be1o$z(hF{P#}wki;!!pC_W(qH{~T!9@d#=@OSj0{+!@3(K;wvv>N%3 zSR8R;ndHF8P+xMOdw8&aXn(39*|AA}<0_VMN5X8~*col9Ylt=`>g8p}n}KL;e{X;7 z=-5cQuQxq7GAz3ty&lxH1CM;i?(TG-+~btizOmjU6n$f3}qALEiEge z4|Vp852wc|XChTwH*T}K7ni0Isrp3S;)34(N%CMX0uxoxPS4ZN6rO?HTvWaCIApfY z=XzOd_KLNWx^{Rf?=cJ(g}Moo?DQV)^Z6>jW(rR7Oi_Q363-2tIi%m+!9B9Woy>&h z43Kn&Q=B(+7M{n*9{eAtbDJAzK68Rn`Sg5(?9UmBh+;C_dg4n+#-Or8@`SrV*c*_) zxKC%O4O%huKzD>O+&A_xNTtHXgedVzLcF!DK)RHJiXTw?b>fISNgOZe6CmaLj`WD9 z+BoW=^*A01)d&uzs2#g|HPMd3AtzbU+@(2K^?LJJhuDr{G{UEyAZ6AC{dL}xx!{0t#Fa+Yu# z5?&wyJHMhFepdL0Lb{%`uwP-d!X<>54GQfR<1PfKVCeSBkG&N|xQfq1p*iID-qZ4J zUs-S%4=k~JcszlpHrh9qo{*<}O*QN`yyo9EYCH~%;Sd=e4&5-Cx8(wVIWLxt{zd$N z+~Tj~cjOEHxHyM$nT!Q2`Gr3)k6rf%_&K>CP$E5n0MA2;i;M8>kZplVzEci?N07Kz z9s!?_X9ID5UfzKA2x`A7gTYF1*^63aBcx}s%jaZwuu{9_k|%?a;RxGJkdaqk(Z-i0&nJ|54OP`T*B SSsb%B`bsDu?x3{4iTw-26hZ<3 delta 2432 zcmb7FYiv|S6rQ>F&i3x!?z>M5?rt9}(8qSSMILQvOB-els;^?QIxu3 zC59lvIwmNfLSoeT#{@~>2N+@jA;BLtXz+&#n3yQ&4>W2F#QL4P(+G)wyvdyJobQ}D zXXfn8?6r!M6$iuoN+wbk?cDprmYJXPe!M$BKfilcV~h=aF$2lwbN1V~cZSGJs>kEy zN62yK;J&SQFVALfR`dO`h#ybeCVB4r_8H}~`xceYF2;1hxIs?q9f-wE_rwkJtRdQ< zcnadMbR&m2@f(<6Q7bW8&IuUeRp>>9oy2hS03g>8$tGDLz^ag9S z)e&4}Aj=g$ z->kp4OjB`VF@E77*FBb%Egmr`-sNRh#OQ!#`FVvWXz8Jnke>$}zCxTiwFZ+hH{vX| zoq4cUbB&d)n_a2b&MLAb<@D9F_F&eewfgOqs3}dwL>#xV?yhcvHo1{(jI#3kas zA|Xncz>kUU=5i4*OGSQ0_|6D@y)a|K&oqM%h@3(5cadO=G($7zL@|-?3SD;&ObH$l z?Mcz9Es<@GWrhQluP(bQz@(TTCaiSCh+YOCW@wi$mhk*KMyU#4gob5=Cc@nkNjKQN5Ja3IO9A|2l$ z$*l%qCzdVNsWjYlsTl4Kb3x<-oi04bgt!}J%BI)3(#zOppyp#Z-dTG>*{=a<-g{)D z`1?T0caeCyV?d6@(R0CJ1PGlh%?h##dw>uJu9OhFp*Mj7>lLn6_^`qa3cC~zC>&Av zibDEBpge~aexUGE8`F$^rijZ5zgBod;V%mRQYh#iA)#MkrNWg8=`V}?k13ocL}y-B zHoe eIBv9!8 t4m#4XW}CwsEIR^G_jOQVq{F`hbIIvI31{-{K(y!@R{N`pB%q4){R4SnRPz7; diff --git a/bin/inserter b/bin/inserter index 4e95e5b1f02e91a5a6ea7443b5c401299c9c4365..955eb8a18be30f7d515b784bb4fa3e1d4174a3ab 100755 GIT binary patch literal 32304 zcmeHwd3aREmG8aXD(N;YwOSimz|fc-qXn@WVI-iT!CMe`y&*v6OD0Svi!+mP7~90c&LEOF2~M1ZmpBRW;E5EN3|1V6@z~bZ~kZEr>!|2y?inA5O}CP*A#8D{l?fFIiOH+F05eY>(|L-M4H>>5@fdkx*GB zH-YS;Iw+>r-%+bUO+VqJ8+l*k%QDe-KJe)8=FZ5T_2`>v@5Ua~AG~ze2aA*rs*`vq zk-Ri1QKq3tJk)-h{+5xWP0J-5=`X}z#py#?)tf)_u~!4%Z!0A#-PpzE_V$4W8>f($H6Zc2fun8{Ab3&&l(4R1$Ynstmz>j#1C7m5VpundYPP`p|4n8DL1 zke=`53G7ixd|2Xl$oXxH97JK@bF>o8Lk&k#^T96UNzcDXd+y;>O}ks-*Gm0yiPtbr z8faK7(E|H|QLQP|5op)^{$M23uza~c5^1PwZ_=8gf!0>7BNmN-6p7X~Jmha^dC1>X z7X&sOY>zf+ZGpB(0P>MQ;2|nBv~_UMv0rOyXbnXIT3cOfYp6ksL>mL)u-3FU9E=9E zrf{IH5lS2O)%lx(?RBlehrt3g^^%{R=+0sOAQ6g7a zwpjCT*uL2h{ek9SBpL{B-@K+Z)E?MgSKmt7n%hF{Qk!3}j>$-_vGIR)38|bW^VE?# z{@F~Qgt6lriI7w z(4C1gU6_Z5Y7%9-AP)~MP-V2{{oANDSZtI1D7RV40ZVz8rCe(%Z?=@ZmhwtVd9kHj zYAMgOly9<>3(VyO|FR+e>I1&`8@|rV*S2oo)Wc>Kqbqx|zKWX%U(d>?3N&q`U>G|J zZ}*~D3|U_f%O$Ym4JPvZp=u}=_AGvbz}ydf@oT=bzgy)y zd)@BS&-q^cAes*apO*@ZkxNb7uS%Z$``F4eAZxK(YJHt64^h?^e>0lt>t6W;3WLE9 zMn(o3(K+YX(O|-a%j7dwszBHd%Znue?&7wQ+MrE zrED{Dwby+5ce0<=4%<4tSE2Um3hyi7pM&Gte@X2fLM@bC$bQz{O`6Hz^U%EPrAWqU zG<4+3k`H6rlX04c+m#%X9bY_6Hvgv#A&Y%0yw@WY1KvT^(!oSaku$e+5G@_#Exoe1 z+xtq~d-cj}w7qjskH4g3YOi+wR*%1$BG`6S?|kH{7W)yHE+*}YKk{C;_r0^1Y)KU6 zo(k$xbjPK5?d#y@eae-0Z@%;#4Rv4q`@ZR@o}(!RLAmQDA`cASENSPe zoo?beSDpDq0kbrMO#{_fP0JkaM`R8+L*3rj&knjpbDH8?5zL$$_aHKdF$A6jLkXb= zs`4GL%<=oT;_xf8G?cD0djC3^3TzP z4g-FVO$6YL)xfWS#|5__t{kobb|V;jlf64=hx*6+qZxzE6xj1q)Sq1rf!(B@H(aXu zn=#aELzKXskMMzi;js5o_Osqg2&3$0FLrw`!y|tIkHpWSX9nXx6Tz|so!pDdSkZk+ zYyPB~?W5Fm;ajls=emCLuOlNiUyrv)L~qaHZV*I~((_r09xLM3l|bDGs2M`$;r!F5EfLH3%g9rwzT156RU1 zG+}*b-(bF;On1@>A$KRYAiL{x5a{OezO$E@PapJ^GPDWPP?bBo>xt3S_uz=^Z{UCb z*-IEL7TI?aRL6yQ*HswM-9w*b(;rg|?+^nOhV^g?Z9K{G{lL4p1aObQ4i2wO>f|=+ zooA>~tFoUcq;M5P8h8l)MJo8~ubYD(om2tUqx$COFvp(sO3&gsbg4w&EJf|2Z-|8| zAviPzVafZ3dW|sDH=L426wx<8oBE~)Q8du?I`LB9oX4m1L-*5!9j|Zx{@?hp?&=*= zK)>MY@fon(Bj!HQp9iJP{S+wP*=Glxa6jtk=JhA~_C2hiQhHC=A$$Iv1eJR462u3( zF5?r{YgcA=^1R;73BI2EE<$7dU|WJJ(`-WE;29Dlzr}vg%>`C)JN8JgVWoG<=^!4DAfqm1`1HW`l~g96>uhEt-iDGsTx zDGqsmcAg=CIJ^a6EQ!`Q{MjGOaaeOt&D}M%HQTpYW(}V?GS4sQKiBmah;R&;`an2A$KsXr?>@Q?L68c}*WBZy37tr$$l8Hv zSCFwPx$!hQ8`tSU9c%7yQvNxj`~LWj@1cII z&tCE!e1n!lzW6`;=KjPNANDPOEt;Dctw&~1$7)xGz3>JbsF6I&VIY^?4S+dZS%#uPQ%PA)!n=m$}G&F{S~TrZ}N3>rtYVy$=yBF+<0bh zC-(sQmC9I()BL`i<06>{&k&M&dCy;^P$s5!uP^@k&|mOyQ{E&?DBt7LQ>)GImF4)i zyVDNGe~9UnzE;9yUr+#3<7qk4AW`v>AC%OP_eXTqZt)pj+ zQP(y;aojPCUXgy${zPOY_-JX8oIg@y3sz!{b!RfJt}In@!tZHtK&`vGhrHuE`)j99 zf7Ns$X~)E#;sDwD=iy?`_D#4M&RRKGhJ|f4xW}A|XM-TRh%PTQ0)5@P&SNDrS*>J_ z60Rq3@xGe-Yj)K)nx>{TWl3Fx$H~7$L*HG?$ny_O`_52R)_ZUaGG3lBzrogCP<}xozncwnRNNAVq zlK1fCcFL+Hh5Al{N~@OF$vAFuV$s8J=O!1l|B7aG_Ye=Y1MW37_Lsk>{`e_q`astu z0${!}4@(AOSF7X1H8JK8%o<$|IeX_B0;~3X@3Awag-@{LU%D!DKhHzcu%aRwsV=|* z3Hfu6Gg<*_SYM%pS)0HJmSTH;l{7VVuDyy8fh808OtZ+t_^KY)0aAUA*QX6-qY2|U z!;{_h7C5m4gBFq@b10uQhe|Mqz=xZ&D`5^6o!}m2+OKZm6P)TD;xQS17F22YD9O(Y z$(1l6JG<+n0HatAnZy%ao9eF@5b)9`qvKbxO&bnZ z@g*Mdehs{1xR|d+Qo=M1Q8*fF9}{Z>R<1o#bZFz?Kfn+ei~rqm@Ylz|zYRPsl}1Gk zjj^#RppnV(Df8a7xtRBS7|&x)D&-)^5kc!|Y9LmU1hAg|3>N8weQMJeOOJjFVPZz@YFcx7m075a%s(&T1%gcj-isL5-k+fz>^kkF$GzB&^PY-(e+oc;adW@a z_WL?t%7cGyMcpu1nDq|Lgn~`DkF(+Ke(^*z+YgA1hv6k`^+Erx^S(H?8FxUa_JS|I z)t&u~+Kbq=EXsbi_GgE;mJD`b#>1|cC;OZIaqs6kANiaXbHO3s!dA#&4zyia1f0%s zFA?tje+JG2oQ>mNBis<-G9h)DNp6JvFrM`UvA>xC)M1usb}TOH)~qW-L$l?-4Opn&Gwr3d$qov(ys%( zZPVQM;TB_X)tj*3?ECiU)CE76-&bsk4{wV9Zf*QGH6!`I@^!wf`%LuT zIrVZ=Q(5-Z$EhFr_dhR7NIH|>d~x!Pj~gDi;ei_-`2W@ebPn;u(wa~#90_<5X>A}9 zY;F%Udg}Lc+@`vE&o+Km(z9XvYL91gU1PwrHyCa4R4pWi(qyWvsH8>0jr9MH2ev#! zw%)a6%XX{H!S+ZXj1#iAddkXG-c!{SYz=s#A&s&ir~Jv4(~~ zB*IPdhQlEkK}yP-LajKu>*31kTIpc$eh&^MN1~eK+R(lS2ZS3vsa7d!ZTmy*p}p-M zoGuQ9wY@lrFv-5+JZdSzU)nE5M(9rCHNY8IZeIqh0{q-BM@B9K-}9@Hk?knI3K#{P zb7^GcDZsA*ejRYbuSZ5+1H2#bZNQChjEv-C=zRll5#X)=1bM)NfKkBnfKLHtUxqy3 z*Rj-j4X_Fe*S7(?e>XDX!M?%~>|U$|d*M zG<~02pOl$l&=*aFr|+^)qdnN7)3mi2*e%lVY>%cbqRi_fBYef{a<6k0ZOnG=H6GJe zmE1aS;f$M!O!D{R?-cmBAR&xKtGy7|73!miFUW4pZNJ>RsY6h{qsTJ z4f^j=-nF7^*`vDu#=YT#R^lHQ#e^x-z zcYf0TiL@SeB)v1kUYgF0^?-K=cpGqYtJ4`S#cRKw#u zG0>kJhfZ<$0_d-So{Kn6$ipA@G`p%sqgc8OzJ82@0P@;8m-~~p4X&b3*u5@KPny>? z=LlQts(2#Z=UUd8vEEhP?pjvks;F_zS?%(yb``C5xmUXk`esO*k9r27hnjRF-SEH- z58Uv;4G-M#z<Nza++!bAtDJhsKiirom)U!YazTKdN(@8w3=ey)%e2M4vaI%ai z^_&-7Pbtw_iqaoG7zq(PN!Ly!bxo^I7i9^wK}lCPX!+7Wr(ol2V9MesaY~Y6+U_j^ zjY}|39w|@jKuQ?`@aI)2dr}&x@aJVl*?m#+V>^^5T7y%XB0!>j_}+lG57lwe4`lhU z)Hjl(b4e^CIRAD@$N4Eaq7o#At>FGNG(r4pC_hb~J zoz}-~^<#w1DCZmnl(+Tyj7Ycv0<)*^VztqZ!opIp*!x~M;GpXCX|8#kxZ5!sP`^39 zf)`sRyel7)tN<6KKjP0B)btqzq`=m%+bJv3{@{kp*BV64s+Jl_esALBuXY1Qe zeVaB@G-<1|KGvk^Oq$S=`LYc38C;EMf4Z!*5x7qv&eJry>y!cfNf<%NI#Qmb2FEcB zY_5sRLAT3bZX;A)5k;|G1h!2EHmOe|8|^89ZI^+)6%^a-f-cU$gWPtO>X@HHGU@d3 z*Z9kz51fy%8yOFz0Z$!y)`U-c1_gt*JM42O62b99^qOYa=Nr_j3FHrZX%@e)7FtJu z7Fshj#|awPhJA%W$K#y$K-6C4qCBc1m(E94nUGnAg2%qi_$T76grt4Di}Kopz`m$v zuXVkPg5gYuo%Xw3)B>kdr@}qFkm+1Z`ggjHfRg3BoeKB5s!?z`>qy&uyx?}e1vU2j zc_G(HJ1O>EypZqw2Fdt&VWRUA6?XGNk@GL9P{#`;&OIbk@1h1w*3y3ub@Z3T{fX{K z-wgssPk z=9&$Reg90-tQoFJRC;*k4~gW~GVg<)N%kiUnm0w)d_0@-(w&F1$(@F9XFQwS>305- z8uMf}H73v54wSu{7YZ?(xNn27p5Yir+cWFyO@y%S1 z7KS*bi5SqWdM;UN?>oPxphwc_oDrq`n@B^uK2dPIvZe61ibF7HSsX|bWSnvlHobBN8G$eelI(u+wbNbG9vrq8Grr(s>Y`CxVQH`DLsz0jXacoS-$XrmLrt(V^vt4-_#KegIYpU64p7z+yjT^kUm^! zi#K!GY8r;NzOCBiHB{Z6^v{gaMRx*1*PdyVPw3iHo}z2ddZlZM@d@xv_57Hs2&CUV zN~Px?sV!U6e~Az{aDfW$c(k=M^I{#)HY5dDm`f7UP>J6vW^0a>Eff>l9c+J@Z#3u_sPWB_lSmUa=VB+XWA}KZPBI} zG*G4(bitWo&~;=}eMHJC6(f$amT#>v11SRWQ{0?&L z3JeT6*IJ$|;agqv4dBN=%fK1vlUMO-j zV;H0@=LL_$MwDB5VUA-x6;|*Qw zT)Lz_<~T}hcVyH3$sxyTqHHlXKwGC{Hc{@(-ig9t$LmDdnRNhcy^bGH;l9E~P>wo! zi0%I35DF(9KC*fjH|Lb&55)F>V?AV^aoCCC=b>`iv6y6bbKCkI6Npl8#Gv3?nymvQ zmQnl}2u-LXwp(1z%TSbVECWNjL4-`|FtRI)qJyv%C^%o?Y<|kp44iH}h%D!&B@gG# z(_H^bTya+)Q0ct0T`P$BNse*l!I<=Jk#_^1QIZ)z4VkZk{T%+XwT$PvB#pR??;`73 z3XzOSB}7HkbwEAhbteF?TEigXRcD~ObQIitt*tvIRzm|{QR@>5Hz05DC6w+q+QF5> zSNl2HEavz0B z`ppxb0?J|cwUABrVUUH%%*;UW%u z-;o3PCA?fhG<)A;2MSihh5Btya<;wieMb)z@MXb{ zsZ)Wr_dRox;#Bs8pXc~;zrSUD&$jr`Bd^eCb|4CkPYRf zg){=VIoVVqyWG^)oN+Azaje#+HqoAJ{1~m(^$mHvVbf4^IeN%2 z+rO%~3JV}do^E$JnP??3PslR0V;Ssr0+H}4Au_p$GbxhA^-~j5SWzKwEShSrmmHac zH-O~{1ysyUc!4cK{%Fd>RXLaI_zX#ST2L4=z* z?3E1}Vni6$4s@jS8#;1AGRzPFd2kKIN~Q{^WQUtORs>Y`G#D&m+Zsu^sodntys?`t zy_s+AlmfE`lAo9=pKvHea-p>^id2{sTf4Mm95ral6S!K~HM+0V^?yfz>qu09o=6)fK< zSJFAD>%U%Q8rCf&Z|2vO2=8T zsC)*2Qsw}F@dck{jwUu;@N=<4tbj^UVeBCx9aYjZQ=~H=MAL0}ai89g3B|6}kJA(7 zbPg)AA4i_rc&8+bYW)SJw)hZ)FC+Qi((0%lZEjv19K*La+<<7~-J7_pH#L73GE zmL2T2$Jr+15DV^LcL9`?v7DgsWj5V7!M30~Z1%mwA|jyOo^_t#2xFQpmVTL)r`sLT z%T%SYhh=<7l}mTBEL53^D#H-g53vmdAhC|aDmK59O{`*7eQYBvXx_oL0!%CmvfLAF zVmb4~VEj3j{UV!wkxfLS7M@@=;CNsMTMsa~jO8}7RprdPk>wm`9ylo1`1VKG)E8v~ zT1*X?EE=Fw11i$(6QahjsRiy$-{cr;O3Z$_3<~UlyNiK}u<{#Bzx$KD5(} zPq7R_SFzm3S?W_>J(QXW6q;&>I=+aR2n~JUgIL$(94!o zu|;KUF{)qK3-|w&~wDG#_Gx zFR|%W%vr^zK90KYXO81+7buv2RukxD_YoLo>j^x^RuM2(@Oey+au4WL>{eg__{31f z)_1XORjjUx-B!h#o@1rPURWMwweaRnW%oANy||YxEMs}bpHcOBaQ|_ZL+Z;}4FOpv z`khM0Wz|#pjDxDl>8LYH0QPriULpFFNAOP(01FVoMiqu*GKSq4EN9eM`O|@0)tMTy zY*q^&Q#l`J#l~0Jlo&2$m!@|5zGtZ~k9w`zH z#~PykbsIK$fxzumGKUpGFa#v7UbsB@aniLF$76+<#_qy(M%^{p=i-YixD;yw8X<`9 z-2|e1rnDboE2*@D-9liaVCiMEsbmCLAs{Zc^0KYp%XV&3%Ib)vTnX?obD7N}Oc!DQ*nlfUbwTEJn(pdXmoZBvq(o2vdAoBM&mv)3h(Le*vahD?ALhU3a2o0@ukrqV`hT1`B zXflyu2bq#bJ-QQS;_fd!(r- zC*xZKP|(O*M{k)%p#fG#1O8~RE$NB2x(T!?Kgl=ur=)^vGJ?f9}FwnS}iZ`P1zJiS+MwC~0^)xybEWz;Ly+T0{Y~1JP zMrlM_SP7c!6wwDf;MQS*UPG@FuZto?g#)RJspCW~(w9NZlDviTkp-noxlp!46QZ(QvRnhE62kh^na_c#U|dsR>5;H*E3qhSBC1UWZPvC#NVT5+A}87Q_VT zudl-k(D5qrW)J^J8Qe)U4fS9f9;`t4@CPikCMZYc;bsgt9=PqHWW@ov-rv+(*NnHa z%X&Bp0gLh08f(Lg+<~WY*surkc)Wsar@>2jOdAiO3+b8yM!bj}uT78eU?!^QLTVdb zX)x2Ekw8c4_5?(0{P6xmvi?fVkMIkSu22kp$z76ALPl{<)su)YmU7aGba~S_NzM|| zGQqev*chOpS*Q8;@Lo_&Xbm+78|qs9Lb9&05!OS5$V(QP`-l5JVLKT^Q;ND2mydOE z_WL{P8u2#ySUX~&5p%S7XFd914_XrCGaG5F$D8nD9l{@`3$g~rhNMgB8qMRIoQug0 zZ^9>!C;Y<=6#kYT!hPw7Bzn_4eG6<&^tSqCW8&@Fjxq6R+Q-JkGwtM<`1Hg%Lv!7B zoDNjy5mWJorq1c5;vJeg-6Z(Dn9A=rfg%3YdS7Q zziXo1A~fR;h^N~m-g=O89q>7jP=}VFl&5XLdqg(n^B&MWbXwaoE<)h9bS;-VI1c_P z;OTc+)HizgriiDLoZmg_P{G%LccWgnrCxeTFtyiu9%6vwrBV}EMtIxkdFkil@c&Wj zSNoFKi_z2^w4+L9$A+e zJPv%0tyoj%p%sn352bie^D}O%c{<7UyEW@6q0dXa_3#KCf2Mx19v%6<#P`k3f>+V?ozYJM7;%ck>rdX-EPwMHH^Io|mOddy1Iq(#p>f2})pDTeU z{nj|#!1cICeH(p;#9I%WZI^iKQRMqLURj-l^qo5SQ|()*Z+jn>_%BQoyy|@6!EyBb z**N$w08jB?ZP(uc?@5}G$kF+qn-isEW{B%|Yt~afA4t6Qi*Y9cY^>jB!p%=K62&qQ zTR_R*De_0#aJLj{$Ig-8-x%^Yw}$Gmv~9#)K*V1c+ov^z+B)d=vaxJg#gYYMGvWTA zJ?O6shwJvsJGQ1UZp{3Rv9`AT5HXef{9eh-n%J)3cZ_75`95~+HS)T6n>VaUrQo(WjJpp%HZ=TRpI>P3t-VY0uivy~bCXW)XKT3 ziQ94t2lG#hC3%;M-wsP6$SJ9J!+w7~_Gv^6@p~xVE8KJVwK4wFWvSxkyH2@3L%(vC z$|Qb?EEVVXJ8ZUYt}e>3-3NI>Q~TBBMS@8>dP%bG(dQU;qoT3M71NtMwQ1nv+YGi8Am zIq9@CLYpcH9b%#q5-N|X>e_-(CdAR%S{Y`(HtatE7pRXlgR`!^8PlXJ2HTrLDqmM$ z9}etMCH(ATK;_}VmQHFpnYa%x!gYX>^^S;m&1pIcbc>72Hc!i>oPr*S zS3q5#Re1?AX89dbUcniPQ9`xOQD{2$W|mj?{|b5(ql8v@I<<%w3e(v`p42{pf@>u{ zT|%q1TkM8+(y_JI`4Qj8K>{r8Vj zUiH7aA5n0U9M4LPM`a}Z9w61POuP&7YQ11rz*k zwJ3xv4zS8E9;g0W?-cS8OTb#?*Nh|oma=pl z=djr<31y4GNqV&epvrW?6c4QmZw4f}e9DXir%5!^P+Lflx?C$1*`?JJ!d;lgckwRJ~e`&?MKP3}S2eMHMubmFhdcyd)>`{z4LhOV4_#O}K9f!@A5h==SZ z8RDTt`jm;0G7U}QA%B>R_pMp1IGb>!KM#N9rw*o9umAc3uX|o=D?Ikg(;NTsgzcu0 z?rFWDgXwed2bKxb98j{yq5c{;`#AVJfE$m0=LC2P>+$$|Ccqz^0Kawu{D;8X@t2s$ zujAFbYXUs^aXkJTC&1q~0sf&0@Ejdz2Q4wx0+Ed<__nH?%9L91NBGP6qR6^cP(Tfv zDN%99=_nEZt2q+=n4*3`;df7!h|OwHZAUSS6*BuqiP!T(qvHR)vga;IW_K!lt zc;<37>~XuAyzTB*@8>+M&E4ASYhXq(beDz1l|6itIh3gXHC8i z4~`A3z5rxAZ2?b^H3dDbt*j#y3=pQFt%C)EjUK;WlsfjZCcnqs$UJ+zK`8VFd<{3< zfaW1zR~y{3U5t_3thgjLF%J0qFB^vFa{qxe+|Xp)u~#DT#3Q! zHWJ}#3|@tr;MZ|V^Y0Xak%v8sKN&?m7Zc?`44&p$oesy~NtaH&F?cmZ1nIFDJk_Do z@ff`J9nnt2;3sKjz>_if)ENA!7<^g`zCQ*ZJAt2z!DqzqpO3+3#^A4IuX*5_2d;VG zng_0VfIRSr%o!g!J1<+E-TYU}8FO~^2Th~rot-aPY30D!=;9SXkBrV)36NPN_*;lD zGW^@o(a~O#Z=nUFyWjeu*47(#`T1box-w`R(iFRbTCQ_yo?8tD1E~7R7c`LOBi)*qU<+g;f zC!xGOp(#)bmZ~{8GXITMcojp98z)e?!lhO6R1QT_A%IIKb(E;N7 ziGtuCCSuRxw+YPu#2LQgJoB&1ooB9^oyJ+`>z@R3q2LLnz&bkEB>bx7$-f7dod%hO z7SuUAmmQ?6GyG04&Dp)|ArywZpNx(UHKKFQ@<&lNwnH{jKgkd829Yv#kaLFj4TVD& z!*zqsZpV-_?6^AM_<$_Fl+#`J0dQBH#`*5Li-&EUjt`7(#|O7K2L104@AjX>=(ykv zht7xVUI77F#}&ZyQrTspY}Mc`4-13{@;M(%Kw$x z+of74JD>TCt(!EH!RMfP>5GBnQ)uYur6r%nv@7`(4Yx~KF?M|C6xsZLWC&U8yv1=f zP(I)o(k&f|v=q76mJXq%L!zaZ7I!;d2|F%dnv1q~4jJJWwM^aR&i9P)>v4i@myOPS zms#k4fayZiuJFE(yB!~&88k&vn0v~pOVJ&J;kviLFZxs{?_NLnEDd#M_%&yEVCYuF ziNJUETc4#V#o3d-eR_mkq{wHhQ*Fd{wmR)Q5~eePO#{_fAgdfMF;ouMH{Fi6&J5XP zi<-h45zm4fmlZ09Yi{sF7zznJP%XA5o!#r($r4T8l&?xHFbIBLwOXzl9)IoR$Yx966mLLs2>cm|?VbPshLVW@iq#X=O>JwV5F&tZhpK-XKuOWku0 zN9rDwy9IWl?)mC}i1FUlJFb9n-r3`{!g#x!8fBN(E17#JRvdHB45gw6;%lPr{TK_Z zxDF)igdIfvFhZpc90X~g>mrV@o?V*LDe^|QAUJz+y9kY0;dhHsrNtx#9z0EAd=~03VLa$?2fa}@IMXx2cmwA z@z=!~k2r$@Bc=@}WM5M#QeRUjivH|8O#q>|0FkVSiJ|!1A7ewY=B}DMYwBvYY)+Uv zoUy@quA={J*WV(@F=nE3hmX!rvYYQmtq9IER7q^ZuVF#m?U>bh)z}RO6N(%xNZ%)t z2+Mw;W5V)%1o%ML5g@4p2fB`{+@*!xA}_H7q1A**XO;#w3X^ z-}MqAo9gKn^tL7%n&&{ zt}+uSO#|asMDzGWv`;XhO+@?XQ)pKsG{@ZTM`aV<#!UPf4BgxX&IjN701RQ1@I+?U zR1DdHuE)_(Isu;T`WxifrB&Sm6*JE6uSDZ4COB`_7@7;T0gH*oom60S3US0TDW(Z% zcglg&LUr&7iKK-FO=c7=G?|I0*@xwp)ZViZLAX92lXpLYPQ_mtb)eAPd zi%u*{o#97M0v&#Fb@-PxFAyJP->5k)vi;hPr_kFtV-Fcvmj9UY&l27F?;kpM_GA5a z#`)lHX`w_{So44Cyy=Z#c4W9dJe#_eT^e?VUwrWGQRJQT--zbBejc>V5!lWltJ4^C zc3Oq$o&9F#nIRKm*yId%oq~;*s=GxKv`v^okC*GNo$l-wOx=%Cd%Jt6t>LuZPSJdH zDwVNRrwRThfeWNPc$$#Z#k>An9A!#;-#Wu@4IdS;rd>&vP`<}$#8;b(^^Pzj{BpPD zaQNq#PahSI>Y4n#=emc3M%#ofktX zl9|ydQLuNOCa`?hs|QY#7BRGro67g#xgFJ>;;a)TIOr24F=j}2#{2{Ant>kNBl*ZbhaN(@>^hRmUS z)Ep|o90H$i&ZdYtSbKtdjA=hwAf`FpJH!)X_}_wxiGhq5Be`EnE`te~nO*k-jA6Ma zMm*BB@&5WA0$%xKZ2TJM^|jh4evVT^WE#ZAfuw~X8dzmm)(cii6h{P|Yt@{EW*tZGr&lS5G44Ic2HlzCEy9Wqk(33l$C zVWG?{m7Q`9I8MMeEXVepVxdX8cxe-A`I@Zd)RnG@f;_@jyD0wK{nx_x_)Qpi#gc$`xyHb za`KVJ{Uc41l2f`;aN_zu{TB(G-NY$ka}bLc)Uy4YGmL%8t&pob?+kCWW&W`40=7N# zGoPvZ_2G?$Lpv}Z;s+pm=8yZsj>kLqJ%`oFMPT?Vuv(Dy6!_X&L+ z&?bTYqCy`bbSA{VrV!mgD9cqueOXXM5PU+!u!yL?!%qR@M$|`AfrzNjA}<^Q&Y{@# zQJr9-Ek3xLw*BD(YxGww>V#|Qng_0V;F<^i|Mq}E>zhwWD}5nad z9h)|6*pg_Ew>{wT<2Klv?WJWpZ?9_dwtDPApIu^ZwO4hx{hs!qy`eQk_sjy{=-C5w zAn5bEn?1KqVt%#?GVSg*kNsx*oWLYe4B*aTDz;LX1wFvd39&g#1MDukt=Aq1H8gkv z0b!EE@Att7Qc~9BYsLM!cA?DON_PkEwd3~SK#(b})$O}*S8}60-YQM4Yv0-K+ud%* zEyg}S+l@OBrkZb|)dk&OYWbg|qn82S0L;bW`6A#AfRF$4=;#~3_x#J~Xf4XG12zKA z8^mv%fZqdr9B}n-M@L@*ya(_i;QF^mN9n5eG~jH&4Znjt;1R$^z_$Sp0nWP!dBF4U zKpyaJEIls*KJ~Aoqc-dqJcoUWMSyd#8M6iOOMpQ@dKQ3^@wrXR*kd!MrX^eHmm|W{ z57*~l|8>Mrn@k&?c#?;)MfkJ5H99KRs2R3Z8To56Q+Hbru;qm}U%znnOyVQ?d+>J( zd_s_rNQ4jK?cdK{un{#jVx4ep79P{v_z-ps$LRcXXQffJ3w&^gBO=ei`%! zKtBN(vEO{mpbAJn4e??@Jc(xpbp7UoCZf*+{RYr|h%fOhfTnNzlKHPJJ^bOM&SY~% zlCaed-mT#MDy~k`>7FgkYqoH~02I~t0C@J}^8H@qBRnRzU?KVu&|jW_ZU_B&&~tDR zvIKD)k%vFb^US&$5U!K*T$O%~icZm#o{)g0DX1Up($Dr> zyj~J;2a-toc`fzWUgY^*+(0Iher}7-uaszQMd^>9jQR+kq;n|}owg}$Op;Imc2&{! z1zM3ZG*zgJp5nNq#Mq7v5{*;0NOmPp>q1KS#Y!an7$DMlWuV4iRT*vf1;vML zSCMr6GgMjg;fDaxK2#@ApQ!Q)rEfG!7m`?Z2>uR5#|=`VtlOc~|33rzx$Q)S8ulcPQAQ-~k1D6+EusNd@~AJg?xOf{FG0xw3wq@3xgIZ??~?s}HpYL-vY_(uJkv zB^4o2toVFoWoh}M`3lW$fKJ)nvNQA}tIz8gPpDJ!)5VPB_o&$L;}+cUlH{*N1WNul z#q|nflkmIoc~NnQ*^7E|(h@T7hl2UC6?Ez#4eaIhHG z?*XJ+IGz{<7yq3=CVd}3j2U~f4ed;Pyw*5ESS#6JpG;X(pVJ8V8<>4=o+wot?aaOq zHvlkm-}C!TrSa9>m>h zB7r(rQSZU8+SU}ZAh{Ta6sp~pOf5@x;*do1Ob*|IW8xFM#*wGNYXmA-PQJ7mEvRHU z^P&yNi8cVg7O`Aq@($BWC{5N4;LWN5E?_CayeV1Ic$k;;GEk|)i-`{x8%GvVl#mxw zr)R?&?Zwok(?p59m|7d>#k4ptrbmphDIqAY9tIAvWPgF3^1}7y{m!zs|MDYmPL@_#o{4^1x zUgAPZ91XO2FljrcrHm=KC7PwkaGg{QOiunC>{u{DLBAE_!K1!tnZfr=gCYOUhc{_@9r`MX3u%_so>wu~^b8)e9UCZihp41+dE z?iP8QK^NQihqsAh9iN5RV=7bO$+Rw7fUmXFUu#u*@evn);Z9CX5CnjU>!{XE#`yK=&GU za;Uj)a6{7anL-P@AHtNZUe;=43zPcJZAkjQBGZjWlq?@Zu-%v<8D80tX`3Yp?@7S! zh8_#;$OMfWR3$FUU69^m6p)|HeFGaTA0qPw z`dHE_wQ8grM=3$RQp-Q)vfhhQfvoU|%km1C78=>q3`^e$7g{RjsFzWm{RD(9eaC^c zeI4cJgbT*JlH{(- zGChpqcl0PB<8s_+%E3=)!yRdr1hA_Kb`^F6{p?_&n5+7>a%%L!d87&_WKL_QgVi*? zOnn(m%E-_>e%71L~lHIz`Zt1MQ{uG_SlslCwZ*{Rg5k61JH?;yPKSL#ye z%MrZo5mZ~7MCTu`B6dWG894Ks3^1%pe*{R(J@>O5`bA*w9<_?o-2aSv&cn*T2@c7rZ@>H7b9ws z%~yckJZCbU7c7QYhNy*go?2#G`JG_l@1b_{V+EfFAOAL@+5Fd2DR2I!6r}TPKIJp+ z1o48Uf>w*hUqRBcFqtNLW{g0CWl74{k+&KT66Howuo?6Kgk`BH?EbW8M1HxO7+ zwUAP@@?FT;rord4B*Cc&NhaWuzikq$vg433{Q{gOF*3!0)Fh(^L}#@8TX?J%YPoKVt&mw`9( zt;9G&Sjw0vBBV%BDLHI0C+5GOivyDQ;R(<<@_8~L1ssbrhym-9{mJhDx44if=Dq{_ ztxJ))HI-~M_Z{DFZ9^R^Rq@1rD_t_wrjc>xzQ^`kXd=cQ%~`%Zh~keOr^K165WcPi9+^T~t~}5r(s1&Nijs zb}7$ty{w$7GWB7iFQzrPaeb1W)}ytE8m`Y5Zc;T|e_d=1*E@6#|8|{J9aF=CNDa)u zTdk}&7aO;v-jupJhwfK4En?=3IE)#Xcpl4amYRBNsyPFrG}Xv5vT>eCDm9iDL?q`H ztj-~Pb^hlH44lf6Gg)e}bv1HU)EKWnjY!Es@;ltLDo(+WTFOaL)oagS;`h68EXn?M!KlqER`;W$T%(Lq9TiS1=r5uV~kJI z?N2t0)j4E{Rl6%9l@fK=B+;T`lWtOKF={G9NTlT$=8RN+b1@lKY$ZkMuoQleu{0y% z#ms2KZ4)^D0pa*8Q2|<+t<8$`bWW^banjrZ*c3&+IX)KV6Wp;(JBJC2PGTq=nuL>$ZzJ*5I=IqJm~ zs*DFa-W8W4Lx`fgn8IVGGAQ*HbWt=sWW#5PE*2y?AZ7~>LUv9>Of^FXcqFQn0V1em z2$>^27CUC*!&;A=h&j?d$zx)TB5VAiqhn8o6M8!hr{xTCLwcgoQhA2zSF%_}Tc(UQ znhAvHrs$c@HXafbBZMfOf!vUoccu7De!Jy(xph9#4!N7{%k0 zVATQ4)*SobH&Q$p#-B3M2>{7x8aE8uOkr#t{%GgMWE7b)%`1^J#DO+oO?)1<4D}x8 zWS(Iy=2@Mb2YDJZS#VGB^j?F34AA%L-lryb(PT7FwOq{?`1rxKh>JY_tS zz)>zrM|lpF#+P4G&U3A0S~{zYPd&%e&hgx}d`gJh&+#mgr}#-U@!`LwOP zpcBHT>vH(Cl@~dG7h;|KX6rd}U%u7Fc_&|BJp@61+V@@L3o56f|n#F^rL*zF;XlTdc=Uj`ACh@tdG?E6*@JZRBX3bJxOo+el}j zk6#B#*{*{;FX=@-tBR*q@uEYh{T`ljly66^m}^!L=;e147~!`Oc$P0GV7*1mSqL3! zkx|8OmSBnmw{`K&Roq?0Z>{1@&+-!MZde}Vb@1mlZTDu_y||YzEaf@YuTb?l=z^m> zi`19#8Um_L^gfl2s;Y~`?1HMv?WkJJnixOdM~~#8Uqu|hi6E#z99ydpztKpxVaS}; zBj(SCZ&hb};PUHQ#K_9}0xz&W&1ZxtE*F%c+O523EiVWOUzry0xnvq8h5U&u&8;~o5C(8lTt-F!S zg-_*#@$~-Q=#23Oi%uD)WX#duR%cB&&BEi&*hwI;cknlYKYHWBt@vxkpP0QGZ{Um-qSI>%`L>MjAP^MOk@+BBMy0KM z0f9A=rB}G$Z1wOwU~%3uzRb6+(^}lbS*AHfEMCOv1IUBI_fRG(s*+mXk5MvnJ=|=? zY~5^{mBS0Z$@6fc4AF5`h!-5hywVBktkaaIlLr20hj_|yAal_f+0W9+VfJa9gRFhl zZ}8;LP?XI)$gf|+v)AyVE^ad|%A+%s%``KIU=G0)(~@kCK24$SMNtE5E}!*owA!@i zae^`xqI>kqp&LE8VOzcMxiJu=9T~g~-M+HoMs?$Xy`(J|YWFN__OyHa-Ubxi{)U!i z@(u0wl4g5JlRL1ty`jbLYxji$C874+xbM6qNN;KmfGFMxUDDzA1w9S854|K11S3gF zLPM)N(4xs+Upoj5O)+H_!W{uiT|pLVSC2w8w7C5Yc2>}#Qoo)$(Bkt4?GYxrjXjQ-cLtcaCEeBFYxFR6D?JLhMMFvY zy}LaAO`iKg=OWO}P3Xo(Mvp8|Cj?yq5d*6CBb_C>4)(|ZVWL0C-^vN1P72G4C5TI^ zR(jssxW^?-5iz^45=7M*k}l$o9-XM6m$bWsh<52PzltSqK+41C;(-k(^Z_UsDroajo9q5-p=WE%l;!ysMqw2T##N9AdfLxjJYKxsY!`>P;f1%r%aJMHHXr4p5$A2g11FJoG@(j3$={6k%!}}D_eIU| zphsLyt?uT)m>0`yGXx8w7{jbJ)YdL!X?Qm5f<8QkLM^0s*b_gdr=19D7v5Kox5o!W zOcP#?aB4f10`6TN**ZjE!%k&DC37JJsm0CS26w9*$}V?fBNPSXyYZ*hs@>Tol_57mymMn_xK+!9a zRtOVZ9qz`(%oS=!JTzA7SNFr?yO`e-6jK=q)Z_j9p$^$__11pyG(^4CO3&|5z?0uF zv8m4jh_40+!|9PADPPv&Yx9)(+uRk4$`_W^`WixQ7-WI6jecLV-`&>cZEr5y8EW;q zTgtpmm6Z$2OX!ib5?^4icjwNX43lvgzA%8PxwL@_0)bBjl(b+HMa7^6_jY(nTLd9G zCzgr%K+2Rw(TQB8rSwe^txU?aV5$r3B#p9OsaiizRfb<3Jbrvgp+sX;CA!GULIFQ% zEo*LQD8mWRwPca{UP*I%sH~y_k7i-~A$|m-DvV{U_u?B1f1ag`njgPPl zMsgL9voq|`ss7RgTHGgexghPC0RKhc>ANWUXF6;Mi}Y2&ZyWPN|F?j*p*_be?EmnH^)%-(S z%#?;E(DM=S6mR9RoK6YtNWjbh?YyzBQmftR+$9_c$` z#Q&x;T>nm3tMG|;;jNuO&pi|1eZW({C${T;;O()7sQjbA&m0F&&o^Y7(#T11=^4TU znRt`U&lEoKSJn%_kM|uUwz+UDT){T1`F-s; zPr6)8%9&2gR?pGhOiW5!zUX-gb;B&RO+Z$W)Eh)Mf2sV1#U9@t> zVi_N~`m}3tbqy#wTwO)Q)s=AVnDD8r2IaL^hJ|v^@P% zPsxiv64CYR=L$8{&k+(Yr9}VV2OK@4r{(o?c^Xy7?Cr=M%o(5@Ox^5nBb`ELM6ELwkL{i4<} z6yLEavHy>N7%OjAavGkG6-Hi>Bk)@Z@>djthMHfi(Pa%!Cdgy)B$I;us;GgM)9^hfIe{{V}^1A=^b%ush)p*cq>^h_1tAMfc z`ubPH=M(r7>;FxH`~}*8K+m>~;}vd|6JwBY`Z;0!z1u{7UDoh@$YjRI z>wS!tWuz?{ud);^Yx(~Kfnry?N$(5j?-ySo=OEGeqon0@KmI*%#G>W(KEaOV#Etut z#W8x&@){;VI95J#9a=4AbUj*wmeb`l2op}%Z{MioswFJf6P+wFyMh{Isrt(`LP1-# z%q&R2ldY6AEL8Fm8$I4qE%Uuml*nm0jh;V&{LiW-N3R}_N+405ZhlV05$oNXq`*3^ z1Qkf+T{(gLx9G%#^t%LyC(3V7@`?WY;tnCsRzxO7S=RCzer5vsSDGaMKWIi-jI>pw z2__6N!FixX3Y<{&&q9%&>Cny5VUYryjvXl>kMrnp7NkoFL|w0>(zWLJ@;L#?ac_b^ HLXrJ%%VT?u diff --git a/bin/interrupt.o b/bin/interrupt.o index 8ab8312dc25c02b3577be8f3da6595ba0c785530..5dbf85d7e352b1406455e2347822336830457366 100644 GIT binary patch literal 11172 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 diff --git a/bin/intsetup.o b/bin/intsetup.o index 85b9b5e573bbde2d6cbbacc2e67bb489877639b8..32909af6a650e779b14cc3b5f72e0fca41b378a3 100644 GIT binary patch delta 815 zcmYjQyGjE=6uq;PY&IGtCKd^4RwQT;6A2&!iwo!P%yPz##E30o`S$-EkSsqkubC~1mun&@+y zlF;!2ykpe^mncXP;h{qq`#h`+v*-Dn!WuQ4wNf2?D;1V!kBJAoN^ii(N3=@*HPCah`i5;a@6 z58f_nEX+IP87zFOW4IV2@G2Z7`=1VcM9mf+gZL0HNq$W41*t83wwMU9Fbf*tcOApU z=!0jkTj$rzh+t+o=XW6TnHt2s=));Wm+FJ_n}K@usw(5DaIb*7W^U1~E2fxLC{oO#g delta 964 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 diff --git a/bin/kernel b/bin/kernel index 749c36f1f7bc184d7b2cc2c95812b43ca98b1f01..ce504c8c581c3befbcb061c386c2f33472b25f5c 100755 GIT binary patch literal 67544 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 diff --git a/bin/kernel.o b/bin/kernel.o index 547dc2b4e43655bd5b70ac89db24701a03e32727..2d302f7aace07bd8077091e451409796af357876 100644 GIT binary patch literal 7896 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 literal 6724 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 diff --git a/bin/kernel_loader.o b/bin/kernel_loader.o index e5c74d3d596132dc91a1024847517bd90c314655..c4b170afcd1351b675c0c0a124b16f25bc6ce23f 100644 GIT binary patch delta 783 zcmZuvu}T9$5S`8K-bK-PCK7~L1XQ$9LyQQbMFcyAYsAXJ#>&E4gj{WP=Yp*Vg0@2N z1JYXh1E#j}6MVDWa9j9bZr;qidy~!Wv{|&WzB2i6BTpJO4RwA~i~_0%qs4a74; delta 754 zcmZuvu}Z^G6n!trYhSErYNQA*g(5m>sO=&y6?D#`n}|*h4i#6S^#g>)yGvYL91G4J z#mP^|?BpkSZj$GFgBMPE&pqdz`x2VR#oDYdSF_P}mab5wg6t-rNMS1qgy>o-jMm2t z(qAPY8( z`9xhD_-zP&O~?Yh=gh|qA!ZQUn8g5Jo-g|WxOx29Iwl0#sp-E!LcmlPWCw&pIDiBB zZ3@8kr64?KAA3q4o%?h8X>MuUiYubqFs4)w^l&#B3OV(%(K_W?$tJz(p49ZI52Qyb ryr*{1qwC;__Jcuo)WP30-K#>Xr(bTf6JIP@g3cE$x==6kq_L2{OcXG~ diff --git a/bin/keyboard.o b/bin/keyboard.o index 63d2763a4b8b43a5717ff55b8ce0d2542ee9ad7d..583e4d0b5e1c8a57ef78ab98e9e8d8fd83ef0feb 100644 GIT binary patch literal 8324 zcmdT}d2n0DdEWgdlHJ#}>PA6$QOvZ5?S+ZrTiKlADw%i#dRj2mIjx??7IEm9JRe#^U z-G?WZ%Jkpv%)__e{q63z-|l<(op|>aP1Bfa8uPLW#+Wt3<}4LpKAXmx1z!hFa@BZ# z7JA^<`$yjY;LhEM4p z7#fSe9U8kZ62Fe#JEHe)Xsk>QhA)(Ym&;Y?u@Cc>QX|Z8VJ7sY3#UXkH1@#A#|!6 zdgL!OywUEh3JrIn7aH4tZM3W-+?5wY!ySTmQx84+E!zzR5B1@10G3a@%Sy{fO+&ql9PDTPAv_K_%j9+&Dko;2V{L@-_o1Qt$-j>KW; z^03zr4*PIFMsV`P&6-k;nBWiNHm-+;!=iVsD>PhYJMX36IKD*R?8Q#D8JpX?%1rq zH6O{)Rhostc&r(}ZFMMtMnr;OdZ(AdV#v2B?Irn^c7_sLQ*&|VwE4;7PZ-YL!uIhA zp1pm5rDx$?Mp{jY+I#z`V1-Vxm(glE;(M^YJwl&7w3WEQ; zg1WXGzoS(+^`ko=6x7~;Q0GPKhZPPBy?oWh4844T66(q&vLf2__Kqb>mn~n>xw0*~ zMz4PAs9CU%momBh;HjI^4w~2J z|5xOP*W=#_;F(OErm0=T(e(8o`FuQ+wIIO%h~%^?KU{eJ1p=PM4dkBZNzvP&{~?g7 zLhdN@Hr9%`s*<@u<_!x(W1Na~+LzXU3^`Hie3`mA=rcd%m#<$SzN%uejrj%yqWe!% z1VeQnLLkWW9}>2{i5yjv=CAP7&?u&`>dD|ySrqtP79bv?t%uV<9q9Vw=V8@PD~&^|GyKbRUq8| zInrqpgRb(^dC%Kxg&S5)aeNbxjIow1YW^UHO9W3N_<`cCWP$IYUpJ3LB0-|tVxa_4 z=FOg4p$^YoxT& zmNeUu?ru5-DY}$|8c1uEn0e3HU7?T2!?qe4hUcu&6oiN${m=-x%!F+9gAi)oK$KY3 zboA-bo^Sq?Of41{9-G3n25nAVIN+_DUe};CR@G01)hWzdw<_>ZU}a!i7}?gs0)e%V zK$6NX?ruD+dFuiJTTFvkRW0-CN~<$~pg?fU5J6w+BCKm(-AtLSZ3~lj%}ll}+zO$< zTy&7i!aT2-uU*QvhHcc-gao?R7Lh`35)tZS+PY>H!o22fVH)82rmt*lCXWr=6)%Z5 zLf;tx5yBx1oPwDYV--WTkuPsV^poB6dmLcv-s{}aBG*&z)pK}^H3hI9jaZ-Ni9`@N zmXYco78Xs`J@ixwBr4S-FB?Wr(|%o7i(lHUbfRm;Lb7$hJl>N_6ndE(X^q8N2Qxj< zw3+BN3ydkDSd#RLRgpiky2w)23erIEKB1ZwfsdKceHc;Fykuv(D1yrqwM*c*)TCAQ`w zXr_3h{umFQ=Yc+6<24TQdhgsAdXK%xz1~F;u3h1?odT`i3eJ_{sOP-;#Eg@a0{RO0!b|FwA3?ne8=ujiFqx$+qs?AKBEtBC;~JG}ac04lU`xk5$T=Yx>Nr zS;!=TCJM>)n#}Uf<&kJ#B$`SUOtWYuvb~usp~ZBrU`3B6(}@DGY%bbU$_!eWtc^f0 zIS|bka+aC2awL%`9?vGzga%3u*a^WmF zXeJ6qF^NdBtS5&On;5hVjI&rYd)ZJTpU3zpB4!rzZBF7Fc80TJGLcQv=#y4q&{0!V zBbmr&ti+%(Xj<3=MOMldGksaJHzF*G@kSgP-8ac+z(% zTSAOR!R*TvVV&9L$V#T@5+2MY2Qa@ZY1^{Qq5LL_*l;!`t*BvUt-|q2fMf>I7mZ{t z+dD2SBayT+M<_lqj1pu!4U2Wj%CWhSIbs&}iUAa@LUJh2_U+vp7xtYoi18V{remtY zkyC248&=Lp6qA{ZfpuW^s1bFqd5q;!DNG4#P3MNp)^sA9New;SwxV^{-quX2qhm>1 zw4<$KNi{Wd8o1Qr0Tj+acOs5?K2)9c{6;rLpz}HXU`BigXYUeJH>dq7r(6Xt)osNqDGf zPw!#c=X7m)(C0gaf=eZy9%^(>vv{5UtY>2_-}@RQY#v=S)X6@czZkqjc+~a7;cxkU z@9+7_+}q~8wBhoG*EiVaSOl_vfJa?m9Qn;_?_TWqj{u8wC-^#2?I8Isu{hSsYd$0PzWI5^kSqM z;q|T|5I$=hPC2CVko?j2DAh=Q2XM6?h@TwC?+32-0`Z?2$A1>M+6Tn{`Z)dz3D7>vCam>TcPB8}bIg5>G{~Jiy)xN*ICLyZj_clO z94%yUIJqe};B$jGhqLz{`1wo{V;i{70Xt+4(IGR@!2nh=e|+MD*6mkZFYbv%#D*y; z(3vccN80`LMnSu{26uScoz*B5vn74Lq{k$EGtp59w@dl~K;LKZEnV`Tk?;#de}J(+ zmiRX%{GNoDC45sts?Fs0J%T6!KPNcF-Wx=J2sudbFre>2#-1h^VeG3Ce^KJE0+QUD z622wjPbB;gKo8!aLpbb6j=oC}Y>_Z3A$_~>&1CGm1mP!$v?ssICENr^dOHCviG{gKau!vBu;rqey0-z ztwrLLmqcG9>7SMOE{Tiv#ZgY@2kCuL!U!OZX}yF`N_a-XzmxD?2|tx^2IhnGRue=K z*-el>>&1%4hwvr@XccUjR=t8b7_;!^#$vq*E5Twtc$^O3b|4qqyIm*UqI4*GopP&wHP0{7`COjt0C>g zUBWH58Z;$`?eEB8%hQ#|x!r9>-~H)@c+}`#L(8-t549FJ)Ul4_7pWrbk$sd1VUK9k znovvJP1#!nBFQVc2;_o_33>z%wKhCNhAYuXZp{R8o!}C=N(vrE;?g{*rd0NU;Fe3= zM^4lkr3`37j%u}P${sDAdtMf!;N1X|=7ny+6a$U(Bp#)MF8e-|{>DX6-RH*1J%!pD zAqCl#+!uhm{RXi>^`MboI+K;(KSf{Zpv%s}-fA_OF5WlLciWo*dn#Y(X0GggTk6od zP3=8T&c^7j%Rf=cdV1V`_1su7Q#*CbT*2i!^P;=PSN{S~1?k3f#{ST%}|+HE{! zSIMo09MyEyl-vg(xX0Uq@$LqV=8w)UHD0$Zkl9!9T5$o!X|E|c3{BW_O#?d!CjAMb Tn(ET<+;SHD{zeG_D!KmwXmv5R literal 8760 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 diff --git a/bin/paging.o b/bin/paging.o index a2e827c42e75ad19bf231678378c093d6f434879..dde8c288cd78dd76627937910e83f84d64da2d8d 100644 GIT binary patch literal 12996 zcmeHNTWlQF8UANx$KK7_>su~%6UgS)1n7<($4#7&kQ+`&<2EV5p-oCxv+LR2sn@&i z?5tx0Erg{ssZ%v+C8|m(LWovS^`RFU^3X~|4sG(l15Z&Tgg`)VP%1>KB9Yep{xfHG zCxo^6#oWbY}&QQMxq*mmY}+nEz5HP*gLvC9Z1>Tk8o zRa-=xwe#&KziYjB_^bEK%6yQ{e4#oz%TZ@))J}AK=*rP=Tp?7e&1aG7+?yNDLP@m? zO6`JLyAY{eco13D-iqv>t48niL(dFd(TTRvaS=p#%-nPJV!f$Aw}+y~XWNupPaeK! zmIH?+L?A>UL?A>UL?A>UL?A>UL?A>UL?A>UMBo#EK#KN_ZX_4laAy)5WQCIivEK-}ZEczF}x)umlh zHdwu2N*FY~0g?V2yzs|-yS6L(W7L`^VWQo!^j9E6&5NMb=H2iB>ip3GDca)b2B>16 zWyO-#y)09jDYlp0Zj^iz#kRA4spNHvy~+Bdeh}Iuv!=?*ty>7+RkWuWX%nT$3QCB&&`FtUoaK}RH_HXRnq{G}WM5_})g{sO8>f7AQW-pHn>Z-z<7Z9;kj*VR)*osq6b zSdxquVZ+~bj0sz-ZksfuZQFaSS)N!0qMj0m&WZkqvBPet1vX5*fEI8r*4Rf(>+mROwVP&%91F&OGX(h{5Ex5hWd_jCb)l@yQP z1`8k~tC&D~m!c)&`Y}cKEEQNbwFVpq@x;TLZ>#ez=pcx${R&aU&W z6SuNBt|)uDxUH6Dw5O{VACUqt-R$JEr>Lm;}9$+Tk{PMQ|u0MxIqp#qtDDNj) z@fIHu@mN)yZ104bw@Z0yRC^7hw`gZl1_eT%uiJTtYqPL&YGn0BO+Ntl2 z4(Njq>Fs%ZkfcVG!}=|UbUnIES88em8Sgjq*6p@qW!0L!LPuGiS_ZLQz zJJ)VVZZrmrzGP~AT|ZuKwPbC{TP4e}vtUgpTi9Z+A6%bI<&&vg&ateDYnDcV1K3KT z?6|2T*@EeSE0t53s$F#LlE=U>JC>So%C1NTLbEbm$`+h*sa&n3s-+`#X*A`UnW9zU zV8T3GG%E$)$u4uKY%ai4!Oj;fCvA?7I##7Zcv;S*H43~<$EE7TsOeg1;BBQxeH79b z!$8=m2h%Q@*(@Bb(R9XkJy;xj(sB;CaHL7wajRxggr_5pjaY0}EmiD%$r?>^S4ng` zS(otvAbLUOq-htqKV%k*vy>YQEs*Df{U4U-T;tV9+sc3aO;WET?374L1Oy35K!cH7ru) zPWKkd<5q9MEZMp7d;2!@?mN(H=lc8C^`-jz`q!n(m1%oyY)qoT`Bd>*yB-jW<}tX| z6k#~sbRC!UL}9vOXMKR#VO>8Q_6H|zaDjPQV?|x-7#t1cD{!pX_gd+J^mvA{X12h? zU*Vw;xD?Ar0&|9T@7%d5xn?9&ExA>1`lr@b1-JH_{e4E?fU))_Pwwp$Em*AB)Of}4 zYJQG)4?b$<E~x*LTppO2VUY6BY*p5jxdo z;(LQ%82^Xx_~XX*KLF3ZV|bYVHK<=YRp8T@?qUP`=nMFN?R)9;)Zls}Z@KA-Y}zeU zOJjyPMd{tc`*v&}PVd{b>%h=m>ASY?7#>Przv2A9jqF2$XQsGMt$5T0+)rE^782bA z8pDtbOZp{Azb5IVq(?zj+;>hh#nt2qQ1*X{^;mQ-u^bEUZ<3E-*tne-DBJNJOu^LR z*`wgaqs$a0f+8d-Sj5YiV#iKyN&0oDa6G_E5VaoG5;dXdU zFinE0pm#`Gl$3E*;q)X^-dc>x^f-=PP}lKj!LK=1(QvIPmkitnOfoXKCSx-?-q?z9)iDx|&pf|17|KY?C<>S)1$N@M zJToZ1u7G;5zgLm)Q&4h$S?>3jG4Op*rE|-P?hXk2>UTb1*S(S`$quskc8`D$w)-Xbn)Y*se=5iJBR``Pho^|{@ zBdKq$2d~MX O-Ro%gAHET&Z}%U&bV4Ek literal 12936 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 diff --git a/bin/portio.o b/bin/portio.o index 37c98e6a683cfdf8cfbe7b97565f3228709dce27..eef471a157cd45a14d6a83c58e7bc86757ac8cef 100644 GIT binary patch literal 3436 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 aR1v}D@1Y<`?T>G$9CrijexpF(3il6Vuj`5c diff --git a/bin/shell b/bin/shell index 317411220824137759cc58abdd704763acda934d..eb207c29a153218f410887463f4cad86a1971430 100755 GIT binary patch literal 21224 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 literal 15324 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 C<0z>B diff --git a/bin/stdmem.o b/bin/stdmem.o index 249b176f9e0f6b24895ae15d85b0f1961e6148f6..13909127f944bba225b8b69041c2876c5d64cf63 100644 GIT binary patch literal 4316 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 VssRfCSN=-wx+-<^jzE{`|6dhk>`edw diff --git a/src/kernel.c b/src/kernel.c index 2c875f3..dde7a44 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -89,7 +89,7 @@ void kernel_setup(void) { .buf = cbuf, .name = "kano2\0\0\0", .ext = "\0\0\0", - .parent_cluster_number = 0xE, + .parent_cluster_number = 0xF, .buffer_size = 0, }; @@ -97,7 +97,7 @@ void kernel_setup(void) { memcpy(request.name, "daijoubu", 8); memcpy(request.ext, "uwu", 3); - request.parent_cluster_number = 0xF; + request.parent_cluster_number = 0x10; request.buffer_size = 5*CLUSTER_SIZE; write(request); // delete(request); diff --git a/src/user-shell.c b/src/user-shell.c index 75cc60b..5df2dfe 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -345,6 +345,7 @@ void where_is(char* name, char* ext, uint32_t* path_number_stack, if (strcmp(curr_name, name) == 0 && strcmp(curr_ext, ext) == 0) { char path[256]; + path[0] = '\0'; for (int j = 0; j < stack_length; j++) { if (j == 0) { @@ -374,6 +375,40 @@ void where_is(char* name, char* ext, uint32_t* path_number_stack, } } +void copy_file(char* name, char* ext, uint32_t size, uint32_t parent_cluster, + uint32_t dest_cluster) { + struct ClusterBuffer cl[size / CLUSTER_SIZE]; + struct FAT32DriverRequest request = { + .buf = &cl, + .parent_cluster_number = parent_cluster, + .buffer_size = size, + }; + for (int i = 0; i < 8; i++) { + request.name[i] = name[i]; + } + for (int i = 0; i < 3; i++) { + request.ext[i] = ext[i]; + } + + uint8_t retcode; + syscall_user(0, (uint32_t)&request, (uint32_t)&retcode, 0); + + if (retcode != 0) { + syscall_user(5, (uint32_t) "cp : No such file or directory\n", 50, + DARK_GREEN); + return; + } + + request.parent_cluster_number = dest_cluster; + syscall_user(2, (uint32_t)&request, (uint32_t)&retcode, 0); + + if (retcode != 0) { + syscall_user(5, (uint32_t) "cp : Fail to copy file\n", 50, DARK_GREEN); + return; + } +} + + void clear_screen() { syscall_user(7, 0, 0, 0); syscall_user(6, 0, 0, 0); @@ -517,7 +552,23 @@ void execute_cmd(char* input, char* home) { WHITE); syscall_user(5, (uint32_t) "clear - Clear the screen\n", KEYBOARD_BUFFER_SIZE, WHITE); - syscall_user(5, (uint32_t) "help - List of available commands", + syscall_user(5, (uint32_t) "help - List of available commands\n", + KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) "ls - List all files and directories\n", + KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) "cat - Print file content\n", + KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) "rm - Remove file\n", + KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) "mkdir - Create directory\n", + KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) "cd - Change directory\n", + KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) "cp - Copy file\n", + KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) "mv - Move file\n", + KEYBOARD_BUFFER_SIZE, WHITE); + syscall_user(5, (uint32_t) "echo - Print string\n", KEYBOARD_BUFFER_SIZE, WHITE); } else if (strcmp(cmd[0], "cd") == 0) { // cd : Pindah ke folder yang dituju @@ -785,87 +836,161 @@ void execute_cmd(char* input, char* home) { } } else if (strcmp(cmd[0], "cp") == 0) { - // cp : Mengcopy suatu file (Folder menjadi bonus) - // format : cp - - // cek apakah ada argumen dan ada tepat 2 argumen - if (cmd_length <= 1) { - syscall_user(5, (uint32_t) "cp: missing file operand\n", 26, WHITE); - } else if (cmd_length > 2) { - syscall_user(5, (uint32_t) "cp: too many arguments\n", 24, WHITE); - } else { - // cek apakah file yang mau dicopy ada - // ?: parent cluster yang file - struct FAT32DirectoryTable current_table; - struct FAT32DriverRequest request = { - .buf = ¤t_table, - .ext = "\0\0\0", - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], - .buffer_size = 0, - }; - - for (int i = 0; i < 8; i++) { - request.name[i] = cmd[1][i]; + int counterCD = 2; + // Change to target directory + // read source directory relative path + uint32_t DIR_NUMBER_STACK_TEMP[256] = {2}; + char DIR_NAME_STACK_TEMP[256][9] = {"ROOT\0\0\0\0\0"}; + uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; + for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { + DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; } - for (int i = 0; i < 3; i++) { - request.ext[i] = cmd[1][i + 9]; + } + if (cmd[3][0] != '\0') { + counterCD = 2; + while (cmd[counterCD + 1][0] != ' ') { + if (cmd[counterCD][0] == '.') { + if (cmd[counterCD][1] == '.') { + // cd .. + change_directory(".."); + } else { + // cd . + change_directory("."); + } + } else { + // cd + change_directory(cmd[counterCD]); + } + counterCD++; } + } - int8_t retcode; - syscall_user(1, (uint32_t)&request, (uint32_t)&retcode, 0); + // parse file name + char full_name[12]; + for (int i = 0; i < 12; i++) { + full_name[i] = cmd[counterCD][i]; + } + syscall_user(5, (uint32_t)full_name, 12, WHITE); + char file_name[9]; + char file_ext[4]; + parse_file_cmd(full_name, file_name, file_ext); + + struct FAT32DirectoryTable parent_table; + struct FAT32DriverRequest request = { + .buf = &parent_table, + .ext = "\0\0\0", + .buffer_size = 0, + }; + + for (int i = 0; i < 8; i++) { + request.name[i] = DIR_NAME_STACK[DIR_STACK_LENGTH - 1][i]; + } - if (retcode != 0) { - // file - syscall_user(5, (uint32_t) "cp: No such file or directory2\n", - KEYBOARD_BUFFER_SIZE, WHITE); - } else { - // file founded - // check if directory does exist - struct FAT32DirectoryTable current_table2; + if (DIR_STACK_LENGTH <= 1) { + request.parent_cluster_number = ROOT_CLUSTER_NUMBER; + } else { + request.parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 2]; + } - struct FAT32DriverRequest request2 = { - .buf = ¤t_table2, - .ext = "\0\0\0", - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], - .buffer_size = 0, - }; + int8_t retcode; + syscall_user(1, (uint32_t)&request, (uint32_t)&retcode, 0); - for (int i = 0; i < 8; i++) { - request2.name[i] = cmd[2][i]; - } - for (int i = 0; i < 3; i++) { - request2.ext[i] = cmd[2][i + 9]; - } + if (retcode != 0) { + syscall_user(5, (uint32_t) "INVALID DIRECTORY\n", 6, DARK_GREEN); + } - int8_t retcode2; - syscall_user(1, (uint32_t)&request2, (uint32_t)&retcode2, 0); + uint32_t filesize; - if (retcode2 != 0) { - syscall_user(5, (uint32_t) "cp: No such file or directory3\n", - KEYBOARD_BUFFER_SIZE, WHITE); - } else { - // folder founded - // copy all content of file to folder - // get file size + for (uint32_t i = 1; + i < CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry); i++) { + char curr_name[9]; + for (int j = 0; j < 8; j++) { + curr_name[j] = parent_table.table[i].name[j]; + } + curr_name[8] = '\0'; - // request.parent_cluster_number = cluster_number; - // request.buffer_size = + if (strcmp(curr_name, file_name) == 0) { + filesize = parent_table.table[i].filesize; + break; + } + } - syscall_user(2, (uint32_t)&request, (uint32_t)&retcode, 0); + uint32_t parent_cluster = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1]; - // check if error or filename is same - if (retcode != 0) { - syscall_user(5, (uint32_t) "cp: No such file or directory4\n", - KEYBOARD_BUFFER_SIZE, WHITE); + //.... + + // change to previous stack + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { + DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; + } + } + for (int i = DIR_STACK_LENGTH_TEMP; + i < (DIR_STACK_LENGTH_TEMP + counterCD); i++) { + DIR_NUMBER_STACK[i] = 0; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = '\0'; + } + } + DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; + + // read destination directory relative path + DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; + for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { + DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; + } + } + if (cmd[3][0] != '\0') { + counterCD += 2; + while (cmd[counterCD][0] != '\0' && cmd[counterCD][0] != ' ') { + if (cmd[counterCD][0] == '.') { + if (cmd[counterCD][1] == '.') { + change_directory(".."); } else { - ; - // success - // syscall_user(5, (uint32_t) "cp: Success\n", 13, WHITE); + change_directory("."); } + } else { + change_directory(cmd[counterCD]); } + counterCD++; } } + char full_name_2[12]; + for (int i = 0; i < 12; i++) { + full_name_2[i] = cmd[counterCD - 1][i]; + } + // enter + syscall_user(5, (uint32_t) "\n", 1, WHITE); + syscall_user(5, (uint32_t)full_name_2, 12, WHITE); + + uint32_t dest_cluster = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1]; + + copy_file(file_name, file_ext, filesize, parent_cluster, dest_cluster); + + // MV + // move file to new directory + + // change to previous stack + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { + DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; + } + } + for (int i = DIR_STACK_LENGTH_TEMP; + i < (DIR_STACK_LENGTH_TEMP + counterCD); i++) { + DIR_NUMBER_STACK[i] = 0; + for (int j = 0; j < 9; j++) { + DIR_NAME_STACK[i][j] = '\0'; + } + } + DIR_STACK_LENGTH = DIR_STACK_LENGTH_TEMP; } else if (strcmp(cmd[0], "mv") == 0) { // mv : Memindah dan merename lokasi file/folder if (cmd_length <= 1) { @@ -1079,7 +1204,7 @@ void execute_cmd(char* input, char* home) { } - } else if (strcmp(cmd[0], "whereis") == 0) { + } else if (strcmp(cmd[0], "whereis") == 0) { // whereis - Mencari file/folder dengan nama yang sama diseluruh // file system format : whereis if (cmd_length == 0) { @@ -1143,22 +1268,18 @@ void execute_cmd(char* input, char* home) { } else if (cmd_length > 1) { syscall_user(5, (uint32_t) "rm: too many arguments\n", 24, WHITE); } else { - // syscall_user(5, (uint32_t) "\nDeleting file: ", KEYBOARD_BUFFER_SIZE, - // WHITE); - - // Change to target directory - uint32_t DIR_NUMBER_STACK_TEMP[256] = {2}; - char DIR_NAME_STACK_TEMP[256][9] = {"ROOT\0\0\0\0\0"}; + uint32_t DIR_NUMBER_STACK_TEMP [256] = {2}; + char DIR_NAME_STACK_TEMP [256][9] = {"ROOT\0\0\0\0\0"}; uint8_t DIR_STACK_LENGTH_TEMP = DIR_STACK_LENGTH; - for (int i = 0; i < DIR_STACK_LENGTH + 1; i++) { + for (int i = 0; i < DIR_STACK_LENGTH+1; i++){ DIR_NUMBER_STACK_TEMP[i] = DIR_NUMBER_STACK[i]; - for (int j = 0; j < 9; j++) { + for (int j = 0; j < 9; j++){ DIR_NAME_STACK_TEMP[i][j] = DIR_NAME_STACK[i][j]; } } - if (cmd[3][0] != '\0') { + if (cmd[3][0] != '\0'){ counterCD = 2; - while (cmd[counterCD + 1][0] != '\0') { + while (cmd[counterCD+1][0] != '\0') { if (cmd[counterCD][0] == '.') { if (cmd[counterCD][1] == '.') { // cd .. @@ -1174,39 +1295,28 @@ void execute_cmd(char* input, char* home) { counterCD++; } } - - // mkdir file in relative path - struct FAT32DirectoryTable current_table; - struct FAT32DriverRequest request = { - .buf = ¤t_table, - .name = "\0\0\0\0\0\0\0\0", - .ext = "\0\0\0", - .parent_cluster_number = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1], - .buffer_size = 0, - }; - // char temp[8]; - for (int i = 0; i < 8; i++) { - request.name[i] = cmd[counterCD][i]; - // temp[i] = cmd[counterCD][i]; - } - int8_t retcode; - // syscall_user(5, (uint32_t) temp, 8, WHITE); - syscall_user(2, (uint32_t)&request, (uint32_t)&retcode, 0); - if (retcode != 0) { - syscall_user(5, (uint32_t) "INVALID DIRECTORY", 18, DARK_GREEN); + + char full_target[12]; + for (int i = 0; i < 12; i++) { + full_target[i] = cmd[counterCD][i]; } + char target_name[9]; + char target_ext[4]; + parse_file_cmd(full_target, target_name, target_ext); + + remove(target_name, target_ext); + // change to previous stack - for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++) { + for (int i = 0; i < DIR_STACK_LENGTH_TEMP; i++){ DIR_NUMBER_STACK[i] = DIR_NUMBER_STACK_TEMP[i]; - for (int j = 0; j < 9; j++) { + for (int j = 0; j < 9; j++){ DIR_NAME_STACK[i][j] = DIR_NAME_STACK_TEMP[i][j]; } } - for (int i = DIR_STACK_LENGTH_TEMP; - i < (DIR_STACK_LENGTH_TEMP + counterCD); i++) { + for (int i = DIR_STACK_LENGTH_TEMP; i < (DIR_STACK_LENGTH_TEMP+counterCD); i++){ DIR_NUMBER_STACK[i] = 0; - for (int j = 0; j < 9; j++) { + for (int j = 0; j < 9; j++){ DIR_NAME_STACK[i][j] = '\0'; } } From 7dd8a4bfceb095b19618b36f96f7f1b29c47a89b Mon Sep 17 00:00:00 2001 From: Rava Maulana Date: Sun, 30 Apr 2023 00:12:45 +0700 Subject: [PATCH 51/52] bonus cp rekursif tapi telat 11 menit :((((( --- bin/OSyikkk.iso | Bin 542720 -> 542720 bytes bin/disk.o | Bin 4700 -> 4896 bytes bin/fat32.o | Bin 14032 -> 14236 bytes bin/framebuffer.o | Bin 6456 -> 6672 bytes bin/gdt.o | Bin 4500 -> 4384 bytes bin/idt.o | Bin 4824 -> 4788 bytes bin/inserter | Bin 32304 -> 32192 bytes bin/interrupt.o | Bin 11172 -> 11264 bytes bin/intsetup.o | Bin 5264 -> 5360 bytes bin/iso/boot/kernel | Bin 67544 -> 66892 bytes bin/kernel | Bin 67544 -> 66892 bytes bin/kernel.o | Bin 7896 -> 6724 bytes bin/kernel_loader.o | Bin 2464 -> 2400 bytes bin/keyboard.o | Bin 8324 -> 8760 bytes bin/paging.o | Bin 12996 -> 12936 bytes bin/portio.o | Bin 3436 -> 3424 bytes bin/shell | Bin 21224 -> 22156 bytes bin/stdmem.o | Bin 4316 -> 4288 bytes bin/storage.bin | Bin 4194304 -> 4194304 bytes src/user-shell.c | 93 ++++++++++++++++++++++++++++++++++++++------ 20 files changed, 81 insertions(+), 12 deletions(-) diff --git a/bin/OSyikkk.iso b/bin/OSyikkk.iso index 874f613694967d2077bb94366a12d2d1161fafe7..c4d3313c2de57901b678e5bf55984cd048123b7e 100644 GIT binary patch delta 29410 zcmb8Y31C&l^*=uM&YkyOl9%^h_LbxX0tri2Hbs;*>|kU^fe^BggoI!gL=+z}fTS@> zw0c{cYHj;+od&aR-|Fm}ZvL6#JYECZ~3Zt;aRTj03un625d zDHr0oz3-^iJ?J&=0*$imY?x|wSaA=v+EZ+`dh*z!3s$(EHK__slhyBJtHRn>uDFm2 z4{OsMHy(fFxaNN-?kx>X;NJx1XM*LjM3!KV-=`NjKFw$B)BFQ<`}7wx9c+JLZ}7SU zQ}5T~eSsAnA1-S@Z3Op%qwVnhWsL1_Kkcsh3W9>o;}~my+PAxR`|17rJJ+3m?y$|h zT5|6Q<1SI$-bZ@dPfXv_+w##sot0?99~>bom7r2*iEm%Y$>6TOKJ(2-_0+r<#>$4g zkCdEjKkX{5`J7sRL?Js%+$56FUl95pbNWF&Mc*^ly!0_Wxp1@M0=>?!v|eM^b9I!Q zJ6dv<4038g@bBOD^#ysKx#uz6uQ!Y}fAN@JoTmDd9gN7M-j7}1uUhrD=HDOH)AAo4 za{`!^EFy84c>pguMeXPYz+e=MEn_O zoBHHvX@EVw-aC53T;N_Q@9IoOGo(V^(f0G+fS;Z1>+>FMXFfmclO@9{DJ3+t z=Jm7E;4LtSQld`pYG<#K2JPpQh;#)8&LU|Pthc|b>g%i4 z%W+bbnq{RsWTlH%52}>#jqFlLrDbiWnGZd3XJdHcgW0n79P_g$^kgl0fKXJk7VRhZ;@v0+X42vKaTSJ5F4<7wbp$ z_abX3U$1g`bU3~=IDUZjOWEPHL&2zyk`tl*Fra4Qh5Kk`_39(^^ImgKuU@o5+KIR? z%$)aazpWyUb(EYvmDjn#)maiebRtR(g4Wl1wlw&<@{G=svz@!)<>rgM`gjUBE5pXR zFvy^K+u`A>%#1^NQsBdEtIw?w2#4=}l5f!PCAEL8T@!tzP3xHTKEY*gx0;ea|91o{DTQIjgnrJUeaM8(numPJ-=CPpJ{a&@Bx* z&F2p3X#w&wB_{=P_W43>OOlVU2SZUw5*6lqi9l5!9psSl7edAkMHU3}JVUX;Jof-H zt#ie>L+^{Ormwr=^Udz3^n}1^gfw{MDRwHmv*ffK2|wG@+Zqe#D2OyJPkOHONT zJ5Tq))vj^u+Ifn#yabS9Vk?fm`%6^}XSM;76xnvnAlwsy3noP1DS3AwQieQKJ0h;cfzpEU-oq0@vss(w)kFLR6J*T(87ZyJ2Rw0)h(ZOM_1bkk`W0VLSuAX6GSyE3Odb^TmHLnpS(L4c(fp1e~rO?bLJ{Tv(PFzB+=c@Cl3a=?n%IBrXGYUpChfYv( z93uOHL99>rvuY*hIv1ZixT(6jy5rfeKJCakcIazy=--(g&yPN~ujKPE8j(6)IP^{C z=;IyckDu3bFQsJm%5)^N+ZWRWDZ!%FE3>5FY{G6-Sn9dNuA<~AJD5wfIg3rt3;Mdz zD~`iHpF?9zI^-p%5h?e#Kb?Km`Z8ICGPHYPEAc1u2QTQC4o8bp%DCWi!Zt0UL3Hmc zd9ADD9ay3?m}-9Wf}WQEe`9SsPyX4-*bOlt^tGN0jxaBMQP0lk2F+)!=Iuy3j)VA+OiMrCYdMCG!CbE&2jVgaO_=EaYdD zK|`#rcyF-Kw7(^^2G%+Ycf`gi)@2s@w?NBb!KNDAQxzH*>O!gF%~8nmP^%ijsflEd z^>gJ2)cgniNB#fA$^N2lO&>6-eo!I%t79UBZgH*;eg?Z?KRVak@`~ORwf7&;{%QX{ z$0zrgr(V%pqMkfuBm8FFt3Yi0r;WJR{Nz=A$C}lj+lX`_N4foLqjgU3(qv&=95qGts<)#~_3oy@>p?O!`vBZK3p+G-o)^6ptA%kHi_c3??;jA7mAEbJ)m zY;m3MXo)=EwZmxt`26Vq8GTH9rTxA0hd$9ep6@u)@yZwf1=Uao%6?a!zy&MCW20lG z6`!n^xs1vzg4phDMB|j0Jm}p$4J4&fXUPf5oSy3HqQ+DrqCHCs7|ra&7-@|#R$AG? z9SCJt=@ixYkhZR5SrXedo!YGc3hl~-QFQGHcEuO8eOdu!_UWN)8!yC0I)}5BTWkT! zKl{a%58>NDdFb*gp1L&;zi@hAK;=JqSf3I|xOCXysW0{zL|;K8pT_38e8E!b)Q?1WAS; zf+V3wr@J9EOe)!Q2rn71I)<{sQ=WF@@a;QKPV?^lfthk#zkK5G4=EPW8Co4S|Hti! z6Q$5N=Luc8jo=fomUz>=_qd*+9W#56>tkk@%WzXTaAryi_!{E7W*P?l(~cGAJ6D`< zf6CRdpS{=DxBt1r)%)%LEnl(p3gihJ59Iz$*Y&`W>Av00oRkbj^a9o>u^Ss(rKh~LE0H$ppTTC z(RS@Tqj~of;yl1U&%MY#bQz9wkglTAa(NsQg}r>2I~Q9Z?b*B~xpZ6h^o-?*RWuAXauXM8aNkhN+>VJx%^mc!&Aj6seZIEVeD@u_raA>mqjvD8|F%_5{2O6>r@p0J{0y2D(HCRR?HX3- zXWD+1j2|cTmlxS+rqEU%eE`I^!+!&{tK@ZmuBI@`00{OsbVyt}8ET_^Nn?Gv;2gr2TlZoYj&AEy1r z{N{w7rF%a$Gv3p4N3O#jdDqU5BeA*`B3qH?gn(V%-D#5Y#zODz7<2P``cmyh^U3%0 z6e}Tr`OvqSqtAAjC*ITjQ-);I7{NO4s+Fa|=uf1TIv1Z9l#Sc)KD1mf{Q=DexPrW`C97oEYLS0KfMU zInr{RrKB&A2DBYc04X><2qi7A&-q9yOrW=`ecNB?*rLw7`vW~Wb?hMN6q@dV6!C=l z+6Q`;Hr4#{1AXj>-p<8{QEu&d?<29^Ctn?XOg=AC$Ki#x|pZYSxeDIW>8hP-=zPf4%pL#GQzuP^OI}C4wmW$FUfyo-YUA*X zBQsQA8P%PNao_t!U*E@YIT-MH)D7r}hPJ_orv*F{-|XwV6Xkw+)R|ib4Dti;`~p0O z2J;+HJY@Jkf@d}s2W*Q5f7`K%15ouWIMy2iYLb#Re9K5(5e? z8}O6apkMYKbQ<g=QbQ<7ua%u9BTjcP_95suOS<{; zXL`oc`PkEM3gxFcS*G;y1AjB>+zcL4t$$!=wDVOVDnA6C-QZDPW?uHWo)~#6;2c@U zTu<`91&tjXRkj)c)y>hNv z0u=oWBr5vD6On^&rl}Fe;ES;t(kCNXM|1W+CEy2eP9@+WYz`vH)2x4r{(e9G;4ELS zbn`I!&v4wT>YtfY>FYhH%92G1Xvnk~9$1Kke~5zbW!dyc7x{Ycvs=fns-HB%rmtU& za){ag~CF2BsWkhaYU7#3B@R z!#M$fV+L+<$+HNfJM#&%3gzUa6@<}TW%zL$qIOFQ=wbory5=ayJY&%mW_Y$^AAU5N zaq`AIqrt<|BMEbNKi1pW+QPWo@dq2D$yo2J-NJZ$1eKq+Da?sj$g*C_!qy0)w4RB$ zySBD8v8+rg>fMY7a8S>(@-hujvPE|?6{$sP&^^j-FhDK3ORN@gj)qzZ-UE`i z-l&1F5NYtDlY>?$-?I7^wvYe{50Kd^gI;S(|3y?%t z3~BBpo09uU^!qkiXYMxCXRhjRfYKTrjBWB$5sb3iAjADZNMe%-3_ud|4m*hDzokLlGzMtwhk|u&8>X^ncwr0 z_IPvwOs6czu}mXrw+{c)e;f(5{811hIK5ow{~d{o@B%8u)TK#W{4}Us-bfIqL13Pj zflMM~R?7Dwc&sP~_MiQnlAWx?l4)AzxS$B%P2>R?fs_tlGo6IaA^KjTKSwuNbNEIS zwT+sq5n|@#`FbfW)_Tfdj}gyPaicbxEQgtG)S!_H%}tzoccbxhkZUzRS&(-hYjoU) z+BX2_s%7F`>RRUbJxJLgL5qDr)BM-FF-09& z1vm);B!%4>B8&)BVJOD96G7xwP90xj1kFr1j(@ z-w|ZD&vTX40!$~fePLN`6}EN=1D4TVY&7lysB6;q_3nB!uWQ3B(|H@l_4Rg)2h%tMP-u9mH=_hK1yOdgBlJv8g84X|Rn+FBf66>BL<`f4KqGRAAM zq%rSaR`TDH2t{j<7cCMy|mrZdfu}8_;`-x8M2Fiw7OU(yZEft z#lZ1KM_G=qw*pmTYuO8yU(`FE1oBI?qyGYwmn^SXt&@|yY(X{p6<~P9_L$f@_Lf!N z0KBgX9r(V~?f#LzUfM2dJ?qgAY5(sRh1vfOOS#QbxqB_;PBuD*p<6FN8g%=4ziu@^ z(jkPNize<*RTit%kHE|iT0VNR4D`6)qtP2TqV+j|w4NWM^;BAaZ%n26hB+ew)S(-5 zl?X#LQ-$fut?OCbO32@q-e3PMyZ*K;n%h*Ls&6X@t-g&O&Vp9Y(msH+s%Nut?1GKn1tiTVOGJSUn`YuEieor25Rzl`f>-FV? zQA@QqFeD=b(RV-GTIVsYTLHM58n0oovr%HMibf0y6*U$^ans0j{uU5j{9(F1PB+q- z#TpTWcJ$&F@d$2l@kr4`d<0ehD=toou|NXlbHeTThmGR0sYH5lPg@{X0_{PM_cW?5b-sEvyvl(@AJeX&(u%^V~SnJX>2QNIP-*ePg(Tu$qG86@#j z;KnJdcW*(}JgdA->;WY|g_!j2t;|7zGb->Rkl7CvLrp1S4u)AHi)Z_11A)-2kr?yEucQ{V0?#!e?u zw!4|ytziBfWdW?)>%f&xT)Uy1V%o2m()*cGI>DrQy;SKiwq1v$tY^qo$$~MK)#_o} zc+0kwv{EIfgZ%bRT7R@2t%*5D&PEYgH?(jTs`6@MYhyEW*~Y|@OOi1OY7t`9-h@1Y zl(RQ=DcF}81F0)lsjD%Nx^k7e8rI%~+UH@L6Ry%$u5wlU&`67eXt9RZJchD%8v65E zxh5f52Z7f~z=&>cE@XVO1YBA*Q5rnYpo*J0ZYF4Y@^(OSA$c7IR}tb5)E5Sb4rAJR zl0Ft?WBPFN4yNVeX3Q9If_#QK))FZ?o~nv(P<9!q6k)Xp!l%n*k!cSTFiZlvc9;Nv zG`XjtzX%{p7IXD0P#%tQfNA3*D!0PJh@j``_ku8z%36W`I?AJDg~EvPZS^ci;wR|; zf&p@6d7@57GoxjBl3s}N7+IdIr=mQT%C3u;XBz+v+XUA!Qn%3oZ6;<=8SOh=_@xfQ z8py<$*PuQ{et;?wBdLfqa08yMN#lGkT5x(06*CoNt=nM_JN7w?fVoYYUhi(frp9uW z9cWCs$|A@jSHb>XZtpwcDk3n%SHuus5d-_!|IJn8I?{#)oYawNEZT5Yqw#pp)c_;P zQFJW+ZxrPSFLA2@4$uf+p1lRS>CE+aFeS#{Li`yhy2i``Ub_UvaW+57d9tkQttc0e ziLu<6C>K(hkJNvHa*-_O>Z4FDrm{9xe*oq2vOLa1L(V5eEg=8LsGkIBq9he)8rG3X zvIz(C9s+^6_zc&2(u78CW-JxanI~`~y=8a%SS0D{lN~fLO6>l^-k3Sy*F+Xk1os0I z`%l7TI_6FC^}dGsTA=4M5Dhv4$dtaRcW*Fm0#oMrHqfyrH=+Ug<$Rz$c``WtQ%P0C z#<+YEmla1cD5ge|PVxUu$Y=+Rvq^3ABpgR^BZ`T&OpEoY%8h_`JJ5d$C~rY(QUfr< z_CpkTiG9d3Hxm6Ha1x;cifh10gbFCG0VfeEo+PWKRj7F4S`DE&39Yyb63h83MS zMJFRPIvEX~z^_rq%~B{l8Pk)gAx8s@k!;Lw0EJhTZ)e5=IjsO}1h7y72Cu3But)+f zUR4QTu>{;;+sce367cb=W&oE7Qvk_6ec;M*VpgI6>&qg<9< z+NIFOP`h6@ix>q3pFl-vphT8x#1iCKj+ys$U&w2v2|8+j4BlNX^|d7*lfPQwlVBi&?o zu$iavzsrVomZo;`qT2NckaGHoO@1oFSDr?nDdm=Rr!hwx;3Tm(9(ft3V-azamuzDh zWS^G|f?*?rba<&z+M1L9hi=Y=y*p2r&g6)}OfJW1<3BM9E2AkN7l&ryuFD&ZhECIo z#VrOhmXO0THh|Z$&aoCiJd38)o)TcO(D=i=wTn_@_k2qbPSWEUF3|;%$i?Gk~QnpZG{(r}tyCklVV{@#6|Y z%HTenQd^hXQEy|EsYV=xTG~$?H%`aCn)kC&yfZ>fJ>&Y4z@O=HUP@`rAdmUV4aw5Qvy4t6!95 zSEmXW1(vHC$(F5dkZ*65uVE8-NJIL#vd6vXouag|M+Wf40; zylx8KaDmtsn>g1Zp1T33SC<5Qy)!`V2GvbYyTn7QAMgHr3v*t5i8{pm6%i}g>LiMc zAl<+co&{+U8LbGND9ffP1HyXax9IE!8{A?{1XyluTUQ&y0qwD%<;HZ>+#4HE<||E@ zG}YHj=WOm;R+*%bYtdXHnQ#1Eu+20;T94;CRExhEPXIiywAQm6c6FB*u>={4aqQzR zFJ=kW$-@@r`3BWJbHQFBt6xLIP}_7hj?U#|=pIwg5;Gw(lGdBCl84Q~kIrHv(LIfx zCD8MNNzu$)yo;xr|19FJ{zH4Bf*B!^TovnB9xBDlb&QUiX&tj4HeBVwQ2=Blb4@ z#HjOXW8CimSO?ZrL@n|G3ggLgxpy)bg{s$pkYX&D{@z8xI*XDq-U#fa*QTt)S89Uk zC2#;^pO=K$r>`W(t$#oSE5hq^{A$<{-g6Fq=ZcM<30azyVo+pqHaQ|G(uzzcA?t|x zeH64tD#=J2aMKF_u|!IVq}XYw*x5-Oq%Mo3C~2rDN$;gt*x#T<$JH`+9w4l0^#jr) zDcYc%o|ia`4@k=c4iBvy5k-!Vh?6X(p7a>MH4ro-raKIpn$A{-LHSgKa%mnviU#B4Jbn{Yn#X76sAqzkyqJI1w$^&&MvINLt+gJx zk+rO?O1zrA;k#OgdICcsEx|tPxdTA7JnJDeimx8T#4`g*0#t8D7UEMhLo zDb*0A`Q3yE+cLbsnFvhIh^J5$JCuhCdyV}-jCNlk5xEZ+_6n7k+=nWfyiKE|jd`f` zR5}3S)B7jPU%*6{_B!3(rQ0a5u{6U=Xb(AzmrjY(BLiqg#5oBq$iZbbYE{(`v~W1C zOC*vUk4QQYS;8lNmU+lmc@i~h?ywYMQUIo|(Flvsh=71`)wHT{4RV~(<$=9$*+J$Km4(hd1 zP_jqFg(?@3r6&GX#ui%>Ho^z(8qmhD4VK;1Ycq`98i3&p>ttD-{zQ?r^yH)x{J3eA zHzm$-nZDIcL zw3uM11N!q|%<^c`;p+7_0S%8QOCYE4?1s#7U_6eC*Rt#cRSVnxhd`lT1Dj)E(P6I< zK`lBN>PJv(J}33SIUP3#WqxsD5sDvSr069Mtw-j+8e)H3vQiGJ7PGkA0ZrpuipD51vApf z4rpvNS~Eskt*NsXyEV~*+QXH6N6aWQawZ=c5Nv)NDrU2&sLP@_Hey^b?wOLMT@rP1 z)ciO-8U7Nb#b~h(Ow|dDyTVA0M9HixW(-WeHZ8@jp5@3%p^DR!uTRm?2wtNcR6W8Q zU{P5HX%Ly;-)vU2T?@vJ(MEdWqIhOnCLw)Tx$qqosVl-*wAEY`wV#tRRW{=ZN6wE^ zXs<-OriAhO0^wMjSz139-SsGbWr#R0S|;m9OI^9@^+iDGk~P$5Fe7!#unEGprU4MGTA(Rt_?8ztN+z&{TndW#TmMi4s$?NV6;#35tfQ7_tym zJ?Mloxfjg_U{vDsSw>S`$Vr(WXO96<&4{++jp9(0N711RgwVJrm{NdIyZ{v%Dhel2tH-bhA*)42*EB8iF{odAwxl zpT)KlB!r`tXC#{ME#Zm2>!rp?_8dz#ub#)_oeG;`7S8DhQY9sDy&MebpOz=c8Lk{w z`Xp=@9Yy&WADEQJAX|(6-q};pG1R#R=DR(?BBD#BJBPOuIb@LkkGLoY+S1;vS;#XX z!S-LXejZO*rCd&RDkM7S1DGs?|DoL+;wK`kLI-$(zCj;Nvc zg!V`2oP*4gqrD@p(r~h_$>uPtct)L9w;(wW);i( z@?&ph{1Bz?T-ycc=NQDgu2X-89gLe+kO&Z$A(CitXL_@ zfS*JSaX)wem|5#Tg%kKKd9v*-mRVF!DHV_@o=-adpsvJN!%|0eR9;~_#}F5dA3F94 z17n{UGWOQcC#j)Nmc2w4AYXFq55+Wt(#R5Ofuvt6MA;FN`V<<6hZt6bILE-G8Irl$ z=62cUj=&43)Lvvr{gAvP%AQ00Q|JQoHQbug{QX1YRr4z>^9rB6SeIk$fr-8FbfYki zRBiBT8$V!jJNc61P3FAN7!00w$#UK`$T~FfLgy^TjZmpW{X0z!WjZa?!@?(;J zuZ_&4z~YyywO=I=S)s!1Oq4P%X0~0<<7L8xZ;r||&vN7eu0i>^S}($LsX-~zki~mo zLes3tf_iq0ms2C4CIR-Mg9N#T4uze#W4jWm%)CzvS+=5v&Ip_zsa-&{Jtrtpsd6f% zhD)_Cm)c)m&Qma0bN z*GV@q3`h0!moy0?%9SPDI6Tw*cu0DwNZ%$g9)(G!pU)M&8uW!?={6x;?dJNWeAJyw zd7?HeJj4QZ3EAYB^7FEEKLq!f8gMY))9kv&`Qw=ULit^RLT!{#Y@5uE@MYTrRCq zbGJxS)^PLmyM;#Og`$v0u7=RZ&Ab&nUl-BlRV#R{cA5Fi3ZCf}*(Jo~*e{|S=08{P z@guH@6U7C>d$-70C}JNMo<$NvE<`I<7} z3^K`gxcbF-GyoHk4W<1=As|iO+6YH~kf+a~8)lNw=~Yp_UxS~2VZL`IPY66}wei1) zg$)p}g@nkka3xBbytPpi@YaKxA6T@@CCwEAY!Oz)xx>S^)FG3 zvhI3Cq!8|IpWr;Ax>aadVs5RNrH?Z2y^4WeM#=kys-lUD@V6Yx&4{ zLVMvNQ}y!|^5F!3+KZ*d5Jp=*)3V?CdS0D5peFCG&ooUU!H zg;|CA=s2Mb7o+Qh_?H-arN~6w-K2BaGK+f73i_7=VHTG0^Z>m%!=tq<`j>r`{24JT zE2p4f+=c!dU%isyz97%~pM?N>KT%a(Sy1p@|E(i20%BGMzMJX0D*r#>`Sb94 z4Q{02A)1;>WpLPjXR02u{Dn=WsK-!AleaeVb2*EoBS{~6ifk&U7yT6rKEi~XnhGiK zD|*3#d*E04Xmj2MUL4bmJ(--}qi6!$D*R^W2EHVqTV7ros@M>{zJPEHpqhWKARtZN z+6XOM9`J3cl5+5DC_+zR`B+WNeO4@L5u+{>^Pd$^~7=@B1 zZ?fCc{-P0Gq$hk;4X`ucFrE zO|~X-&=5U6OGR9O;5$UxWhfyI{7hgG&L~vCp9sL}A`l%`X+M*buTE2){eNk_l3JHH z)p{;kr$;gg6uAFT$w$PdDsOV{7TTp^ZXw}MRr0)W{G>t=|AjfWis$s>J1B<%9{g2R z$igzrJF9rXm<&rC7N1My5Ve#yZK`Tvcy3rUiOpC(qipulE6T1ZuWd2^QO%dT=Pp^{ zpH(#3UsRMo-kepmZC}K+D$ye+`oy>cQjNrR5WZgk8k4XW?T(l5m&pV zwxz7HwyCP3rLpPSvX=6V^=5Sq_nTX5k*MBQ!*_aGo2#12%3E5RYB#pFRGFSyzR}!W z%O_@6*Vb1x*Y2od6-`y;EmdWexG`#B>$R-9yhRCnv6dG*o2ssEt!i#DW9#_DsEYd5 z<`#&=I}~PF9UpD}qmD*;0lN0F-Tl{^ry5yu?gw;8o_W4gC5M zvbC~i^ss6Rn^n4EY1O7$(wrS$V}~)_iSqMR|R_dGB7HU|zk2r{~mescvLafNdKkna$sVrkYx} zwk&HwP4n`7Jf)b#G*vZ}Z)KKM%Qly9X)NxCnro`+>)Gb2rY%+Vus}=eR&z}g&l*`p zvdXqLHMU@^t18PHU?B!NnF|6{x6I6&_^_Pn+NS0fh_IAzZlQt27+03T>Q!4Rt=i__ zw(@z&7;v(DMfuid49J#B3{At8bU1|BIs@YAo z+p3z-G048AsTTf21GTNTsin2NzO1~mvWZDH+c!(Lv@~5ir@nj>nyRRQx79YoSyip_ zZRNG~He@;*d1k>@G*MP5+bB~VsHVDMBAH~8YMK5fJ}dzDX@;GoCR^dbq>*%gD%8|& zf_qBMs^DRp>KivgQ4&EsvOu$;Y(iPXMusMvT3C5S1z2G&s$buDO-Oas<(8VJ#@0eY4yVVMHA2Pg{g|(o7zn{|A2Fk?h{uF0rwP~4xUEY1536i$p;wb|716Dj z;b}b~@KYeC>$Py+h631bbAAOYsA+7dDrjmbFPcy|zF>A^MQa0k-&|1I)VK*VuAz3z zrh?6_^|j?S1+~@1_RDF^a?(1MsrIg&8PetsZ?zv3WJ(M zFc@Eq_SA0;lg)#xr4q{L*O=bTJkz{=HNSLb$V9iqY^rQQO?v47GfqtgPzbA<>Bkme z8iUh}xr&bwRIV{^-on%7+iq;}tBC^9gtTk2Y8_O%Jn)^xYt|Hh?uw1ATUuIWfX*+>D{7Ts(GA7L`Gpfk ztGIo2GmpFNgxfsU!dL5Tzd5FrZ;Z*OoPu$70Kf7J8s?8%IbPS=Zys#rTV&1%3jT3+ z5mq+(P^1I_(;t`7MRl5_fiqz$(KrjJS5@IJuwVM!aN|8pby$fONF1eiWJX~R!z zcX~TiXfWQYj`w=e#rf+rH4D_lseYewwzXnX-@U-D*gE9J^rwtDWOyBmj;mLsM zJD)a8f8aph{IuWzD+hu;0BSRA1N?pnOz&>ehe2)nPXM0_f&T!QJ{W4#p9k!iIRL*4 zm_8(G)6W5>kBTZ7_NQ0$=?kMaLk3{_+NceW2TWfcwc&Yy=_{l*ybdsZk<^Bp0Mpk= zZTKd@^rcc8?g30+EtPNp(~SNsfIeYrGaLa-pEI@LHv!Y9O>OurVEW9d4GRZe(+`1D z0n_JCZF&J<`V^`S&jw7NMI{)o*KxK6h=*sIV|MW0EVDlc5(_;20j2#i7s00i+b^jR zdYJ#KaHM;u?&9T?Z!r@53Sj#kK7#4bkL1gF=6|l^X%p=~rX-RR z$L#hSuLNfSwm;H9a3Ns%tps!N^*n8){q+wbT@Dic9VmU%JBCH!OZVzM*qhBRumDfz5J-v%*nUHs;9molfAV|a(g5G-HlOe0qrCJn zhWtuwHa3;j*KVmYoj3Cz>+k2A_ub4h95k};uQ#8+nGbh-gz`u04}5+zpQ#0EMxssY zAC^4%kG?u#mkE)91^gSlzG?rnY0m*+mx-40EaD-aDA@mO+Ur12xzHjczJ(!07w~Vc zyc3bG6V|WI`1Qw(HDKMc|Ji(J2k}*XYA(2ykBZ*^DgJ^F7LNygqq*}|Bpd#JnRkH{ lc>)dmTgov%yIzlbIQqBx-u-;f{Ms09&7&JSJgd72>w3)_W${hcBbD^uU@@+ z_3Bl1O|!K#V{2!|gAZCJ-b;J6eJVD>`{Q2=`(^I9!8|3F> z1&ejx?EXX)?TZ)mjr&%__9NmU*=N1}`8dIobdG8&ZjgJ7YJNhKg9giy=Vy=Vr>2e4 z5{ZA79I}zQY=?|iu6$u(|C-ISUVqTk?BMzR@!ZgRz-S)y8r@H&(ne{ZsC8g|H`Usm zZ`69^;DUY&{6cCJ1sW%a?+HeMwXZGdPlbo%sn+d>9yuiY9*j-0pbC7(N7vc!|b{5{E|f5}rqT+73_M+0*<< zWVw~7$`^Pwo2r^vr?{UyV;_dqiM53bgiu(+3Jpk};0Y3%vScZ*SoHAN5U-Dn3OT zt^e1fN`88%9hrd#(aLT6Q!Wi^v+_s%)jumK3Firco=3vf8LapISxHb_ql7xAMsjsV z92yx^Cs(NRoWL9?IL#F)3_S90DDts>&0|WQqK?v^dQ2HQ)6gT(jh3tI5>i3icUZY3 z%+PUxD?!}0|C6p7FSnET7abcwG6-2(w|^PFN5b0@t7k z+xCBRF_c3IxklQv^9xb5;ja`?u}CJFIZq4}Yb?MGyhM zIM9}w2Hv;r_G=0^GSt)0(6l< zlZh&@#iGR7j&~Q7-nQQby#n8$|Q*bcg4WldyGQ7jL!&m8>l6ZP5a2e&A|%?kPWW@uk5o6Ow)$K{{5SL((Ke z(pXc}m*4d#YT33Ek{4!kWup7y0{@50Owji|p(M$>^?y8}O!7}fyQwRLmmT{P+O}us ziEny)J64?D_mL_eB$H@`dkWwx>BD&!ptEA8Y`GOo{_Ham9!(rWrJec)5d1*^U@oT}h7M!j@wA^z^!dAR?UygfA7l8m$YdNitp zq0w=Lf#2&7KBc6k^Hy4@mHjqH|LiGcu#%dj$Mh(9EB*)OtRI7Bu^((NIC(q^qqbv3 zVBbehVQ;y&qhY1t&XpU=ZlH^eAJ@kh?OXYjXpBG-%uYPgjzSKIlEmMr(%{ z?QOBcB|kWpW1;R#Y&#=g9)$vMP#^~wNE+ycdv?AK(-j61g%Sq}0x+s{V1B^~`aV9m zqu>N}e#;s8WL6TH1)cx9W)0}}g1~V{TfqsrZS#pKt#1iIymhMlw9pLQwlr{s-nCCj z@dtupk|Acdx1%70A;Hf{zY9ktauk&3T-?wJKvf)oR^fbceUO|prVkR@`%h-M`(Qb% zD}+oD%2QiUZTnOMpA zW8sl~A3Eh1$y;Pdi#fwv-Bxf!hT-=GoD&Oh8sQYImIr*kTr;>@Npunh(7B%#h3o^*W6ss z6&#ljz+}+&ct%IT5mTDJ%pQZnb8N7nf~AX%Wyo5??WiW zLy}M8)gKCz5F$;c>AYocWCgrqdRtDwa@=W`39&SIB8+JS3K8a)2J-!B+@KLWA(w0W z;WZ47r2&`V3R6BS9=3WeqCHs@8v3AO&qjsC3iOEInCJK~60s)KPhkNhgyh4jFDC^L zBUEwD6=&pwSv0E>K@J;3`^XQA=h`GHo7P1`sA5~fv>;Poi8YZVol#~ zJCPYkhn2SNzgnO4f-+XVO~32~B_(!C3b!w1!=9?w;fH}*{f-xu#P!Psp>Glu7uboV z$+rC`7or>rxkaI&rWjY%8ziJaz)0u!9j2^0O#b%+j2aEr0V(>x7nQ`!ec!3=&+j{v zKH};2gS!h(bu2vnSY<^;Mf>yLd>SO_YhP4`&o$Z0hcy9@CX{K4qQOp=-OTI`ksRKu@EW@SoL4Oq-4cEjsD%X`N&^wlC&in zgx;1TffPOPl9HJgY!{lmr%-M`h?G&rA4_||k>l(2kuNK0_8^b`!pn+pKoCR`S}dJ# z)ksjIFp!{M_p&lDc@zd8qG&&+qv8|JDz2euN-!Ng9o}eIDlU+Z-coPXQ(r;m_=G<0 z6=hJ?LY{B=+K_LxZ9ctt%ezQo+5&~)s{pdsi^(XNr*C~l$upA|F0l>xZTml;3*YaN zI=wyc^C&TCPDQD`pQ?8bnAg0 z(n-JaRi)eC_Dt%!0pmHlM zd!y>#umNPG3vQIJ>IFuKaP%VrqbPjU^ou|a7k3-Pp|g zEqdc?O0)B_<0c|m|B?^`KQ|G#=?#BVuE=Gj@m?_KRrLjY##_BS7sFd=og z+qI4b2|u(i@cyu8qowViKa4mt;yI-CAN_!+6;RsueRVIkWOykk(_bn7Ig7=$7aTV7 zm1E0zD#TNk%>nsLRtYNSOAODBu^zT;JIp#qt^TcC0vpnh5pXwogl;%Wfr{tE3+;(m2S~62LO4m~;6f zg%zL0wvO2LMslqKR|Ty)xMp11rTws78o{`k6;tEE*ESO z-D_rzF|;|bG%yQJjgTL~a?l@KsI;XBHlJWK^y2zcxAPP+WN+%@5Q^K3Iy8jh1*!9l ztSwZOtPw=|P1mBH31Vp^U3~Bcm|zgg|3ss&6J>3H<2BR9CK#I;5n@Z|L(5 zDIle7T;@W}hYFwSTZp3vPt~+u47S|tfJ&x-cT(97I6W0g0 z0=T}!^&KvGy4T<1nC|U~#g&R{2(D~g6L3w%H5b=XTo>ai!?gidD=t5--MG4N-G=LK zT=(Pp6Rsz59l&)E*W0*0!gU=sJV1pg z84P4#e4uNeEBHd*y7>#)voi;q3g%|=1ao^E>%!A}atn+3&YY9>^7di%1V>ynoHk?3 z?FuBVt#2Bco~3oa-u|vqrabYX{>8h>`Erpy@;zmM+@Tk|r(7Uw`nLC!8u@np1dypO zprqkoY)@OB*tFZ%)`RKj_xhmsmAUdjz4m>j%KzpE)D&Of-fztY+S)W%^GOq*_Uy>l z-G`NY`8WEs!%C{0tY3Oq87SB4zc{R9C~qIr?>MXsAF@IwN&j?Ut{sl-=2(G%HP4P% ze)raV&knnO;;^z5OR^~+D#>9PfBA=sZ~R#)J36_>bJ5Dez&inMx{ig1`$_ko`cN61 zcve{r#m;=9fBT^_eEF-Na67{gZ?*F#LCutw*uWnVS&ox^4D@lC9_-Y}fscjP^&1@? zMB0F7A^K$>De-;@Gc~zj+aPq8batY)5zOibW69>0DtZdJr*kkkmtt=m*)tQRBZ>tm zEm7}B5ziys34$)b)9}H(_G>{7SOdMwgX84Aqr8bUY&?6~TK`TfgzvNMvXYwvG4~h_T=Z0B6yzcp(Sgd2Ldap z&=X_yM?Y3J3~Gf%Npj?VKVJ>)N2S~LXX`VMC=+F$zU7FLR%H17vOp1t>T~%#Wo+N? zkuT^BEkDqvz!akYTF{XUR8Fn>wc+?H3I9>9UiXRe&LaWk9%hAJl2oAo?zl3*@#M?B zy%@>_ze@1m0H>n{z^9HYgB;Jk(%YLV;ItFUfRf_xdwby;{DbAY^gOQLzS`T1B`C_c zwVlfJD0RuRE(@4^Km~C93Ut2LZPI79G3D^H`-zEoT##S$*WTWXaF2R#Lp_e$0qX*O z5^%QnU~lg#4jXllj0HkQCh!{}V#XRtu`C5co%E4uu9ARY0pg)ZI!6(B)G#nC6>(R!4RMuR=l{4ksoA&_!ySY*ij1=UKa9`nV|pa9CYgT zGSD9ceVPz1ddTedTy|E2Zd9?Uu@4EScv2r~82Oie)0c{8WIA>Tn!-uSdfwNIk z4GrTX7@W~2!?p?Do;y&T`4RMwvGpbd_^_mKkf#|RLC=Dh3;M{J-X8Ae5%kUI6hSYU z?f3S)F77#t5b#!p$uNF_w5CV7dmb@s$G0f|(h!rbF2Xt~tUMVrOkadZ z$wcY*C$kIvW3zMfC3&kN`Mf<`?#(D|k!UztWTaP;tHlsk2oCDJVjz*tG;5#L_w^vK@$XLNttK9)C zd4#=2l5-OUwhFMravXo1s(&&`aakWj<0!9?yEovksM(c7_raQ7LnCT-6&N+UmQuNR zPd#U@)tb?WwT2>zh9XtkRltrj6d7PBQidr|in2H?l;OCC;g76EZHFcV%iZoM5^RYg z!&6QqcS|=RY+HEJVsy^eV4;sWa$M-3pVK-1q(suyLTxd+NR8+sWjHl!So~(rz zPL+&~rZQIga~Ccfxk9fjS*f%C7>2MARZBq;HX?xt_MzmE8XK*BK<#&wv{XneD+&NU zT;56C2zfTi{-NN$K%+Fw`4GvOZwF^J^+o%z**UeMBj7`>6@3l9is_(iKH!w4p1k z=K1WyIiA1g>xzCxN!)X zR&rkp4BIpWk}(--?(GC#F=Ri{ypro_2uftTwBG|Qdgm$+-5VC&?IDX=ghksuWKoyX zN8+#cP-{HSH-KUt9Eg=X7Vk9((6Y@(%BhTw6-Ms`pKSN2%r6ysdt!bGid-UVmjUh1 z!e$DLBX`F^X6)yX1=0v=1cqYVZwW4yUBs$%)yL5jOu1V2k(F52!TR`*QGN@M35Wx= za*P2QJ?<3@A-PUY5Cnf&;tinuA4uyV>gA+Kvpm`mw>a)qlD|<-Cht(X zO5>(R0lrEex*ss=D@y!5iX$x_4-`;Y;yok1JU_J`M|6ta&2?)>ma*9$H0GCJi*ef+|q-kz5r zk(9hAsN^lcHj~w9YnVq*EkcA1~R=p7!y#)mrT+NtxJH>IVQ-P-ITCP=L+Iq5>%nDnN5C?JLOL$@LsNNQeRI8Ne3lOZqF4D3=N>K4u1c z57mIg$q$*#$ebpFZlN`eBGM;jq)#-w6_Gv}bF(7SCq?G-vGpq$Nnz32jbOAj)?Y5M zn5db!mu#i=wGxZvfGlb6kum3~WWl5c3ft$Rt@8LWq=C}4z*#EE6Jk*qz)Cl- z=RCWRxYk+im*j~Cv4M(mRq$WLm$?_x4=N<GKOMS4EMfBezdcLF@!F&4Vh`gU(&{wI};S8Yn8L1z6qJAX=7|&LA*s;1`&og*P_w%G1pt2 zSTg;btTVJEAph^XCJ;-4>ma`kmz4s3uVkaevXK_qcH>@~jVNMCk0l=aINVpUD(xcx zj^)sfRrA?Pa&!<-!vTx4sVP@t>p7q~J|v2NgZp!oaY>pDBG}ZVI{~Rf@aIQdyzT_a z{x`5%)hXIgXzREOfI4*$`Ih83gFouD!DC<{Mbh3PQdArjwOOc-Ce#eCb`JyN~T3oHfm%#xUW^o-Lc#wc)rbd-D)JkfmfE8JFrBdM`23=BJ zRRj+;%Uh$0TfslfEN^vPLh^N<*TDq=G}_raLgYe zQtq;v&LnqxpC-)pM*h((NwiUKkv0HpIN;?7pY*|kp0DQzIsTUp=Qx#hJ8@hD&UKrYs?@i^@ENG8!9+gE1(x zkA#i(8--$sEsFz+^LrxYILResm06r4iI^*3CRvyAkK?dkmL=73A0fu`+O1O5azM~?nnod>cQKnDL-**wXYq$xW{gTV zPVFM&CnI2?itfe9DeucrM^-b4f^;94Xj-<)>DK$uTeJ%*cc-C4WBwahznq>S5`%Ry z131?hvN_iGF?W&}A0KDP5M!Pqs&A5DUKOhnm=Q~YDAv0zq*+`6VWX_%7QX^4cgK&% zZ3%8E;f(Q$()EB}hjfc{&`)1tif-3{#>d{_8EP>? zrISLX#R!!S3Y8WkR5~eCS~j>3ql^(LEmM=ICc6zrOR~(>?f}VAUc6DV%rgRSc?p2| zBJeuOO93n}0&jU4fQ1}z`5omY4U%OMhrEvRCIE{$5GUF1gK)3qQjO|$(xSyu#7U^B z8hqT_ zLpq9vv=u{|=3g0OI*P`$H8iH9XiQr}V>*h)v^6xQ-881Hp)nmrW7;alG!1Dd4QZ=6 zq&;FtTc>ICsFm*2Vq5N}d$HMKTj5raF^J6;8{cd(_F8OVdo6y+7QWeH3*T(9g>Sal!Z%xN z;hQbC@XZ!m_-2bOe6z(SyaIbIG;~NcYA@;+h!_Kb{`H7m7F&gu4J5agtn9;#Msme|B*iC^rN*v#FM7#f$d zH5#F`7>8EoUE&(}zMD|m z&?3p#WzT>>rAtbrgYR;uYX}&WuB61f3GB-A)4)}_Ql2fgB>hmJh7sodlE*MP4VO@{qP)oc1Yp*6s!mG!6ja)}K2B6* zwyP2;Qi8Tc8Zd2=w}*}v#QCYcMgC0cc#tlQOC!WO6RwMUm#k21th86f(fJm6rvWaH z+e_TL46ty(!w_4Io=(OzQ)X=M(sYn=oQjRHI$?_$fH5PzL?z>_-{Mh2jEwfn-FLv! z$;EltMCOSWc21Iuu_+&K?4#64@l&ac+xj5N%;i#^rOv5tybRa`F{#x`DI18(VWkO- z9`Rtmcox1CX%p}knVwzH^NC4#0MOJ}nuC4#@C&eW}~=8mf&Wi+@IR6HN#`9*61PN(B6O}S#9F}Wx{LX zz8ra$)gH}v!nIulI5@$2H6+l*MNUB`z!Pv=ocjtO#bj+;sPzEg{bCDGz-@~&Z^v*n zCTm+l+$~gwC*Zb`+GQZ|1l(p!){Yn(MxxD_tz9%*+d{Loi)L$EC;@lTWNiyg)-IZ? zZK289MU%BHG+Fayn=Le1yJ)hug(hpBfBS9bZ0#1awQZWVhxDX1mTh_r-NQC7qaD;T zV`z9R)D}Kj|2J-ell9#I_++h(NFah+Jao}UYxF$DNg-3qisH{!#50#u{$O#;h&0H| zDUxPLg2QG=G3#l&G}=}W!mjdAS4j@qVAG6Q&q)tmG-K9t_`s_fj|d#}h(HTYdPC6b zemr{c+GlBgu<8gxhMob&i zsW+9b!h}aKBhY!#c=04+P#Tr7bp7fN{&+s24;w5d$VdJ0#Z+>Y0sqR+i_St*6EWuv zHs-wAgr^{I3}LYoo|!TPcS=_k{z^&D!ZoQ3N4-=G#eFB)tdF zM%zi|8p#&EMzV&lk!;2q=@^QH)<{&7oow(eE(c}Om{H(P;QU+X2RU6`U$mvzPE?A zSLE(_@X6>VemV*7 zw359R04w%i6OIwK-5L+f1kQ&CkbWteCz9OQ~ zTcke#Zw#<~a2EQQCrqZrpq{Yt<^K=PahQh$j697W>7bZSA)U?AZpWRKrZF}r{f}r8 zT$e&Ra}30!D``BWGk*%-ldw}vXQ7yGrV!(iuWJse+X=^x-j`m0~&@Gx)b4Ae<8ris^QW=`0k}?G)2l zD5g6oq_a>+w^K-Gp^$E;kj{*d?x&dUq%>+jE*D>QGfA6>ZnLmLjh=2=?*YJ;xcS*n z92}Vso!n$xnN7fgu>k|_Dsaeg=_CS}t=NrgB}~3G4+n>E3<=IMl1A2CjvYw%7+RRy zWBn@$<?_;L1tgJ!R$E(E69V4Mwxu7ReeFblrErK_Bz*lNyVX;?$A(?Cd;{ z`#d%y>llyCj3*3{kImFKsNx~kH(?npm+&O2yBkHUUvPB%Xe!>-Y(34vOaV4Xwr>ee zaS;4ll+41nc!nklrX&f@GA%%ispi8eoJ*>crL8H<73um=v zg3U4pe}3|b1XBy&$3@TiGUG+TX313vQkH=pW}wmTD2tow^;k5B za8sRL4{wXdLbkCwse)LQ9OI@%I3;XG&uj}2)}2p2En3>Asu@ys3l4IkBeDC zogow}l~6NC=m4%YZc+>x+(n3z`V;Oju&+C$nO&StUF_54g)GjBI&n^e*I%yFeZQR< z@y3TGcw?g08;a*`522bSpju`(_l4+1dpTih5a$_PLSbumH z8?M|uU4M5LOP}?oHR7I>OSU4^a)&zjEeh6qFH-bKvj3+t0$Wz+>RVr0bfo-;FtbNT!^ z%y{kuCX|Ktn_C-7QEttZvYVNnjjNMj&_jOA6G8oRBsnDp&B5Dh1ls?3GCjAdVibhu z&B7!`J!hx;eZAWr?gM06Bi*8|KRe+v1BIFUPvC zt-7#ZA^TJ(eVtO}`D$t_$GZhSN#HrTg2+o-RaJXjmzS`WvRz-bgbnnn#4}Ho*Qu+o z2k`+S4%P2j!p3FW1!*+C6m~_Ynkrzk1hcPFY}nhPXB12-38_C#swi$Ali!d20&g^B zB$`5Jo!J0cjnm>(xl*lR_G~pnTdh|tWs|aYsxGwgeC>HvUack-LA%xJC~eIhcc|)} z+8XT+RR&@hb5y8y6#45?Helf6s;mr&RTnYGc511BCNl4Kh&rlUm$9WjLwy&E`lTvU z)8?x=XbKqGRBeH3)1Av$vOZxY%h3P0jAh6%`s>SB_K4YP`c3L$Zi=d3;vfXKnv10h zA+T*ObC8g)^sMD9TfbyEtCn;11It;uqX3Qw_Dee1o)n=7kAGJ`wG3`3DyW?x%q}*oL(&#mOzJxZT z=;T9fbxBrLd2v~Jqa;<9HTPpVem0`9rJ-32=k?{AO6rRn%LGJ4jG>VVbJDq0)+u9? zM4PN%x0=P=j+AoT1LHR%a+9W7XJsYU*2R@&jth18V#WYPxn*oj6Gy zs4rNn@W!`f#`Z%RZ z)wBmxd!-ts!r|njYC@Ij(AMgotzknvs|kfZUQ>l(126Sm%zkahsVW+A_67R$7qd!l z-Xt|MTV1B)VT{tPK5;F}U6O-?$=T{0*eo00^YNX>zb8k0FN6kTfH%MA04Dwg_|6aG z&($u}A6v_&V0?eKmgSF{uiCWMmfVb7WdsJnAaz6y8Z<>6wMI=x)UL#DZe)2o@B17w zaI#*12}@l<=V@4!oI#(=i}}~!85t~Ak>yI`Grw{^eJU&Qe^>C+!jkd+CgxD?RAkI4 zvOI+cfLnOy`TKL`_2rFq<+cB7e-_1yYU^oeh@R}POB;$StLu^~%`uRS{-sCG%#Hq4 zvh*)5W%<$NDi4P9)S=|B*Qs_rzlbfe(FqYI?GWyF{4zG&F9pZef9id-{iZ7P2#k%A z)!AB%x}aGddZ9WOcZ-_UeWZ#G&0zHRq&3f)cE^V`~9k`P>p+2^}dPOT~W}NJ>fB8SR>1vAaT1^_S1U?!H7BaXeB*s zVTrPuiuv^YVm44-qyJFMhDOuCX~V${S>DFwPdT@*G}JdXSJ(Gb8-6f=8t7@m0QBcz zc=}Ch%7yUsJ5|@+l$@mH3g9UMFcGL2qpEy2cmHPYqEk@0EPs#HxQvgaXBRT-r2ZTh zNz;b%4gD%H97@oBbv%!&XG$0}p%5@nb?s2)$?EJ}PRJ@{S&@YCxipRFwWVxWkkC$< z2?!LT?=5Ay7}0N*vYcomb8=z6n9RGSOU|B>Q&qpAJg2I-uDW8w6}b~~7B9=GuE@u~ zkCH|IN=jCJ)28b6>(@)umQO31xpYa<<;B&_((FYmd^7SU_$FqL)vqgKw|RY86{Qtf z4UP59<)yH5ot{(1mM?6?tW#7{TVJ}qNoqkX9g|fpDXEvWlsBElWh8g{3*~H;KE8sb zIZJC>P`gI;^v~};*#2OeOMJ6p;uI~an|~Zil*{r{kAHW zrq8Tm3Hp;&2#t?70Q?Ad-lm4)(sHR8XN8LJsJy;Wm#bN_om$N2X8prz7OM}hX7enw zXD(l=Usla7(~oRG)_1&`t=5;+u$eAIsUp~`s1lm$H`cI&(i#`dTDWOtyEnvI=iVEq$2v^ zB&rdmLMJL|CJj{$*RHOssF&uNlk74oE2@h$xkObqmTxF-kcw)n>kx~JjCib9>|oLE zs_M!rvUOQmV|i1Peq9|)Us76JT7~|pZX%NxmB0@xYwJsjYl{#u%B4d5nOWTk|Eq7@ zR8ZI4xJha(=blp3R8@}ln9%y-y83*nqPDnFAG?DM*7NIG>>wC0IGvm=X%;bGw~J-^ z4JFaVEe%p}X(^N|hmS*-%NwiVJ)yhis>b@3$|{bDkSGHI$%R`L};dz+` zG`t*LMiPT2(r?|x;#hfInLc_q8!(>xBQZ3<6Y0C6x)CFTk2`o6_@LZ|`m%DVw6?ss zv8V|?US6j!+b_bGoR~W1)8DUSsj1uwjn%E?jc^MLpVsQe<`#4xb+Vp& z70bw~z=*9XYO1cQL-!d*HR0^ zAj(J>(#3F0upsr5L0{7CK^QAq`V^XUJP5>RD~=w~H^b3I{#j?jc;1MQ#lO`= z;3xt<^cqI`W)co$(5KUcvBMYk!3n`Ii1_hrHSELq%T+=MCV4^t?idcl(B}gF7k`)( zZQk;4CgD_0*aznV!yw|1)9+|x<%;hX-P*)TqVqvkfZmI5{^k&UQxi)e^36@G&L2F> z4Hi0HMz7)&0-3Y`Fuj)3gx3M4S9F>%P382uP7~e@m|op!!u0P{(Q73Ah zhTbl*E(~!94D_x~li>tldhe$R)5ZzC1Js1$0n__HO_7X% zQxm=iFunEEg#C{Kfmfgg5%e|-dK;?A@G)R|GpY%n0Zea6HQ^`>IC^8M2@e8HZ%;Mh z34rNMswPaYAEURb8n9oYGrRPPRg<9=FuiWogz2m-y?WJz?*vS*VKw1D0j5{7n(&K& zTf^WF0Mn~lP5NoT^x9Sd!~S?4E73b#O@=hU^gdS;9s`)(?P|hv0MmP3O_+W$OYeL& z;YPsp{#O&e3NXD3)`YtO(|ch#Oz*{{AF$9%VoiqU0Dlw)zXOonY)IP@vRpn^@LT^Hd8FE(O6nT1jvtVDpzN1YZHzJj_J! z7Qp8Fmk7QZ@a>EBU7J|iIP;wugzo{~JjO=wYk-ZzTL_y>dJ8Z=lBK`BiKUD)e*{V- z`kA76T#{gVWv=-S3xab2^A|_xBd%a6L(JEw5NR<;^dnY!DSEWz#J@2h&QNyh)nN5! z3=?l2A=W!VGGAdu@B@I&A7v5z3}ACEi@_@GAArq+NJRe-uz5A-G?l-%L0^3(tYf|ofml8Oi}`j9 zf=>cAPvR2%4Pbt#T>mYK#+#=DiIfKy;Ct&l{5(Mjj&4a!KKLD8jUU_fwX13X7ZD=W}E8d`A)6Q}e`L39h zU51_4O6-=D8n^mO?d*2t)*QX&YL;fDj=r@>zxHZ2$W95%?M3JZecIJ5cJIqqv(d7g z$pQaU=uAw6!OuT%9CPCEAQ+^*1XMU*>|=P3A&yd+ACvZWAHLV0M}`n@a)o3Z6@24^ zltb^~d1Mg6DGuZQlwb@}j)2d{bw8g@$Wgty2GN4EdIe!A5z*+P^LXKWbC2s!Uc-h) zH6KT?grO6_)2e@Y4KkOR-{|f=Y^Z9%3AkwewGOQPdVis)T~UK~b+TQZmR+6du1@Q( IJ8ggdzk@c8!2kdN diff --git a/bin/disk.o b/bin/disk.o index 5119dc48f96a31df4ebdd09e90a61d42d8a5d021..22f0a987cf007f175fa13f9987b7af7a0c33406b 100644 GIT binary patch literal 4896 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 literal 14032 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 G>-`s?HAtfX diff --git a/bin/framebuffer.o b/bin/framebuffer.o index 412bed94710e1e6b274165dc9179ad82b35b82cf..b24109fa667a5f6267f52ba8ac0f6c6bfc6d43be 100644 GIT binary patch literal 6672 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 literal 6456 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 BGLQfO diff --git a/bin/gdt.o b/bin/gdt.o index 588415d7f81b5f2e9e7c02ba43b7982f1e1ce974..453fc32b821e1ebccdd006999194dcd5cc0d7a9e 100644 GIT binary patch literal 4384 zcmds4O>7)V6|U}{amJa<*p5SDZxTGt=Fdsy$3N?MLpHHvC$<-htP)$|M5kw_J=1um z$Lj9c#MWwAAXz259Gpl9306Wm!C@~eEe8-BAc_#D%>^VR4rl=ZE2P!3hyw)7_f=Qh zjRA=RC!SRGy}x=@^{V>S%oi>%T+uX5M5GBrY)e8+WJ1fBgp;CQ^htXP^3uKEeEARE zzk9m5xtZjcH#g{XZ|UiqOHY6JLEqhtB9-L3?{1t4`Q>{Xr@(Fe~W$tMB(#NNMwA!B#ad)q_l^$G_#KZdql^NDNXj>0_@_*pQ5F>!u=xI2k1y#d6~*&^5gmp_*0TUs~5nZCZC!R*>`|Av$50@MAYs>NIdsR@<>Gr!0pLY42Z4-ceMLO zbe+e@CJnlE%hbG)38kLTL8aaApoK@Sy|vo~JdkU^Vw4Vb8^>75H?bVEIIxRyCrnFw1&sZ$v#s2gCSiGjq z_tKUc6!X1BMC`;gsdGD~*)2y7YGG_0hn?=xj%9n(#=yaW9&K+&_W|4xwHB0&j0_T6 zd{!(~{Yb zlXLfzrRC&Y4Sg-CeN#vE)K)$FWi#-7QE63e)3e<{U*VTq*!VI=Hh%ERR6Yh_@UA%t%vWy{j+;plU zSX;GCW@alXPLq<{3@50`g_+f@m6_pIIM?tvCL=RC!dYV(k_nvs%u;T8_-t-?xG-8M z&uI)J$h?ZBWzu=5djOX&TTz+}kvuz~StvW76xsLoz?hcaM%9qs0$oHcum05hNpUVdiq@MZoXj?kFE5&lQwKCw* zz*_Jc;U@v!Ic$9|@N}0DgTPKac6o(o6|#_O&@-zV2J@$;hLm!gwwpg6LVSh@=OaWo zpCH1Ize$PA5%G$`Pb>Va!exa`g?L4!|C+)(3LhwZTjBQ<{!HQf3O`i%H-(+NE13Ti z3Xdo(Dm<@nPT>uO4TZNAZYX?9;kOljN8uxdzg74#5&2_oG+ez7BG!>1qV*}6cS}RZ z`?8fsmDOXxe)>%<-QP2x`J@T?*hhgoyxs2M22sN+OUd2}bTly6odt%Pa zG4DI?Ht#!sv#_xa;5bnMLSkKu|B9iFbPx?kEOSnI0q0W!G9HdX#$xt zQ4p{>%AnI|^xyJ}`OqGT@^0+#i`GiN=(iWw5?|jdSYNbX`qPi{3>ste&XQh-5XGPz z&0CG})V~442{ndtvJOV`eieK)2CR4=29J!Q$i}}A@pykiygBF?kFQA-?;&`OVZKR= z`2H^J$P4YVmH{Kb2dJ};I6x77*uJkY@>{|ool!o7$nUY@Bhwdom@%Rs>E~$Vqb1#j z-vSMaxDOKl@x}Lh9!ZPxw-2^xzwuZc_k{C8iT@Y!)ukWDF;;Y!!HOqPtdmg`t&iVi P+;0K%eh@hUBfozDLu_QY literal 4500 zcmds4TZ|k>6|J70o}Jx!cpt{ujYB+k{ER)bPy9-Z*XzgHuwcpd3dwwQdS-fO+B?%T z>F$ZWt0*8Mkew*7wm^{(D2W8($KnHtABY4fiXy%uAtCtSgNJyEhcHM;#9}$8x@xzz z1c`61RDJHbb?a8ut=G(_&M#ar3`1x!gef+e5cdml&5VSDVoZ$6`Yib0wmtsd;+!yo)%^!C~~jnoHkuN{f?r8{d+LR4Kx z!Oe8v*OBtn4Vp$q&(W0XJ4Vy@_!)iV6Eqpw?O^G&<@pgDtc!3rjm+}^>kDvr5hcTT z>!L4|4Km+BgTC9)vk%yG*9d+3e-6EAHnYD389))HSKLP&_!G$uNz%D6oDk-+B-w!* zXk)gEe}+R|WY@@MM_&iC*z#W}vg;k9|C6Z8Sf{i5fdjWWxY<3&Q7;C*1&ej&iD}qU zBKr(kVLO}lzXDU%dNkVy9QXw+)?>xE%}?m{K}k{rzb7dbJ_?6){-<=?Es24o-t5EN@gta&3DZ|RY!}@)yURe1N4sE|~pD8%c!T*46pUL+#{z1i4nfycYLy8-j ze3tQ^R9s~84_JRV=2_7vimPxChBcFYg0otr`-dK<4rL0Ad5}rlCAOtG^T8|l82$UG zTPFR6m_*~HLd=HukloF&W)8*p${^zy_v9L-?ncmujD5wCoLSsi95RM8gO4E9JA_$0 znR_aCB6n#7%eGhKavz2Va;Q{X0hVE08Zl6v*)1-OjOVaz_Cvv{%p_W$9(#HWt2JVn z#au29$uUDA+GdgOA2Ts}v0A?deiF3Jw9%?`G0!A$FF-nY1r%p zQTb-A=>%YYr(ErMt;q9Z0>|2Nxf^sMw-$Bi;e;!Gtr>LuPA@F?{F|O%FGo(b<%SF< z-D@o;Z0bf{hoNdq2`-o}TTaAnv|T^4Tb-N24Z9Pf>V&S{^cqc3cbDu=*ABf^S2P00 z@3ox3i&jL>550!()=L~Q7s2*?blYmlM6tb)Qz~sZ^0M?hz8$WFk=qt>uwwq)wJXAA zbCdv4d!bvm1GnM9DG>9mPSt6hbHiHTbusa4oS{HhYIWI}jgbe50kI#aUJqO@7zfvb z<+zRIQnSQzhG-K{l2g8R_3C*ULr%Nx)rH2Abod2MWj z@n)y(jyE0OTWYUP92>uUb=+H;nwp*{PfbismpkE#x4gWp62kE{DJ69USW9 zQH+(!^b`l3$g*67tMM#0b0Wn2*|R502N$b7KkCI>usqq5Z1RPviOR%GW%5vL=NaRv z%cEE**ta|_-=~fD+;$X-v3f-q&u5LD`E>d=&KA!s3uTU<^Tq6EQ*#At^=)|II)LeE z+98{#I_CO|FdLEX#d7i$m9g}O#eoZ!s2yM?Qp@V{-X%|o{Ju-f*m<3}qEH?q4mJ8Jg)<%)Al*z*gQ=gutH zmoHqndj6Vy?abW5dHem39^M&@C56qyaSePxh#kPg`0yu5KQ2#I_z3^?y5qLcl~&fm`TV~&XP3KtY!SIFPEtnVm%S>cxyzM=4&3csuH z#|nR?keiip^4xpCF@*;dPAi;M_%Vgo6*d&!RQRgGZzz06A%A2s{%;ljLtz#$Lf5iHeVBv4X3tEZ;rF8-A~X2ea!2c&%i=Zqr^0oHl|qf=;iC+5XS+ z8+ay9F!vvC818f4nY{B@c&4#ra$f`Foj6BD3F|03QFtTGqEP;hU;h(8#=~;RSd7o} z!!}rADWB$72Z355ha5(s|At?}hxMAubF;;ddqa!9yE&HR`1a!L>G{&1e&pv+7(2s3 zLp}$hW0329>j|Cqmr%jKOIozwGcY9kJ%WClsAOJf*Zp$wbQ`E5gNjEJ2CaT(i(d(k zUWpBqXulW0ll^`XXKfHR_6v2RUwLnUYnes7dzt`syt~ko@xF#2I)8jw^gP~BZJ2M$ zj}Y&*EqP(g`zS>1_bh@P0oTGhmTxQ6_OG(jgwTibHVTis?&@{;>Gz%W93SOpC~Tws z-h|%*E0kzI{sm0V_de#U^QX@ThmwrN5Lq4m!XPVI3KH2l<0Zz QOeX!_M8Ev&tVR3%8^Y9F2mk;8 diff --git a/bin/idt.o b/bin/idt.o index 94a1f3addeccdc541a81867eadec9f014260ed66..f24a863ea33d4cafb8795b8d2385b5bab8f878f0 100644 GIT binary patch delta 2432 zcmb7FYiv|S6rQ>F&i3x!?z>M5?rt9}(8qSSMILQvOB-els;^?QIxu3 zC59lvIwmNfLSoeT#{@~>2N+@jA;BLtXz+&#n3yQ&4>W2F#QL4P(+G)wyvdyJobQ}D zXXfn8?6r!M6$iuoN+wbk?cDprmYJXPe!M$BKfilcV~h=aF$2lwbN1V~cZSGJs>kEy zN62yK;J&SQFVALfR`dO`h#ybeCVB4r_8H}~`xceYF2;1hxIs?q9f-wE_rwkJtRdQ< zcnadMbR&m2@f(<6Q7bW8&IuUeRp>>9oy2hS03g>8$tGDLz^ag9S z)e&4}Aj=g$ z->kp4OjB`VF@E77*FBb%Egmr`-sNRh#OQ!#`FVvWXz8Jnke>$}zCxTiwFZ+hH{vX| zoq4cUbB&d)n_a2b&MLAb<@D9F_F&eewfgOqs3}dwL>#xV?yhcvHo1{(jI#3kas zA|Xncz>kUU=5i4*OGSQ0_|6D@y)a|K&oqM%h@3(5cadO=G($7zL@|-?3SD;&ObH$l z?Mcz9Es<@GWrhQluP(bQz@(TTCaiSCh+YOCW@wi$mhk*KMyU#4gob5=Cc@nkNjKQN5Ja3IO9A|2l$ z$*l%qCzdVNsWjYlsTl4Kb3x<-oi04bgt!}J%BI)3(#zOppyp#Z-dTG>*{=a<-g{)D z`1?T0caeCyV?d6@(R0CJ1PGlh%?h##dw>uJu9OhFp*Mj7>lLn6_^`qa3cC~zC>&Av zibDEBpge~aexUGE8`F$^rijZ5zgBod;V%mRQYh#iA)#MkrNWg8=`V}?k13ocL}y-B zHoe eIBv9!8 t4m#4XW}CwsEIR^G_jOQVq{F`hbIIvI31{-{K(y!@R{N`pB%q4){R4SnRPz7; delta 2392 zcmZ8iYiv|S6rQ>F&hB>azVCM1rUiCw>q@1&Td0;ILTSO;K&c67C0FMj~h=kSvvuXo5iv#>5Z<1QR7bVnQsWe&_Dg7AKkeo$tKn z%$b?pQ`J|iUybtLbFs?QU;c&Dori90n*23<>+a0V%u9KVF_xXghxK1%GtPpTm83ZI zl$P7u6E3Lyymb22?Do5t@{}X@V=d3hBK}@x@oX9ylj1gdmP2N4le{{r4g)mi`iWR1 zoTHd^__*{M*B&N56-n(N`tg>IN{ku_D|Iy21oiPtGEgxL;9 zeQAMnLzpMXzD#jlcy^6G!}R(B*SIjFRPH0n&V+jd@ki~|9n3q0=nU#y<2ne*{RA1C zLnNaqQ~1!%?54~xpFw-^Qxmx16*~C@7DB~Ej4TCBum(B!BFAx9e<0VR3!w=0*xDFv zQ_JlPWhrdYGHSV&$r+(~@{B$}P1SPSLPO*oU0Lw%vWCbzW>F$7H;O{2vhb&*^*SK3 zF%6&l|as*0{C&F~!3iC0u(af}2*mV^%%~f#0 z6;YxKTjIRU;xId6tj$WAj5+=A!^zhF7v5&AE?ZfK6SuhTGtELyigA1A`7B3}IbUI# zUejZeI^%)fRfub|=JZ}Wp_ohPqqV76e?4op3VN*+hj4!}qzWeZ>ay7l#>;5eO{H%% zmr-3IF|RH-OCjRg;bvJYmd0n_7n;Vt#njn0>K~u>dGY1wIHRu1o35G$Hn=OD=q0`{ zy(>L2+Lsu>Be1o$z(hF{P#}wki;!!pC_W(qH{~T!9@d#=@OSj0{+!@3(K;wvv>N%3 zSR8R;ndHF8P+xMOdw8&aXn(39*|AA}<0_VMN5X8~*col9Ylt=`>g8p}n}KL;e{X;7 z=-5cQuQxq7GAz3ty&lxH1CM;i?(TG-+~btizOmjU6n$f3}qALEiEge z4|Vp852wc|XChTwH*T}K7ni0Isrp3S;)34(N%CMX0uxoxPS4ZN6rO?HTvWaCIApfY z=XzOd_KLNWx^{Rf?=cJ(g}Moo?DQV)^Z6>jW(rR7Oi_Q363-2tIi%m+!9B9Woy>&h z43Kn&Q=B(+7M{n*9{eAtbDJAzK68Rn`Sg5(?9UmBh+;C_dg4n+#-Or8@`SrV*c*_) zxKC%O4O%huKzD>O+&A_xNTtHXgedVzLcF!DK)RHJiXTw?b>fISNgOZe6CmaLj`WD9 z+BoW=^*A01)d&uzs2#g|HPMd3AtzbU+@(2K^?LJJhuDr{G{UEyAZ6AC{dL}xx!{0t#Fa+Yu# z5?&wyJHMhFepdL0Lb{%`uwP-d!X<>54GQfR<1PfKVCeSBkG&N|xQfq1p*iID-qZ4J zUs-S%4=k~JcszlpHrh9qo{*<}O*QN`yyo9EYCH~%;Sd=e4&5-Cx8(wVIWLxt{zd$N z+~Tj~cjOEHxHyM$nT!Q2`Gr3)k6rf%_&K>CP$E5n0MA2;i;M8>kZplVzEci?N07Kz z9s!?_X9ID5UfzKA2x`A7gTYF1*^63aBcx}s%jaZwuu{9_k|%?a;RxGJkdaqk(Z-i0&nJ|54OP`T*B SSsb%B`bsDu?x3{4iTw-26hZ<3 diff --git a/bin/inserter b/bin/inserter index 955eb8a18be30f7d515b784bb4fa3e1d4174a3ab..4e95e5b1f02e91a5a6ea7443b5c401299c9c4365 100755 GIT binary patch literal 32192 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 HLXrJ%%VT?u literal 32304 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 B-y{G4 literal 11172 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 diff --git a/bin/iso/boot/kernel b/bin/iso/boot/kernel index ce504c8c581c3befbcb061c386c2f33472b25f5c..9d4dfcb18574ca550a8c75fa0281fb7f3b3e7716 100755 GIT binary patch literal 66892 zcmeIbdwf*Yxj(%2?7b$FWRjU&Ng$93ggd!&CqTFi_e%t#fMUo!6GD$$9FJ?pk-^G)}fWs0IO}1oTnYcpAHvzvL`0Y-X>;nD@7qcP@ z?AR6Wdn+k9_%8w(Ko9>7TVU7%!xk8}z_0~|Eii0>VG9gfVAuk~78tg`umy%KFvtRy z$9^)3vc6is;p`yPFgR?1VG9gfVAuk~78tg`umy%KFl>Qg3k+Le*aE{A7`DLw5eq!Y znw-or_LReQrv13nG4_nZ)f)rd_BzJ)cJWN7vv=UkF%F_$QrMx)r^jzucW$r$h62X= zzan_}Z`cCE78tg`umy%KFl>Qg3k+Le*aE{A7`DK$1%@p!Y=QsP7I<@cO$!dk_BcL=pxQacXywx225-|PJ#SmsKwaQVlHLT(i5bUP2Y&-(Wc3=DL-?T7s- z?PnbA$InpFbGG8z&x!eqQw4L!A@^C4`6*={l9?!E(0)eQ_Xn~M`)vm10%7)>Fir#W zXTt1|7}Qlh_WQu^7fm^q&Tz?Z{Wx2n<>ZoZ-NAwgI2L*ngd&)rGEN8)d6q?U!C1 z7%1N0-xfd}EsA!c)X^gQBjOQ5ZCQP+D3+?E1b&nv6Y-RA9`-v)u?y85#fdUM*&s*} z1O|2_u*Zth$Via7v*bc&REMo|mDaIB>s)2)Sb?;?V}-qQm9t}ovvXBq$BM+xRVf`S zQaU%Jbrh!s`&ipK4Nl|#1R}Q`Nr0-Pic-hA_Fsh2i~SE>8W=#&L~bp47j%3D*#`FM z$-+VQ_d4$G4e7Q&5cM6oI#{19=x;%-sP9<&C5MZh9~f{PYiCXugVQ6W=_x6L0%jm8 z_TL6Ar3`hJu6Fhs3DACN1fi~g`|2o#kTMr-Ndo+ z2C=IOvNP4Weo&nhX`KFF!vJkZ{;*Xv&H?uY^gMW{#c}97oWy^hP*y|zR~8TWZ2neh zOjD+M2GvFkZQnp4sv}=ay=#;0i6MuT88Q&7BCx%{Vz`tVn8pjbbr<# zPl2F)&xM7KTNaVuAur&&ZRavk)c-CdZ#$xGAq#$}3YE+jJSJYL}JF(bb5G4Iv z7<<%Ve{_fYjM=;zSzScRA?E!m#JuN?5h#v^7)096eWEXRxX)8Kh0%N;U~HP_KGRCt z!r!5757-`a`*DXMf=&JRGwsjnEU|UE{fEy)OFvQueCLb(ughK&B1OB~F9JJB@&Y8F zmi^G&vJb<`lxMW;l_7m3*UwHOywLVM2A2LP8nxT^oL|`b zX4l!**?{S=2vr>WUjSG58%7tjML+A$ zglFmgh8`9V=$Q0A@;gu{#@ z780Ri^hFWE0!Z0-0+sn!6UrEj17>rQJ{>UbxvpZpg=E-@l8C<7AW_Gej-$uiXQ2#~ zIzvi9P3nrzw11>@l$`2H+)p^nkKti{MLF?nNt z{jN^;*{&5REGpExJ4;S>CVso)E)goIYFP!eM9s~wNl2LPWT)0`YSa*)KM|NuA*^li zJ2OO2Bqr?gEdr#N8U@-)ZxZmtm^ROlcw$i2wU{-XJ^XI8@&ffS1)L^IzX%FWQor0G z=6p(BU^c=G_X)&x+4248wip>4h4*$i_;8W`|2cIwc z?p0;*(c{c15oi8QvO|DDi?F_0&~)8RC>d!&24YN=Xg-8#+Wx|!bRomYx%2>OoM0Oa zThyIHMvoxM8bFD`*&OVW2t^~kWB+3iR2W-~+owLHar@%uy4Cs>G!m)Op8>ZDv|}RX zy&_intRgrdQo?2ToL$)Z7WI7U@Ly0g{-V!|zJ?x$8d!fkOo0v!$5&1u(2ys<<5c?*ZzV`!taTnVr^|od&bh;h7!zvk_oA+^4`Y zFs`2Kbf40D1XUVD%@?EX6QSY_)}dZjOHzk8fj)l7$=?S5v!g}hhL8lio3VVkPf`um zTnre&Ca1AsP*%dwEYn~SEGput9BYD^rV%8VDSGi>N*X-$p(;p8vkW2(Z87r8A*6v} zNmw!1u_s>8+a}PlWlwX+d~!0HVaS2nam#HO=qb;bVPHLw>3f%LaG^%%Ea%aq@-;HQSxI6qc&Pz(^5CU?BPMA&jv^p ztcurOc&etly1L`|7au{%i~R6k(mP(5eByxn(;=vi7Y~1#KKWF~GeK)iKHTv_2Z%qz z)ZOt!$){p~wUJSu(HQoO{9Ccg<=3sHFyO{ytXHhh{YxpgT;|e7jL5x;mQ$JjNrYPl zT&E8e**n)dJ4+Hf_oQ^3M_U}&cKX2DQwK^;c3eE*K7l#|O>z=@Ly^=fpQ63a1SJ&h zg}$xyX0g2&7Gm*H?RdK66dLMNSPB6TEB12`ySx3_%&V_06_U`}@CRrj{%>o@1Kppy zB96H)vc1v00Pto^e`=@uBGmmH<=WjBEvW3P_Fe_dzp8HA^VKU6t#5aCi(N3G(=`7k zs;n86kyE)(cDetEhAZ}u6pIP$QMv!s|+oP7mx@TNGF zAMl;^k3-DG*Z``|k~3%n?4=-DNBO37u00#JUAu}E6Cd~4{zk{4J(vLm3Ok-YKcwts z8{To~)DU9bfgn9(tYff1v44zE)D@Vp1ljGws5TKna!57*Fi#u!O${v7FBokK-%9kq zl!|uvXchG&t@Z&-x@sRlqt;T(b{z3nV6+OZj{C=8(rsu_=cscP*QNMN#A+Ujh&AZ? zn@-b`)n9Bx5ptYb;&BUj9D9b9IeR-wE_Ie%I{Z<}f$phipeLoB2f8Q8bahAf1f-<1 z2ocAlc-xWXC|`|jmWkM0iRfxio8)30?Oy?+L8AZ4y?egMX`P7s!d)3~_b-n1KDF4{ z+nLx6SNft(t+Qd_ycq5RZTi!P5->r4X$%oi5jgJw_ZL)O>pwe`?-ScE%|R&5e+O*C zA6p3z-iE1{vvY0Yfo=Zk4n)Qm4wO)c^dEj#?b=I%lG9#KX;@56Ic!VpQsA0gH_TlxK5|DqvUXBvDQ)Y zTxYSZqvZL{VtYr)?>mc;|ElAO;zT&_>yAT3$p_qTV(+%&D0sG?OX)b%S#lVw#FFPa z;AJJxcND|jf8SAT?<{$>qu2>Yex0^=;qY%BC{Fg@@?SLTa2>Gr-ES4#vBwZ#|A=7! zQ^Ee{g8d=E{wbaP(~^DfAoh|9!#!e!>1h$^OSW`-h2r`JW8-KfpJjC=^~) zeS4(w@4gZ4CM4J^B-ke;I3OgrRhIzaitK+!s05-U39(lQu}=tbKnQWG6rx)f;tmpG z^QVRoX@(G*Z<+t2XoeW(Ka3#1uh&OCtZV;nvG%%w7TeEaB@GsK3<4O=1TFjOva<0@1nF zt{fMm3B~44IpgoEGb)JiMfs-@*Ud(b&9Q%tNZY&a-i@WsuW47bGq)qZ(`&ob;f=b~ zwOecd@Y3YJPClW$+WyX^!+jP^#g2Bo`q{sLG0H&K|52GlY&YtQxV~$};!&>S{Mhg< z2XC>CbL?9Pl#~eTGc5F!XS%wGmt6KK$7xzlDkjGXTZWL*YGn|$T)t9)_py^AnuTiZ zDk8QO$O2oj)aPBh{auMUZ68%YmID|`f@J04$VlW+va)bwWkble6WJ4km~IDIHSKu? zxn3KN?%JVr*My_HW+>g}5V~e4i>xD7 znn69bhO2Mu(E2uo(CPJUl5`=hY%E*j>C9q-KSp-95E9d}b(Ey~Fg9%KAEmGol$Ba~ zP!_f7pe$-Zecc!=O6C}nNo_MElNw@3CRu$*Ce?YY$c}+3L`t&g5Lz;y$n1|bHbES> ze3w?CPwc-9{Tm0MVp|{JT@s|tnA3zs?=oX0NZ348L&m;Ov5?OD>ahEM z%7}Q;G8<=3=IOBJ{~e6Z|F962-{Z>lj-mUm#kj|M9k^%sW@0*Y0s~8b)`k8l7#|0G zxdXmw1HMTEzRUq%+JG-*z?V4SbK(xSj-I`&eC*v3-QKUx>v|4^O1|DWq(`X8u^?LP>NqyHYYCQ|w~%0z14Dw!DBN5@boHL7p6 zOr-VY$i(Qr36vPqmoDM-zGRtj^~K0UMxQ1VWBb0uJPNgp>-$tDGW+^uVtn5jnV8V` zrc6xids!wX^*tvOlly)v6I1$rArn*k9+8P@eLs+itiJEcM0VfpGLh4Fi%jJ9?U9MR zKCevV_wA60g1&N@nBKQVCT8>%%f!sSkR#iYP?E4&vqeaf%mvFs?>lCg(;yR1#BV6ZkeU9s|xbSJpUJaKG zmmQZAS0b*FxLmk0aZSOMi)#+9#kf}E+JtKxu4-J3xUR$1hU?q7dT`x~>p@(P;`$Y? zr*R#_bpqF0xZcIpkLv=i|Hfs-%FBT(1y=^HDYy!772>* zKf&!DY1 zc@yU$?!(92&v|x~H5_w4U*@T-!bZyP5xCv2U?cVfxZ!b<*!@GZaP}^#@A`+z-uIqf zR-Ch^%X9X_?Y&yDf1@39}n7NlZ{j*}G@0T-u?97S9)}Lsmp9}Cf@911?Lv+i%##5C2`D`Pb$!vXN?g=2?fwQVJ|GHK zlY95PImH)ow0#T-0pJk>U;yU&;!#b8sK#+u?*Wl?9J-T;b;Rsvd~sm0u6Fw?3oU{~8v!${_sMP5Gya#vy4ne( z!fQA z6?Ez#b~JOnANBIzO{b7{2}WW4Be8jlO%Acgjvx^6q{uC7cVAM17;KG#NG$f*>}R|$ z1u@L=bT1)G?8*y{L-CHMUz>bF91_Ua$a0C@I)2?IHf zq$vB<>b7?VR6;%RR{MKu#~%-WV09ci*!>la_3tf={Oo{CS7SrK@x=3vLvJ|_-P%1s z*mqduTc}K=>G;6zZ-xeFAM?v-v`zj4SflO85&RCgvmGlGjNTX8_Fy*PWKbILK>sYE zJ6;-YKW=G1uC*WE3yEM>dQonJe-cPgEoKS`ibdGDrbzIN!W|Lg4T#9AtUR@Yh(cxelD6Z z!1)H^{JlKciu8^xlsJ!epXWZ`GR>q6^2i=wA66N>JBBS-_r?Bns3qPp!P~(Qt7{e9 z{(_-E^b4!&g@VB(8#Z%KzRp~0fUgWJQ zD|c=5w0NtUT`M;&cDdG;RaUundAzl*!U7`53jXEi<`+Z_VKAKWzt{rncaU%!*0106 zHA2#Q1U;9r$}&$~qp#e@e7k%B`cZlL1=DBDoHct+Zq|HT&*c9dp)SxG_@SHk0^M5})Dx+vz=J;zTOqn#f z6yjfTm3zacEhQ`07hk&LGVu(3}U2aB4#qqFEzpGEw$A(6~+v3@kI(BW!X*z#>HV z0O%I{4LTR-j)U$R(5=KXpx-*!ZI-kf(VPRW4L=_F4>Jw)Q(uuZ)K*SRXl7v!dIK5P zkbS*qt7*k~&k^1R5K@rQu{dc7@B zj_h+=(1zay&5uEIcreWql7`y+x1d>y%^@7n2J6`%+nn&HfuDOBJn4D~_*~$>Z7#p; zTk0)ZJGbm1WseEhq`D?S$J;=Q_Z@<~EXxG5X~K@_cvNF)Ra9s!b z_&*K|R3RN=gKa_m4uWPQXcm}hb_ad>x4?e``0tqUWXIcsc02=`v!FRSnC7J*4fW-_ z-X0hb(a&ge)KeizguIhL^L@~`2i3F3L__C*zXeUK*#@XbwFZ5#2{b>#{)h-cCSJSr zddQY{f#xu1mSWQ*V4GsuE^1apuwj69;d&PIuVb0`b++__CK;=$Owb4F+%9dIf-Di&@s^Mz)v*SQ4z$S2mT)5C!6tALA(<_cmLPap9}mW zz|S|Azdl&L82DqruQuZ?%LM`H)dc*f!0!&hw+8Utz^}pX_{|~snVG9gfVAuk~78tg`|5XdL|6f&XSg&CV3|nB>0>c&9{Nc4d`g7se!!jlA67NP@;){X5TNW_k z$0A4K-yiGjU4b9?8-KF=^N|KVE6>@xOlL(j5`Wxo;9F)md&OIrdIb{yF=~{B9tVC0 zz4aJ^cg7j`v|0GwHnHR`{KM*fz;xd}E3~8$iWU zdm6mJ^HsoG6l)nWWAJVrQ!}jx@KEUGN^xVclR=`UMN!Uufm}zU&&yQ1^;brYB3}J< zdK#FT7)j|D3=E6NMKa^}8TfHMs!|Tlz}?p5ZDtwil(h6P72h&s8Cer>xAbT#y&agb zhAOf2T%+9x?<_}}d)Sl&Nm8%fj?BfBS;ulm$V_?%CUPz+jEhLb-Tre}lHRFT4*m*v z!O)&UNrRz%f|;S+tuu^AJxob*JRwNywOR;%4YBgD5jy``?FMAdpv*cpQs-X@FhPqX zJ4MCdX1fUj(2M`d!65erB4NiogA(fGWJ1{gLhfywb_(~Ha$hyeilL-lS056xt_Ws-ocgV8o&|*! zA4(O*QWBJ8`vfEnu1WN}0N2Lf5Fxz^yeXEFET-x@FDr?quR7Zl`XTC-u_SM_@(R*d ziL_nWj`Wt;HX>I%NNbH1oF3w zbc*s6^2c`Qks#v)!sMRdiHxgyA*}@E|Y}H>POKG$+R^y^1 z2!+x>it-DY{b3?Z*+oAWP`bhBy76Q{mb?KO*HewmmTX1z+Wl{o?PN)9$QyCXKA?r| zzvZB$G}-|N8BG&L>u`|K?8qS5*zl)edhHV>;;;zwr_K;~OYTM*CcH zyIvV1YrTe9e+g73P!QsPk&Uit&B(zAM367)y2n%(A*jG&$bmxNF$!&BRyR=O<=O&> zcfWy<(*Wlw(0?kd_TnUbrv#94FdOf)bF$i}lXRO|$Y!7ECcDZ)CL7Q#_EMupU%a*` z!s{(P_26z%M(cKS?4084eGbh3BIQaoELy42hs8!JBCwEgXxcGJ$&|Kxsgzk~O~p51 zz!vr88>Oznw@vbuR9X3EEeTZ=DDfn(+Ox(A?nP}GVx4MRr4yaK6(||+VK3?~QEz=3nV*A=<;TGMUiXO2 z7IKi6b*R>I1qfa>yd}Ploz~Oak?*X6{C5e_9vbiLrJo!r2d{-1!uWUPnMJ!>7j1_S z?FYJOXB(}fq121045IyWK(tz9HY3wQKeu^kq_kJ1w`o#alC|PaDNsbIiqC z>0773)w5CWp|+~ynfOMJ;694}Re|CefazCZ%0cIAsD7!n4ZyqOoV^=G=6J|sSp~$h z82YV*+H?K}mOwFS;$Jb+`vpxiX!!kC zVvMDV;r<@PRk;KqZy*JX6<;b=ZCY@as>+_A1!t+MoTX~PC5Rfb1X1M#Rh6?+JgAlV zQ(#!D*8ZBLJqPJkj~Iq9D+fZY6Mz=e(vr*69Rjc^)r8p@`y7hcne|qJic+ovBnFSy zk+9L=k<7RToK;(R36L@sX{~51IR;ZE;ifGbcZS@BS+5~fOd^%!Um|VOMp9T+A}K#j zObnUwI02&tU{Q_`;EEy7)GRLn$Pmd%mMf4RD;#!OWMvbai_(@X3w>^NyvWb7ypHq) zL7y90)>_Xdiu~!8KSTXVB0a-GzqFey(laf&NKX;zS(cGVPo=bNHj8ZqfHs?MJ3-Pm zS|QAgF_eZWYB1{5J=?W!fhN764QMR7wHQDy(GzIVodQZ% zp^&;~b3!)NmXSbpP#}#WkrIDSnK4#s5&Nlz68s+z5CdC@GGdMx`Tm zB4cznY9a?Qc7B#eLHnSCa2ZEzK?mV7j@W_@!etyuk%K9ham2P6l}=3Z;m&19u>}{p z$f4*G`7PXNCnJiyXMkDtk@q=3Dvbncyy(MBrGY?A5P-t0A}DFKR){Wwl4cG{njVx? z3QC$eC}}%me}W=*mP$cMD@q|g)}1g#inekmFltrVbxd0&`V)Y10ILO{sZ|vK)(F6+ zR#gI6D*!vlnwYju08X{41;Ba%Br+>~U(l&tOH37-;A^DsvdEoB;WLhjPL^$!w$+PmpyJZZ5uYH#)mmpYu(zlt#5!p!1U0Zv&7kbZ%av zbMqRVZ;8DTMdTu#n^)=FyiDijbvidM)VX=3&dp18ZeFW%sZ5$|Ym!VTgT*?v{k;Zk zVWZ?KomblqAyf1_UgM%P{N!0^Omk~db|JI20Up6`NyH>frA-h1EeBafH^%`7nFDP` z=IC$`Q@Qr$=EXNRR^MsDkF0U%!DZNe{1cks${3n>^TSJUw}=}xg@yVNOIVA{c*-0- zW; z8Ic}ADm_QF{TmV%`r*EX`U#7PhF@71y+IjN0dw6;axAq{i#q6Y&hw&axz4}9YL2n5 zh>onsqG>V8|0Hq~k<@dmK*0Y~o@~F33deT^|3UKyYvz@*48+_$p65^XEK0CuGP6cLfk_A#C zz5*#N+(uIbpe6Xcaby~`=hxq45qsuHN(;U;WM&2#oH0gK` zeNEmRq*|)HzNRpn^Hd0RAU=}wVnoYAU5R2uqm5-6(TuZ*t7FldhKy+X8gU5-^$|^9 z9+ps!9MO_OMzn|_Bbq*pMT>c?y6_Q{w9q_O)#tI6Nyt$bjq4(BWyv&;Rbvv{kr^%K zvFgZj!q7Zc)#tGmn#Zd8Jk~<=tH^H`^89;=$>v8rhv ztD5Gqs(Bt8C+D&1^gSeOBU)FTF^19@We(v++C@@eN{4qNJwl7W1CKblGp$Yg1ps|( znvy6*Zb2bEMGX59%tnFhBr-%bI(*iEEi78Zd+iqbimtxVN*tKQjmFmwIH)dTro-IoeS&f&)AvY>5iuIg=~cH?dyj;C z!Wv71NMxS%1EM55$s!>;sxXBv(GF9h_Nr4Iq-I%={ zw~;?Uw&JiFT|qhcOW>?dECSN?&Rk=?6ExSQlLnTaV#ng8*fRxlD{ZEZ9Yge5&kuk0 zO)CYZw*aIw*X!SL5B=z&;mubD7oAcHaL5Q#8(;EIV^HH(y7&`NpaaJA7mTxf$wDG zOdR(Niei28SZ=S@jf~0mD@4Ym$8&qRoSys$MHF!p#)&27BgqTZqCijichJzKyiT{b z={5mmY?S7p>{xOZ2W<$aMST-octQkab8>A_jF`$2oW>CA4atNO{GwAevoA5Ca7XiliyVSR6vvzNYC zqZ}MdvoodJN^Sdl7c{~cy ze(rmJmOBbX{*^1+Kpx`@DMJtAqb7ROB zmY((6Qa~%C$cmPp(zuUT$Rl`-_OUz;S(b*r_+Nr9+lo3y08$Rd(@b+3by5klCR9y5 z`BDsgRP^Lg$RTx&WBr|RU>+Qb_3c{ICY8QZYucpJcWNU;c51cYP^=%BNBt}ce4Naw zF0*|XDNf_migYndMehbqjT%j(jCBdp$|!OCeHO_y5e5~eOam}R0*Z1GfDDj|MOlx) zB*$PyRki|?A;~mlI)z9HaHSrYaT2g9X~1MkAc9#B0MhbF7XApR3da32BvG8moh~-4 z6q`~8V5R`nNaZ|;W=V`)QGl5(09%aG3a#cyOp>w)n7IOoN@jLa={8(3d8m9qW!5y* zq80xOaoqvRD`F{$bF@{sJIF?gI*Y5U39;k5AWclC(#Y70eW&lEfRb-Lh}SLBi1Oga zz^OAqmOq-3=mIQGi>IMYafzXA21siJ=_E9|Aa!Nyq!HPuZmd4%#~%7|)T5+W$t&oH zK{=@I20_Xsom>=7nJ*ztC6lFi$YhotTg2}Gr3R|PVHISC&&|ge!G+Il)P2r)RU|$h z1?A|W%3X}vRF&MtP~|ShD$0>_K5K}h>E0EAZpW1I)&jgHSyZ^k ztIz;8K2t(XOpFCM)*v_v#U_hlCyZjFQY2xe_9%*_8T;fc5@AY-6JhE-SahN&WrdQ0 zS)9&HmWtPV@GcfbD=;O}N;*sV1bJkFS+>Xw+2NZQTdVifNGF8b2_B=j>sB*fpt&7Z z)AT)LX{u;4l_h0_8xK2;>Y6lIj5L@gw|z9Z*JJ%1v~op!46^O(oV_>W2_M4U_8cLS zCB zu=A8?G(`o|Mv)b;0|{o@c%7NtUon_*FA9&9)mwwr!)5VbyyCQ~w(+(Ej#{!=oKj>Z zHk_?YKt?8uj$RP0Vlk$2rpC_AQ07I?iC&ptNr5XxQ*lu9=}fhSpsdM)*dmG1XxYlb zRHJx?bz&+NEK0dCRRI%x9B)wZNC%6~&`5x&>;Tt{7^4sxB2F0}n-Hz0k4mRZC&n_n zE-HdsjLt2Z*iTF?6kK9MGgl_aY=_9U%?+V-hGrVH!gTRaHdc~LGbBJ*I1u)7g{Z33 zc{Z{vg4Ap>7?HHn3C4+aymZ@}Ww8Q_$IHnk@x zYHT#0n?ZtQ*r=iyNQ(9{Ng5k4xI?lfGHC^)%_pX=OfVXNuyKL`v?POswtj+cYN7ey z)=r!tOpqkRiZ+Xt95i6Y_>~E>A^eD0f;h#fP$M1SKCGP#gOgon$DIdNI#&?kU>3ftS(UWJT%c@gN+kSEKt&M zGKB?Ec6^|i@K?jmyTOb);vii@`=sGcp)Ei?(r7rz%o=Fnh(VrW3wlFF49t!ujg)4O z8se1EhQ%oi1Y35u=wbE{BoFE@(dfQ|`)uqC(Y%hJy`8#S2#X6h8*Cfd^zpJ063FU_ zf+Wy|4KGLv&6eeo2Ully=@CYkGyF{t3T7I^=X94aqeh0-ZJ+|7K`$aAgLHM_&ain* zG|lzYU~kq2wGlU&MfPFqP{#}g3ZrXA%BBrv6E(y){zfzvm6(Lb3m_t*h%9O&2Q>yc zqBFEl#Dz16^lGWUDFRu7IT@&Hs6~eOA|GTUs}Uz74E1eNQ=N~l+2yHBi7xCe3L*`5{QxmOpaDC7Tj-8ma7!I3^B6HpvBZfw>lwjyeH8-1d zwvp0fMh$k4w9tMcUJD#!b_i*wbaR$rJeMw?$)*^61m%zrD958fwybrWxmLrQWQhKI zW`v@jn*53!3PX*UT?9gz7AhnSn`W;PmNL(ahKxyuD@7Wv6csA9VU6e^4r&jyt3J)4 z=!b|xzHT^_ZaZlYeZ~Xl5nW@DCUK^=labf(pCL}1IMh9og6=_GG%#gc0ntESP^WOVp`8PeAcS5BueSr8Z;6cvv28m#!@DvIE@8j zVjeohHk)tI&=_Wr9cl(^&|8i5atK=~we9OXByzC7ne~+&I4~L*C|#G(1w@#oD_S9x zM$_fR<^VVPr&X6u26wYFQcFezIz6rVL#=15IfmLLS{PH9g5nTfDxgP9&|0y6GT z$D>jJTR;Mxb$5WZ8rf~Zj-j#_!zr168JYC&Gr#pgq7^q`{(mL&|AKB$(Tz_*L(r@4 z>PUqrzQC0lUZ+N8^9*X08nUV#duXugNZT5o?&GPgJk8hB!{^_@)2sRDY(A#7=MJ8> znWy?5;EH8>0#DlsBdtZTEqt!+I9Imtlv3nm@Ts<~_utR?1GcTU`_a@}_(V0Tnnwa- z+sq?Zb7dQkoQiD4ww0FYoyGphdyk6goTHZLEwhC5&6wn9qq zmw3DlF5=}gw{ZI#a3vR?&<+R5<_p&FHDKh~%s1dMGMmROwAJu&kYzm@!J_WEkB>qE z4q#Vf?&At&uI4#vq9_5a^`M7;N`9)sfcyn`#gzM?S@UwY>AZ z5NL*;D@qh{hX5Dw2znOs_1>*rej(DsR#s=e&1R?z0fDSVPjVau^Id>7|A3q5jDy zED&8M?pr+7=HX*j^EfY`ngfQ}Jbg7!T`f$6*Ni4g*%EegS1nIl&BxqLlo=#{4xder z*A1GuJNZ)7m3E3}^@xhb9JnlP5>p82r(H1bnhj%_vIJaM0fR1U0{D=60@l?Xe^Sn#uNRtJN@NoGU? zY&Iy$ZX zZHDq*u6)KP(pa1TLp?5rSt%8Hh-$lCjO*wtNp(;_G+F~}Oatd4YJ|%UoHNlT;>3&Q zEj$;HB5rLcWuf1YTb9uc!!ozf4j?D$)*uHx`$iV*mXOGJ(B&mt^aY_E!` zjdCAX5yX95$>7U9e2Ha30#8`Q)86E%KjEW(!jqoo>9+UD>Z1`#?tK0;p87l=1@Cy? zVukGe;z(8#^SH}fD@69l`$%?i6S9+gOOuHS$0G!a35T5iB|<5B1rskAh-Rdj->6y) z=RO{ZSqB_id7mfO@+e#8X8`lw-(t(y61Z)p)YU|UzAO8K4s5MOPk5fjxpiwOxTjdA zB~X-gVWMIwjOU7af+6&(kAHJBcYtVXc%qjC(XYOq=(r&Ibtq9zkljLJ_pBiLog|9f zx`jJ3L>IzrH}Vb$omR*r3vCNPId?PUS}w#Y+KNmJy>!Qm5yyd8B@m;w^64mq?2JM_ z6&(yD*&r$S6eQ~eN#s_*xm4E`0^V>3&n)B{EqMv_jHl-a4~=!1TSRPM)y}8B!QQxDCvMQAPnW)>!AGX>>pekI)8qE=)ZgYO(Q<37IpK3=w&uf31s zC&BaP^Og7U%FTSe03#QmFpU`MJqQsIwpL$mMy_RY0$0ZJ$#tCnnNQtH(^mus>Z%#! zoGeF#hH$Hy`wf=2%uWfXC1 zLn#?NMK}J0&$-?3n{jnKQgv8z6iZRjRoSJSaRCuH% zXQZVt5g8M4jYbgi@pLtwP#Bd{Erm%K#ZZ;Q9?`EkAmy?g;*d7Q8F^3(-bhAp7MHDVPXU(HijQH-== zrnQEr{Epi`V6m=hgDDUhcxutd7t2(y%#W4UGa>FVa2!F>XVel)4#joWM-? z@x=Ri)P3CEF2)j!AvA$gFh6MLlWKTOH5N+=^vJU9qsJsnnea%p{ghAe^4UGqBNLwI z(RDoGem;qc-p6O(Pr0drBJB?DI7^j{CtNj8uEEM9iO*WaqiJZ*+zil*_55nSoZ#rq zd@^R|$Xc!XD8AXVF-a07r=nyv2E(n~em{@KvPCV$ghic@jnvksF!W8uUINxgcW@Qa zGM$!@K0dw<5JCXN&459KWs`D=V$1soYzn9_d5!pt+CDmm+a9GAiM5s6m+_=Wx${wO zFXoAFaOWH7c)|;QfOh=bdm%aSfVqYjXcLf9#7%gC=mNYEEl%&BsUsB3U^94ZAzxC< zm%YiWkhl*XSOl@(-sY7TLoJ-obCEH75urt{f1b0;DmS?6ksZTwBHw2Ptq zdR@p_cwT-e59zk*RV1Xi2_Z3a=N5XKYa~WuRD3rdwE!uMv=7pb%$QsW{2Kw7WO9tZ z%7Y@X)+y3L{}bkw#9Z7Y^GRS%ZwV#QL@N$+1I$aIC#InB^nl{@zz9!|EV(Yi5`G_7 z=JVybR4B1VFGtIJl%v%r$_eOZxgiw3_{?0M_?b~SE{BhT4?M~fSMy{acRkAEM4I?o zngV<~Xu|^qC0`6fN$nJ>^rev4eY#mfH_J_^LK(oaiaG9l;g8<3=9(%m!|Qi>Gufg|i%OSnxT17dna9iO8*4ljW%Z@y^^FxfO3NxM zo2yz{*sf-ew@T)<2>Nng%k{E=B&=xk;d4+v41cpr@odolk@1S!vSzk?U5RT+-Yi#M zUUortt}AP2K|XF?UqjXWnyQAXW={pcvgV4~`JS1xXS%X#Tv^rC%~e$`-m->DPXmE1 zwT;c*tX)(cpoYe*a-XN(>uHbx2rG7EH8nSSt17&WL{ZjqeM3cUb7MoJuO-XZu*=g> zndL1judiyM$^>~ueOXJbQOMKSz?!SdDh(A$k%D|^;%R_^1b0_JDd2>rc{4#Eag?=e z(WZj@rJiOX!}ab4Z}asGKL~1UF0E*+tYQ`QKKfVI0g$C6akHnjs(C}zH+;~Q?X22a zv9pQcUFvF(wRm<{8EIKW$fwuR!m3+9Qqxf8^)-X85${VI47`n{_2p%d+~f6>)uWoy zY7Z2@9^7}9R+m99R#s7gRxPb8^HvGE(n>?@Qeh?5RMuS8;HB(Ry;@e`Yi`!DsHn-v zFKzJcEU#*2L@LUb(#zn!hRUk?D%4&9so^PvL5e?3z0y}d@Zn8nb+IwDffY9XJe(O+EZ1jvy~!9&6mhlSS*YjBovl_R^IE$ zLWXWFl7nFZk`GovvsR-GD_KJsl!EEKr7fb(^1Em z7E(!Y%a?B2zyR(fPbYW6kDzl|d09(UsjyCERds1&lkA8!&1DU~`Z7ep z>sd{ux75>sAObT`wN!#NG&YpVF2+iU02kb{0ghQ&+FVuRp{~!C*Eg1z)i14Tsc7~z zp_yb0qB8x5gH)aFVTN1~z6J70u}H{G8#lUz4+=qddMcUs`X+>CC}fnW_v{2zBbb#| z2Ds@Cwoz&vltL(kvM^1(^y8IFH!Y{WRzqDE;ld*{hEkL%ttt!_6nqF5RI!11S`f#) zzH*&241s>yT2*OC%4!kd>#6JN!%S&2DN>rp^hCaqC@_aJh{IMIHVf)uSd)Y$>$f8K zA)pJxP>UHxHftdo8mQ7%sd*D3GCCQ$501c$Fkpl@%0l;KRn_%nH7y9DUQa7oXh+rc z<&9;{l|dfpS#Z))v~yKe16xvDvcYfx;-z;*vW7QUSav(u%#=lAF!d;y*N6{PhTKH| z3M7R<@?SBgv7Kd}hM*Lsx_Mbpz?Np#(#L;vIK+Q6pl+x@@6)TSx~{4MgM?m5b0fO; zPQ>PBjKBdi2$5y{Mx3d_V5V1ER__HLjGb^|sL+IbF~xx?8{{bOZLSYc7`detWlbK$ zi&6}UkhFz{dWgr6-GU&7p@c@%B_#D~-Itb%sR1mB9*3|>6N6Cc(6Iw14PtaLO7luF z3n*);@OVne(t1e8EU4-_%ve0tK`jDhJq=AhZz<+M#W1Xyy*~FtKWg$4ChZ-N~h)JoU9uUKrWX z@=_OH6OE!&C-KxrT0Mm7p;O4vgz=Rkf$$tkP{%g~9z9r6Jwziaq#0Q0^wOQ>Xn?Y2 zuRdu)i&J@oVpDNxA}=PKzM9%lr5UD~-qN5Anrg3a5fkjo7!eBlT51hX_tuxg4O@Km z7{)LIX3(~byk}=)YnAAPtg^+6)}^-om`Px;Yw=dn$dp|xB=tx&F}0$~;k0M-OUI zDhDq_TT1imEmgsmFIh6zHL0W=D;uA_%*)F22{7-b{QT_P>67(rY%PTTVc8$})7U2i zNfPRnk52~Z_h*c`#2qIt1M`rwJoXd%rZN6L8CW3``u&rEbx3`+e#2Rr@yP)Fj)`?U zdx?+2qQ$!=%<1f0ZCHcky4t#V1 z^2`Q&6dTd9n9ArwTQ8a5?SNl1!Ct_pOz;7~e>A}d0iQL&j{*M31V0P-ya_%9_;V9{ z4)9-1Fzte}MM3$J0Ba_A3SgTFUI^H3f;Rznnqc~vZ=wml3Ghf0d?#R+34RE0rV0KX z;3+2fHNd$h_(Q;ROz1F?VEQF{zX^T-@C6h6E5QG4f?oh^T^#g}w*fm$@CCprCYU}_l3{|!0G?uk z3ji0G;1z%iP4G6rZWG)Lc%2FU7T~K)@Ik=aP4MG@Jtp`l;ARv22f#O&;PZg@nP851 z(Pe^10=~xt=K#Ln1TO{rkO|%b_;C~50ND7W_&{I1G5!d?88CfcAp-r9;!_V``n&?n zD&Ttu(LYG=UVNZI;BD+zgYZuef(>5uB!0($*HE5dg+;Re1Z;e#IUe!qEMViOxCEaA zZ2ZEQ;Ew?t-%%p?3&6(rFA0vOpFrb#cF1ON&?kvtCiywRaTefd&`;z7e-&Wk=dD!! zTENEF4hW|2#v0!YB6v4o9D~i2^AK)5afF}HU;OYB-7>a~F>+m#w5Ks790FO86_kF;|_mv6% z0AS-o{sjL7u<;o!f}aKaZopm|`wzgz51a}A4wc7;C6dXWfIkOpd~23qJLEOK?LcrQ zVER@7zOev#ZEP`MxKMENBqvNC5 z?*SX%h9&&lfQ^q76MP=9@qrG4zW{7}=7wM`3RM+DUW_Ag%*x^b8{a4(d>&xqgH#01 zA$q4FpM@0xHa>wx_*H<7@3s2&7%+X1 zWiHr~e9r);@6Xi=*v8%jY<#Ji%G1Xxj1PYh{0U&=lU)SIM2Ggj34m`dl3!jU{1U+Q zofvdg!LJgq@ey3YUkBLuW(mRj0UKZUCHOmljSrC!{1d>&H!TU)C(5D!q{eFZhIQ^W zrB|-pu&HFxn$pFKHoBRfw{g>=C96v}uH5QoYZonFxukSm$=b#44Q$z(Maws`B`e%Z zR&Oj>Ys}fPa;(^a#UibU5$gmaM_*760<-HI8=LfXAU?9FFWu;rCV1)+a%K}) zNCnqBn4!xR&E>WM*a*Vzrg>8|xCu=fsLui+0baZSYk4{SKJq_4(GB#<=`@SS4HX^@{ z0{i9SFdOHZ;SLv!0|@%|k2up7ZxFPw($ec@&&;M0O*m_)^)>9sF2ip1;B9z zbh}i((;&|6zi!9hbUY5V1MMLj!MwRH_jdKOjur$H98d+=8z);BYvqw&FxCt?68pNn z>aZsm;z(BCeW#NkoEwBc-w9(md;=P-PR9me50uQ!k5elBMAvXi-3Wp6g|FYU2Om1j zAQPC6Ma?XPhsmSdL5>r$9j^P9xujHIKUn_SvgCC`N|N^SSl>7n4;-e586O14`BKQc z6o2cm$)lf*$w{5*ILpkOyjDLvH#u5xKVE+i!#F|HU)d0@9rU!&)6>fyCYcWWg6GHj zI|gEA7<6tN$bRjy)nfvWIWx;H$J+tfm3X0|rnC$%5Y$u!Kyip_pvuef zhJrze$XjKkp@)&AY;+I66^%RT%m&M?zx$6i%dvbCTdPo?qD89J|(4eC={kzxFo=@=4sE_vS4LaH@ z`QS2ibvhlE#&V6koiX|c+KfM!o}dq9Q+-6Y7i-nM71-0HeG>i8kbyp|NOYo#?a;Rg z^0{;(dgzMWjHE%Q?ZsYK_fY#$ot-8++Cy=IPS^KGN8>kbJwbmjSwI87}0= zKbQV|08ip;{1M#)rVJzbGY}YQqCJGK9M+CEN(FJh8ALY*hZSEshtfSih%WUw_F(aY zW8=@@^}0@%t0R(6!w;~&HvWi?t~2`6pu6}XV>?A70-@uG?!2Bh=&C-#XK0B)|1;{N zr>QvW23oUa_^U4Wi()(q2?-ZAFVZG2Dtsl`1vgf9-wF%$Z@ZwY~TE`+og$ z;LO=;ueH}+Ywfk)=j>eXDP1VbvZP-oNs&0@a!HdBnQj)i$&y=Q(l{wy8lvGTg{v30 zG}k5Z+svaRfiD2y2Hx$GzMC)cn;G#N0F(h==8`7aC4NVwZw7t?@EcrGnj84b+>!-R z$WFTK9e))Cqc1BWfF8UCEih<-K?@98V9)}C78ta^paljkFld263k+Id&;o-Nh_Zm` z;fFFQ>hqPWPDY^y!9fcQT42xugBBRHz@P;NEih<-K?@98V9)}C78ta^pauR-EbvpQ z*(Etg9(TG=v_I=|jy&OX_r^oF9nO)xds&Xl)jM$F2m?_sDeRPI(&HEIZx-|h`->&1 z|8s%|uR#k8T42xugBBRHz@P;NEih<-K?@98V9)}C78ta^pauS)w!lwELA+az-_m=_ ziCcnQr8(xZ&S%;Af!R{ozMs;I0RYQh{Ctc;a`_h;hOepRaesY|-$ zD5XAB{nK%hQ#yX=Bj@nX$~)g#-G0g(oPqcLcOJUWD@on$ryRAP;7fvFT&^UwKkeGx zyY*Cech}YDpNmLgaNm&b81nBIQE+5YSaN`6W$tNoaSxLjxHImsi>P~=<@i9|Z>M-)9F6n!wL>X@^H zaTzc!9rG8$d_0kh4Bqu_q4r4;)8$b+J!g-2PLogcFAe_s^MQfjXZShdImM$!b$L$h zO@R_;_f9sE(M}UTN#s=BX=(SIl6P)DH2~taV>>-(B@=S;oR*yjJSS&(PPaVN-M!a$ z?#gSfHkdUh_%MvV^HAnBA?sX@EH7%E=|Waw8Me+SU4cmJI6~3U);TkQ+kod1X`SN& zL)Iw});TOOIh&1-(Z(N;YT-B0W5OxgxdL37nD89Eu_jvosUb-$3?kvJ24>* z8D!`7)7(f$+C67A>zr-*L8R*h_rb1Axth$P9z0<(8Z9OGRUmgBdiN@hMX5moICZ@` z5O9hK)L1N;$Bb(UpC*1Mgj%y6RM&CqD)p--+vsX-mNUfR3o z915T;c>DQ*0n}K;){?hD&zBLcV=oih?&)>j(R)F4CQsUV=w1U)kvQlDXYf4Io|W#F zP7e$?kF>iHLY+k@M+*r#`*@_O?sEB_Z0g+-|8X=z%+HD4CEVwn8L` z)D}#G--XOO4>=%K@Drrm-RrjoJ;=L%QdE|8;Vcb&SI6K>t&l6Ym^1##dR|x^p0lVz z@LH4ez-c&5@CL558F^e@HV{yQ>xE&XDy-o2`FIks?joE@qBwo{Dc3)W)9Rh4=yRxc zM-GZKCHQ|h%QUb&?D+_73tV|`yN#RC1CQ<)$IZK^HxLi}07U<&?=xuH<;g^&OL0_< zKI(gq#|`0@f)||ngnVQUtViW3DsbOppb;qe=E1j_e3WNbwwE#*p=adW)5&?kL~QT! zbMmvfvWg|<15$}A&;kz+MW4fmU81TVJRxdG2z4fevK{U8oZ?aq1WqCI4@j>2g=3(S z08=Z0{oXqWv#Kn(WGvSgdLc{CNBv3EP_%D9JKK4~T=GN2Mfmg1Q#nW;^g;TahmNi# zE54_2wTxDrC%6hLzDHc%>$?q2IJY8M_C2!VdwtjQxR4c3a4UYQ)d~m#!}Kl3@7_Ix ziebgq$7sdn+>?DFU9V`9VDOZ(;E%$jA9I6e9t_$$JtvGNHk0LfYkGl+KaVi+X}zaP zq^=09ao28MKRZ3AsmX=m0`H;^yVP@{jkJaDL)#u?d&CpOFANbb*MB_I{#@S91rNS$ z7rrD91WuO)pA$7GLW*`zkT?D$$@7qaO7}fu>HZKtL2<^?y*#3>6a=q_$#x!k|8gqj zHwP{#<*P>Pb;SlwZ}#Qm%f&9wkoI%(7AXBDDg9>OP%V;LQ&f~=yoiSgi8W*BwxnL4 zTsm-0KAoFRMS)82fL}#)rzhASN2YGyerk5xYl2T#IV{2Cr-WdrRI7s5=5UdOpIM)v z&mjBR6K-ztV2F1Hcz2*r3dc}xhb8n+;iw@z3JUQkUNO@URedCiSxQtSr%Z~9>^Le>w6X%F z_0?e$xk#N(O1QTSW2>+^;{>Ab7$oXE(RuiYhreih#B+j_f|^ttpK5a}sif=VYsG=k^l=P^9hoa2(7o%-VUjZ!~b4g8l9;&&j=uk(e_ycXyQ^>q@z) z^LE}|5Np8#dV+JyZw17ScdX0YZ79|Vr#~7RaG`Bm72K7r`Temj>EdMqB#oWIrKJ@L z@&HpbE z4_i@-^k&p3J=|4m{mkE>*Um$=tGL@=*aH2|&DBPMQ491*-(8AmfwacvIB#qo5Lu%7 zpj4#3TF~s>L#VFu)6uhvI7t0Td+#`oY7OU?j~+&*c#?0Uvuk_DAOWxWLxcoq zjy10&4%8g=J%T_iRou=!cLH_9^9GTIGkZhJ>0y8q3u78EJGdasV>iP zL;hjZOy2E29w}b54yy%Qk~+c(v`$2f1Xl%%hI7Y?APHAFeU9T0RMDElj>ahJAflp@ zFN`wu7-5&-t*Pj6!jXnpi zaMGb6QW;a|eD;&~4}QuzpFMbP#Q3K=k9K&@TnwdKWBkF+=ZWwm3VDJ^5Y%l$2!k-c#eX1sCbTHamhv{bOvR2BC70A*X7<^ zX~)^wn3+^NpY$9j!4JNzq5(u_bP6hTw?Cb8Q3rTU2HW4HB)=bW^| z-V5M!G$a{ao^#;xG19hs&Y9o^pSSnQ5a9Fbo!dWu+1mDIcXwz>jq5fkcmPGfhLLV? zG|#cUo1DqdKWImwiv6+U(qZ@@2G-*CitTK;UFB z6KyfZN}%d0KY?<=f(%;mxWEK-oiWRKZ<%b!$8)lOlk>oKj1EGHoll;=SlUQOa2_~* zF+%R=j(Q2&mj%YmU3uLbG^Y)g z8Cw=|p64@=Pa*O-@|0BJ>g_B)-&KD8;QQ$v-4jnxzmKr1qkEhPS9f-gK`87nT-0{3 z*m>yBOTeR7>UP2Nqyei49(l@*h|V$-ABta&(RF^?Sp3f3k`4d=I<_KA)% zGqOL?enR17?Qbiczd8681D3VFeUI(=jxr{eF*Pn_mQdOeNGkx4o@;;J!fA;y;13MI z^QsZp0RVMB>&j5ghw05h2PxLNn7l--muh#wH1Ll5Q@IJF?T`|DeF&A*0?@-5u-vp` z`&nyS5~X$5?nl~Sqe#hP>fJ%_#Ta#SMxQOBj2-paL7yN*VNg5%U1GILkK%n%BDf`r znBb+TM3h6K$n(<_E^RPJi*|$GyIPz}iyJ3+zN|(6l`1JXm_hn;E$$sH&Ll|h)1qm? zoJeJmc587@=y8H{n-+a9qPbUL*&oy!MKoeagEB$8Ttr5j)*QSJE3Z2b^)2K@QTsPo z04zobY9z0vwPnE~E(QxUJ>da;`>C%5^Etb_zz!vaLHic?Qjq~xc6nOM2&I5DOIy|poak%p^@@+qr-g%;{{2=C;<MIe?rUtNs)bTRQ3;0_CMm;KgzQ|z_WisWdEd=eJ^GIm3}?@3_W{ODEr%Z_TS*y zf178256}KyE&Csc?0*=Q{cV)}H+c5n=GouFv%gnl{{t=iA5!+i{;X$z7Y?7ICi9#U z*e;BJ$JeO!C;RW<671#@bZ`l7)+9hnM)tooQUacmgxJA_*v*CL;6mIigy`0UxRr!R zKcfqgstaKbEDWBsOVVfE!6WDlcJ~ITrtRHxEuXh-r^!04K_CkU+J!O=u_|~wHVS|V zjsQj95x&3#l~^FN%Yzc{<>V^ns-KCOU*CN?i6t-tOnCL9-3vaSaUcWq{LTX$(Y4$m zKg)X^YPMI3*+O5X&cGUo3r?k6FVkaG=N=Caqj3dqOq4obeS(%)yArS&QJQwXv($Bd z?^bjBd*{cW8-Ez9Rd1h1?;kXE9{lM0WKc>4Ilok@6s%inlfb^!eDcZXf!l-fx!jG& zo6iHCyC(xBWWwNrFO`YB(AlG5m!G9!J@1_oxjiFDX|@wZEvCPeAXy~9tdXXlJljev z(I8tanjj;J`gG^}l@O%^JxrKtV+<-1I7Af)V(qUY235rcR2zuu@yHxEfU25S&3Kla z*)=i9u8AO99>k*iRS{%Ji6ywJBFMO!Ya_^z^7)eOy$qvfwX8Gvsi_Tfj7CzLWC#+blTW`(nJ@{*>C;ExpA@#%sz~U52rJ6`IbqSFT%s^;PXp ztDn7GFV#!^C-E!y{}I2Y{^R&n`hSaGbN`F@W&MZoYw7RBueJY)dbR%mLbm>g>*M+# zthe{yU+?I@uRgwie|Be=|Z!{Wst@xqkk@82zW$anh3c2;zc04 z&nyBX`{?Zia2eHiMg(&D`b1!K-w6>I)AyPPjP0Z08HmUAJtG3+`+gw;6Z(E40u%cl z5P?a3-xYz}zHf>^Uf_ia<$Uq6p0FQ$=7_-vB0EP-k}E#|TWF>*_7R zJrDO{+$(Ue!Mz^$2He%S>v1>Z_T%1)`)jy&;NFeF@%KNK*83+dE#ImiA-3udw)dLLf?dKY{}fwQu+_DH;E3l( zP`>Gi=g}f)3GEM%_CJN%KSS4t$1fwb{}RLh@4Ri)V4P8d=TI10hk$oy3PUIiPk$s( zus*sgZhe&EKc(t0k^d3TaZ&-R@vXJK1}tec0D__?Nzr!^|DGu3emXxkoY8R_vw#b^=BI6zxpOuH2D3$;>rE?-s2SG>i=9{oXkg&u{)+cKn)?T7p`3j+gp$H2Z3*uKSY+p+^`O}6c?JB-f{WiL_OyD3Ibtt$#|J#G^3ZYT zfp*FOlM2V-_K^rleXm155^F>7CQLyf7Uq;Yw!bzZU_IPEoP+`JfDT9iE(s)oO*Uua zyuG)B2b~9QBVr9P{i#4AGJVZ?V7F3&;_JXRyE7E=Up#_9EVOl$B7e%yxqF@nUEfcD3gex}_OUId>Ov+8CJ{1&+sNHN3mYdq)ltxerZ^3H(kPt90E7M4`-r()69gPOxI`KZMu&GR zqadQxS1yx&@5Nz~G!C6dpGUx|b9XWiy|&D`+lEj$DKvkt<9420=a1Dd&YyhFunPd; zX%*{4`}Q}|14Cejl>xB;dm?xjpIy6vbUwNLJ?z=WTy>3J$;1+CY(j1tuKpF5mj!>% z?c=@}aU@qP-Qg{#i_r?3gXg(-MWq-AXIK^(31v+FteBMB2q^r3(~ZhJ%}XE1H7t-Dmq`}8^wjW1?ZrKOsw^Re|U$R zhCt`u_BPa?vfyOlq8AFi3^fI{I3imb%Dw<`SP@ts@tlXDr2e5;^2M?TUztaPVErkN zoz?C+FNZN$K?Mduh8Sw80?WRlA%r{UtsI#`43#_tQG@ zI$}B2ua~9iS!pzB^?%bg5bX#Vh}GaviFgDN^PNU4DxL=&R>6f7 z)gKtea0NSw{B)tv%5zTlqr|RqETf;7k3tg~Pr-dW=P&iRTHb^9hWzjjBr*7LhynZx z1)#IZfT0DO<>%mK^7B|Y=`b1Cy@|#22zGx(L9-1PVYg71%6R}j%sFET3qIVcm2}a? z2->V<7pWg_v4vE5loxVD?q8+c-!9OsfefI`X?Vt^p3|+93`!x6Yy%NuDn+lap_M+@ zKLYCUH(c-%Geq0FlwR%At+4qVwvEq8f!}kJc4g%p>N4jXf&~AY*ZihHORLWvx)%6a z>uMT(Rql=3cwA}4M)&HvR==;sy=2Wiw|jX-mCwDU&R^>;DJFv4@Ksb$R2=ueDeEBb z|7i=X+(aB#tz5b03pmr{02NS@sw(R0n*tjHQeaCUM4wq$R6Kdg)M?Xa6y(lSZC}~q zYw>Rj)HO9Dj{JWg z&di=ODrfXHVg*7cnn&qb76#Bs>U%N_@zoyEW4zsxfO_LjOco9H)_{D(%G~SZrNl^Eg=EbuG z?Mn4Chd?E0<^c61Xr@QeG-x!KIZ0Kxe+!xs)a2=WJU1xqX2m5RntOQeGn^gIvl}GO zyfiS7Wu!%(pyV7~!2c)iiq8fHAOgQg&S!D|@Ye$a*gOvBahYK3MpO{@_kcfzXGjL{ zHJuQGx)JR_p7k#e3|wKPSp=HJVVVNadp(bY#O@!Z!RSXo8?Q7)|pNK|^K!3()L&V_*R9HiY@C5@k;KH-NwX zB6!mEJn-9qzr~n-;f=}-=5}VN7OzZ&^D-^unN32}V&H9m7#O(0 zXj_e!65w7gn1R-fdn@R@e;gR7LpZ_)SBLf62b%jpGtWqKZP=%O0sLX$zh}gg9q$a= z@dRiVyg4xNdNj?8hq!KT#8X}VD`@hgXetF8F7G(dn6W&Dw;m$o-EN>M1e7c2hCK_%*Coh$Tnr7T$C}^aKQlW#{D$t7h$UMMYarr z=4Q}Pxrcae5VlN5l#0Rj-I&f!hrL4hE87(+`yAk&`18O3-eU^mR)_s;0q~Dq1W)=@ z0sk=YbB(-p-zZQn(C2p0AH*tTi7`LevDskjpMvIf&`gV>*`(2M{|C(}(8Q`AZlpU8 z{JX&81GKPS@YhA) z+d}wm;0J)eAp(D$$bjMouR#k8T42xu|7I2#pf}p?`at5(TVEE>Pc`_=%Tg`r+c}4R z|1B>XD-T|S78ta^paljkFld263k+Id&;o-N7_`8k1qLlJXn}uM3$*{cDmJLspaljk zFld263k+Id&;o-N7_`8k1qLl}fdvM~{}-5KFk;XG{~uXkPK2&_4PXPlF-9C-&cub+ z_UOWK|79Y?e+Axyu)voA|9z3bfPWM3AqxCoM!I?f_{?6vMEb2Z9baFF@2It`h(_S) z`~ngA^b~w|ttSrQe*umHZ<*@q{VKins9hqxJyFN+oaXAi2f68&!2b!y9*OjBd}AF) zZwG3Zz~|z{M1h~Mz}3s&OpL&9$BS|Tzj2YP_m~KB3X%S`Y#l$f)YY4DfqXaT==hb( zUA_3~Hgt*fFO1glYz4j>H>4+18pH*3Va!~_YGo+%@|9s~pxH`C(aG7R%Q$$IdNcfOBf_>iNuH1_h#J&; z5KCTUtC!@0AtILEYl$=WOPf4Pz&NzJ5*Ry-`CcueC+sj;csIRP&! z(>TgwQX-|X(9d^qTgeRuWXVDW{iq?rOjtD&NtE$vgs_bvA6At~>YI4RZ46XPN^U%b zHFl>F5bUgy?JYrksY;&-*dHM_B?%M;F_p$J1ogZfcK~oJt5rI5*?t33Dr*ubEXDb@ z!r{sj`T?l+#i+B&&e=?<4-}5_SUOeBS)ahm#{L*_yOIMg`&z{hJ~LBReOyi%*wxG4-;GQ(H|Ig8nn?H!6vSGK71y`p^;iMVw% z8CFWT1u@FD(ZpF&6Lt}F?dU^*IBy&3K zZsLBElaz7VUqO`8#ernWX?ER=)?Bu_Nj9d?ACOIa9CWhH$rP!~)thuT5b{P@{R(2Q z&&633u#CJf1uT>Q3|2smClyc!Q+`NrrR*S5Q%_S09gHg1$!^j_={eez`X{8n4v}f7 z{|$1IsI0ZA^YUE1bYyYLOQ<|@qnsuXRYPtA;{SkIFCn*(Abxq0#%*QFuZjIuIfLB9 z)Ki%uVeM$3`wJz>Ezt+H7S2kgPqkB>&^ms!9HXw2O ze~>1(a~Y3~cJ=q?+ai^GTs-G@tcTk zAsNdloBM|f`&GG21U{g7@JTNyvf`;Ec3EzM5cJKHyss5P4CB4(uAx-1QL!?(Zo9?_ zHs*}~Bx!fe5q{qwWqpI#wpr$ccsw5B(Uwi&5RbO}2p(;VbRNUazXaU~cL|B(vjlTo zy{HTj$DKjU%Pf~6<{LaF-AU!Uz_J_Ar=wiG)St-v;!$;7T|~Puz8f)0&*`s9S)WqG zSW5!PPI0mgAj|kYWioaeg-J5%2jsDft2t=SsN&cwgrKTxBVos7H*jo&G~7%+JKRjo z@o+P>s@b`eATy24V(+Cx zOrXv5sftOAl@x8PMAh0P#!6aeY!YK7lNc*mK7xr%N}_r@2(2wmTO=hZegVRgwX&%} zQj$3!OX?rUluH?z@JfuO=}R4X-z)gF{fuKq5C%4iyBS#eHp31-D@zcu4QSix7zBB$ z<&|PrFMVPq?<>Y{d@BvgrjDU765EzQQ+aAJ31I3ewO2~=v}7a(aCrjHXSz*ryo%V* z(15k*9cB;*(c8H`rK~1eZbViqkXYWg3&@1|#CuW#H&Ra{rO(xXwuD|vk(WVqrk-Z7 zqZZONfl}mH{#xYfy@m5JqfFw-NOF!_$hbD1%p~XJ3UEz4M9!JUh3P4audV?!PoPTU zHxjf$K#LPfi1}zuf_fpr7)^pIDe?PepxxZwZz6yC0!ZFBiSkDd?y!hmvoruoK8w-2HioD%5>?Iv2IMLzT5zl3jYg-{$B?{ zJ8la#@&l5UzIxIILDmk0)kSDL%p;PC#x@1vTBTNf8$jF|2&dHX4oiybBA}iFW_xRE zfuwBWfExD}VKyiH8A%+HY6S;nPWpa8Y&3p8$IY`&lWe~QrG?E_$H1xLz6*fO8A)y> z#ht?sn>(r)_AyE7YlMnVp`^B3z)xjERVz?&*ApU(5A&qBNx-mS95BVrBETI_O`_S+ zMr7F>kTD-}93gn5fMtcnS2Z_CEJwg5nK`m3@n{V%F$aCv#m4CAE%6m?pdYKJw>YjQ ze&aOQWO2|zsBF9j%N7Uqmu!LtOBP2r!4n0nVhoz_AtEH1O;$&dRy5dTQ-)I*fg%`( z?7)pS0u_%mceQ~=wvDDR#&#Pp<{~4?H^noBjs#136ddIqi|$AAKG#zebE`Jo@JkY% zZ(^1OanQj3uTjU?heWd zN7x;dl{qw1hC6JAph{Xw52bkoX;ftbPAf*5gT!o-mL`d)ayekKe@2*mJGq0*6tjH- zAqxboNM?I6!G#*knEhu&UnF4J%7$7NmnUO z>+=bawf50mt4>4F*r|OqYwe?*+DEh2KE_e|Xx7?CJGGDI%?T%vL~9?-bJ8gzn-wNY zH!o4|0V>W{u~jlJ)mmHMMgYr1Ya8dQ1W>BAw!SI=%Q@hP^KERF%quwHiu1JsSjmAD z$#y?@yUf?9l&zhnCT1^3A)`9bZQy`8uCi4!SMacEKZ(LOi+Qs{Ob+Oe+;9`6Nfy5F z1#jW-Y{?<&vLz*fh(uksELW$3!cI-QMXSqpYT7L;bCQ)%UACm9B!EoRWy=KhJxW7$ z*`n2DJJn^2R+rbvd5uvL#%XarzVm*c4} zTf%kOPIcL$*JY=u%a*w+9ZSX61uXNDC=4Pxc#ZVZyvSuHP52tk1jHtCN0cQqjl%gF zjdgq)A#8jl#kyKO1AwojSl1*_SgfR2%M(l(A&8X}t8pd8YFtUN8dp-R#+4MSaV5oS zTuHGSS5mCTl@zOSCB03Zd6U-}+T?$`AxO5bmM(O$4^-}uB8a5EZ&epKs12%ApNPAldJ72?g z0ecqMt>FAf2)jtbp1n>=-*zd41omZMGiYH@UXk!iz)H`VMk)P$U}-hFpRgFjt{p<{ zBXHMAL+6&bdg%bRlSKnHmK01(rZ)bJW6?w2ChkYoTzWm zz_Ot~0oOW|at3;qDs9P@x`51nN>*&K!xHlWqZ4|8Ql?n`iY*5^h(X?W56qlVQHXV6 zKB~g9NJa%#*;BPel13?Y4kbyjJd7kuIFo0nWa?VJg4i_CjWtM_n~5sULVXr}eUCNZ zg?N~xZHPC^w1q+26eZXRpe24RtovG80?mF3wfg*l<{0bzln6W|iI!N*((RlRU+2Fc zgL}I4osXx$_R9^pzPN|f7W*wUaqYmZHA`l432nr0-ixq4;wPHWi2pqRUvk9%5t4*Q z{ND$}SCkZ#5YO5$;-84Pm?M5Vu`)d3r=+0~KP|TZ`;GW%d0!v#Qy500#*6I0jYcDW z(p>e!TP&8u(V$RYffHG*BK#u7t0L^f7|UW4;TtF%$HSJJz<_SfaW=*QeB5nKNw^k~ zqOZ0tS9<~QLa`dh-PV;k-$AX^#@*Jmly6cRKJK|gZgS~xUc3jHEX!9c2Hk!4foZ2+-=qS>IBhOTj#2K zNlKblS?48D7?ybvT995^rEr=_6W&+K)u+AFbBv#I9B z^L-PsJwyGJNR1wmYL~HxB-qeHie68>L8UbW!RtmRm6H@l>u9Rh>)B~PMb&z}I5%ij zt=EgA4FEON>y1W{V_(5#Tc}9wp#j`IZT(`%FDSVr`twDhv% zxRdbObQ|(ZwmFC}+lo{YG_HxT#iP0)rsSARz+^F}mmwM*erKC1U$Mkv8w*gqRc@ZHbi#9JJK*s6?EKrMi z(H{U>jq@U_ab9FG&Wo(tyr>UJ!t)}^$VL|UEABWNL8BK*rEUQ?hX*7(;j|9TowvVKBjxg1ceuM&{Q z0kd@jk>zuMSsy2$AfA-ASpS=V!g!vIWFt8q!mY^|KNMlv>R`&8RjyvH!3#QehGhRa z!U<+DklhZ72ia;I4L^)x#*U(;9ZdWmF6eb$K{4YUtB@m8#ywKd>$;O-zQri!&@xvq zt)$5NmcknoTQMH`RuTnGJ%BJC`c{^J%t`RKyR4LkhQ7&Wk~ZZza`Ia()!=e9=aM>) zcz0XKTBV2;ORQ;q6x(S@Ma(o#N85^td<5C)*0Pg^Od_gXme-eJyu_)}-k_KQ%QqKc z+zH>6_f5gC^HsRAT$D(1n|hWf@wMY(O|&&heC@b6Lr7Yd{yx&uLEFwhldP9;%bR*u zI^QO6NgSzR>hY#GE^+l*5q9zw@j9nSz7|gx8pWy$CYq9ZliWw3Af`mYR8puJ8q+PTnV@1HfqO} za64|Jc3cU!<2Gu?6+=6&@^)M)Qb|w?wc|?hs2`HdHfqO}a64|Jc3cU!<2Gu?m2f*A zNA0*0ZpUrZjw|7I+(zxVVra+hG-f)4+rcNj~79t3phFFvtkB#hh>n+|l;b{N@4+YZxS z9g5rzW3N!Yqb;w(E@Fzb4=F4k@YvKzl)A@nIm^Ku0X9q4zY?4oNAM4kFc(j;HzpEh zrVGj{HAsY6`mPgRE^3tIL>o1**CE|Ro{nDB5$O`O47GGgT6?esqlkbX0~`zKE~FF* zY{?=cO+s5Xai;Z7{DB!V-+zV8mw_N%`g>tFh0PabvIU?r7vtv(w-G-l&D>Vn?oM8b z9X>qeeFRyl%w%^eh2c{wW|jcQ{*8lX6$Wq@i!p#|OCr+j1nSsKJq@;51ddE`av(elW$QGnV39ysx}01Ur~B!Ko{2wya3pG(K6O z4hiddL}usKS_qPrJYyTmNLb50>>Of3*0N8hnIaas$y)XW5!P~O)@l~kiVtO74Zah! ztP{dnCr+cR%{*%=`?)-86&rWv6upR0K;}$dQ$&{L(OQ;#5HB-5OUDQ>7^&qU=5Aa) z_%!|{$u5hcj#iTyWY3cloEPNj2s_JEJ565+}<_ zf}IKVh&V3Zpu%(r2QLl_O%V>B#2!u(&-s%c5v0h_DfDw0^Cg2NPL>uW3kC!xg{;qJ z*vDopNe)?oBti*u0f1YQOynsVyHG@`ZvQl3sHj4iAp%2e(nFcR6E4UEQ<<=ql*v%8 z6atwVPwqd2$%33@Gb zMm9VV4b$uls@+lU6*2{PuIP%89BtQ>i@MkaZB)qXV^;5roA~dw^YD*<)Wi|Kc40d& z%8Q&WhUuxT52X*6h%?&sF5UErdM}g|Sq3p{rB>JVmOf;w)Qi}cN+&J4eHjv6T1Dw_Y@21NKmxC})xcgTj3L-GZV5>GN+tV&RkrAF7E zG`$ptM3+xGwY2|AOU)%Vw7!@)gdLa!rCu?@nz5>p-rmRQ?L7u|B$pTz8LSZ>>E#i9 zooH#$7KQVprYEY`i|W}Dqum?zD69abPu8niiqVA)-CU|MK0E~C4Sm`d)T|+q=?omT z8il2ySS0^q4-&)2sL&$H$Z9mA7}BcIC2cb%3O7{|-7k;k{%Y5Y*r7LAe3VXwj6yUH zTmN1GM)Vz2d?CrHw30-XifaD!Lf3{~7ppuMsR$Pz6o#h-F{e6OdrMQD2qCSJUZG2; zkw;Vz(#gijwQeE8!Kk*!MPftd`O?Z<*nEDWm>161qN@_k+amPVMz|#Pg=1X3jnZdt z(|8LI9&FJ_KWZ3ZFdUFq<5ut|W-zjsKSM_<<063m$NKsWILz!!^wFC#TV9u+pI@*n zJAYkv_PXr+k)yMh&6zoK&X#3}9kpzxNd~(gfR!R$PCTPCApX^L4ZhZG_?tAVtt&hID!yhK7Yu%KG`8sy&{Z;;L&Az-^?E65LVGxJD^-5)SbsckHX-sxA zJ3Y=Yc^S)U$5O31cPdOy*LTA;1a|WYyXOVW&Eh zCD$_MXEx9eS;uUCti_qkNv4+g=z=8_QKU6Jc zBLXa=jb#OTdf3cc*@$X3ERPLu>$#O>tz{X3{Y*AZPG(t~Va7d3wvJt*KFj2FEZvKk zb!?)#{+@dn+pn%y?_n|`#wu~u%!Yuvmf4mu`D$jH2uN1fvvNX_N64np$!vuZw<`?I zP+Ysf{vBp3Wvc3DnWfD20#i#Uz+PlYDtyV$rb7N#;d5>_rX8M?$7YqXQe;%OmaW2L zXdX+Pt=6znkYy!`$)s$#8!sqvPjM*mcQctHm$7`53NmYFP~Upd%%-hl@vnl+&02`X zF1CaoY7$$>pKdm(ou!qqk{Y%YdDpCEWq73J&1OjfmX^=l%UIH3=DdTA+Rf5lV8sD8 z7x-(|vPF1g<*}rhY<52Ll(58V=2r7r(sPU0h&wd7YYlQ|3As_B3Y0jK`y*X$SLyBC zMC-^z2{6$xKZ^^n?0lFgj}2MI;%iu3frpJ(Y%u(*Y^s{ga!W{h%ToNB{P;1iWkvL` z07p)2hA8>s$$yuLUsf0aP0?a`iV}v`(2%eI@5ka|~w^oyQH6)(0 zmKEo*WVMj;PKMF_ERmS!v$^!pazr^3RLywC18fv!wUnD08B?Qz2m30j$`BMJchq)P zs6Y#cX;gB!#2LFyuML0KK}*^rU6YNO#cNICjVwd0W5buRL_eFDj~w&Zh-EBe8Ml#X zA{Q0HC2wZ#T9&np4ZnjZvq^z`Hk}@?Xf(I61>lr*oaOd#Hp4qEM#)l1$2-_?dLRw= zg&MY;z~>nE%GU(byV-&^miQXWQft}x6;uhwqtl3o?Q`)gg2^Y5ldAdZGQfrWcM?MR zFkS(tT#ir*%qSXxA72mer)-uohjL8Pn*n^qC(A+Jh0;D@>@KvWVzUpsw$M$x+b8W!ZqGLc`TZ~4VK9EUg*`|@nsLpVQ8kPxfW4q8n^1xEGqchn! z^pj992`C#%4pdGA9aQCK!w*~d!<6G>_~=5uhfUZI+a)tOz$O&vp;ZMe(=;QQW&7D! z^b3BLkjF|&*`hOSN;R|RF;_bqx|X@%e8{qf4Zjug%w+aD<_J)~LESDqfjWNW9?>x_ z7AAg>$v3dsrSOHHv*dSJx}S}C0#$Jxvj><1zTE={Oks`($@#{k=}m#}zsnXcW$_PE zRgX92B(X8*WC?{F<4T!RX-#rxps@|9|;{4q){^gg2)BW?0++t^gmQY9ctqV>U9C{FsaMCq2X z*8x=)_ppL$wzvkrH7FK*8Y#=ixJ2}ZVodJ~<;7(SLD8;O7hBMJY`-JJD^#IS*W@h-r#KrH2m%=P#f3r$c7%PwIPQPDw?2a@75AX&jlZ0i9R5UaTOw@$PA9JDd0_OMI0rc?aXmB$kL9!F)7uE8ww{ z@m!2&9-hnaEacCb5zpl)%3{Rp&&7a=z7)?Q1APIsMMl(XP%e}4oB@`_xTk9U%XHqq z6azC2L(8e0av8fEW-}?3dHO_ThLU|ZTXZ+8Sj(2*&64uir8C)*yIIv*wvvOkSx8LX zo^mHRSk<;bL3V*@JeuW^Yo<`qjtEgqz#ajlxEgwC#i)swu`1)(rNP~{V zL~m0o@p3j@Ial(>sQKASvPqU}v};+-GP-JN@V_T?1(XbvO;+Me`%N}5`Q*H{XD6V(c zt-F%NKTpG^A^XX-GV^I%#=8a#%~5z5iP0cdQGH_YwUmvaBK^8vprh)UO>vs?Wm8Eq z{$oZlN*Q4)k(teuKh#u`f{3xWhXt4|z(y!ZghKuqrjk^2c3>sHU^2mM^pdh7?Y@W! zaRQF$Ke#P*Ai9Cq<6~I%%e8~FZ_G^e7MC(n#zZ8Em7?CxVoMNO;b$4MFe7ZEe%rc~ zjjm=HOR4#@VB}ZI(tpV?@yJ2**v5aiu)=`vUzYJu z$q>6(DVg?emU0h^yPG-Mc}FAP%?Fe+#vbi#Tn&q_#=I?=9=YmndW^#u2agQ(M{JCr zP4A)VmHZsD*R$k%*f?T*H=BMB#b$7dtXrA$BrzLJxN4SGgK1DIn}*Q?jT&;+0`y}I zVi{XRuzf8XkLd!UmMH;hvK)CBgYlFTk+Pcil#Y9t9djtfi@}gGCJ&+YPcTK9=tfV+ zM&8O4$T$KMNer$=*8{?Y7vg5aAi}n(1w^v$9W*;K$S|6=A_$MM=QH&|nj%@+m}4PJ zeURB6WR5bH@+xz^ip3M|JH7B4MY`zlKvV?oIU*SCH(E`(aAha53N^te=nX4nh>S7! zI+{|=uVo8gV?G2h5jD+0GxZuP*^Q=iJxi!&P{Gy1Hn#Ek&@y#0o4$*=(7ciLuB3L1 z_nqx{t6P?LavkVJnSZDWX>Mxq*EPj(bbL$+XrxsD0&GALHT>{!A*4of53*ogzw$$%3tREQZ z9~97oIz81mD67#sFyfxc9T=tlC9eG1rp>dn^{Ov_)nI=`;EsHnIgm;R)C zZd2>Fx=oulNpsiC^)6U-nRiP?onKnCqTD^daGHBY9?lGN=WZ@8!jC`D=$l#NYxK3$ zRRXMNsjQt@H+A|{cW#Y4x4OE;=WF#>G*+P(0oYm#m&@H!SzFNpsIe(`W1z0VU)LxA z5LRx=ZEk7u`zrlSL{ZVYt+BGUrKzzg(3%@)+)~$AmFutAh^1Cy#>p!iDq3sxM0HKX zsdgtOP${*;##?u8w0J|gqnh|vMGRL$^z(3 zr+e{iY^|$l^i{b@bg2bvxq4cnC@untzoMna=a;yO?hrk&oJrwSkVGyN=W9#BA_WmH zoV%vDXhB^ISAUzQ(ciL7!UtVVE#AteDxXx@5WoU60VJ9rZmDbYwXE`eB>*!^V)E>5 zt-IEzhXrkmMi3={zQD=#(YUut)ve%A(^%mTw17<$4$IIp_BVMOHdaV=jdlLIiU#E3 ztqv`FdpR?yqOua^^;T8*eNuBpi?7k|t*V2_LUgGz(9+`NB^Y8#?`abWkM$B1Xsq%z z_`skNf}xcBb(_P=Z?0&LveO!J2&vJxMPnRdW;D@iKS`u3(tra&xG12NHaAt(RoD5d zw0uOKLYw(Q?0I5QB1Fiw^KEYSZzHXBZ1s;DbTDFc^dgBTN_7>*l?y(TXGq(YbUx9|c zQ5cF!kIF>w)NEGQDtLHXQSmBjLS?5LOp{s%YwK!ir9dluvBK|fsoNNUZOIZMYjP*a zFj#_eFjAnUwF!EM7h}Al4&po+G!796)FU`~!v%5lE`C#WHP}g8YJKodBR8Jt!c1B# zTbdeh>|vuc|3YMCgAdg?dUeUb75$O{5)w;7XQ{QhqSA-L-O7c9Zp|&eHm@Iz3)NOA zUE`fPc_Z3Q;x86BXurqHqkJ`+scw)X;=A|)X=6pJ&&$nP<*W8KHH%7I(^AnGXsAG2 zwT+fYymgIe^q>u8LMf!irbe%}?X+mYnpKjQsBpKS5}|ymye+<(I%+heMGZ|GD;gH~ zV2`?H6tZvzaMV5-C7znULUJw~^l62VLN=0g&Fa-2?$KP@&2?3hK3ev+R#r4}wF|vy zB`R7g>*~BzRdrqsb(>*{8ZLylDkO#GF{?$6Vd=QGWWiRn1k@QwOBSqIL{+$kT7=>v z%@4Q%!#Z5R7^zlCb*(M%>%c}WGuQ;4+vcl6)ghC@V8mFHR6?Iv)j*!D^%UL~Dlu=N zqy>ufK(VnQ1S@HYZp*M9x|Jy-Div~tkhuA@R8lFmp(#aoLXHBz4`GE=!rg^ukf?3K z8qH|;QMXZ1!7;jRp(wNplCQd%|#~9(dLuXsFGjvzFC52qUUXp>Y7B)%45BB{uqfU8jC24=q7|+ z>Kdz?!Zuw^y}iT*(_0};SQ4ehN6zMLsB45Ex-e~ZE&c#{ekw`0c9lf+%UH5vXs(k3 z@`W2lsj{N7mf%+EUU>0n-b-{+8GaV%ji{&;S|{smt@S}?9okgU*i#IGP^}G#jOg2EUgCK+7k=A(}zN2W)!lBvtxb8bTC$thchF zxeo2B7lRJ8hOLHHjgK0rz3_)?j~Hd4RWU@-*phlN>gwlnu_~Hd(T73DP_2V@sDyfL zl$L3$5exVbZZ*#dePQM1=IA6#1TVof8oxPgij$`K8pMBQR2LKD5U zwyrvCs1UZUu{q%PHZ@9tW*TB}PYhQGt#jZ$VG!9^$zXpz)$JtSvfo7P;2>md!_j|+M72!TydC(%CeDQ$d_{gSks=(+|Ta;a( z0d53Xf>Jbi1qo)Ho15Bvyg4C&j9%61hk1DF(3}(zg*1{b;zZ5c=s}|oG+IQvX#V_5 z+~dkOVmKZUv!dL>0EY^%D=Nq$D#{wgV004Kf1n! zZ)WI;1ET05eJ2Ftg$KIIbUePBz_kOPY|>M~kzV}Tpu_k)1DAP+4&zHMTy#(uJrxOG zv(eS9!+4d?a4Gm6)_5rrzECk-3O|`T9HB_gFo2MPZ^Dh2A|)C@;PE-5;lg*+28hz9 z=oaJgIC*g(RZ#Sb$A<&sdB80|;4ekU^zcLUK_#va2NsKf_WR+$3WPpix$2~d_;7%} z0djxS)k`HKOLnP>5L)?wlhg(<#{lmEJlg<&3-C;>{7B;O0oE^)m%f_QPj)r||2Sa% z>}P_X)`{^X`ukC&bbzjeSHei17o+gMi-J!OOyvy)a2BL=E(%X)*XbAWkJDU`;s_oE zICN47nAK z0X_w|-T>1(RLurB6|mm`(?^S24e)HhUo*gT{^AY;TnBi!0lp6KO$PWjz}*J;e!#aI zU^;8)E(82q!21m_oy7D*1AHFvqXsw;RsQD&n2vdP+5ppeMK2lPGQh7IU?1Q&4e+&q zg9iBPfIl?A_W}OY0RIfIJWter@?Sa+InDt832?FjJ_k6<0LP<=8f}0_0?sqQ(*RF1 zz@>oa7+^XTVu=B61H8%r({T(}8sPnas|@gCfHxc9=K;4F;5PwZZ-CDN?l8dkKu78^ zz{3IGWq^wT-)n#u1Afo|(-Dl18Q>PcPa5DG0Ut5I`v4y`z>ffa-2fj3{I&u9J>Y%= zd>Zgs1I*A~{LKIl1#FogE>Aj+*lBi}mP;6}juMeW0uD0mlOIuL-qa;NsG z2QVE60JC!Vt|ZUn_5KUz-bsG|te+sB1b;mVSpTe* zU^>@P|6-Tm4*=_@ViEiaVEr^!g6-60cDs5Jjo!&FrSdLR*v|!xrU9M={eVVqW|yu2 ztbZ6v>8}B-A4ouO3t;`MCxWj9tbYtl@O6OoKSDt8O@Q_OJqd4A><7G9>rY(3KLS`k zT$<>60qdWJ6Z{*%`X|u@zXn+U(wSg72}>Ve5WjPP^-~oIPe=dhXA%&c4Ol+|n&1M! z`pL@#djQk;LD(x!dL2Uw{Wx;MuLPb>ibPk$?OBhf{-rtLZvZ^npx?IvXO9&Jd=q{@ zVEwN{5d08e{bV$Pp9ZXtkCLz%@;ku#7s7;p3$T7<1i>Ez)=xzy*a3NIuZ{)?1m^&z z5AkWJk_e&a0oK31CHxY=`eCL7uLG=~(o1kNrPsze^&Ho>O>>&Iyj{0U(FOc;XAG-NA-yy!;~aTsDEVEyxd!WRP8PZ1+{2GP63 zI1IZe(j370@nwWx3RpivnBWS)`oY}nMHSpUWV*3Z);{CvQ4vI$Jj z^Q!`^pUO=5t$_7|MF`#lSU-K5;BNrdj|w69A;9|SrUdhOf;OuT{YjN&o>eP6rQXYz ztXfk(x70gt?rM*u#jRd5cm6W(>Lu$v((<{Bmdy9AC|^F$vr1Z6I(N}(Y5ro*{AH`l zmt*S2|40j;2xI4hUKtnjuI45z&*(AQoGS{Bp<>40-i+qfkmc##?|XnZ5?8ue}w< zh%1T_M@w+gX(z{sn-U{#YK*vPG2*7jh?@~3t}vz|u{0@+rAlEeT?%6 zU+7xJu{0`&Jfhr%|a`Wq17H7P!2NN>|Q9SG98C!quKN-ZgXQ zm3q8b7USEm`YIXDywO+k4P6DAcCkxGI~!tmBWgLEqfrR$eM9X~d5A*5##U&hR;;Usc5-9xx`ub_!fnk3TUcMT z7>lhI3=p(8CiE3?zWo~Ue#1rfaAH^v$9ikap)r@Yjm00{Sk;|UGeXD&w1ABD*^BJ2 zXiKTl+nUBKxQB^d+bGA0SPRyC%a}4^YxxV)lGk03k{0*H{=U8+9$J|f<1;#2H}XY^ zzqjifv%p{S~!Pt5K1=gCX`I5u^5wjg}!-pzZ&N?H(h6 z*vgN+RYPKgq!s(BQeNw}&Dep&)88VVwR&hXb`0w1tqdUfAR@wV zBfT;bg83GyPP%a;-apVOu@mak!`w_dfIRdd^p)}|n>N#CKc->-@I`ZEOsivXh+@|}y&)oXNI51Kn~z?ui9cKYSk0{Ci|^CLRiQ`u38wLV%u(Jozv zuK}ifIg1V8*9`gG8WBA-MRp;m)0wemV(!La2L>|YM|YQjj@DLOpyM{uX!U%*4H(NA zBHwOTZyM+(WxEk0F1PmFkEd=UiXyuGh6p|Q<0!fVI9M=;5@?rB_bb49m{7k1-HdS> zkpcIDAxzlhCnV`04hz#SoyY5l(8Gi~_AGo%+t?vk$Gu|+Q~dEiMB0yRLDw1FdK!XH zg3e6=?b2~{OdN#^w0JYltMeGPvV`o2L3h+nC&gk7 s8aI`j3pbW$$9FJ?pk-^G)}fWs0IO}1oTnYcpAHvzvL`0Y-X>;nD@7qcP@ z?AR6Wdn+k9_%8w(Ko9>7TVU7%!xk8}z_0~|Eii0>VG9gfVAuk~78tg`umy%KFvtRy z$9^)3vc6is;p`yPFgR?1VG9gfVAuk~78tg`umy%KFl>Qg3k+Le*aE{A7`DLw5eq!Y znw-or_LReQrv13nG4_nZ)f)rd_BzJ)cJWN7vv=UkF%F_$QrMx)r^jzucW$r$h62X= zzan_}Z`cCE78tg`umy%KFl>Qg3k+Le*aE{A7`DK$1%@p!Y=QsP7I<@cO$!dk_BcL=pxQacXywx225-|PJ#SmsKwaQVlHLT(i5bUP2Y&-(Wc3=DL-?T7s- z?PnbA$InpFbGG8z&x!eqQw4L!A@^C4`6*={l9?!E(0)eQ_Xn~M`)vm10%7)>Fir#W zXTt1|7}Qlh_WQu^7fm^q&Tz?Z{Wx2n<>ZoZ-NAwgI2L*ngd&)rGEN8)d6q?U!C1 z7%1N0-xfd}EsA!c)X^gQBjOQ5ZCQP+D3+?E1b&nv6Y-RA9`-v)u?y85#fdUM*&s*} z1O|2_u*Zth$Via7v*bc&REMo|mDaIB>s)2)Sb?;?V}-qQm9t}ovvXBq$BM+xRVf`S zQaU%Jbrh!s`&ipK4Nl|#1R}Q`Nr0-Pic-hA_Fsh2i~SE>8W=#&L~bp47j%3D*#`FM z$-+VQ_d4$G4e7Q&5cM6oI#{19=x;%-sP9<&C5MZh9~f{PYiCXugVQ6W=_x6L0%jm8 z_TL6Ar3`hJu6Fhs3DACN1fi~g`|2o#kTMr-Ndo+ z2C=IOvNP4Weo&nhX`KFF!vJkZ{;*Xv&H?uY^gMW{#c}97oWy^hP*y|zR~8TWZ2neh zOjD+M2GvFkZQnp4sv}=ay=#;0i6MuT88Q&7BCx%{Vz`tVn8pjbbr<# zPl2F)&xM7KTNaVuAur&&ZRavk)c-CdZ#$xGAq#$}3YE+jJSJYL}JF(bb5G4Iv z7<<%Ve{_fYjM=;zSzScRA?E!m#JuN?5h#v^7)096eWEXRxX)8Kh0%N;U~HP_KGRCt z!r!5757-`a`*DXMf=&JRGwsjnEU|UE{fEy)OFvQueCLb(ughK&B1OB~F9JJB@&Y8F zmi^G&vJb<`lxMW;l_7m3*UwHOywLVM2A2LP8nxT^oL|`b zX4l!**?{S=2vr>WUjSG58%7tjML+A$ zglFmgh8`9V=$Q0A@;gu{#@ z780Ri^hFWE0!Z0-0+sn!6UrEj17>rQJ{>UbxvpZpg=E-@l8C<7AW_Gej-$uiXQ2#~ zIzvi9P3nrzw11>@l$`2H+)p^nkKti{MLF?nNt z{jN^;*{&5REGpExJ4;S>CVso)E)goIYFP!eM9s~wNl2LPWT)0`YSa*)KM|NuA*^li zJ2OO2Bqr?gEdr#N8U@-)ZxZmtm^ROlcw$i2wU{-XJ^XI8@&ffS1)L^IzX%FWQor0G z=6p(BU^c=G_X)&x+4248wip>4h4*$i_;8W`|2cIwc z?p0;*(c{c15oi8QvO|DDi?F_0&~)8RC>d!&24YN=Xg-8#+Wx|!bRomYx%2>OoM0Oa zThyIHMvoxM8bFD`*&OVW2t^~kWB+3iR2W-~+owLHar@%uy4Cs>G!m)Op8>ZDv|}RX zy&_intRgrdQo?2ToL$)Z7WI7U@Ly0g{-V!|zJ?x$8d!fkOo0v!$5&1u(2ys<<5c?*ZzV`!taTnVr^|od&bh;h7!zvk_oA+^4`Y zFs`2Kbf40D1XUVD%@?EX6QSY_)}dZjOHzk8fj)l7$=?S5v!g}hhL8lio3VVkPf`um zTnre&Ca1AsP*%dwEYn~SEGput9BYD^rV%8VDSGi>N*X-$p(;p8vkW2(Z87r8A*6v} zNmw!1u_s>8+a}PlWlwX+d~!0HVaS2nam#HO=qb;bVPHLw>3f%LaG^%%Ea%aq@-;HQSxI6qc&Pz(^5CU?BPMA&jv^p ztcurOc&etly1L`|7au{%i~R6k(mP(5eByxn(;=vi7Y~1#KKWF~GeK)iKHTv_2Z%qz z)ZOt!$){p~wUJSu(HQoO{9Ccg<=3sHFyO{ytXHhh{YxpgT;|e7jL5x;mQ$JjNrYPl zT&E8e**n)dJ4+Hf_oQ^3M_U}&cKX2DQwK^;c3eE*K7l#|O>z=@Ly^=fpQ63a1SJ&h zg}$xyX0g2&7Gm*H?RdK66dLMNSPB6TEB12`ySx3_%&V_06_U`}@CRrj{%>o@1Kppy zB96H)vc1v00Pto^e`=@uBGmmH<=WjBEvW3P_Fe_dzp8HA^VKU6t#5aCi(N3G(=`7k zs;n86kyE)(cDetEhAZ}u6pIP$QMv!s|+oP7mx@TNGF zAMl;^k3-DG*Z``|k~3%n?4=-DNBO37u00#JUAu}E6Cd~4{zk{4J(vLm3Ok-YKcwts z8{To~)DU9bfgn9(tYff1v44zE)D@Vp1ljGws5TKna!57*Fi#u!O${v7FBokK-%9kq zl!|uvXchG&t@Z&-x@sRlqt;T(b{z3nV6+OZj{C=8(rsu_=cscP*QNMN#A+Ujh&AZ? zn@-b`)n9Bx5ptYb;&BUj9D9b9IeR-wE_Ie%I{Z<}f$phipeLoB2f8Q8bahAf1f-<1 z2ocAlc-xWXC|`|jmWkM0iRfxio8)30?Oy?+L8AZ4y?egMX`P7s!d)3~_b-n1KDF4{ z+nLx6SNft(t+Qd_ycq5RZTi!P5->r4X$%oi5jgJw_ZL)O>pwe`?-ScE%|R&5e+O*C zA6p3z-iE1{vvY0Yfo=Zk4n)Qm4wO)c^dEj#?b=I%lG9#KX;@56Ic!VpQsA0gH_TlxK5|DqvUXBvDQ)Y zTxYSZqvZL{VtYr)?>mc;|ElAO;zT&_>yAT3$p_qTV(+%&D0sG?OX)b%S#lVw#FFPa z;AJJxcND|jf8SAT?<{$>qu2>Yex0^=;qY%BC{Fg@@?SLTa2>Gr-ES4#vBwZ#|A=7! zQ^Ee{g8d=E{wbaP(~^DfAoh|9!#!e!>1h$^OSW`-h2r`JW8-KfpJjC=^~) zeS4(w@4gZ4CM4J^B-ke;I3OgrRhIzaitK+!s05-U39(lQu}=tbKnQWG6rx)f;tmpG z^QVRoX@(G*Z<+t2XoeW(Ka3#1uh&OCtZV;nvG%%w7TeEaB@GsK3<4O=1TFjOva<0@1nF zt{fMm3B~44IpgoEGb)JiMfs-@*Ud(b&9Q%tNZY&a-i@WsuW47bGq)qZ(`&ob;f=b~ zwOecd@Y3YJPClW$+WyX^!+jP^#g2Bo`q{sLG0H&K|52GlY&YtQxV~$};!&>S{Mhg< z2XC>CbL?9Pl#~eTGc5F!XS%wGmt6KK$7xzlDkjGXTZWL*YGn|$T)t9)_py^AnuTiZ zDk8QO$O2oj)aPBh{auMUZ68%YmID|`f@J04$VlW+va)bwWkble6WJ4km~IDIHSKu? zxn3KN?%JVr*My_HW+>g}5V~e4i>xD7 znn69bhO2Mu(E2uo(CPJUl5`=hY%E*j>C9q-KSp-95E9d}b(Ey~Fg9%KAEmGol$Ba~ zP!_f7pe$-Zecc!=O6C}nNo_MElNw@3CRu$*Ce?YY$c}+3L`t&g5Lz;y$n1|bHbES> ze3w?CPwc-9{Tm0MVp|{JT@s|tnA3zs?=oX0NZ348L&m;Ov5?OD>ahEM z%7}Q;G8<=3=IOBJ{~e6Z|F962-{Z>lj-mUm#kj|M9k^%sW@0*Y0s~8b)`k8l7#|0G zxdXmw1HMTEzRUq%+JG-*z?V4SbK(xSj-I`&eC*v3-QKUx>v|4^O1|DWq(`X8u^?LP>NqyHYYCQ|w~%0z14Dw!DBN5@boHL7p6 zOr-VY$i(Qr36vPqmoDM-zGRtj^~K0UMxQ1VWBb0uJPNgp>-$tDGW+^uVtn5jnV8V` zrc6xids!wX^*tvOlly)v6I1$rArn*k9+8P@eLs+itiJEcM0VfpGLh4Fi%jJ9?U9MR zKCevV_wA60g1&N@nBKQVCT8>%%f!sSkR#iYP?E4&vqeaf%mvFs?>lCg(;yR1#BV6ZkeU9s|xbSJpUJaKG zmmQZAS0b*FxLmk0aZSOMi)#+9#kf}E+JtKxu4-J3xUR$1hU?q7dT`x~>p@(P;`$Y? zr*R#_bpqF0xZcIpkLv=i|Hfs-%FBT(1y=^HDYy!772>* zKf&!DY1 zc@yU$?!(92&v|x~H5_w4U*@T-!bZyP5xCv2U?cVfxZ!b<*!@GZaP}^#@A`+z-uIqf zR-Ch^%X9X_?Y&yDf1@39}n7NlZ{j*}G@0T-u?97S9)}Lsmp9}Cf@911?Lv+i%##5C2`D`Pb$!vXN?g=2?fwQVJ|GHK zlY95PImH)ow0#T-0pJk>U;yU&;!#b8sK#+u?*Wl?9J-T;b;Rsvd~sm0u6Fw?3oU{~8v!${_sMP5Gya#vy4ne( z!fQA z6?Ez#b~JOnANBIzO{b7{2}WW4Be8jlO%Acgjvx^6q{uC7cVAM17;KG#NG$f*>}R|$ z1u@L=bT1)G?8*y{L-CHMUz>bF91_Ua$a0C@I)2?IHf zq$vB<>b7?VR6;%RR{MKu#~%-WV09ci*!>la_3tf={Oo{CS7SrK@x=3vLvJ|_-P%1s z*mqduTc}K=>G;6zZ-xeFAM?v-v`zj4SflO85&RCgvmGlGjNTX8_Fy*PWKbILK>sYE zJ6;-YKW=G1uC*WE3yEM>dQonJe-cPgEoKS`ibdGDrbzIN!W|Lg4T#9AtUR@Yh(cxelD6Z z!1)H^{JlKciu8^xlsJ!epXWZ`GR>q6^2i=wA66N>JBBS-_r?Bns3qPp!P~(Qt7{e9 z{(_-E^b4!&g@VB(8#Z%KzRp~0fUgWJQ zD|c=5w0NtUT`M;&cDdG;RaUundAzl*!U7`53jXEi<`+Z_VKAKWzt{rncaU%!*0106 zHA2#Q1U;9r$}&$~qp#e@e7k%B`cZlL1=DBDoHct+Zq|HT&*c9dp)SxG_@SHk0^M5})Dx+vz=J;zTOqn#f z6yjfTm3zacEhQ`07hk&LGVu(3}U2aB4#qqFEzpGEw$A(6~+v3@kI(BW!X*z#>HV z0O%I{4LTR-j)U$R(5=KXpx-*!ZI-kf(VPRW4L=_F4>Jw)Q(uuZ)K*SRXl7v!dIK5P zkbS*qt7*k~&k^1R5K@rQu{dc7@B zj_h+=(1zay&5uEIcreWql7`y+x1d>y%^@7n2J6`%+nn&HfuDOBJn4D~_*~$>Z7#p; zTk0)ZJGbm1WseEhq`D?S$J;=Q_Z@<~EXxG5X~K@_cvNF)Ra9s!b z_&*K|R3RN=gKa_m4uWPQXcm}hb_ad>x4?e``0tqUWXIcsc02=`v!FRSnC7J*4fW-_ z-X0hb(a&ge)KeizguIhL^L@~`2i3F3L__C*zXeUK*#@XbwFZ5#2{b>#{)h-cCSJSr zddQY{f#xu1mSWQ*V4GsuE^1apuwj69;d&PIuVb0`b++__CK;=$Owb4F+%9dIf-Di&@s^Mz)v*SQ4z$S2mT)5C!6tALA(<_cmLPap9}mW zz|S|Azdl&L82DqruQuZ?%LM`H)dc*f!0!&hw+8Utz^}pX_{|~snVG9gfVAuk~78tg`|5XdL|6f&XSg&CV3|nB>0>c&9{Nc4d`g7se!!jlA67NP@;){X5TNW_k z$0A4K-yiGjU4b9?8-KF=^N|KVE6>@xOlL(j5`Wxo;9F)md&OIrdIb{yF=~{B9tVC0 zz4aJ^cg7j`v|0GwHnHR`{KM*fz;xd}E3~8$iWU zdm6mJ^HsoG6l)nWWAJVrQ!}jx@KEUGN^xVclR=`UMN!Uufm}zU&&yQ1^;brYB3}J< zdK#FT7)j|D3=E6NMKa^}8TfHMs!|Tlz}?p5ZDtwil(h6P72h&s8Cer>xAbT#y&agb zhAOf2T%+9x?<_}}d)Sl&Nm8%fj?BfBS;ulm$V_?%CUPz+jEhLb-Tre}lHRFT4*m*v z!O)&UNrRz%f|;S+tuu^AJxob*JRwNywOR;%4YBgD5jy``?FMAdpv*cpQs-X@FhPqX zJ4MCdX1fUj(2M`d!65erB4NiogA(fGWJ1{gLhfywb_(~Ha$hyeilL-lS056xt_Ws-ocgV8o&|*! zA4(O*QWBJ8`vfEnu1WN}0N2Lf5Fxz^yeXEFET-x@FDr?quR7Zl`XTC-u_SM_@(R*d ziL_nWj`Wt;HX>I%NNbH1oF3w zbc*s6^2c`Qks#v)!sMRdiHxgyA*}@E|Y}H>POKG$+R^y^1 z2!+x>it-DY{b3?Z*+oAWP`bhBy76Q{mb?KO*HewmmTX1z+Wl{o?PN)9$QyCXKA?r| zzvZB$G}-|N8BG&L>u`|K?8qS5*zl)edhHV>;;;zwr_K;~OYTM*CcH zyIvV1YrTe9e+g73P!QsPk&Uit&B(zAM367)y2n%(A*jG&$bmxNF$!&BRyR=O<=O&> zcfWy<(*Wlw(0?kd_TnUbrv#94FdOf)bF$i}lXRO|$Y!7ECcDZ)CL7Q#_EMupU%a*` z!s{(P_26z%M(cKS?4084eGbh3BIQaoELy42hs8!JBCwEgXxcGJ$&|Kxsgzk~O~p51 zz!vr88>Oznw@vbuR9X3EEeTZ=DDfn(+Ox(A?nP}GVx4MRr4yaK6(||+VK3?~QEz=3nV*A=<;TGMUiXO2 z7IKi6b*R>I1qfa>yd}Ploz~Oak?*X6{C5e_9vbiLrJo!r2d{-1!uWUPnMJ!>7j1_S z?FYJOXB(}fq121045IyWK(tz9HY3wQKeu^kq_kJ1w`o#alC|PaDNsbIiqC z>0773)w5CWp|+~ynfOMJ;694}Re|CefazCZ%0cIAsD7!n4ZyqOoV^=G=6J|sSp~$h z82YV*+H?K}mOwFS;$Jb+`vpxiX!!kC zVvMDV;r<@PRk;KqZy*JX6<;b=ZCY@as>+_A1!t+MoTX~PC5Rfb1X1M#Rh6?+JgAlV zQ(#!D*8ZBLJqPJkj~Iq9D+fZY6Mz=e(vr*69Rjc^)r8p@`y7hcne|qJic+ovBnFSy zk+9L=k<7RToK;(R36L@sX{~51IR;ZE;ifGbcZS@BS+5~fOd^%!Um|VOMp9T+A}K#j zObnUwI02&tU{Q_`;EEy7)GRLn$Pmd%mMf4RD;#!OWMvbai_(@X3w>^NyvWb7ypHq) zL7y90)>_Xdiu~!8KSTXVB0a-GzqFey(laf&NKX;zS(cGVPo=bNHj8ZqfHs?MJ3-Pm zS|QAgF_eZWYB1{5J=?W!fhN764QMR7wHQDy(GzIVodQZ% zp^&;~b3!)NmXSbpP#}#WkrIDSnK4#s5&Nlz68s+z5CdC@GGdMx`Tm zB4cznY9a?Qc7B#eLHnSCa2ZEzK?mV7j@W_@!etyuk%K9ham2P6l}=3Z;m&19u>}{p z$f4*G`7PXNCnJiyXMkDtk@q=3Dvbncyy(MBrGY?A5P-t0A}DFKR){Wwl4cG{njVx? z3QC$eC}}%me}W=*mP$cMD@q|g)}1g#inekmFltrVbxd0&`V)Y10ILO{sZ|vK)(F6+ zR#gI6D*!vlnwYju08X{41;Ba%Br+>~U(l&tOH37-;A^DsvdEoB;WLhjPL^$!w$+PmpyJZZ5uYH#)mmpYu(zlt#5!p!1U0Zv&7kbZ%av zbMqRVZ;8DTMdTu#n^)=FyiDijbvidM)VX=3&dp18ZeFW%sZ5$|Ym!VTgT*?v{k;Zk zVWZ?KomblqAyf1_UgM%P{N!0^Omk~db|JI20Up6`NyH>frA-h1EeBafH^%`7nFDP` z=IC$`Q@Qr$=EXNRR^MsDkF0U%!DZNe{1cks${3n>^TSJUw}=}xg@yVNOIVA{c*-0- zW; z8Ic}ADm_QF{TmV%`r*EX`U#7PhF@71y+IjN0dw6;axAq{i#q6Y&hw&axz4}9YL2n5 zh>onsqG>V8|0Hq~k<@dmK*0Y~o@~F33deT^|3UKyYvz@*48+_$p65^XEK0CuGP6cLfk_A#C zz5*#N+(uIbpe6Xcaby~`=hxq45qsuHN(;U;WM&2#oH0gK` zeNEmRq*|)HzNRpn^Hd0RAU=}wVnoYAU5R2uqm5-6(TuZ*t7FldhKy+X8gU5-^$|^9 z9+ps!9MO_OMzn|_Bbq*pMT>c?y6_Q{w9q_O)#tI6Nyt$bjq4(BWyv&;Rbvv{kr^%K zvFgZj!q7Zc)#tGmn#Zd8Jk~<=tH^H`^89;=$>v8rhv ztD5Gqs(Bt8C+D&1^gSeOBU)FTF^19@We(v++C@@eN{4qNJwl7W1CKblGp$Yg1ps|( znvy6*Zb2bEMGX59%tnFhBr-%bI(*iEEi78Zd+iqbimtxVN*tKQjmFmwIH)dTro-IoeS&f&)AvY>5iuIg=~cH?dyj;C z!Wv71NMxS%1EM55$s!>;sxXBv(GF9h_Nr4Iq-I%={ zw~;?Uw&JiFT|qhcOW>?dECSN?&Rk=?6ExSQlLnTaV#ng8*fRxlD{ZEZ9Yge5&kuk0 zO)CYZw*aIw*X!SL5B=z&;mubD7oAcHaL5Q#8(;EIV^HH(y7&`NpaaJA7mTxf$wDG zOdR(Niei28SZ=S@jf~0mD@4Ym$8&qRoSys$MHF!p#)&27BgqTZqCijichJzKyiT{b z={5mmY?S7p>{xOZ2W<$aMST-octQkab8>A_jF`$2oW>CA4atNO{GwAevoA5Ca7XiliyVSR6vvzNYC zqZ}MdvoodJN^Sdl7c{~cy ze(rmJmOBbX{*^1+Kpx`@DMJtAqb7ROB zmY((6Qa~%C$cmPp(zuUT$Rl`-_OUz;S(b*r_+Nr9+lo3y08$Rd(@b+3by5klCR9y5 z`BDsgRP^Lg$RTx&WBr|RU>+Qb_3c{ICY8QZYucpJcWNU;c51cYP^=%BNBt}ce4Naw zF0*|XDNf_migYndMehbqjT%j(jCBdp$|!OCeHO_y5e5~eOam}R0*Z1GfDDj|MOlx) zB*$PyRki|?A;~mlI)z9HaHSrYaT2g9X~1MkAc9#B0MhbF7XApR3da32BvG8moh~-4 z6q`~8V5R`nNaZ|;W=V`)QGl5(09%aG3a#cyOp>w)n7IOoN@jLa={8(3d8m9qW!5y* zq80xOaoqvRD`F{$bF@{sJIF?gI*Y5U39;k5AWclC(#Y70eW&lEfRb-Lh}SLBi1Oga zz^OAqmOq-3=mIQGi>IMYafzXA21siJ=_E9|Aa!Nyq!HPuZmd4%#~%7|)T5+W$t&oH zK{=@I20_Xsom>=7nJ*ztC6lFi$YhotTg2}Gr3R|PVHISC&&|ge!G+Il)P2r)RU|$h z1?A|W%3X}vRF&MtP~|ShD$0>_K5K}h>E0EAZpW1I)&jgHSyZ^k ztIz;8K2t(XOpFCM)*v_v#U_hlCyZjFQY2xe_9%*_8T;fc5@AY-6JhE-SahN&WrdQ0 zS)9&HmWtPV@GcfbD=;O}N;*sV1bJkFS+>Xw+2NZQTdVifNGF8b2_B=j>sB*fpt&7Z z)AT)LX{u;4l_h0_8xK2;>Y6lIj5L@gw|z9Z*JJ%1v~op!46^O(oV_>W2_M4U_8cLS zCB zu=A8?G(`o|Mv)b;0|{o@c%7NtUon_*FA9&9)mwwr!)5VbyyCQ~w(+(Ej#{!=oKj>Z zHk_?YKt?8uj$RP0Vlk$2rpC_AQ07I?iC&ptNr5XxQ*lu9=}fhSpsdM)*dmG1XxYlb zRHJx?bz&+NEK0dCRRI%x9B)wZNC%6~&`5x&>;Tt{7^4sxB2F0}n-Hz0k4mRZC&n_n zE-HdsjLt2Z*iTF?6kK9MGgl_aY=_9U%?+V-hGrVH!gTRaHdc~LGbBJ*I1u)7g{Z33 zc{Z{vg4Ap>7?HHn3C4+aymZ@}Ww8Q_$IHnk@x zYHT#0n?ZtQ*r=iyNQ(9{Ng5k4xI?lfGHC^)%_pX=OfVXNuyKL`v?POswtj+cYN7ey z)=r!tOpqkRiZ+Xt95i6Y_>~E>A^eD0f;h#fP$M1SKCGP#gOgon$DIdNI#&?kU>3ftS(UWJT%c@gN+kSEKt&M zGKB?Ec6^|i@K?jmyTOb);vii@`=sGcp)Ei?(r7rz%o=Fnh(VrW3wlFF49t!ujg)4O z8se1EhQ%oi1Y35u=wbE{BoFE@(dfQ|`)uqC(Y%hJy`8#S2#X6h8*Cfd^zpJ063FU_ zf+Wy|4KGLv&6eeo2Ully=@CYkGyF{t3T7I^=X94aqeh0-ZJ+|7K`$aAgLHM_&ain* zG|lzYU~kq2wGlU&MfPFqP{#}g3ZrXA%BBrv6E(y){zfzvm6(Lb3m_t*h%9O&2Q>yc zqBFEl#Dz16^lGWUDFRu7IT@&Hs6~eOA|GTUs}Uz74E1eNQ=N~l+2yHBi7xCe3L*`5{QxmOpaDC7Tj-8ma7!I3^B6HpvBZfw>lwjyeH8-1d zwvp0fMh$k4w9tMcUJD#!b_i*wbaR$rJeMw?$)*^61m%zrD958fwybrWxmLrQWQhKI zW`v@jn*53!3PX*UT?9gz7AhnSn`W;PmNL(ahKxyuD@7Wv6csA9VU6e^4r&jyt3J)4 z=!b|xzHT^_ZaZlYeZ~Xl5nW@DCUK^=labf(pCL}1IMh9og6=_GG%#gc0ntESP^WOVp`8PeAcS5BueSr8Z;6cvv28m#!@DvIE@8j zVjeohHk)tI&=_Wr9cl(^&|8i5atK=~we9OXByzC7ne~+&I4~L*C|#G(1w@#oD_S9x zM$_fR<^VVPr&X6u26wYFQcFezIz6rVL#=15IfmLLS{PH9g5nTfDxgP9&|0y6GT z$D>jJTR;Mxb$5WZ8rf~Zj-j#_!zr168JYC&Gr#pgq7^q`{(mL&|AKB$(Tz_*L(r@4 z>PUqrzQC0lUZ+N8^9*X08nUV#duXugNZT5o?&GPgJk8hB!{^_@)2sRDY(A#7=MJ8> znWy?5;EH8>0#DlsBdtZTEqt!+I9Imtlv3nm@Ts<~_utR?1GcTU`_a@}_(V0Tnnwa- z+sq?Zb7dQkoQiD4ww0FYoyGphdyk6goTHZLEwhC5&6wn9qq zmw3DlF5=}gw{ZI#a3vR?&<+R5<_p&FHDKh~%s1dMGMmROwAJu&kYzm@!J_WEkB>qE z4q#Vf?&At&uI4#vq9_5a^`M7;N`9)sfcyn`#gzM?S@UwY>AZ z5NL*;D@qh{hX5Dw2znOs_1>*rej(DsR#s=e&1R?z0fDSVPjVau^Id>7|A3q5jDy zED&8M?pr+7=HX*j^EfY`ngfQ}Jbg7!T`f$6*Ni4g*%EegS1nIl&BxqLlo=#{4xder z*A1GuJNZ)7m3E3}^@xhb9JnlP5>p82r(H1bnhj%_vIJaM0fR1U0{D=60@l?Xe^Sn#uNRtJN@NoGU? zY&Iy$ZX zZHDq*u6)KP(pa1TLp?5rSt%8Hh-$lCjO*wtNp(;_G+F~}Oatd4YJ|%UoHNlT;>3&Q zEj$;HB5rLcWuf1YTb9uc!!ozf4j?D$)*uHx`$iV*mXOGJ(B&mt^aY_E!` zjdCAX5yX95$>7U9e2Ha30#8`Q)86E%KjEW(!jqoo>9+UD>Z1`#?tK0;p87l=1@Cy? zVukGe;z(8#^SH}fD@69l`$%?i6S9+gOOuHS$0G!a35T5iB|<5B1rskAh-Rdj->6y) z=RO{ZSqB_id7mfO@+e#8X8`lw-(t(y61Z)p)YU|UzAO8K4s5MOPk5fjxpiwOxTjdA zB~X-gVWMIwjOU7af+6&(kAHJBcYtVXc%qjC(XYOq=(r&Ibtq9zkljLJ_pBiLog|9f zx`jJ3L>IzrH}Vb$omR*r3vCNPId?PUS}w#Y+KNmJy>!Qm5yyd8B@m;w^64mq?2JM_ z6&(yD*&r$S6eQ~eN#s_*xm4E`0^V>3&n)B{EqMv_jHl-a4~=!1TSRPM)y}8B!QQxDCvMQAPnW)>!AGX>>pekI)8qE=)ZgYO(Q<37IpK3=w&uf31s zC&BaP^Og7U%FTSe03#QmFpU`MJqQsIwpL$mMy_RY0$0ZJ$#tCnnNQtH(^mus>Z%#! zoGeF#hH$Hy`wf=2%uWfXC1 zLn#?NMK}J0&$-?3n{jnKQgv8z6iZRjRoSJSaRCuH% zXQZVt5g8M4jYbgi@pLtwP#Bd{Erm%K#ZZ;Q9?`EkAmy?g;*d7Q8F^3(-bhAp7MHDVPXU(HijQH-== zrnQEr{Epi`V6m=hgDDUhcxutd7t2(y%#W4UGa>FVa2!F>XVel)4#joWM-? z@x=Ri)P3CEF2)j!AvA$gFh6MLlWKTOH5N+=^vJU9qsJsnnea%p{ghAe^4UGqBNLwI z(RDoGem;qc-p6O(Pr0drBJB?DI7^j{CtNj8uEEM9iO*WaqiJZ*+zil*_55nSoZ#rq zd@^R|$Xc!XD8AXVF-a07r=nyv2E(n~em{@KvPCV$ghic@jnvksF!W8uUINxgcW@Qa zGM$!@K0dw<5JCXN&459KWs`D=V$1soYzn9_d5!pt+CDmm+a9GAiM5s6m+_=Wx${wO zFXoAFaOWH7c)|;QfOh=bdm%aSfVqYjXcLf9#7%gC=mNYEEl%&BsUsB3U^94ZAzxC< zm%YiWkhl*XSOl@(-sY7TLoJ-obCEH75urt{f1b0;DmS?6ksZTwBHw2Ptq zdR@p_cwT-e59zk*RV1Xi2_Z3a=N5XKYa~WuRD3rdwE!uMv=7pb%$QsW{2Kw7WO9tZ z%7Y@X)+y3L{}bkw#9Z7Y^GRS%ZwV#QL@N$+1I$aIC#InB^nl{@zz9!|EV(Yi5`G_7 z=JVybR4B1VFGtIJl%v%r$_eOZxgiw3_{?0M_?b~SE{BhT4?M~fSMy{acRkAEM4I?o zngV<~Xu|^qC0`6fN$nJ>^rev4eY#mfH_J_^LK(oaiaG9l;g8<3=9(%m!|Qi>Gufg|i%OSnxT17dna9iO8*4ljW%Z@y^^FxfO3NxM zo2yz{*sf-ew@T)<2>Nng%k{E=B&=xk;d4+v41cpr@odolk@1S!vSzk?U5RT+-Yi#M zUUortt}AP2K|XF?UqjXWnyQAXW={pcvgV4~`JS1xXS%X#Tv^rC%~e$`-m->DPXmE1 zwT;c*tX)(cpoYe*a-XN(>uHbx2rG7EH8nSSt17&WL{ZjqeM3cUb7MoJuO-XZu*=g> zndL1judiyM$^>~ueOXJbQOMKSz?!SdDh(A$k%D|^;%R_^1b0_JDd2>rc{4#Eag?=e z(WZj@rJiOX!}ab4Z}asGKL~1UF0E*+tYQ`QKKfVI0g$C6akHnjs(C}zH+;~Q?X22a zv9pQcUFvF(wRm<{8EIKW$fwuR!m3+9Qqxf8^)-X85${VI47`n{_2p%d+~f6>)uWoy zY7Z2@9^7}9R+m99R#s7gRxPb8^HvGE(n>?@Qeh?5RMuS8;HB(Ry;@e`Yi`!DsHn-v zFKzJcEU#*2L@LUb(#zn!hRUk?D%4&9so^PvL5e?3z0y}d@Zn8nb+IwDffY9XJe(O+EZ1jvy~!9&6mhlSS*YjBovl_R^IE$ zLWXWFl7nFZk`GovvsR-GD_KJsl!EEKr7fb(^1Em z7E(!Y%a?B2zyR(fPbYW6kDzl|d09(UsjyCERds1&lkA8!&1DU~`Z7ep z>sd{ux75>sAObT`wN!#NG&YpVF2+iU02kb{0ghQ&+FVuRp{~!C*Eg1z)i14Tsc7~z zp_yb0qB8x5gH)aFVTN1~z6J70u}H{G8#lUz4+=qddMcUs`X+>CC}fnW_v{2zBbb#| z2Ds@Cwoz&vltL(kvM^1(^y8IFH!Y{WRzqDE;ld*{hEkL%ttt!_6nqF5RI!11S`f#) zzH*&241s>yT2*OC%4!kd>#6JN!%S&2DN>rp^hCaqC@_aJh{IMIHVf)uSd)Y$>$f8K zA)pJxP>UHxHftdo8mQ7%sd*D3GCCQ$501c$Fkpl@%0l;KRn_%nH7y9DUQa7oXh+rc z<&9;{l|dfpS#Z))v~yKe16xvDvcYfx;-z;*vW7QUSav(u%#=lAF!d;y*N6{PhTKH| z3M7R<@?SBgv7Kd}hM*Lsx_Mbpz?Np#(#L;vIK+Q6pl+x@@6)TSx~{4MgM?m5b0fO; zPQ>PBjKBdi2$5y{Mx3d_V5V1ER__HLjGb^|sL+IbF~xx?8{{bOZLSYc7`detWlbK$ zi&6}UkhFz{dWgr6-GU&7p@c@%B_#D~-Itb%sR1mB9*3|>6N6Cc(6Iw14PtaLO7luF z3n*);@OVne(t1e8EU4-_%ve0tK`jDhJq=AhZz<+M#W1Xyy*~FtKWg$4ChZ-N~h)JoU9uUKrWX z@=_OH6OE!&C-KxrT0Mm7p;O4vgz=Rkf$$tkP{%g~9z9r6Jwziaq#0Q0^wOQ>Xn?Y2 zuRdu)i&J@oVpDNxA}=PKzM9%lr5UD~-qN5Anrg3a5fkjo7!eBlT51hX_tuxg4O@Km z7{)LIX3(~byk}=)YnAAPtg^+6)}^-om`Px;Yw=dn$dp|xB=tx&F}0$~;k0M-OUI zDhDq_TT1imEmgsmFIh6zHL0W=D;uA_%*)F22{7-b{QT_P>67(rY%PTTVc8$})7U2i zNfPRnk52~Z_h*c`#2qIt1M`rwJoXd%rZN6L8CW3``u&rEbx3`+e#2Rr@yP)Fj)`?U zdx?+2qQ$!=%<1f0ZCHcky4t#V1 z^2`Q&6dTd9n9ArwTQ8a5?SNl1!Ct_pOz;7~e>A}d0iQL&j{*M31V0P-ya_%9_;V9{ z4)9-1Fzte}MM3$J0Ba_A3SgTFUI^H3f;Rznnqc~vZ=wml3Ghf0d?#R+34RE0rV0KX z;3+2fHNd$h_(Q;ROz1F?VEQF{zX^T-@C6h6E5QG4f?oh^T^#g}w*fm$@CCprCYU}_l3{|!0G?uk z3ji0G;1z%iP4G6rZWG)Lc%2FU7T~K)@Ik=aP4MG@Jtp`l;ARv22f#O&;PZg@nP851 z(Pe^10=~xt=K#Ln1TO{rkO|%b_;C~50ND7W_&{I1G5!d?88CfcAp-r9;!_V``n&?n zD&Ttu(LYG=UVNZI;BD+zgYZuef(>5uB!0($*HE5dg+;Re1Z;e#IUe!qEMViOxCEaA zZ2ZEQ;Ew?t-%%p?3&6(rFA0vOpFrb#cF1ON&?kvtCiywRaTefd&`;z7e-&Wk=dD!! zTENEF4hW|2#v0!YB6v4o9D~i2^AK)5afF}HU;OYB-7>a~F>+m#w5Ks790FO86_kF;|_mv6% z0AS-o{sjL7u<;o!f}aKaZopm|`wzgz51a}A4wc7;C6dXWfIkOpd~23qJLEOK?LcrQ zVER@7zOev#ZEP`MxKMENBqvNC5 z?*SX%h9&&lfQ^q76MP=9@qrG4zW{7}=7wM`3RM+DUW_Ag%*x^b8{a4(d>&xqgH#01 zA$q4FpM@0xHa>wx_*H<7@3s2&7%+X1 zWiHr~e9r);@6Xi=*v8%jY<#Ji%G1Xxj1PYh{0U&=lU)SIM2Ggj34m`dl3!jU{1U+Q zofvdg!LJgq@ey3YUkBLuW(mRj0UKZUCHOmljSrC!{1d>&H!TU)C(5D!q{eFZhIQ^W zrB|-pu&HFxn$pFKHoBRfw{g>=C96v}uH5QoYZonFxukSm$=b#44Q$z(Maws`B`e%Z zR&Oj>Ys}fPa;(^a#UibU5$gmaM_*760<-HI8=LfXAU?9FFWu;rCV1)+a%K}) zNCnqBn4!xR&E>WM*a*Vzrg>8|xCu=fsLui+0baZSYk4{SKJq_4(GB#<=`@SS4HX^@{ z0{i9SFdOHZ;SLv!0|@%|k2up7ZxFPw($ec@&&;M0O*m_)^)>9sF2ip1;B9z zbh}i((;&|6zi!9hbUY5V1MMLj!MwRH_jdKOjur$H98d+=8z);BYvqw&FxCt?68pNn z>aZsm;z(BCeW#NkoEwBc-w9(md;=P-PR9me50uQ!k5elBMAvXi-3Wp6g|FYU2Om1j zAQPC6Ma?XPhsmSdL5>r$9j^P9xujHIKUn_SvgCC`N|N^SSl>7n4;-e586O14`BKQc z6o2cm$)lf*$w{5*ILpkOyjDLvH#u5xKVE+i!#F|HU)d0@9rU!&)6>fyCYcWWg6GHj zI|gEA7<6tN$bRjy)nfvWIWx;H$J+tfm3X0|rnC$%5Y$u!Kyip_pvuef zhJrze$XjKkp@)&AY;+I66^%RT%m&M?zx$6i%dvbCTdPo?qD89J|(4eC={kzxFo=@=4sE_vS4LaH@ z`QS2ibvhlE#&V6koiX|c+KfM!o}dq9Q+-6Y7i-nM71-0HeG>i8kbyp|NOYo#?a;Rg z^0{;(dgzMWjHE%Q?ZsYK_fY#$ot-8++Cy=IPS^KGN8>kbJwbmjSwI87}0= zKbQV|08ip;{1M#)rVJzbGY}YQqCJGK9M+CEN(FJh8ALY*hZSEshtfSih%WUw_F(aY zW8=@@^}0@%t0R(6!w;~&HvWi?t~2`6pu6}XV>?A70-@uG?!2Bh=&C-#XK0B)|1;{N zr>QvW23oUa_^U4Wi()(q2?-ZAFVZG2Dtsl`1vgf9-wF%$Z@ZwY~TE`+og$ z;LO=;ueH}+Ywfk)=j>eXDP1VbvZP-oNs&0@a!HdBnQj)i$&y=Q(l{wy8lvGTg{v30 zG}k5Z+svaRfiD2y2Hx$GzMC)cn;G#N0F(h==8`7aC4NVwZw7t?@EcrGnj84b+>!-R z$WFTK9e))Cqc1BWfF8UCEih<-K?@98V9)}C78ta^paljkFld263k+Id&;o-Nh_Zm` z;fFFQ>hqPWPDY^y!9fcQT42xugBBRHz@P;NEih<-K?@98V9)}C78ta^pauR-EbvpQ z*(Etg9(TG=v_I=|jy&OX_r^oF9nO)xds&Xl)jM$F2m?_sDeRPI(&HEIZx-|h`->&1 z|8s%|uR#k8T42xugBBRHz@P;NEih<-K?@98V9)}C78ta^pauS)w!lwELA+az-_m=_ ziCcnQr8(xZ&S%;Af!R{ozMs;I0RYQh{Ctc;a`_h;hOepRaesY|-$ zD5XAB{nK%hQ#yX=Bj@nX$~)g#-G0g(oPqcLcOJUWD@on$ryRAP;7fvFT&^UwKkeGx zyY*Cech}YDpNmLgaNm&b81nBIQE+5YSaN`6W$tNoaSxLjxHImsi>P~=<@i9|Z>M-)9F6n!wL>X@^H zaTzc!9rG8$d_0kh4Bqu_q4r4;)8$b+J!g-2PLogcFAe_s^MQfjXZShdImM$!b$L$h zO@R_;_f9sE(M}UTN#s=BX=(SIl6P)DH2~taV>>-(B@=S;oR*yjJSS&(PPaVN-M!a$ z?#gSfHkdUh_%MvV^HAnBA?sX@EH7%E=|Waw8Me+SU4cmJI6~3U);TkQ+kod1X`SN& zL)Iw});TOOIh&1-(Z(N;YT-B0W5OxgxdL37nD89Eu_jvosUb-$3?kvJ24>* z8D!`7)7(f$+C67A>zr-*L8R*h_rb1Axth$P9z0<(8Z9OGRUmgBdiN@hMX5moICZ@` z5O9hK)L1N;$Bb(UpC*1Mgj%y6RM&CqD)p--+vsX-mNUfR3o z915T;c>DQ*0n}K;){?hD&zBLcV=oih?&)>j(R)F4CQsUV=w1U)kvQlDXYf4Io|W#F zP7e$?kF>iHLY+k@M+*r#`*@_O?sEB_Z0g+-|8X=z%+HD4CEVwn8L` z)D}#G--XOO4>=%K@Drrm-RrjoJ;=L%QdE|8;Vcb&SI6K>t&l6Ym^1##dR|x^p0lVz z@LH4ez-c&5@CL558F^e@HV{yQ>xE&XDy-o2`FIks?joE@qBwo{Dc3)W)9Rh4=yRxc zM-GZKCHQ|h%QUb&?D+_73tV|`yN#RC1CQ<)$IZK^HxLi}07U<&?=xuH<;g^&OL0_< zKI(gq#|`0@f)||ngnVQUtViW3DsbOppb;qe=E1j_e3WNbwwE#*p=adW)5&?kL~QT! zbMmvfvWg|<15$}A&;kz+MW4fmU81TVJRxdG2z4fevK{U8oZ?aq1WqCI4@j>2g=3(S z08=Z0{oXqWv#Kn(WGvSgdLc{CNBv3EP_%D9JKK4~T=GN2Mfmg1Q#nW;^g;TahmNi# zE54_2wTxDrC%6hLzDHc%>$?q2IJY8M_C2!VdwtjQxR4c3a4UYQ)d~m#!}Kl3@7_Ix ziebgq$7sdn+>?DFU9V`9VDOZ(;E%$jA9I6e9t_$$JtvGNHk0LfYkGl+KaVi+X}zaP zq^=09ao28MKRZ3AsmX=m0`H;^yVP@{jkJaDL)#u?d&CpOFANbb*MB_I{#@S91rNS$ z7rrD91WuO)pA$7GLW*`zkT?D$$@7qaO7}fu>HZKtL2<^?y*#3>6a=q_$#x!k|8gqj zHwP{#<*P>Pb;SlwZ}#Qm%f&9wkoI%(7AXBDDg9>OP%V;LQ&f~=yoiSgi8W*BwxnL4 zTsm-0KAoFRMS)82fL}#)rzhASN2YGyerk5xYl2T#IV{2Cr-WdrRI7s5=5UdOpIM)v z&mjBR6K-ztV2F1Hcz2*r3dc}xhb8n+;iw@z3JUQkUNO@URedCiSxQtSr%Z~9>^Le>w6X%F z_0?e$xk#N(O1QTSW2>+^;{>Ab7$oXE(RuiYhreih#B+j_f|^ttpK5a}sif=VYsG=k^l=P^9hoa2(7o%-VUjZ!~b4g8l9;&&j=uk(e_ycXyQ^>q@z) z^LE}|5Np8#dV+JyZw17ScdX0YZ79|Vr#~7RaG`Bm72K7r`Temj>EdMqB#oWIrKJ@L z@&HpbE z4_i@-^k&p3J=|4m{mkE>*Um$=tGL@=*aH2|&DBPMQ491*-(8AmfwacvIB#qo5Lu%7 zpj4#3TF~s>L#VFu)6uhvI7t0Td+#`oY7OU?j~+&*c#?0Uvuk_DAOWxWLxcoq zjy10&4%8g=J%T_iRou=!cLH_9^9GTIGkZhJ>0y8q3u78EJGdasV>iP zL;hjZOy2E29w}b54yy%Qk~+c(v`$2f1Xl%%hI7Y?APHAFeU9T0RMDElj>ahJAflp@ zFN`wu7-5&-t*Pj6!jXnpi zaMGb6QW;a|eD;&~4}QuzpFMbP#Q3K=k9K&@TnwdKWBkF+=ZWwm3VDJ^5Y%l$2!k-c#eX1sCbTHamhv{bOvR2BC70A*X7<^ zX~)^wn3+^NpY$9j!4JNzq5(u_bP6hTw?Cb8Q3rTU2HW4HB)=bW^| z-V5M!G$a{ao^#;xG19hs&Y9o^pSSnQ5a9Fbo!dWu+1mDIcXwz>jq5fkcmPGfhLLV? zG|#cUo1DqdKWImwiv6+U(qZ@@2G-*CitTK;UFB z6KyfZN}%d0KY?<=f(%;mxWEK-oiWRKZ<%b!$8)lOlk>oKj1EGHoll;=SlUQOa2_~* zF+%R=j(Q2&mj%YmU3uLbG^Y)g z8Cw=|p64@=Pa*O-@|0BJ>g_B)-&KD8;QQ$v-4jnxzmKr1qkEhPS9f-gK`87nT-0{3 z*m>yBOTeR7>UP2Nqyei49(l@*h|V$-ABta&(RF^?Sp3f3k`4d=I<_KA)% zGqOL?enR17?Qbiczd8681D3VFeUI(=jxr{eF*Pn_mQdOeNGkx4o@;;J!fA;y;13MI z^QsZp0RVMB>&j5ghw05h2PxLNn7l--muh#wH1Ll5Q@IJF?T`|DeF&A*0?@-5u-vp` z`&nyS5~X$5?nl~Sqe#hP>fJ%_#Ta#SMxQOBj2-paL7yN*VNg5%U1GILkK%n%BDf`r znBb+TM3h6K$n(<_E^RPJi*|$GyIPz}iyJ3+zN|(6l`1JXm_hn;E$$sH&Ll|h)1qm? zoJeJmc587@=y8H{n-+a9qPbUL*&oy!MKoeagEB$8Ttr5j)*QSJE3Z2b^)2K@QTsPo z04zobY9z0vwPnE~E(QxUJ>da;`>C%5^Etb_zz!vaLHic?Qjq~xc6nOM2&I5DOIy|poak%p^@@+qr-g%;{{2=C;<MIe?rUtNs)bTRQ3;0_CMm;KgzQ|z_WisWdEd=eJ^GIm3}?@3_W{ODEr%Z_TS*y zf178256}KyE&Csc?0*=Q{cV)}H+c5n=GouFv%gnl{{t=iA5!+i{;X$z7Y?7ICi9#U z*e;BJ$JeO!C;RW<671#@bZ`l7)+9hnM)tooQUacmgxJA_*v*CL;6mIigy`0UxRr!R zKcfqgstaKbEDWBsOVVfE!6WDlcJ~ITrtRHxEuXh-r^!04K_CkU+J!O=u_|~wHVS|V zjsQj95x&3#l~^FN%Yzc{<>V^ns-KCOU*CN?i6t-tOnCL9-3vaSaUcWq{LTX$(Y4$m zKg)X^YPMI3*+O5X&cGUo3r?k6FVkaG=N=Caqj3dqOq4obeS(%)yArS&QJQwXv($Bd z?^bjBd*{cW8-Ez9Rd1h1?;kXE9{lM0WKc>4Ilok@6s%inlfb^!eDcZXf!l-fx!jG& zo6iHCyC(xBWWwNrFO`YB(AlG5m!G9!J@1_oxjiFDX|@wZEvCPeAXy~9tdXXlJljev z(I8tanjj;J`gG^}l@O%^JxrKtV+<-1I7Af)V(qUY235rcR2zuu@yHxEfU25S&3Kla z*)=i9u8AO99>k*iRS{%Ji6ywJBFMO!Ya_^z^7)eOy$qvfwX8Gvsi_Tfj7CzLWC#+blTW`(nJ@{*>C;ExpA@#%sz~U52rJ6`IbqSFT%s^;PXp ztDn7GFV#!^C-E!y{}I2Y{^R&n`hSaGbN`F@W&MZoYw7RBueJY)dbR%mLbm>g>*M+# zthe{yU+?I@uRgwie|Be=|Z!{Wst@xqkk@82zW$anh3c2;zc04 z&nyBX`{?Zia2eHiMg(&D`b1!K-w6>I)AyPPjP0Z08HmUAJtG3+`+gw;6Z(E40u%cl z5P?a3-xYz}zHf>^Uf_ia<$Uq6p0FQ$=7_-vB0EP-k}E#|TWF>*_7R zJrDO{+$(Ue!Mz^$2He%S>v1>Z_T%1)`)jy&;NFeF@%KNK*83+dE#ImiA-3udw)dLLf?dKY{}fwQu+_DH;E3l( zP`>Gi=g}f)3GEM%_CJN%KSS4t$1fwb{}RLh@4Ri)V4P8d=TI10hk$oy3PUIiPk$s( zus*sgZhe&EKc(t0k^d3TaZ&-R@vXJK1}tec0D__?Nzr!^|DGu3emXxkoY8R_vw#b^=BI6zxpOuH2D3$;>rE?-s2SG>i=9{oXkg&u{)+cKn)?T7p`3j+gp$H2Z3*uKSY+p+^`O}6c?JB-f{WiL_OyD3Ibtt$#|J#G^3ZYT zfp*FOlM2V-_K^rleXm155^F>7CQLyf7Uq;Yw!bzZU_IPEoP+`JfDT9iE(s)oO*Uua zyuG)B2b~9QBVr9P{i#4AGJVZ?V7F3&;_JXRyE7E=Up#_9EVOl$B7e%yxqF@nUEfcD3gex}_OUId>Ov+8CJ{1&+sNHN3mYdq)ltxerZ^3H(kPt90E7M4`-r()69gPOxI`KZMu&GR zqadQxS1yx&@5Nz~G!C6dpGUx|b9XWiy|&D`+lEj$DKvkt<9420=a1Dd&YyhFunPd; zX%*{4`}Q}|14Cejl>xB;dm?xjpIy6vbUwNLJ?z=WTy>3J$;1+CY(j1tuKpF5mj!>% z?c=@}aU@qP-Qg{#i_r?3gXg(-MWq-AXIK^(31v+FteBMB2q^r3(~ZhJ%}XE1H7t-Dmq`}8^wjW1?ZrKOsw^Re|U$R zhCt`u_BPa?vfyOlq8AFi3^fI{I3imb%Dw<`SP@ts@tlXDr2e5;^2M?TUztaPVErkN zoz?C+FNZN$K?Mduh8Sw80?WRlA%r{UtsI#`43#_tQG@ zI$}B2ua~9iS!pzB^?%bg5bX#Vh}GaviFgDN^PNU4DxL=&R>6f7 z)gKtea0NSw{B)tv%5zTlqr|RqETf;7k3tg~Pr-dW=P&iRTHb^9hWzjjBr*7LhynZx z1)#IZfT0DO<>%mK^7B|Y=`b1Cy@|#22zGx(L9-1PVYg71%6R}j%sFET3qIVcm2}a? z2->V<7pWg_v4vE5loxVD?q8+c-!9OsfefI`X?Vt^p3|+93`!x6Yy%NuDn+lap_M+@ zKLYCUH(c-%Geq0FlwR%At+4qVwvEq8f!}kJc4g%p>N4jXf&~AY*ZihHORLWvx)%6a z>uMT(Rql=3cwA}4M)&HvR==;sy=2Wiw|jX-mCwDU&R^>;DJFv4@Ksb$R2=ueDeEBb z|7i=X+(aB#tz5b03pmr{02NS@sw(R0n*tjHQeaCUM4wq$R6Kdg)M?Xa6y(lSZC}~q zYw>Rj)HO9Dj{JWg z&di=ODrfXHVg*7cnn&qb76#Bs>U%N_@zoyEW4zsxfO_LjOco9H)_{D(%G~SZrNl^Eg=EbuG z?Mn4Chd?E0<^c61Xr@QeG-x!KIZ0Kxe+!xs)a2=WJU1xqX2m5RntOQeGn^gIvl}GO zyfiS7Wu!%(pyV7~!2c)iiq8fHAOgQg&S!D|@Ye$a*gOvBahYK3MpO{@_kcfzXGjL{ zHJuQGx)JR_p7k#e3|wKPSp=HJVVVNadp(bY#O@!Z!RSXo8?Q7)|pNK|^K!3()L&V_*R9HiY@C5@k;KH-NwX zB6!mEJn-9qzr~n-;f=}-=5}VN7OzZ&^D-^unN32}V&H9m7#O(0 zXj_e!65w7gn1R-fdn@R@e;gR7LpZ_)SBLf62b%jpGtWqKZP=%O0sLX$zh}gg9q$a= z@dRiVyg4xNdNj?8hq!KT#8X}VD`@hgXetF8F7G(dn6W&Dw;m$o-EN>M1e7c2hCK_%*Coh$Tnr7T$C}^aKQlW#{D$t7h$UMMYarr z=4Q}Pxrcae5VlN5l#0Rj-I&f!hrL4hE87(+`yAk&`18O3-eU^mR)_s;0q~Dq1W)=@ z0sk=YbB(-p-zZQn(C2p0AH*tTi7`LevDskjpMvIf&`gV>*`(2M{|C(}(8Q`AZlpU8 z{JX&81GKPS@YhA) z+d}wm;0J)eAp(D$$bjMouR#k8T42xu|7I2#pf}p?`at5(TVEE>Pc`_=%Tg`r+c}4R z|1B>XD-T|S78ta^paljkFld263k+Id&;o-N7_`8k1qLlJXn}uM3$*{cDmJLspaljk zFld263k+Id&;o-N7_`8k1qLl}fdvM~{}-5KFk;XG{~uXkPK2&_4PXPlF-9C-&cub+ z_UOWK|79Y?e+Axyu)voA|9z3bfPWM3AqxCoM!I?f_{?6vMEb2Z9baFF@2It`h(_S) z`~ngA^b~w|ttSrQe*umHZ<*@q{VKins9hqxJyFN+oaXAi2f68&!2b!y9*OjBd}AF) zZwG3Zz~|z{M1h~Mz}3s&OpL&9$BS|Tzj2YP_m~KB3X%S`Y#l$f)YY4DfqXaT==hb( zUA_3~Hgt*fFO1glYz4j>H>4+18pH*3Va!~_YGo+%@|9s~pxH`C(aG7R%Q$$IdNcfOBf_>iNuH1_h#J&; z5KCTUtC!@0AtILEYl$=WOPf4Pz&NzJ5*Ry-`CcueC+sj;csIRP&! z(>TgwQX-|X(9d^qTgeRuWXVDW{iq?rOjtD&NtE$vgs_bvA6At~>YI4RZ46XPN^U%b zHFl>F5bUgy?JYrksY;&-*dHM_B?%M;F_p$J1ogZfcK~oJt5rI5*?t33Dr*ubEXDb@ z!r{sj`T?l+#i+B&&e=?<4-}5_SUOeBS)ahm#{L*_yOIMg`&z{hJ~LBReOyi%*wxG4-;GQ(H|Ig8nn?H!6vSGK71y`p^;iMVw% z8CFWT1u@FD(ZpF&6Lt}F?dU^*IBy&3K zZsLBElaz7VUqO`8#ernWX?ER=)?Bu_Nj9d?ACOIa9CWhH$rP!~)thuT5b{P@{R(2Q z&&633u#CJf1uT>Q3|2smClyc!Q+`NrrR*S5Q%_S09gHg1$!^j_={eez`X{8n4v}f7 z{|$1IsI0ZA^YUE1bYyYLOQ<|@qnsuXRYPtA;{SkIFCn*(Abxq0#%*QFuZjIuIfLB9 z)Ki%uVeM$3`wJz>Ezt+H7S2kgPqkB>&^ms!9HXw2O ze~>1(a~Y3~cJ=q?+ai^GTs-G@tcTk zAsNdloBM|f`&GG21U{g7@JTNyvf`;Ec3EzM5cJKHyss5P4CB4(uAx-1QL!?(Zo9?_ zHs*}~Bx!fe5q{qwWqpI#wpr$ccsw5B(Uwi&5RbO}2p(;VbRNUazXaU~cL|B(vjlTo zy{HTj$DKjU%Pf~6<{LaF-AU!Uz_J_Ar=wiG)St-v;!$;7T|~Puz8f)0&*`s9S)WqG zSW5!PPI0mgAj|kYWioaeg-J5%2jsDft2t=SsN&cwgrKTxBVos7H*jo&G~7%+JKRjo z@o+P>s@b`eATy24V(+Cx zOrXv5sftOAl@x8PMAh0P#!6aeY!YK7lNc*mK7xr%N}_r@2(2wmTO=hZegVRgwX&%} zQj$3!OX?rUluH?z@JfuO=}R4X-z)gF{fuKq5C%4iyBS#eHp31-D@zcu4QSix7zBB$ z<&|PrFMVPq?<>Y{d@BvgrjDU765EzQQ+aAJ31I3ewO2~=v}7a(aCrjHXSz*ryo%V* z(15k*9cB;*(c8H`rK~1eZbViqkXYWg3&@1|#CuW#H&Ra{rO(xXwuD|vk(WVqrk-Z7 zqZZONfl}mH{#xYfy@m5JqfFw-NOF!_$hbD1%p~XJ3UEz4M9!JUh3P4audV?!PoPTU zHxjf$K#LPfi1}zuf_fpr7)^pIDe?PepxxZwZz6yC0!ZFBiSkDd?y!hmvoruoK8w-2HioD%5>?Iv2IMLzT5zl3jYg-{$B?{ zJ8la#@&l5UzIxIILDmk0)kSDL%p;PC#x@1vTBTNf8$jF|2&dHX4oiybBA}iFW_xRE zfuwBWfExD}VKyiH8A%+HY6S;nPWpa8Y&3p8$IY`&lWe~QrG?E_$H1xLz6*fO8A)y> z#ht?sn>(r)_AyE7YlMnVp`^B3z)xjERVz?&*ApU(5A&qBNx-mS95BVrBETI_O`_S+ zMr7F>kTD-}93gn5fMtcnS2Z_CEJwg5nK`m3@n{V%F$aCv#m4CAE%6m?pdYKJw>YjQ ze&aOQWO2|zsBF9j%N7Uqmu!LtOBP2r!4n0nVhoz_AtEH1O;$&dRy5dTQ-)I*fg%`( z?7)pS0u_%mceQ~=wvDDR#&#Pp<{~4?H^noBjs#136ddIqi|$AAKG#zebE`Jo@JkY% zZ(^1OanQj3uTjU?heWd zN7x;dl{qw1hC6JAph{Xw52bkoX;ftbPAf*5gT!o-mL`d)ayekKe@2*mJGq0*6tjH- zAqxboNM?I6!G#*knEhu&UnF4J%7$7NmnUO z>+=bawf50mt4>4F*r|OqYwe?*+DEh2KE_e|Xx7?CJGGDI%?T%vL~9?-bJ8gzn-wNY zH!o4|0V>W{u~jlJ)mmHMMgYr1Ya8dQ1W>BAw!SI=%Q@hP^KERF%quwHiu1JsSjmAD z$#y?@yUf?9l&zhnCT1^3A)`9bZQy`8uCi4!SMacEKZ(LOi+Qs{Ob+Oe+;9`6Nfy5F z1#jW-Y{?<&vLz*fh(uksELW$3!cI-QMXSqpYT7L;bCQ)%UACm9B!EoRWy=KhJxW7$ z*`n2DJJn^2R+rbvd5uvL#%XarzVm*c4} zTf%kOPIcL$*JY=u%a*w+9ZSX61uXNDC=4Pxc#ZVZyvSuHP52tk1jHtCN0cQqjl%gF zjdgq)A#8jl#kyKO1AwojSl1*_SgfR2%M(l(A&8X}t8pd8YFtUN8dp-R#+4MSaV5oS zTuHGSS5mCTl@zOSCB03Zd6U-}+T?$`AxO5bmM(O$4^-}uB8a5EZ&epKs12%ApNPAldJ72?g z0ecqMt>FAf2)jtbp1n>=-*zd41omZMGiYH@UXk!iz)H`VMk)P$U}-hFpRgFjt{p<{ zBXHMAL+6&bdg%bRlSKnHmK01(rZ)bJW6?w2ChkYoTzWm zz_Ot~0oOW|at3;qDs9P@x`51nN>*&K!xHlWqZ4|8Ql?n`iY*5^h(X?W56qlVQHXV6 zKB~g9NJa%#*;BPel13?Y4kbyjJd7kuIFo0nWa?VJg4i_CjWtM_n~5sULVXr}eUCNZ zg?N~xZHPC^w1q+26eZXRpe24RtovG80?mF3wfg*l<{0bzln6W|iI!N*((RlRU+2Fc zgL}I4osXx$_R9^pzPN|f7W*wUaqYmZHA`l432nr0-ixq4;wPHWi2pqRUvk9%5t4*Q z{ND$}SCkZ#5YO5$;-84Pm?M5Vu`)d3r=+0~KP|TZ`;GW%d0!v#Qy500#*6I0jYcDW z(p>e!TP&8u(V$RYffHG*BK#u7t0L^f7|UW4;TtF%$HSJJz<_SfaW=*QeB5nKNw^k~ zqOZ0tS9<~QLa`dh-PV;k-$AX^#@*Jmly6cRKJK|gZgS~xUc3jHEX!9c2Hk!4foZ2+-=qS>IBhOTj#2K zNlKblS?48D7?ybvT995^rEr=_6W&+K)u+AFbBv#I9B z^L-PsJwyGJNR1wmYL~HxB-qeHie68>L8UbW!RtmRm6H@l>u9Rh>)B~PMb&z}I5%ij zt=EgA4FEON>y1W{V_(5#Tc}9wp#j`IZT(`%FDSVr`twDhv% zxRdbObQ|(ZwmFC}+lo{YG_HxT#iP0)rsSARz+^F}mmwM*erKC1U$Mkv8w*gqRc@ZHbi#9JJK*s6?EKrMi z(H{U>jq@U_ab9FG&Wo(tyr>UJ!t)}^$VL|UEABWNL8BK*rEUQ?hX*7(;j|9TowvVKBjxg1ceuM&{Q z0kd@jk>zuMSsy2$AfA-ASpS=V!g!vIWFt8q!mY^|KNMlv>R`&8RjyvH!3#QehGhRa z!U<+DklhZ72ia;I4L^)x#*U(;9ZdWmF6eb$K{4YUtB@m8#ywKd>$;O-zQri!&@xvq zt)$5NmcknoTQMH`RuTnGJ%BJC`c{^J%t`RKyR4LkhQ7&Wk~ZZza`Ia()!=e9=aM>) zcz0XKTBV2;ORQ;q6x(S@Ma(o#N85^td<5C)*0Pg^Od_gXme-eJyu_)}-k_KQ%QqKc z+zH>6_f5gC^HsRAT$D(1n|hWf@wMY(O|&&heC@b6Lr7Yd{yx&uLEFwhldP9;%bR*u zI^QO6NgSzR>hY#GE^+l*5q9zw@j9nSz7|gx8pWy$CYq9ZliWw3Af`mYR8puJ8q+PTnV@1HfqO} za64|Jc3cU!<2Gu?6+=6&@^)M)Qb|w?wc|?hs2`HdHfqO}a64|Jc3cU!<2Gu?m2f*A zNA0*0ZpUrZjw|7I+(zxVVra+hG-f)4+rcNj~79t3phFFvtkB#hh>n+|l;b{N@4+YZxS z9g5rzW3N!Yqb;w(E@Fzb4=F4k@YvKzl)A@nIm^Ku0X9q4zY?4oNAM4kFc(j;HzpEh zrVGj{HAsY6`mPgRE^3tIL>o1**CE|Ro{nDB5$O`O47GGgT6?esqlkbX0~`zKE~FF* zY{?=cO+s5Xai;Z7{DB!V-+zV8mw_N%`g>tFh0PabvIU?r7vtv(w-G-l&D>Vn?oM8b z9X>qeeFRyl%w%^eh2c{wW|jcQ{*8lX6$Wq@i!p#|OCr+j1nSsKJq@;51ddE`av(elW$QGnV39ysx}01Ur~B!Ko{2wya3pG(K6O z4hiddL}usKS_qPrJYyTmNLb50>>Of3*0N8hnIaas$y)XW5!P~O)@l~kiVtO74Zah! ztP{dnCr+cR%{*%=`?)-86&rWv6upR0K;}$dQ$&{L(OQ;#5HB-5OUDQ>7^&qU=5Aa) z_%!|{$u5hcj#iTyWY3cloEPNj2s_JEJ565+}<_ zf}IKVh&V3Zpu%(r2QLl_O%V>B#2!u(&-s%c5v0h_DfDw0^Cg2NPL>uW3kC!xg{;qJ z*vDopNe)?oBti*u0f1YQOynsVyHG@`ZvQl3sHj4iAp%2e(nFcR6E4UEQ<<=ql*v%8 z6atwVPwqd2$%33@Gb zMm9VV4b$uls@+lU6*2{PuIP%89BtQ>i@MkaZB)qXV^;5roA~dw^YD*<)Wi|Kc40d& z%8Q&WhUuxT52X*6h%?&sF5UErdM}g|Sq3p{rB>JVmOf;w)Qi}cN+&J4eHjv6T1Dw_Y@21NKmxC})xcgTj3L-GZV5>GN+tV&RkrAF7E zG`$ptM3+xGwY2|AOU)%Vw7!@)gdLa!rCu?@nz5>p-rmRQ?L7u|B$pTz8LSZ>>E#i9 zooH#$7KQVprYEY`i|W}Dqum?zD69abPu8niiqVA)-CU|MK0E~C4Sm`d)T|+q=?omT z8il2ySS0^q4-&)2sL&$H$Z9mA7}BcIC2cb%3O7{|-7k;k{%Y5Y*r7LAe3VXwj6yUH zTmN1GM)Vz2d?CrHw30-XifaD!Lf3{~7ppuMsR$Pz6o#h-F{e6OdrMQD2qCSJUZG2; zkw;Vz(#gijwQeE8!Kk*!MPftd`O?Z<*nEDWm>161qN@_k+amPVMz|#Pg=1X3jnZdt z(|8LI9&FJ_KWZ3ZFdUFq<5ut|W-zjsKSM_<<063m$NKsWILz!!^wFC#TV9u+pI@*n zJAYkv_PXr+k)yMh&6zoK&X#3}9kpzxNd~(gfR!R$PCTPCApX^L4ZhZG_?tAVtt&hID!yhK7Yu%KG`8sy&{Z;;L&Az-^?E65LVGxJD^-5)SbsckHX-sxA zJ3Y=Yc^S)U$5O31cPdOy*LTA;1a|WYyXOVW&Eh zCD$_MXEx9eS;uUCti_qkNv4+g=z=8_QKU6Jc zBLXa=jb#OTdf3cc*@$X3ERPLu>$#O>tz{X3{Y*AZPG(t~Va7d3wvJt*KFj2FEZvKk zb!?)#{+@dn+pn%y?_n|`#wu~u%!Yuvmf4mu`D$jH2uN1fvvNX_N64np$!vuZw<`?I zP+Ysf{vBp3Wvc3DnWfD20#i#Uz+PlYDtyV$rb7N#;d5>_rX8M?$7YqXQe;%OmaW2L zXdX+Pt=6znkYy!`$)s$#8!sqvPjM*mcQctHm$7`53NmYFP~Upd%%-hl@vnl+&02`X zF1CaoY7$$>pKdm(ou!qqk{Y%YdDpCEWq73J&1OjfmX^=l%UIH3=DdTA+Rf5lV8sD8 z7x-(|vPF1g<*}rhY<52Ll(58V=2r7r(sPU0h&wd7YYlQ|3As_B3Y0jK`y*X$SLyBC zMC-^z2{6$xKZ^^n?0lFgj}2MI;%iu3frpJ(Y%u(*Y^s{ga!W{h%ToNB{P;1iWkvL` z07p)2hA8>s$$yuLUsf0aP0?a`iV}v`(2%eI@5ka|~w^oyQH6)(0 zmKEo*WVMj;PKMF_ERmS!v$^!pazr^3RLywC18fv!wUnD08B?Qz2m30j$`BMJchq)P zs6Y#cX;gB!#2LFyuML0KK}*^rU6YNO#cNICjVwd0W5buRL_eFDj~w&Zh-EBe8Ml#X zA{Q0HC2wZ#T9&np4ZnjZvq^z`Hk}@?Xf(I61>lr*oaOd#Hp4qEM#)l1$2-_?dLRw= zg&MY;z~>nE%GU(byV-&^miQXWQft}x6;uhwqtl3o?Q`)gg2^Y5ldAdZGQfrWcM?MR zFkS(tT#ir*%qSXxA72mer)-uohjL8Pn*n^qC(A+Jh0;D@>@KvWVzUpsw$M$x+b8W!ZqGLc`TZ~4VK9EUg*`|@nsLpVQ8kPxfW4q8n^1xEGqchn! z^pj992`C#%4pdGA9aQCK!w*~d!<6G>_~=5uhfUZI+a)tOz$O&vp;ZMe(=;QQW&7D! z^b3BLkjF|&*`hOSN;R|RF;_bqx|X@%e8{qf4Zjug%w+aD<_J)~LESDqfjWNW9?>x_ z7AAg>$v3dsrSOHHv*dSJx}S}C0#$Jxvj><1zTE={Oks`($@#{k=}m#}zsnXcW$_PE zRgX92B(X8*WC?{F<4T!RX-#rxps@|9|;{4q){^gg2)BW?0++t^gmQY9ctqV>U9C{FsaMCq2X z*8x=)_ppL$wzvkrH7FK*8Y#=ixJ2}ZVodJ~<;7(SLD8;O7hBMJY`-JJD^#IS*W@h-r#KrH2m%=P#f3r$c7%PwIPQPDw?2a@75AX&jlZ0i9R5UaTOw@$PA9JDd0_OMI0rc?aXmB$kL9!F)7uE8ww{ z@m!2&9-hnaEacCb5zpl)%3{Rp&&7a=z7)?Q1APIsMMl(XP%e}4oB@`_xTk9U%XHqq z6azC2L(8e0av8fEW-}?3dHO_ThLU|ZTXZ+8Sj(2*&64uir8C)*yIIv*wvvOkSx8LX zo^mHRSk<;bL3V*@JeuW^Yo<`qjtEgqz#ajlxEgwC#i)swu`1)(rNP~{V zL~m0o@p3j@Ial(>sQKASvPqU}v};+-GP-JN@V_T?1(XbvO;+Me`%N}5`Q*H{XD6V(c zt-F%NKTpG^A^XX-GV^I%#=8a#%~5z5iP0cdQGH_YwUmvaBK^8vprh)UO>vs?Wm8Eq z{$oZlN*Q4)k(teuKh#u`f{3xWhXt4|z(y!ZghKuqrjk^2c3>sHU^2mM^pdh7?Y@W! zaRQF$Ke#P*Ai9Cq<6~I%%e8~FZ_G^e7MC(n#zZ8Em7?CxVoMNO;b$4MFe7ZEe%rc~ zjjm=HOR4#@VB}ZI(tpV?@yJ2**v5aiu)=`vUzYJu z$q>6(DVg?emU0h^yPG-Mc}FAP%?Fe+#vbi#Tn&q_#=I?=9=YmndW^#u2agQ(M{JCr zP4A)VmHZsD*R$k%*f?T*H=BMB#b$7dtXrA$BrzLJxN4SGgK1DIn}*Q?jT&;+0`y}I zVi{XRuzf8XkLd!UmMH;hvK)CBgYlFTk+Pcil#Y9t9djtfi@}gGCJ&+YPcTK9=tfV+ zM&8O4$T$KMNer$=*8{?Y7vg5aAi}n(1w^v$9W*;K$S|6=A_$MM=QH&|nj%@+m}4PJ zeURB6WR5bH@+xz^ip3M|JH7B4MY`zlKvV?oIU*SCH(E`(aAha53N^te=nX4nh>S7! zI+{|=uVo8gV?G2h5jD+0GxZuP*^Q=iJxi!&P{Gy1Hn#Ek&@y#0o4$*=(7ciLuB3L1 z_nqx{t6P?LavkVJnSZDWX>Mxq*EPj(bbL$+XrxsD0&GALHT>{!A*4of53*ogzw$$%3tREQZ z9~97oIz81mD67#sFyfxc9T=tlC9eG1rp>dn^{Ov_)nI=`;EsHnIgm;R)C zZd2>Fx=oulNpsiC^)6U-nRiP?onKnCqTD^daGHBY9?lGN=WZ@8!jC`D=$l#NYxK3$ zRRXMNsjQt@H+A|{cW#Y4x4OE;=WF#>G*+P(0oYm#m&@H!SzFNpsIe(`W1z0VU)LxA z5LRx=ZEk7u`zrlSL{ZVYt+BGUrKzzg(3%@)+)~$AmFutAh^1Cy#>p!iDq3sxM0HKX zsdgtOP${*;##?u8w0J|gqnh|vMGRL$^z(3 zr+e{iY^|$l^i{b@bg2bvxq4cnC@untzoMna=a;yO?hrk&oJrwSkVGyN=W9#BA_WmH zoV%vDXhB^ISAUzQ(ciL7!UtVVE#AteDxXx@5WoU60VJ9rZmDbYwXE`eB>*!^V)E>5 zt-IEzhXrkmMi3={zQD=#(YUut)ve%A(^%mTw17<$4$IIp_BVMOHdaV=jdlLIiU#E3 ztqv`FdpR?yqOua^^;T8*eNuBpi?7k|t*V2_LUgGz(9+`NB^Y8#?`abWkM$B1Xsq%z z_`skNf}xcBb(_P=Z?0&LveO!J2&vJxMPnRdW;D@iKS`u3(tra&xG12NHaAt(RoD5d zw0uOKLYw(Q?0I5QB1Fiw^KEYSZzHXBZ1s;DbTDFc^dgBTN_7>*l?y(TXGq(YbUx9|c zQ5cF!kIF>w)NEGQDtLHXQSmBjLS?5LOp{s%YwK!ir9dluvBK|fsoNNUZOIZMYjP*a zFj#_eFjAnUwF!EM7h}Al4&po+G!796)FU`~!v%5lE`C#WHP}g8YJKodBR8Jt!c1B# zTbdeh>|vuc|3YMCgAdg?dUeUb75$O{5)w;7XQ{QhqSA-L-O7c9Zp|&eHm@Iz3)NOA zUE`fPc_Z3Q;x86BXurqHqkJ`+scw)X;=A|)X=6pJ&&$nP<*W8KHH%7I(^AnGXsAG2 zwT+fYymgIe^q>u8LMf!irbe%}?X+mYnpKjQsBpKS5}|ymye+<(I%+heMGZ|GD;gH~ zV2`?H6tZvzaMV5-C7znULUJw~^l62VLN=0g&Fa-2?$KP@&2?3hK3ev+R#r4}wF|vy zB`R7g>*~BzRdrqsb(>*{8ZLylDkO#GF{?$6Vd=QGWWiRn1k@QwOBSqIL{+$kT7=>v z%@4Q%!#Z5R7^zlCb*(M%>%c}WGuQ;4+vcl6)ghC@V8mFHR6?Iv)j*!D^%UL~Dlu=N zqy>ufK(VnQ1S@HYZp*M9x|Jy-Div~tkhuA@R8lFmp(#aoLXHBz4`GE=!rg^ukf?3K z8qH|;QMXZ1!7;jRp(wNplCQd%|#~9(dLuXsFGjvzFC52qUUXp>Y7B)%45BB{uqfU8jC24=q7|+ z>Kdz?!Zuw^y}iT*(_0};SQ4ehN6zMLsB45Ex-e~ZE&c#{ekw`0c9lf+%UH5vXs(k3 z@`W2lsj{N7mf%+EUU>0n-b-{+8GaV%ji{&;S|{smt@S}?9okgU*i#IGP^}G#jOg2EUgCK+7k=A(}zN2W)!lBvtxb8bTC$thchF zxeo2B7lRJ8hOLHHjgK0rz3_)?j~Hd4RWU@-*phlN>gwlnu_~Hd(T73DP_2V@sDyfL zl$L3$5exVbZZ*#dePQM1=IA6#1TVof8oxPgij$`K8pMBQR2LKD5U zwyrvCs1UZUu{q%PHZ@9tW*TB}PYhQGt#jZ$VG!9^$zXpz)$JtSvfo7P;2>md!_j|+M72!TydC(%CeDQ$d_{gSks=(+|Ta;a( z0d53Xf>Jbi1qo)Ho15Bvyg4C&j9%61hk1DF(3}(zg*1{b;zZ5c=s}|oG+IQvX#V_5 z+~dkOVmKZUv!dL>0EY^%D=Nq$D#{wgV004Kf1n! zZ)WI;1ET05eJ2Ftg$KIIbUePBz_kOPY|>M~kzV}Tpu_k)1DAP+4&zHMTy#(uJrxOG zv(eS9!+4d?a4Gm6)_5rrzECk-3O|`T9HB_gFo2MPZ^Dh2A|)C@;PE-5;lg*+28hz9 z=oaJgIC*g(RZ#Sb$A<&sdB80|;4ekU^zcLUK_#va2NsKf_WR+$3WPpix$2~d_;7%} z0djxS)k`HKOLnP>5L)?wlhg(<#{lmEJlg<&3-C;>{7B;O0oE^)m%f_QPj)r||2Sa% z>}P_X)`{^X`ukC&bbzjeSHei17o+gMi-J!OOyvy)a2BL=E(%X)*XbAWkJDU`;s_oE zICN47nAK z0X_w|-T>1(RLurB6|mm`(?^S24e)HhUo*gT{^AY;TnBi!0lp6KO$PWjz}*J;e!#aI zU^;8)E(82q!21m_oy7D*1AHFvqXsw;RsQD&n2vdP+5ppeMK2lPGQh7IU?1Q&4e+&q zg9iBPfIl?A_W}OY0RIfIJWter@?Sa+InDt832?FjJ_k6<0LP<=8f}0_0?sqQ(*RF1 zz@>oa7+^XTVu=B61H8%r({T(}8sPnas|@gCfHxc9=K;4F;5PwZZ-CDN?l8dkKu78^ zz{3IGWq^wT-)n#u1Afo|(-Dl18Q>PcPa5DG0Ut5I`v4y`z>ffa-2fj3{I&u9J>Y%= zd>Zgs1I*A~{LKIl1#FogE>Aj+*lBi}mP;6}juMeW0uD0mlOIuL-qa;NsG z2QVE60JC!Vt|ZUn_5KUz-bsG|te+sB1b;mVSpTe* zU^>@P|6-Tm4*=_@ViEiaVEr^!g6-60cDs5Jjo!&FrSdLR*v|!xrU9M={eVVqW|yu2 ztbZ6v>8}B-A4ouO3t;`MCxWj9tbYtl@O6OoKSDt8O@Q_OJqd4A><7G9>rY(3KLS`k zT$<>60qdWJ6Z{*%`X|u@zXn+U(wSg72}>Ve5WjPP^-~oIPe=dhXA%&c4Ol+|n&1M! z`pL@#djQk;LD(x!dL2Uw{Wx;MuLPb>ibPk$?OBhf{-rtLZvZ^npx?IvXO9&Jd=q{@ zVEwN{5d08e{bV$Pp9ZXtkCLz%@;ku#7s7;p3$T7<1i>Ez)=xzy*a3NIuZ{)?1m^&z z5AkWJk_e&a0oK31CHxY=`eCL7uLG=~(o1kNrPsze^&Ho>O>>&Iyj{0U(FOc;XAG-NA-yy!;~aTsDEVEyxd!WRP8PZ1+{2GP63 zI1IZe(j370@nwWx3RpivnBWS)`oY}nMHSpUWV*3Z);{CvQ4vI$Jj z^Q!`^pUO=5t$_7|MF`#lSU-K5;BNrdj|w69A;9|SrUdhOf;OuT{YjN&o>eP6rQXYz ztXfk(x70gt?rM*u#jRd5cm6W(>Lu$v((<{Bmdy9AC|^F$vr1Z6I(N}(Y5ro*{AH`l zmt*S2|40j;2xI4hUKtnjuI45z&*(AQoGS{Bp<>40-i+qfkmc##?|XnZ5?8ue}w< zh%1T_M@w+gX(z{sn-U{#YK*vPG2*7jh?@~3t}vz|u{0@+rAlEeT?%6 zU+7xJu{0`&Jfhr%|a`Wq17H7P!2NN>|Q9SG98C!quKN-ZgXQ zm3q8b7USEm`YIXDywO+k4P6DAcCkxGI~!tmBWgLEqfrR$eM9X~d5A*5##U&hR;;Usc5-9xx`ub_!fnk3TUcMT z7>lhI3=p(8CiE3?zWo~Ue#1rfaAH^v$9ikap)r@Yjm00{Sk;|UGeXD&w1ABD*^BJ2 zXiKTl+nUBKxQB^d+bGA0SPRyC%a}4^YxxV)lGk03k{0*H{=U8+9$J|f<1;#2H}XY^ zzqjifv%p{S~!Pt5K1=gCX`I5u^5wjg}!-pzZ&N?H(h6 z*vgN+RYPKgq!s(BQeNw}&Dep&)88VVwR&hXb`0w1tqdUfAR@wV zBfT;bg83GyPP%a;-apVOu@mak!`w_dfIRdd^p)}|n>N#CKc->-@I`ZEOsivXh+@|}y&)oXNI51Kn~z?ui9cKYSk0{Ci|^CLRiQ`u38wLV%u(Jozv zuK}ifIg1V8*9`gG8WBA-MRp;m)0wemV(!La2L>|YM|YQjj@DLOpyM{uX!U%*4H(NA zBHwOTZyM+(WxEk0F1PmFkEd=UiXyuGh6p|Q<0!fVI9M=;5@?rB_bb49m{7k1-HdS> zkpcIDAxzlhCnV`04hz#SoyY5l(8Gi~_AGo%+t?vk$Gu|+Q~dEiMB0yRLDw1FdK!XH zg3e6=?b2~{OdN#^w0JYltMeGPvV`o2L3h+nC&gk7 s8aI`j3pbW*ynOOG^e(n{0F-dDr%heG|X9_d0C~ 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 literal 7896 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 diff --git a/bin/kernel_loader.o b/bin/kernel_loader.o index c4b170afcd1351b675c0c0a124b16f25bc6ce23f..e5c74d3d596132dc91a1024847517bd90c314655 100644 GIT binary patch delta 754 zcmZuvu}Z^G6n!trYhSErYNQA*g(5m>sO=&y6?D#`n}|*h4i#6S^#g>)yGvYL91G4J z#mP^|?BpkSZj$GFgBMPE&pqdz`x2VR#oDYdSF_P}mab5wg6t-rNMS1qgy>o-jMm2t z(qAPY8( z`9xhD_-zP&O~?Yh=gh|qA!ZQUn8g5Jo-g|WxOx29Iwl0#sp-E!LcmlPWCw&pIDiBB zZ3@8kr64?KAA3q4o%?h8X>MuUiYubqFs4)w^l&#B3OV(%(K_W?$tJz(p49ZI52Qyb ryr*{1qwC;__Jcuo)WP30-K#>Xr(bTf6JIP@g3cE$x==6kq_L2{OcXG~ delta 783 zcmZuvu}T9$5S`8K-bK-PCK7~L1XQ$9LyQQbMFcyAYsAXJ#>&E4gj{WP=Yp*Vg0@2N z1JYXh1E#j}6MVDWa9j9bZr;qidy~!Wv{|&WzB2i6BTpJO4RwA~i~_0%qs4a74; diff --git a/bin/keyboard.o b/bin/keyboard.o index 583e4d0b5e1c8a57ef78ab98e9e8d8fd83ef0feb..63d2763a4b8b43a5717ff55b8ce0d2542ee9ad7d 100644 GIT binary patch literal 8760 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 literal 8324 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 Tn(ET<+;SHD{zeG_D!KmwXmv5R diff --git a/bin/paging.o b/bin/paging.o index dde8c288cd78dd76627937910e83f84d64da2d8d..a2e827c42e75ad19bf231678378c093d6f434879 100644 GIT binary patch literal 12936 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 literal 12996 zcmeHNTWlQF8UANx$KK7_>su~%6UgS)1n7<($4#7&kQ+`&<2EV5p-oCxv+LR2sn@&i z?5tx0Erg{ssZ%v+C8|m(LWovS^`RFU^3X~|4sG(l15Z&Tgg`)VP%1>KB9Yep{xfHG zCxo^6#oWbY}&QQMxq*mmY}+nEz5HP*gLvC9Z1>Tk8o zRa-=xwe#&KziYjB_^bEK%6yQ{e4#oz%TZ@))J}AK=*rP=Tp?7e&1aG7+?yNDLP@m? zO6`JLyAY{eco13D-iqv>t48niL(dFd(TTRvaS=p#%-nPJV!f$Aw}+y~XWNupPaeK! zmIH?+L?A>UL?A>UL?A>UL?A>UL?A>UL?A>UMBo#EK#KN_ZX_4laAy)5WQCIivEK-}ZEczF}x)umlh zHdwu2N*FY~0g?V2yzs|-yS6L(W7L`^VWQo!^j9E6&5NMb=H2iB>ip3GDca)b2B>16 zWyO-#y)09jDYlp0Zj^iz#kRA4spNHvy~+Bdeh}Iuv!=?*ty>7+RkWuWX%nT$3QCB&&`FtUoaK}RH_HXRnq{G}WM5_})g{sO8>f7AQW-pHn>Z-z<7Z9;kj*VR)*osq6b zSdxquVZ+~bj0sz-ZksfuZQFaSS)N!0qMj0m&WZkqvBPet1vX5*fEI8r*4Rf(>+mROwVP&%91F&OGX(h{5Ex5hWd_jCb)l@yQP z1`8k~tC&D~m!c)&`Y}cKEEQNbwFVpq@x;TLZ>#ez=pcx${R&aU&W z6SuNBt|)uDxUH6Dw5O{VACUqt-R$JEr>Lm;}9$+Tk{PMQ|u0MxIqp#qtDDNj) z@fIHu@mN)yZ104bw@Z0yRC^7hw`gZl1_eT%uiJTtYqPL&YGn0BO+Ntl2 z4(Njq>Fs%ZkfcVG!}=|UbUnIES88em8Sgjq*6p@qW!0L!LPuGiS_ZLQz zJJ)VVZZrmrzGP~AT|ZuKwPbC{TP4e}vtUgpTi9Z+A6%bI<&&vg&ateDYnDcV1K3KT z?6|2T*@EeSE0t53s$F#LlE=U>JC>So%C1NTLbEbm$`+h*sa&n3s-+`#X*A`UnW9zU zV8T3GG%E$)$u4uKY%ai4!Oj;fCvA?7I##7Zcv;S*H43~<$EE7TsOeg1;BBQxeH79b z!$8=m2h%Q@*(@Bb(R9XkJy;xj(sB;CaHL7wajRxggr_5pjaY0}EmiD%$r?>^S4ng` zS(otvAbLUOq-htqKV%k*vy>YQEs*Df{U4U-T;tV9+sc3aO;WET?374L1Oy35K!cH7ru) zPWKkd<5q9MEZMp7d;2!@?mN(H=lc8C^`-jz`q!n(m1%oyY)qoT`Bd>*yB-jW<}tX| z6k#~sbRC!UL}9vOXMKR#VO>8Q_6H|zaDjPQV?|x-7#t1cD{!pX_gd+J^mvA{X12h? zU*Vw;xD?Ar0&|9T@7%d5xn?9&ExA>1`lr@b1-JH_{e4E?fU))_Pwwp$Em*AB)Of}4 zYJQG)4?b$<E~x*LTppO2VUY6BY*p5jxdo z;(LQ%82^Xx_~XX*KLF3ZV|bYVHK<=YRp8T@?qUP`=nMFN?R)9;)Zls}Z@KA-Y}zeU zOJjyPMd{tc`*v&}PVd{b>%h=m>ASY?7#>Przv2A9jqF2$XQsGMt$5T0+)rE^782bA z8pDtbOZp{Azb5IVq(?zj+;>hh#nt2qQ1*X{^;mQ-u^bEUZ<3E-*tne-DBJNJOu^LR z*`wgaqs$a0f+8d-Sj5YiV#iKyN&0oDa6G_E5VaoG5;dXdU zFinE0pm#`Gl$3E*;q)X^-dc>x^f-=PP}lKj!LK=1(QvIPmkitnOfoXKCSx-?-q?z9)iDx|&pf|17|KY?C<>S)1$N@M zJToZ1u7G;5zgLm)Q&4h$S?>3jG4Op*rE|-P?hXk2>UTb1*S(S`$quskc8`D$w)-Xbn)Y*se=5iJBR``Pho^|{@ zBdKq$2d~MX O-Ro%gAHET&Z}%U&bV4Ek diff --git a/bin/portio.o b/bin/portio.o index eef471a157cd45a14d6a83c58e7bc86757ac8cef..37c98e6a683cfdf8cfbe7b97565f3228709dce27 100644 GIT binary patch literal 3424 zcmbtWU2GIp6uz^&({BH1%TG(uE~d4B?zG)DrGP*wE_$5#7N?c5kdlqi9D%^Pnu|mR>dbtY^dKibMJObOfc~z_nyD+ zoO{o`bI8qDK=<+2{;N}`G#OF?3oI4p`0C?dJu<_pzatk51 zB(Qwx(eO+hOOMV2gH`>N$N#|6Gog-4F>Fs4T8KsHn=#!*sg4uqoX!0w|s`!?r#;5%1DAqLL4 zS~Il{Uz-x)Ux{d|rhWipJ=NK3oEg^E;seJ*H(***?`wxdm1PU9v1&VvT!?$rW-Acj zMK#LB{HUfn!UAUwi|TyQ6JK_2E-Uri)5ZF?)y*Dyg> zD-lC2M_aXKA;L>tdz;IhRh~1f;+8tAmpWA(>RrP#VBy012s}yMJC$h+9lnj=cn^Qq z*OB2wdD{@$gCmeDVn<`=&Ot=D<)jJ@hBb`k;qL9ryZe$5~_oJhc zoxK|)y}d@C(G!VI_Qf!{Mcdq(F>TYa(omC5I=j^x7#xU1Gm+@{xMP|HH))SqHdQZc z^e|U|X6K`+qLp(k+gAWgPeiAjylbZ2JQ2ylw4Kg6c{^V$M2q%e%N~om$yClPFqsEW z=aPl23bOLH+&QbWY!u~~g<{UHw08idmP+!wYkwE>4=ZTj<{Je@^+%VMNjR0Ar>=w`qxXd z5X0=8h`O}O3r(jl% zrQ|mk{m0-}{i@`b`Io^ri$Pj^HNLK9z7Rzr@#lh)$%FF6DO*r@tw*WwSC(W7K_P!^ zq%|mNZ}X$?lu=MLh-#_xe44OcIie|0Av((NtcT5CwM^o-LRaTQeHgkr59*7z%;uR$ z92p!iGNzlDN+;ZG(Vj4pM?_-x$iCqnBZ+;xc0Cq9m^ip&cqE>{m+pV;{yS!+0c@5P zRo4g+lREni^(UnMwA5dg`fE~0$7DbDezPbzE*PVr%Q5v5&T`| z-IVr^y6&Y8zCm*S|De7}@@q>OOcLFJY({h$4&w;@ecDe6*G4JB?uelVv{k z9-a~Ny^g^co+Ig1%fJ&s;=zXC}g{hvr#xlRCA zPQ;KdV^EgYD_#NaBRV9hyZ#b-dA(Oq+&b7;FMFh_w>%fjq6dxV1~~t+GoH9*QdZv{ aR1v}D@1Y<`?T>G$9CrijexpF(3il6Vuj`5c literal 3436 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 diff --git a/bin/stdmem.o b/bin/stdmem.o index 13909127f944bba225b8b69041c2876c5d64cf63..249b176f9e0f6b24895ae15d85b0f1961e6148f6 100644 GIT binary patch literal 4288 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 delta 13516 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 IDzD*x0OyJ;oB#j- diff --git a/src/user-shell.c b/src/user-shell.c index 5df2dfe..7347009 100644 --- a/src/user-shell.c +++ b/src/user-shell.c @@ -408,6 +408,64 @@ void copy_file(char* name, char* ext, uint32_t size, uint32_t parent_cluster, } } +void copy_folder(char* name, uint32_t parent_cluster, uint32_t dest_cluster) { + struct FAT32DirectoryTable req_table; + struct FAT32DriverRequest request = { + .buf = &req_table, + .ext = "\0\0\0", + .parent_cluster_number = parent_cluster, + .buffer_size = 0, + }; + for (int i = 0; i < 8; i++) { + request.name[i] = name[i]; + } + + int8_t retcode; + syscall_user(1, (uint32_t)&request, (uint32_t)&retcode, 0); + + if (retcode != 0) { + syscall_user(5, (uint32_t) "SHELL ERROR\n", 6, DARK_GREEN); + } + + request.parent_cluster_number = dest_cluster; + syscall_user(2, (uint32_t)&request, (uint32_t)&retcode, 0); + + if (retcode != 0) { + syscall_user(5, (uint32_t) "cp : Fail to copy folder\n", 50, DARK_GREEN); + return; + } + + struct FAT32DirectoryTable new_table; + request.buf = &new_table; + syscall_user(1, (uint32_t)&request, (uint32_t)&retcode, 0); + + for (uint32_t i = 1; i < CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry); + i++) { + if (req_table.table[i].user_attribute == UATTR_NOT_EMPTY) { + char curr_name[9]; + for (int j = 0; j < 8; j++) { + curr_name[j] = req_table.table[i].name[j]; + } + curr_name[8] = '\0'; + + uint32_t curr_cluster = req_table.table[0].cluster_high << 16 | req_table.table[0].cluster_low; + uint32_t new_dest = new_table.table[0].cluster_high << 16 | new_table.table[0].cluster_low; + + if (req_table.table[i].attribute != ATTR_ARCHIVE) { + copy_folder(curr_name, curr_cluster, new_dest); + } else { + char curr_ext[4]; + for (int j = 0; j < 3; j++) { + curr_ext[j] = req_table.table[i].ext[j]; + } + curr_ext[3] = '\0'; + + copy_file(curr_name, curr_ext, req_table.table[i].filesize, curr_cluster, new_dest); + } + } + } +} + void clear_screen() { syscall_user(7, 0, 0, 0); @@ -877,6 +935,11 @@ void execute_cmd(char* input, char* home) { char file_ext[4]; parse_file_cmd(full_name, file_name, file_ext); + uint8_t isFolder = 0; + if(file_ext[0] == '\0') { + isFolder = 1; + } + struct FAT32DirectoryTable parent_table; struct FAT32DriverRequest request = { .buf = &parent_table, @@ -902,21 +965,23 @@ void execute_cmd(char* input, char* home) { } uint32_t filesize; + if(isFolder == 1) { + for (uint32_t i = 1; + i < CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry); i++) { + char curr_name[9]; + for (int j = 0; j < 8; j++) { + curr_name[j] = parent_table.table[i].name[j]; + } + curr_name[8] = '\0'; - for (uint32_t i = 1; - i < CLUSTER_SIZE / sizeof(struct FAT32DirectoryEntry); i++) { - char curr_name[9]; - for (int j = 0; j < 8; j++) { - curr_name[j] = parent_table.table[i].name[j]; - } - curr_name[8] = '\0'; - - if (strcmp(curr_name, file_name) == 0) { - filesize = parent_table.table[i].filesize; - break; + if (strcmp(curr_name, file_name) == 0) { + filesize = parent_table.table[i].filesize; + break; + } } } + uint32_t parent_cluster = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1]; //.... @@ -971,7 +1036,11 @@ void execute_cmd(char* input, char* home) { uint32_t dest_cluster = DIR_NUMBER_STACK[DIR_STACK_LENGTH - 1]; - copy_file(file_name, file_ext, filesize, parent_cluster, dest_cluster); + if (isFolder == 0) { + copy_file(file_name, file_ext, filesize, parent_cluster, dest_cluster); + } else if (isFolder == 1) { + copy_folder(file_name, parent_cluster, dest_cluster); + } // MV // move file to new directory From 5ce186cd56a8e2e644bec3db684eac862c312f78 Mon Sep 17 00:00:00 2001 From: NerbFox <13521043@std.stei.itb.ac.id> Date: Sun, 30 Apr 2023 00:14:16 +0700 Subject: [PATCH 52/52] update readme --- README.md | 22 +++++++++++----------- other/example.png | Bin 0 -> 45390 bytes 2 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 other/example.png diff --git a/README.md b/README.md index 9cbb2fa..7e62db4 100644 --- a/README.md +++ b/README.md @@ -13,26 +13,24 @@ Tugas IF2230 - Sistem Operasi 2023 Tahun 2022/2023 * [How to Run](#How-to-Run) * [Project Status](#project-status) * [Room for Improvement](#room-for-improvement) -* [Division of tasks](#division-of-tasks) - - +* [Authors](#authors) ## General Information -Operating System +Operating System 32 bit yang dibuat menggunakan bahasa assembly dan C. +Program ini dapat menjalankan beberapa perintah seperti ls, cat, rm, +cp, mkdir, dan lain-lain seperti shell pada umumnya. ## Technologies Used - Qemu -- NASM +- Assembly - C - ## Features - Implementasi dari mata kuliah Sistem Operasi ## Screenshots -![Example screenshot](./doc/OS.png) +![Example screenshot](./other/example.png) ## Installation Clone the repo @@ -42,21 +40,23 @@ git clone https://github.com/Sister20/if2230-2023-osyikkk.git ## Usage ```sh +make first make run ``` + ## Project Status -Project is: _not complete_ +Project is: _complete_ ## Room for Improvement Room for improvement: -- speed up the code +- speed up algorithm - add more features -## Division of tasks +## Authors [13521043 Nigel Sahl](https://github.com/NerbFox) [13521087 Razzan Daksana Yoni](https://github.com/razzanYoni) [13521149 Rava Maulana Azzikri](https://github.com/RMA1403) diff --git a/other/example.png b/other/example.png new file mode 100644 index 0000000000000000000000000000000000000000..f3dca062ed25eac026e766c940f76b982a05fe18 GIT binary patch literal 45390 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