summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenji Dial <benji6283@gmail.com>2020-09-19 14:53:29 -0400
committerBenji Dial <benji6283@gmail.com>2020-09-19 14:53:29 -0400
commitde20d7430df08731d9108acb83e1234ba7f1fe16 (patch)
tree8646f3d1bae3d30391df34766e3e58c0c2af8aab
parent20853582d5385d12421433d21910e783caa00764 (diff)
downloadportland-os-de20d7430df08731d9108acb83e1234ba7f1fe16.tar.gz
file manager
-rw-r--r--doc/ints.txt43
-rw-r--r--makefile68
-rw-r--r--src/kernel/drive.c6
-rw-r--r--src/kernel/drive.h6
-rw-r--r--src/kernel/fat.c67
-rw-r--r--src/kernel/idt.c11
-rw-r--r--src/kernel/isrs.asm2
-rw-r--r--src/kernel/kbd.c38
-rw-r--r--src/kernel/main.c21
-rw-r--r--src/kernel/main2.c69
-rw-r--r--src/kernel/vga.c11
-rw-r--r--src/kernel/vga.h1
-rw-r--r--src/user/dirinfo/dirinfo.c2
-rw-r--r--src/user/fileman/fileman.c130
-rw-r--r--src/user/highway/line.c6
-rw-r--r--src/user/highway/main.c2
-rw-r--r--src/user/include/knob/block.h3
-rw-r--r--src/user/include/knob/file.h5
-rw-r--r--src/user/include/knob/quit.h7
-rw-r--r--src/user/include/knob/user.h3
-rw-r--r--src/user/include/pland/pcrt.h16
-rw-r--r--src/user/include/pland/syscall.h182
-rw-r--r--src/user/knob/block.c9
-rw-r--r--src/user/knob/entry.asm23
-rw-r--r--src/user/knob/file.c21
-rw-r--r--src/user/knob/quit.c8
-rw-r--r--src/user/knob/user.c13
-rw-r--r--src/user/runtimes/asm/elf.ld (renamed from src/user/elf.ld)0
-rw-r--r--src/user/runtimes/c/elf.ld22
-rw-r--r--src/user/runtimes/c/entry.asm42
30 files changed, 603 insertions, 234 deletions
diff --git a/doc/ints.txt b/doc/ints.txt
index d8abd6d..45c09b3 100644
--- a/doc/ints.txt
+++ b/doc/ints.txt
@@ -24,19 +24,23 @@ invalid system call numbers change eax to -1, and have no other effect.
table 1:
- function | eax | eax out | ebx | ecx | edx | esi | edi
----------------|-----|---------------|---------------|-------------|-------------|--------|-----
- open file | 0x0 | handle | drive number | path | | |
- close file | 0x1 | | handle | | | |
- file read | 0x2 | read | handle | file offset | count | buffer |
- get file size | 0x3 | size | handle | | | |
- start task | 0x4 | handle | drive number | path | passed sz | |
- log string | 0x5 | | sz string | | | |
- get key | 0x6 | keycode | | | | |
- allocate ram | 0x7 | start pointer | pages | | | |
- memory info | 0x8 | see table 2 | see table 2 | | | |
- wait for task | 0x9 | | handle | | | |
- enumerate dir | 0xa | count | drive number | path | see table 3 | max |
+ function | eax | eax out | ebx | ecx | edx | esi | edi
+----------------|-----|-----------------|----------------|-------------|-------------|--------|-----
+ open file | 0x0 | handle | drive number | path | | |
+ close file | 0x1 | | handle | | | |
+ file read | 0x2 | read | handle | file offset | count | buffer |
+ get file size | 0x3 | size | handle | | | |
+ start task | 0x4 | handle | drive number | path | passed sz | |
+ log string | 0x5 | | sz string | | | |
+ get key | 0x6 | keycode | | | | |
+ allocate ram | 0x7 | start pointer | pages | | | |
+ memory info | 0x8 | see table 2 | see table 2 | | | |
+ wait for task | 0x9 | | handle | | | |
+ enumerate dir | 0xa | count | drive number | path | see table 3 | max |
+ vga print at | 0xb | | row << 8 | col | sz string | | |
+ count of dir | 0xc | number of files | drive number | path | | |
+ clear screen | 0xd | | | | | |
+ set color | 0xe | | VGA color code | | | |
table 2:
@@ -55,12 +59,7 @@ eax -1 indicates unrecognized ebx
table 3:
edx of "enumerate dir" is a pointer to a buffer where an array of "directory info"s can be put.
esi is the maximum number of "directory info"s to be placed in the buffer.
-the "directory info" structure is defined as follows:
- struct {
- bool is_dir;
- char name[100];
- uint32_t size;
- }
-"is_dir" here indicates whether or not the entry is another directory.
-"name" is a null-terminated string, up to 99 characters, indicating the name of the entry.
-"size" is the size of the file this entry refers to, in bytes. \ No newline at end of file
+the "directory info" structure is 128 bytes long. The first 100 bytes are a null-terminated string
+ indicating the name of the file represented by this entry. The next four bytes are a 32-bit integer
+ indicating the size of the file represented in bytes. The next byte's lowest bit is a one if this
+ entry is a directory, and a zero if this entry is not. The remainder of the structure is reserved. \ No newline at end of file
diff --git a/makefile b/makefile
index bb8e2dc..c698f3c 100644
--- a/makefile
+++ b/makefile
@@ -1,5 +1,6 @@
kgccargs = -Wall -Wsuggest-attribute=pure -Wsuggest-attribute=const -m32 -Og -ffreestanding -fno-asynchronous-unwind-tables
ugccargs = ${kgccargs} -Isrc/user/include
+ugppargs = ${kgccargs} -Isrc/user/include/cpp -Isrc/user/include
nasmargs = -f elf32
partlink = -r -m elf_i386
@@ -30,7 +31,7 @@ out/fs/bin/%: obj/%.elf
out/fs: out/fs/bin/init out/fs/bin/meminfo out/fs/bin/highway \
out/fs/bin/hello out/fs/bin/dumptext out/fs/bin/dumphex \
- out/fs/bin/dirinfo
+ out/fs/bin/dirinfo out/fs/bin/fileman
mkdir -p out/fs
cp -r fs-skel/* out/fs/
@@ -42,14 +43,14 @@ obj/kernel/%.kao: src/kernel/%.asm
mkdir -p $(shell dirname $@)
nasm ${nasmargs} $< -o $@
-out/kernel.bin: obj/kernel/drive.ko obj/kernel/fat.ko obj/kernel/ide.ko \
- obj/kernel/idt.ko obj/kernel/log.ko obj/kernel/main.ko \
- obj/kernel/main2.ko obj/kernel/pmap.ko obj/kernel/paging.ko \
- obj/kernel/panic.ko obj/kernel/pci.ko obj/kernel/elf.ko \
- obj/kernel/serial.ko obj/kernel/task.ko obj/kernel/util.ko \
- obj/kernel/vga.ko obj/kernel/isrs.kao obj/kernel/kbd.ko
+out/kernel.bin: obj/kernel/drive.ko obj/kernel/fat.ko obj/kernel/ide.ko \
+ obj/kernel/idt.ko obj/kernel/log.ko obj/kernel/main.ko \
+ obj/kernel/panic.ko obj/kernel/pci.ko obj/kernel/elf.ko \
+ obj/kernel/serial.ko obj/kernel/task.ko obj/kernel/util.ko \
+ obj/kernel/vga.ko obj/kernel/isrs.kao obj/kernel/kbd.ko \
+ obj/kernel/pmap.ko obj/kernel/paging.ko
mkdir -p out
- ld -T src/kernel/elf-link.ld obj/kernel/* -o obj/kernel.elf
+ ld -T src/kernel/elf-link.ld $^ -o obj/kernel.elf
objcopy -O binary obj/kernel.elf out/kernel.bin
out/boot.bin: src/boot.asm
@@ -64,29 +65,46 @@ obj/%.ao: src/user/%.asm
mkdir -p $(shell dirname $@)
nasm ${nasmargs} $< -o $@
-obj/knob.so: obj/knob/env.o obj/knob/file.o obj/knob/format.o \
- obj/knob/heap.o obj/knob/quit.o obj/knob/user.o \
- obj/knob/task.o obj/knob/entry.ao obj/knob/block.o
- ld ${partlink} obj/knob/* -o obj/knob.so
+obj/%.po: src/user/%.cpp
+ mkdir -p $(shell dirname $@)
+ g++ ${ugppargs} -c $< -o $@
+
+obj/c.rto: obj/runtimes/c/entry.ao
+ ld ${partlink} obj/runtimes/c/* -o obj/c.rto
+
+obj/cpp.rto:
+ #TODO
-obj/init.elf: obj/init/init.o obj/knob.so
- ld -T src/user/elf.ld obj/init/* obj/knob.so -o obj/init.elf
+obj/knob.so: obj/knob/env.o obj/knob/file.o obj/knob/format.o \
+ obj/knob/heap.o obj/knob/user.o obj/knob/task.o \
+ obj/knob/block.o
+ ld ${partlink} $^ -o $@
-obj/meminfo.elf: obj/meminfo/meminfo.o obj/knob.so
- ld -T src/user/elf.ld obj/meminfo/* obj/knob.so -o obj/meminfo.elf
+obj/cove.so: obj/cove/cove.po obj/cove/buttons.po obj/cove/label.po \
+ obj/cove/vlist.po
+ ld ${partlink} $^ -o $@
+
+obj/init.elf: obj/init/init.o obj/knob.so obj/c.rto
+ ld -T src/user/runtimes/c/elf.ld $^ -o $@
+
+obj/meminfo.elf: obj/meminfo/meminfo.o obj/knob.so obj/c.rto
+ ld -T src/user/runtimes/c/elf.ld $^ -o $@
obj/highway.elf: obj/highway/main.o obj/highway/cmds.o obj/highway/line.o \
- obj/highway/vars.o obj/knob.so
- ld -T src/user/elf.ld obj/highway/* obj/knob.so -o obj/highway.elf
+ obj/highway/vars.o obj/knob.so obj/c.rto
+ ld -T src/user/runtimes/c/elf.ld $^ -o $@
obj/hello.elf: obj/hello/hello.ao
- ld -T src/user/elf.ld obj/hello/* -o obj/hello.elf
+ ld -T src/user/runtimes/asm/elf.ld $^ -o $@
+
+obj/dumptext.elf: obj/dumptext/dumptext.o obj/knob.so obj/c.rto
+ ld -T src/user/runtimes/c/elf.ld $^ -o $@
-obj/dumptext.elf: obj/dumptext/dumptext.o obj/knob.so
- ld -T src/user/elf.ld obj/dumptext/* obj/knob.so -o obj/dumptext.elf
+obj/dumphex.elf: obj/dumphex/dumphex.o obj/knob.so obj/c.rto
+ ld -T src/user/runtimes/c/elf.ld $^ -o $@
-obj/dumphex.elf: obj/dumphex/dumphex.o obj/knob.so
- ld -T src/user/elf.ld obj/dumphex/* obj/knob.so -o obj/dumphex.elf
+obj/dirinfo.elf: obj/dirinfo/dirinfo.o obj/knob.so obj/c.rto
+ ld -T src/user/runtimes/c/elf.ld $^ -o $@
-obj/dirinfo.elf: obj/dirinfo/dirinfo.o obj/knob.so
- ld -T src/user/elf.ld obj/dirinfo/* obj/knob.so -o obj/dirinfo.elf \ No newline at end of file
+obj/fileman.elf: obj/fileman/fileman.o obj/knob.so obj/c.rto
+ ld -T src/user/runtimes/c/elf.ld $^ -o $@ \ No newline at end of file
diff --git a/src/kernel/drive.c b/src/kernel/drive.c
index 731088c..2ae050f 100644
--- a/src/kernel/drive.c
+++ b/src/kernel/drive.c
@@ -36,6 +36,11 @@ static uint32_t unknown_enumerate_dir(const struct drive *d, const char *path, s
return 0;
}
+__attribute__ ((const))
+static uint32_t unknown_n_dir_entries(const struct drive *d, const char *path) {
+ return 0;
+}
+
static inline void determine_fs(struct drive *d) {
if (try_fat_init_drive(d))
return;
@@ -46,6 +51,7 @@ static inline void determine_fs(struct drive *d) {
d->load_sector = &unknown_load_sector;
d->get_file_length = &unknown_get_file_length;
d->enumerate_dir = &unknown_enumerate_dir;
+ d->n_dir_entries = &unknown_n_dir_entries;
d->get_free_sectors = &unknown_get_free_sectors;
}
diff --git a/src/kernel/drive.h b/src/kernel/drive.h
index b02b4ad..03fe4b6 100644
--- a/src/kernel/drive.h
+++ b/src/kernel/drive.h
@@ -13,10 +13,11 @@ typedef uint8_t drive_id_t;
#define DCI_NAME_LEN 100
struct directory_content_info {
- bool is_dir;
char name[DCI_NAME_LEN];
uint32_t size;
-};
+ bool is_dir;
+ uint8_t pad[23];
+} __attribute__ ((packed));
struct drive {
char *drive_type;
@@ -34,6 +35,7 @@ struct drive {
void (*load_sector) (const struct drive *d, file_id_t fid, uint32_t sector, void *at);
uint32_t (*get_file_length) (const struct drive *d, file_id_t fid);
uint32_t (*enumerate_dir) (const struct drive *d, const char *path, struct directory_content_info *info, uint32_t max);
+ uint32_t (*n_dir_entries) (const struct drive *d, const char *path);
uint32_t (*get_free_sectors)(const struct drive *d);
fs_id_t fs_id;
};
diff --git a/src/kernel/fat.c b/src/kernel/fat.c
index c4461de..362539c 100644
--- a/src/kernel/fat.c
+++ b/src/kernel/fat.c
@@ -365,6 +365,72 @@ static uint32_t fat_enumerate_dir(const struct drive *d, const char *path, struc
}
}
+static uint32_t n_root_entries(const struct drive *d) {
+ uint32_t sect = infos[d->drive_id].root_start - 1;
+ struct directory_entry *entry = (struct directory_entry *)(fat_driver_buffer + 512);
+
+ uint32_t count = 0;
+
+ while (true) {
+ if (entry == (struct directory_entry *)(fat_driver_buffer + 512)) {
+ entry = (struct directory_entry *)fat_driver_buffer;
+ ++sect;
+ d->read_sectors(d, sect, 1, entry);
+ }
+
+ if (!*(uint8_t *)entry) {
+ d->done(d);
+ return count;
+ }
+
+ if (entry-> attrib & FA_LABEL) {
+ ++entry;
+ continue;
+ }
+
+ ++entry;
+ ++count;
+ }
+}
+
+static uint32_t fat_n_dir_entries(const struct drive *d, const char *path) {
+ d->ready(d);
+
+ if (!*path)
+ return n_root_entries(d);
+
+ if (!try_load_from_path(d, path)) {
+ d->done(d);
+ return 0;
+ }
+
+ uint16_t cluster = cur_dir->first_cluster;
+ load_cluster(cluster, fat_driver_buffer);
+ struct directory_entry *entry = (struct directory_entry *)fat_driver_buffer;
+
+ uint32_t count = 0;
+
+ while (true) {
+ if (entry == (struct directory_entry *)(fat_driver_buffer + 512)) {
+ entry = (struct directory_entry *)fat_driver_buffer;
+ load_cluster(cluster = next_cluster(cluster), fat_driver_buffer);
+ }
+
+ if (!*(uint8_t *)entry) {
+ d->done(d);
+ return count;
+ }
+
+ if (check_fat_names(entry->name, this_dir) || check_fat_names(entry->name, parent_dir)) {
+ ++entry;
+ continue;
+ }
+
+ ++entry;
+ ++count;
+ }
+}
+
void init_fat() {
next_fi = allocate_kernel_pages(1);
next_id = 0;
@@ -388,6 +454,7 @@ bool try_fat_init_drive(struct drive *d) {
d->load_sector = &fat_load_sector;
d->get_file_length = &fat_get_file_length;
d->enumerate_dir = &fat_enumerate_dir;
+ d->n_dir_entries = &fat_n_dir_entries;
d->get_free_sectors = &fat_get_free_sectors;
d->fs_id = next_id;
diff --git a/src/kernel/idt.c b/src/kernel/idt.c
index 90d8dc3..1b8e548 100644
--- a/src/kernel/idt.c
+++ b/src/kernel/idt.c
@@ -8,6 +8,7 @@
#include "paging.h"
#include "pmap.h"
#include "kbd.h"
+#include "vga.h"
enum {
IDT_PRESENT = 0x80,
@@ -100,6 +101,10 @@ static uint32_t sc_enumerate_dir(uint32_t drive_number, const char *path, struct
return drives[drive_number].enumerate_dir(drives + drive_number, path, buffer, max_entries);
}
+static uint32_t sc_count_of_dir(uint32_t drive_number, const char *path) {
+ return drives[drive_number].n_dir_entries(drives + drive_number, path);
+}
+
void const *syscall_table[] = {
&sc_open_file,
&sc_close_file,
@@ -111,7 +116,11 @@ void const *syscall_table[] = {
&sc_allocate_ram,
&sc_memory_info,
&sc_wait_for_task,
- &sc_enumerate_dir
+ &sc_enumerate_dir,
+ &vga_print_at,
+ &sc_count_of_dir,
+ &vga_blank,
+ &vga_set_color
};
//these aren't really void ()'s, but gcc complains if we take an address of a void, so we give it a type
diff --git a/src/kernel/isrs.asm b/src/kernel/isrs.asm
index 6ad469d..e7573ae 100644
--- a/src/kernel/isrs.asm
+++ b/src/kernel/isrs.asm
@@ -23,7 +23,7 @@ extern on_kbd_isr
extern make_sure_tasks
extern exception_halt
-n_syscalls equ 0xb
+n_syscalls equ 0xf
;section .bss
;_debug_is_start_task resb 1
diff --git a/src/kernel/kbd.c b/src/kernel/kbd.c
index f3eb323..c251db7 100644
--- a/src/kernel/kbd.c
+++ b/src/kernel/kbd.c
@@ -226,19 +226,34 @@ static const uint8_t codes_e0[] = {
0, 0, 0, 0, 0, 0, 0, 0
};
+#include "log.h"
+
+uint8_t get_code_byte() {
+ for (uint32_t spin = 0; spin < 10000000; ++spin)
+ ;
+ return inb(PS2_DATA);
+}
+
void on_kbd_isr() {
while (inb(PS2_CMD) & PS2S_CODE_READY) {
- uint8_t code = inb(PS2_DATA);
+ //char nbuf[11];
+ uint8_t code = get_code_byte();
+ //logsz("code: 0x");
+ //u8_hex(code, nbuf);
+ //logsz(nbuf);
if (code == 0xe1) {
- code = inb(PS2_DATA);
+ code = get_code_byte();
+ //logsz(" 0x");
+ //u8_hex(code, nbuf);
+ //logsz(nbuf);
if (code == 0x1d) {
- if (inb(PS2_DATA) != 0x45)
+ if (get_code_byte() != 0x45)
code = 0;
else
code = CODE_PAUSE;
}
else if (code == 0x9d) {
- if (inb(PS2_DATA) != 0xc5)
+ if (get_code_byte() != 0xc5)
code = 0;
else
code = 0xff;
@@ -247,17 +262,20 @@ void on_kbd_isr() {
code = 0;
}
else if (code == 0xe0) {
- code = inb(PS2_DATA);
+ code = get_code_byte();
+ //logsz(" 0x");
+ //u8_hex(code, nbuf);
+ //logsz(nbuf);
if (code == 0x2a) {
- if ((inb(PS2_DATA) != 0xe0) ||
- (inb(PS2_DATA) != 0x37))
+ if ((get_code_byte() != 0xe0) ||
+ (get_code_byte() != 0x37))
code = 0;
else
code = CODE_PRSCR;
}
else if (code == 0xb7) {
- if ((inb(PS2_DATA) != 0xe0) ||
- (inb(PS2_DATA) != 0xaa))
+ if ((get_code_byte() != 0xe0) ||
+ (get_code_byte() != 0xaa))
code = 0;
else
code = 0xff;
@@ -268,6 +286,8 @@ void on_kbd_isr() {
else
code = codes[code];
+ //logch('\n');
+
if (!code)
PANIC("Unknown scancode.");
diff --git a/src/kernel/main.c b/src/kernel/main.c
index 06b83d1..b34f816 100644
--- a/src/kernel/main.c
+++ b/src/kernel/main.c
@@ -15,9 +15,6 @@
#include "vga.h"
#include "kbd.h"
-void reset_tree();
-void tree(struct drive *d);
-
void _start_user_mode() __attribute__ ((noreturn));
__attribute__ ((noreturn))
@@ -35,6 +32,7 @@ void main() {
pci_init();
+/*
u16_dec(n_pci_devices, nbuf);
logsz(nbuf);
logsz(" PCI device(s) found:\n");
@@ -63,6 +61,7 @@ void main() {
}
logch('\n');
+*/
init_fat();
//other fs drivers
@@ -72,6 +71,7 @@ void main() {
init_ide();
//other drive drivers
+/*
u8_dec(n_drives, nbuf);
logsz(nbuf);
logsz(" drive(s) found:\n");
@@ -108,20 +108,7 @@ void main() {
}
logch('\n');
-
- for (uint8_t n = 0; n < n_drives; ++n) {
- if (drives[n].get_free_sectors(drives + n) == -1)
- continue;
-
- u32_dec(n, nbuf);
- logsz("sd");
- logsz(nbuf);
- logsz(" tree:\n");
-
- reset_tree();
- tree(drives + n);
- logch('\n');
- }
+*/
if (BOOT_INFO->support_flags & BIS_PAE)
logsz("Processor supports PAE (but Portland OS does not yet).\n");
diff --git a/src/kernel/main2.c b/src/kernel/main2.c
deleted file mode 100644
index 27ef8de..0000000
--- a/src/kernel/main2.c
+++ /dev/null
@@ -1,69 +0,0 @@
-#include <stdint.h>
-#include "drive.h"
-#include "util.h"
-#include "log.h"
-
-static char nbuf2[11];
-
-static char path_builder[200];
-static uint8_t path_builder_len;
-
-static char indent_builder[20];
-static uint8_t indent_builder_len;
-
-void reset_tree() {
- path_builder[0] = '\0';
- path_builder_len = 0;
-
- indent_builder[0] = ' ';
- indent_builder[1] = ' ';
- indent_builder[2] = '\0';
- indent_builder_len = 2;
-}
-
-void tree(struct drive *d) {
- struct directory_content_info infos[100];
- uint8_t n_infos = d->enumerate_dir(d, path_builder, infos, 100);
-
- if (!n_infos) {
- logsz(indent_builder);
- logsz("(empty)\n");
- return;
- }
-
- for (uint8_t i = 0; i < n_infos; ++i) {
- logsz(indent_builder);
- logsz(infos[i].name);
-
- if (infos[i].is_dir) {
- logsz(":\n");
-
- indent_builder[indent_builder_len] = ' ';
- indent_builder[indent_builder_len + 1] = ' ';
- indent_builder[indent_builder_len + 2] = '\0';
- indent_builder_len += 2;
-
- uint8_t name_length = 0;
- while (infos[i].name[name_length])
- ++name_length;
-
- memcpy(path_builder + path_builder_len, infos[i].name, name_length + 1);
- path_builder_len += name_length;
-
- tree(d);
-
- path_builder_len -= name_length;
- path_builder[path_builder_len] = '\0';
-
- indent_builder_len -= 2;
- indent_builder[indent_builder_len] = '\0';
- }
-
- else {
- u32_dec(infos[i].size, nbuf2);
- logsz(" (");
- logsz(nbuf2);
- logsz(" bytes)\n");
- }
- }
-} \ No newline at end of file
diff --git a/src/kernel/vga.c b/src/kernel/vga.c
index daa92fe..42da312 100644
--- a/src/kernel/vga.c
+++ b/src/kernel/vga.c
@@ -2,8 +2,9 @@
#include <stdint.h>
#define VGA_COLUMNS 80
+#define VGA_ROWS 25
#define VGA_START (uint16_t *)0x000b8000
-#define VGA_END (VGA_START + VGA_COLUMNS * 25)
+#define VGA_END (VGA_START + VGA_COLUMNS * VGA_ROWS)
static uint16_t *cursor = VGA_START;
static uint16_t mask;
@@ -25,7 +26,7 @@ void vga_blank() {
uint32_t *p = (uint32_t *)VGA_START;
while (p < (uint32_t *)VGA_END)
*p++ = f;
- cursor = VGA_START;
+ cursor = VGA_END - VGA_COLUMNS;
}
void vga_printch(char ch) {
@@ -37,4 +38,10 @@ void vga_printch(char ch) {
*cursor++ = mask | (uint8_t)ch;
if (cursor == VGA_END)
vga_scroll();
+}
+
+void vga_print_at(uint16_t pos, const char *sz) {
+ cursor = VGA_START + (pos >> 8) * VGA_COLUMNS + (pos & 0xff);
+ while (*sz)
+ vga_printch(*sz++);
} \ No newline at end of file
diff --git a/src/kernel/vga.h b/src/kernel/vga.h
index bfc9fe6..fa97e11 100644
--- a/src/kernel/vga.h
+++ b/src/kernel/vga.h
@@ -3,6 +3,7 @@
#include <stdint.h>
+void vga_print_at(uint16_t pos, const char *sz);
void vga_set_color(uint8_t color);
void vga_blank();
void vga_printch(char ch);
diff --git a/src/user/dirinfo/dirinfo.c b/src/user/dirinfo/dirinfo.c
index 7484eb8..21afcc7 100644
--- a/src/user/dirinfo/dirinfo.c
+++ b/src/user/dirinfo/dirinfo.c
@@ -13,7 +13,7 @@ void main(const char *arg) {
tell_user_sz(*arg ? arg : "drive root");
tell_user_sz("\n");
- _dir_info_entry infos[MAX_DIR_ENTRIES];
+ _dir_info_entry_t infos[MAX_DIR_ENTRIES];
uint8_t count = _enumerate_dir(dn, path, infos, MAX_DIR_ENTRIES);
tell_user_sz(
count == MAX_DIR_ENTRIES
diff --git a/src/user/fileman/fileman.c b/src/user/fileman/fileman.c
new file mode 100644
index 0000000..0b28d1d
--- /dev/null
+++ b/src/user/fileman/fileman.c
@@ -0,0 +1,130 @@
+#include <pland/syscall.h>
+#include <knob/block.h>
+#include <knob/file.h>
+#include <knob/heap.h>
+#include <knob/task.h>
+
+char path_sz[1024] = "";
+uint32_t path_len = 0;
+
+_dir_info_entry_t *dir_info;
+uint32_t dir_info_len;
+
+uint32_t scroll;
+uint32_t selected;
+#define MAX_PER_SCREEN 18
+
+void paint_screen() {
+ _clear_screen();
+
+ _set_color(0x70);
+ _print_at(0, 0, path_sz);
+ _log_string("/");
+ _set_color(0x07);
+
+ for (uint32_t i = scroll, row = 2; (i < dir_info_len) && (row < MAX_PER_SCREEN + 2); ++i, ++row) {
+ if (i == selected)
+ _set_color(0x17);
+ _print_at(row, 0, dir_info[i].name);
+ if (dir_info[i].is_dir)
+ _log_string("/");
+ if (i == selected)
+ _set_color(0x07);
+ }
+
+ _print_at(21, 0, "keybindings:\n"
+ "UP, DOWN, PAGE UP, PAGE DOWN, HOME, END: move selection\n"
+ "ENTER: enter selected directory, run selected program\n"
+ "ESC: go up a directory Q: quit");
+}
+
+void load_dir() {
+ free_block(dir_info);
+ dir_info = get_directory_info(path_sz, &dir_info_len);
+ scroll = 0;
+ selected = 0;
+}
+
+void main(const char *sz) {
+ path_len = strcpy(path_sz, sz);
+ dir_info = get_directory_info(sz, &dir_info_len);
+
+ paint_screen();
+
+ while (true) {
+ _key_code_t key;
+ switch (key = _get_key()) {
+ case 0:
+ _yield_task();
+ continue;
+ case '\n':
+ {
+ uint32_t old_len = path_len;
+ if (path_len)
+ path_sz[path_len++] = '/';
+ path_len += strcpy(path_sz + path_len, dir_info[selected].name);
+ if (dir_info[selected].is_dir)
+ load_dir();
+ else {
+ try_run_command_blocking(path_sz);
+ //TODO: handle error
+ path_sz[old_len] = '\0';
+ path_len = old_len;
+ }
+ }
+ break;
+ case _KEY_ESC:
+ {
+ char *cutoff = path_sz;
+ for (char *i = path_sz + path_len - 1; i >= path_sz; --i)
+ if (*i == '/') {
+ cutoff = i;
+ break;
+ }
+ *cutoff = '\0';
+ path_len = cutoff - path_sz;
+ load_dir();
+ }
+ break;
+ case _KEY_HOME:
+ scroll = 0;
+ selected = 0;
+ break;
+ case _KEY_END:
+ selected = dir_info_len - 1;
+ scroll = dir_info_len >= MAX_PER_SCREEN
+ ? dir_info_len - MAX_PER_SCREEN : 0;
+ break;
+ case _KEY_UP:
+ if (!selected)
+ continue;
+ if (--selected < scroll)
+ scroll = selected;
+ break;
+ case _KEY_DOWN:
+ if (selected == dir_info_len - 1)
+ continue;
+ if (++selected >= scroll + MAX_PER_SCREEN)
+ ++scroll;
+ break;
+ case _KEY_PUP:
+ selected = selected >= MAX_PER_SCREEN
+ ? selected - MAX_PER_SCREEN : 0;
+ scroll = scroll >= MAX_PER_SCREEN
+ ? scroll - MAX_PER_SCREEN : 0;
+ break;
+ case _KEY_PDOWN:
+ if ((selected += MAX_PER_SCREEN) >= dir_info_len)
+ selected = dir_info_len - 1;
+ if ((scroll += MAX_PER_SCREEN) > selected)
+ scroll = selected;
+ break;
+ case 'q':
+ _clear_screen();
+ return;
+ default:
+ continue;
+ }
+ paint_screen();
+ }
+} \ No newline at end of file
diff --git a/src/user/highway/line.c b/src/user/highway/line.c
index ca38c9b..40dccfa 100644
--- a/src/user/highway/line.c
+++ b/src/user/highway/line.c
@@ -1,5 +1,5 @@
+#include <pland/pcrt.h>
#include <knob/block.h>
-#include <knob/quit.h>
#include <knob/task.h>
#include <knob/user.h>
#include "cmds.h"
@@ -68,7 +68,7 @@ void run_line(const char *original_line) {
else if (blockequ(line, "vars", space - line))
dump_vars();
else if (blockequ(line, "quit", space - line))
- quit(space + 1);
+ __pcrt_quit();
else if (blockequ(line, "help", space - line))
tell_user_sz("Highway is a command shell for Portland OS. It includes variables and a couple\n"
"of pseudo-commands. Variables are addressed by surrounding with \"$\". The\n"
@@ -78,7 +78,7 @@ void run_line(const char *original_line) {
" echo STRING print STRING\n"
" vars dump variables\n"
" quit exit highway\n"
- " help show this\n\n");
+ " help show this\n");
else if (!try_run_command_blocking(line)) {
struct no_null_sn arg = {
.data = "_path",
diff --git a/src/user/highway/main.c b/src/user/highway/main.c
index 1934920..61f736e 100644
--- a/src/user/highway/main.c
+++ b/src/user/highway/main.c
@@ -9,7 +9,7 @@ void main(const char *arg) {
yield_task();
tell_user_sz("Portland Highway\nType \"help\" for help.\n");
while (1) {
- tell_user_sz("> ");
+ tell_user_sz("\n> ");
ask_user_line_sz(cmd_buf, 127);
run_line(cmd_buf);
}
diff --git a/src/user/include/knob/block.h b/src/user/include/knob/block.h
index 4625577..43137e1 100644
--- a/src/user/include/knob/block.h
+++ b/src/user/include/knob/block.h
@@ -7,4 +7,7 @@
void blockcpy(void *to, const void *from, uint32_t size);
bool blockequ(const void *a, const void *b, uint32_t size) __attribute__ ((__pure__));
+//returns length without null-terminator
+uint32_t strcpy(char *to, const char *from);
+
#endif \ No newline at end of file
diff --git a/src/user/include/knob/file.h b/src/user/include/knob/file.h
index a0d084f..8862098 100644
--- a/src/user/include/knob/file.h
+++ b/src/user/include/knob/file.h
@@ -2,6 +2,7 @@
#define KNOB_FILE_H
#include <stdint.h>
+#include <pland/syscall.h>
struct file;
@@ -9,7 +10,6 @@ const char *remove_prefix(const char *path, uint8_t *dn_out);
struct file *open_file(const char *path);
void close_file(struct file *f);
-void _close_all_files();
uint32_t read_from_file(struct file *f, uint32_t max, void *buf);
//return value and max_length don't include null terminator
@@ -19,4 +19,7 @@ int32_t seek_file_by(struct file *f, int32_t by);
uint32_t file_size(struct file *f) __attribute__ ((pure));
+//return value must be manually freed, unless it is a null pointer
+_dir_info_entry_t *get_directory_info(const char *path, uint32_t *count_out);
+
#endif \ No newline at end of file
diff --git a/src/user/include/knob/quit.h b/src/user/include/knob/quit.h
deleted file mode 100644
index 7b10d09..0000000
--- a/src/user/include/knob/quit.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef KNOB_QUIT_H
-#define KNOB_QUIT_H
-
-void on_quit(void (*run_f)());
-void quit() __attribute__ ((noreturn));
-
-#endif \ No newline at end of file
diff --git a/src/user/include/knob/user.h b/src/user/include/knob/user.h
index 479a731..cd7676c 100644
--- a/src/user/include/knob/user.h
+++ b/src/user/include/knob/user.h
@@ -2,6 +2,9 @@
#define KNOB_USER_H
#include <stdint.h>
+#include <pland/syscall.h>
+
+char key_to_char(_key_code_t key) __attribute__ ((const));
void tell_user_sz(const char *sz);
diff --git a/src/user/include/pland/pcrt.h b/src/user/include/pland/pcrt.h
new file mode 100644
index 0000000..07ad453
--- /dev/null
+++ b/src/user/include/pland/pcrt.h
@@ -0,0 +1,16 @@
+#ifndef PLAND_PCRT_H
+#define PLAND_PCRT_H
+
+#define BEFORE_MAIN(f) \
+ __attribute__ ((section (".__pcrt_before_main"))) \
+ __attribute__ ((unused)) \
+ void (*const __pcrt_bm_##f)() = &f;
+
+#define BEFORE_QUIT(f) \
+ __attribute__ ((section (".__pcrt_before_quit"))) \
+ __attribute__ ((unused)) \
+ void (*const __pcrt_bq_##f)() = &f;
+
+void __pcrt_quit() __attribute__ ((noreturn));
+
+#endif \ No newline at end of file
diff --git a/src/user/include/pland/syscall.h b/src/user/include/pland/syscall.h
index e16a7fb..331002e 100644
--- a/src/user/include/pland/syscall.h
+++ b/src/user/include/pland/syscall.h
@@ -7,14 +7,9 @@
typedef uint32_t _file_handle_t;
typedef uint32_t _task_handle_t;
typedef uint32_t _drive_number_t;
-typedef uint32_t _pages_t;
typedef uint32_t _process_handle_t;
typedef enum {
- _KEY_BACKSPACE = '\b',
- _KEY_RETURN = '\n',
- //etc.
-
_KEY_LSHIFT = 0x00000100,
_KEY_RSHIFT = 0x00000200,
_KEY_CAPS = 0x00000400,
@@ -33,8 +28,103 @@ typedef enum {
_KEY_ALT = 0x0000c000,
_KEY_CTRL = 0x00030000,
_KEY_META = 0x000c0000,
+
+ _KEY_BEGIN_CAPS = 0x80,
+ _KEY_BEGIN_INSERT,
+ _KEY_BEGIN_NUM,
+ _KEY_BEGIN_SCROLL,
+ _KEY_BEGIN_LSHIFT,
+ _KEY_BEGIN_RSHIFT,
+ _KEY_BEGIN_LALT,
+ _KEY_BEGIN_RALT,
+ _KEY_BEGIN_LCTRL,
+ _KEY_BEGIN_RCTRL,
+ _KEY_BEGIN_LMETA,
+ _KEY_BEGIN_RMETA,
+ /* 0x8c - 0x97 reserved */
+ /* 0x98 - 0x9f unassigned */
+ _KEY_F1 = 0xa0,
+ _KEY_F2,
+ _KEY_F3,
+ _KEY_F4,
+ _KEY_F5,
+ _KEY_F6,
+ _KEY_F7,
+ _KEY_F8,
+ _KEY_F9,
+ _KEY_F10,
+ _KEY_F11,
+ _KEY_F12,
+ /* 0xac - 0xaf unassigned */
+ _KEY_NUM0 = 0xb0,
+ _KEY_NUM1,
+ _KEY_NUM2,
+ _KEY_NUM3,
+ _KEY_NUM4,
+ _KEY_NUM5,
+ _KEY_NUM6,
+ _KEY_NUM7,
+ _KEY_NUM8,
+ _KEY_NUM9,
+ _KEY_NTIMES,
+ _KEY_NPLUS,
+ _KEY_NENTER,
+ _KEY_NMINUS,
+ _KEY_NDOT,
+ _KEY_NSLASH,
+ /* 0xc0 unassigned */
+ _KEY_DELETE,
+ _KEY_HOME,
+ _KEY_END,
+ _KEY_PUP,
+ _KEY_PDOWN,
+ _KEY_UP,
+ _KEY_DOWN,
+ _KEY_LEFT,
+ _KEY_RIGHT,
+ _KEY_ESC,
+ _KEY_MENU,
+ _KEY_PAUSE,
+ _KEY_PRSCR,
+ /* 0xce - 0xef unassigned */
} _key_code_t;
+typedef enum {
+ _COLOR_FG_BLACK = 0x00,
+ _COLOR_FG_BLUE = 0x01,
+ _COLOR_FG_GREEN = 0x02,
+ _COLOR_FG_CYAN = 0x03,
+ _COLOR_FG_RED = 0x04,
+ _COLOR_FG_MAGENTA = 0x05,
+ _COLOR_FG_BROWN = 0x06,
+ _COLOR_FG_LGRAY = 0x07,
+
+ _COLOR_FG_DGRAY = 0x08,
+ _COLOR_FG_LBLUE = 0x09,
+ _COLOR_FG_LGREEN = 0x0a,
+ _COLOR_FG_LCYAN = 0x0b,
+ _COLOR_FG_LRED = 0x0c,
+ _COLOR_FG_PINK = 0x0d,
+ _COLOR_FG_YELLOW = 0x0e,
+ _COLOR_FG_WHITE = 0x0f,
+
+ _COLOR_BG_BLACK = 0x00,
+ _COLOR_BG_BLUE = 0x10,
+ _COLOR_BG_GREEN = 0x20,
+ _COLOR_BG_CYAN = 0x30,
+ _COLOR_BG_RED = 0x40,
+ _COLOR_BG_MAGENTA = 0x50,
+ _COLOR_BG_BROWN = 0x60,
+ _COLOR_BG_LGRAY = 0x70
+} _vga_color_t;
+
+typedef struct __attribute__ ((packed)) {
+ char name[100];
+ uint32_t size;
+ bool is_dir;
+ uint8_t pad[23];
+} _dir_info_entry_t;
+
enum _scn {
_SCN_OPEN_FILE,
_SCN_CLOSE_FILE,
@@ -46,72 +136,74 @@ enum _scn {
_SCN_ALLOCATE_RAM,
_SCN_MEMORY_INFO,
_SCN_WAIT_FOR_TASK,
- _SCN_ENUMERATE_DIR
+ _SCN_ENUMERATE_DIR,
+ _SCN_PRINT_AT,
+ _SCN_COUNT_OF_DIR,
+ _SCN_CLEAR_SCREEN,
+ _SCN_SET_COLOR
};
-typedef struct {
- bool is_dir;
- char name[100];
- uint32_t size;
-} _dir_info_entry;
-
static inline uint32_t _sc0(enum _scn eax) {
- volatile uint32_t out;
- asm (
+ uint32_t out;
+ asm volatile (
"int $0x30"
: "=a" (out) : "a" (eax) : "ecx", "edx");
return out;
}
static inline uint32_t _sc1(enum _scn eax, uint32_t ebx) {
- volatile uint32_t out;
- asm (
+ uint32_t out;
+ asm volatile (
"int $0x30"
: "=a" (out) : "a" (eax), "b" (ebx) : "ecx", "edx");
return out;
}
static inline uint32_t _sc2(enum _scn eax, uint32_t ebx, uint32_t ecx) {
- volatile uint32_t out;
- asm (
+ uint32_t out;
+ uint32_t dummy;
+ asm volatile (
"int $0x30"
- : "=a" (out) : "a" (eax), "b" (ebx), "c" (ecx) : "edx");
+ : "=a" (out), "=c" (dummy) : "a" (eax), "b" (ebx), "c" (ecx) : "edx");
return out;
}
static inline uint32_t _sc3(enum _scn eax, uint32_t ebx, uint32_t ecx, uint32_t edx) {
- volatile uint32_t out;
- asm (
+ uint32_t out;
+ uint32_t dummy;
+ asm volatile (
"int $0x30"
- : "=a" (out) : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx));
+ : "=a" (out), "=c" (dummy), "=d" (dummy) : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx));
return out;
}
static inline uint32_t _sc4(enum _scn eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi) {
- volatile uint32_t out;
- asm (
+ uint32_t out;
+ uint32_t dummy;
+ asm volatile (
"int $0x30"
- : "=a" (out) : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi));
+ : "=a" (out), "=c" (dummy), "=d" (dummy) : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi));
return out;
}
static inline uint32_t _sc5(enum _scn eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi, uint32_t edi) {
- volatile uint32_t out;
- asm (
+ uint32_t out;
+ uint32_t dummy;
+ asm volatile (
"int $0x30"
- : "=a" (out) : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));
+ : "=a" (out), "=c" (dummy), "=d" (dummy) : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));
return out;
}
static inline void _yield_task() {
- asm (
+ asm volatile (
"int $0x39"
: : : "eax");
}
__attribute__ ((noreturn))
static inline void _exit_task() {
- asm (
+ asm volatile (
"int $0x38"
);
__builtin_unreachable();
@@ -145,27 +237,27 @@ static inline _key_code_t _get_key() {
return _sc0(_SCN_GET_KEY);
}
-static inline void *_allocate_ram(_pages_t pages) {
+static inline void *_allocate_ram(uint32_t pages) {
return (void *)_sc1(_SCN_ALLOCATE_RAM, pages);
}
-static inline _pages_t _kernel_dynamic_area_size() {
+static inline uint32_t _kernel_dynamic_area_size() {
return _sc1(_SCN_MEMORY_INFO, 0x0);
}
-static inline _pages_t _kernel_dynamic_area_left() {
+static inline uint32_t _kernel_dynamic_area_left() {
return _sc1(_SCN_MEMORY_INFO, 0x1);
}
-static inline _pages_t _total_userspace_size() {
+static inline uint32_t _total_userspace_size() {
return _sc1(_SCN_MEMORY_INFO, 0x2);
}
-static inline _pages_t _total_userspace_left() {
+static inline uint32_t _total_userspace_left() {
return _sc1(_SCN_MEMORY_INFO, 0x3);
}
-static inline _pages_t _this_process_memory_left() {
+static inline uint32_t _this_process_memory_left() {
return _sc1(_SCN_MEMORY_INFO, 0x4);
}
@@ -173,8 +265,24 @@ static inline void _wait_for_task(_process_handle_t handle) {
_sc1(_SCN_WAIT_FOR_TASK, handle);
}
-static inline uint32_t _enumerate_dir(_drive_number_t drive_number, const char *path, _dir_info_entry *buffer, uint32_t max_count) {
+static inline uint32_t _enumerate_dir(_drive_number_t drive_number, const char *path, _dir_info_entry_t *buffer, uint32_t max_count) {
return _sc4(_SCN_ENUMERATE_DIR, drive_number, (uint32_t)path, (uint32_t)buffer, max_count);
}
+static inline void _print_at(uint8_t row, uint8_t col, const char *sz) {
+ _sc2(_SCN_PRINT_AT, (row << 8) | col, (uint32_t)sz);
+}
+
+static inline uint32_t _count_of_dir(uint8_t drive_number, const char *path) {
+ return _sc2(_SCN_COUNT_OF_DIR, drive_number, (uint32_t)path);
+}
+
+static inline void _clear_screen() {
+ _sc0(_SCN_CLEAR_SCREEN);
+}
+
+static inline void _set_color(_vga_color_t color) {
+ _sc1(_SCN_SET_COLOR, color);
+}
+
#endif \ No newline at end of file
diff --git a/src/user/knob/block.c b/src/user/knob/block.c
index 7524ad3..4ec0564 100644
--- a/src/user/knob/block.c
+++ b/src/user/knob/block.c
@@ -14,4 +14,13 @@ bool blockequ(const void *a, const void *b, uint32_t size) {
if (*(uint8_t *)(a++) != *(uint8_t *)(b++))
return false;
return true;
+}
+
+//returns length without null-terminator
+uint32_t strcpy(char *to, const char *from) {
+ uint32_t i = 0;
+ do
+ to[i] = from[i];
+ while (from[i++]);
+ return i - 1;
} \ No newline at end of file
diff --git a/src/user/knob/entry.asm b/src/user/knob/entry.asm
deleted file mode 100644
index e9548d4..0000000
--- a/src/user/knob/entry.asm
+++ /dev/null
@@ -1,23 +0,0 @@
-bits 32
-
-global _entry
-
-extern main
-extern quit
-
-extern current_disk
-
-section .text
-_entry:
- mov esp, stack
- push edx
-
- ;TODO: determine current_disk
- ;any further needed initialization
-
- push quit
- jmp main
-
-section .stack nobits alloc noexec write align=16
-resb 4096
-stack: \ No newline at end of file
diff --git a/src/user/knob/file.c b/src/user/knob/file.c
index 23b7564..f1a039d 100644
--- a/src/user/knob/file.c
+++ b/src/user/knob/file.c
@@ -1,4 +1,5 @@
#include <pland/syscall.h>
+#include <pland/pcrt.h>
#include <knob/format.h>
#include <knob/heap.h>
#include <knob/env.h>
@@ -11,11 +12,13 @@ struct ofl_node {
static struct ofl_node *head_ofl_node = 0;
-void _close_all_files() {
+static void _close_all_files() {
for (struct ofl_node *i = head_ofl_node; i; i = i->next)
_close_file(i->handle);
}
+BEFORE_QUIT(_close_all_files)
+
struct file {
struct ofl_node *node;
_file_handle_t handle;
@@ -121,4 +124,20 @@ int32_t seek_file_by(struct file *f, int32_t by) {
__attribute__ ((pure))
uint32_t file_size(struct file *f) {
return f->length;
+}
+
+//return value must be manually freed, unless it is a null pointer
+_dir_info_entry_t *get_directory_info(const char *path, uint32_t *count_out) {
+ uint8_t dn;
+ path = remove_prefix(path, &dn);
+
+ uint32_t count = _count_of_dir(dn, path);
+ if (!count) {
+ *count_out = 0;
+ return 0;
+ }
+
+ _dir_info_entry_t *buffer = get_block(count * sizeof(_dir_info_entry_t));
+ *count_out = _enumerate_dir(dn, path, buffer, count);
+ return buffer;
} \ No newline at end of file
diff --git a/src/user/knob/quit.c b/src/user/knob/quit.c
deleted file mode 100644
index 98881b7..0000000
--- a/src/user/knob/quit.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <pland/syscall.h>
-#include <knob/file.h>
-
-__attribute__ ((noreturn))
-void quit() {
- _close_all_files();
- _exit_task();
-} \ No newline at end of file
diff --git a/src/user/knob/user.c b/src/user/knob/user.c
index b642f79..dfcb791 100644
--- a/src/user/knob/user.c
+++ b/src/user/knob/user.c
@@ -116,10 +116,8 @@ static const uint8_t shifted[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
-static char get_key_char() {
- _key_code_t key;
- while (!(key = _get_key()))
- _yield_task();
+__attribute__ ((const))
+char key_to_char(_key_code_t key) {
return
key & _KEY_CAPS
? key & _KEY_SHIFT
@@ -130,6 +128,13 @@ static char get_key_char() {
: key & 0xff;
}
+static char get_key_char() {
+ _key_code_t key;
+ while (!(key = _get_key()))
+ _yield_task();
+ return key_to_char(key);
+}
+
void tell_user_sz(const char *sz) {
_log_string(sz);
}
diff --git a/src/user/elf.ld b/src/user/runtimes/asm/elf.ld
index aaef517..aaef517 100644
--- a/src/user/elf.ld
+++ b/src/user/runtimes/asm/elf.ld
diff --git a/src/user/runtimes/c/elf.ld b/src/user/runtimes/c/elf.ld
new file mode 100644
index 0000000..f321be2
--- /dev/null
+++ b/src/user/runtimes/c/elf.ld
@@ -0,0 +1,22 @@
+OUTPUT_FORMAT(elf32-i386)
+OUTPUT_ARCH(i386)
+ENTRY(__pcrt_entry)
+
+MEMORY {
+ kernel (!a) : ORIGIN = 0x00000000, LENGTH = 0x08000000
+ user (awx) : ORIGIN = 0x08000000, LENGTH = 0xf8000000
+}
+
+SECTIONS {
+ .__pcrt_before_main : {
+ __pcrt_before_main_start = .;
+ *(.__pcrt_before_main)
+ __pcrt_before_main_end = .;
+ }
+
+ .__pcrt_before_quit : {
+ __pcrt_before_quit_start = .;
+ *(.__pcrt_before_quit)
+ __pcrt_before_quit_end = .;
+ }
+} \ No newline at end of file
diff --git a/src/user/runtimes/c/entry.asm b/src/user/runtimes/c/entry.asm
new file mode 100644
index 0000000..bba8060
--- /dev/null
+++ b/src/user/runtimes/c/entry.asm
@@ -0,0 +1,42 @@
+bits 32
+
+global __pcrt_entry
+global __pcrt_quit
+
+extern main
+extern __pcrt_before_main_start
+extern __pcrt_before_main_end
+extern __pcrt_before_quit_start
+extern __pcrt_before_quit_end
+
+section .text
+__pcrt_entry:
+ mov esp, stack
+ push edx
+
+ mov ebx, __pcrt_before_main_start
+.before_main_loop:
+ cmp ebx, __pcrt_before_main_end
+ je .call_main
+ call dword [ebx]
+ add ebx, 4
+ jmp .before_main_loop
+
+.call_main:
+ call main
+
+__pcrt_quit:
+ mov ebx, __pcrt_before_quit_start
+.before_quit_loop:
+ cmp ebx, __pcrt_before_quit_end
+ je .end_task
+ call dword [ebx]
+ add ebx, 4
+ jmp .before_quit_loop
+
+.end_task:
+ int 0x38
+
+section .stack nobits alloc noexec write align=16
+resb 4096
+stack: \ No newline at end of file