diff options
74 files changed, 2345 insertions, 1301 deletions
diff --git a/applications/goldman/makefile b/applications/goldman/makefile index 5b93fe4..56f633d 100644 --- a/applications/goldman/makefile +++ b/applications/goldman/makefile @@ -6,7 +6,7 @@ build/%.cpp.o: source/%.cpp $(HILBERT_CC) -c $^ -o $@ build/goldman.elf: $(SOURCES:%=build/%.o) - $(HILBERT_CC) $^ -o $@ + $(HILBERT_CC) $^ -ldaguerre -o $@ clean: rm -rf build diff --git a/applications/goldman/source/main.cpp b/applications/goldman/source/main.cpp index 28ed00a..d74eaad 100644 --- a/applications/goldman/source/main.cpp +++ b/applications/goldman/source/main.cpp @@ -1,4 +1,79 @@ +#include <daguerre/framebuffer.hpp> +#include <daguerre/ppm.hpp> + +daguerre::hilbert_color trans_color; + +void convert_pointer( + daguerre::hilbert_color &dest, const daguerre::hilbert_color &src) { + if (src != trans_color) + dest = src; +} + int main(int, char **) { - while (1) - ; + + trans_color = euler::syscall::encode_color(0xff, 0x00, 0xff); + + daguerre::image<daguerre::hilbert_color> framebuffer = + daguerre::get_hilbert_framebuffer(); + + int fw = framebuffer.width; + int fh = framebuffer.height; + + daguerre::image<daguerre::hilbert_color> double_buffer(fw, fh); + + std::optional<daguerre::image<daguerre::hilbert_color>> + background_original = daguerre::try_load_ppm("/assets/background.ppm"); + + daguerre::image<daguerre::hilbert_color> background; + + if (background_original.has_value()) + background = background_original->stretch(fw, fh); + else { + background = daguerre::image<daguerre::hilbert_color>(fw, fh); + background.fill(euler::syscall::encode_color(0, 0, 0)); + } + + std::optional<daguerre::image<daguerre::hilbert_color>> + pointer_original = daguerre::try_load_ppm("/assets/pointer.ppm"); + + if (!pointer_original.has_value()) + //TODO + while (1) + ; + + daguerre::image<daguerre::hilbert_color> + pointer = std::move(*pointer_original); + + int mouse_x = fw / 2; + int mouse_y = fh / 2; + + while (true) { + + double_buffer.copy_from(background, 0, 0); + double_buffer.convert_from(pointer, mouse_x, mouse_y, 0, 0, + std::min(pointer. width, double_buffer. width - mouse_x), + std::min(pointer.height, double_buffer.height - mouse_y), + &convert_pointer); + + framebuffer.copy_from(double_buffer, 0, 0); + + auto result = euler::syscall::get_input_packet(); + if (std::holds_alternative<euler::syscall::mouse_packet>(result)) { + const auto &packet = std::get<euler::syscall::mouse_packet>(result); + mouse_x += packet.x_changed; + mouse_y += packet.y_changed; + if (mouse_x < 0) + mouse_x = 0; + else if (mouse_x >= fw) + mouse_x = fw - 1; + if (mouse_y < 0) + mouse_y = 0; + else if (mouse_y >= fh) + mouse_y = fh - 1; + } + + } + + return 0; + } diff --git a/applications/init/source/main.cpp b/applications/init/source/main.cpp index 5dfa81a..ca137a4 100644 --- a/applications/init/source/main.cpp +++ b/applications/init/source/main.cpp @@ -1,19 +1,11 @@ -#include <euler/start_process.hpp> +#include <euler/syscall.hpp> -int main(int, char **) { - - __euler_process_handle dummy; - - euler::start_process wm_process("/bin/compositor"); - wm_process.add_env_variable("ARGC", "1"); - wm_process.add_env_variable("ARGV0", "/bin/compositor"); - wm_process.start(dummy); - - euler::start_process hello_process("/bin/hello"); - hello_process.add_env_variable("ARGC", "1"); - hello_process.add_env_variable("ARGV0", "/bin/hello"); - hello_process.start(dummy); +//this does not keep track of the processes or whether they have started +//successfully, nor does it set their argc, argv, stdin, stdout, or stderr. +int main(int, char **) { + euler::syscall::process_handle dummy; + euler::syscall::start_process("/bin/compositor", {}, {}, dummy); + euler::syscall::start_process("/bin/hello", {}, {}, dummy); return 0; - } diff --git a/documentation/compositor.txt b/documentation/compositor.txt index 7442d51..4d8e70f 100644 --- a/documentation/compositor.txt +++ b/documentation/compositor.txt @@ -13,7 +13,7 @@ data types: color: opaque dword (result of encode color system call). - from c++, use __euler_encode_color in euler/syscall.hpp. + from c++, use euler::syscall::encode_color in euler/syscall.hpp. color rectangle: multiple hilbert colors, top to bottom by row, left to right within row diff --git a/documentation/euler/heap.txt b/documentation/euler/heap.txt deleted file mode 100644 index de1deec..0000000 --- a/documentation/euler/heap.txt +++ /dev/null @@ -1,32 +0,0 @@ -this file documents dynamic memory allocation and deallocation in userspace. -the unused areas of a process's usable mapped memory are divided into "chunks" -with a start and a length, satisfying the following properties: - a) the length of a chunk is a positive power of 2 - b) the start of a chunk is a multiple of its length - c) let s be a power of 2 and k be an integer. there are never two chunks with - length s and starts s * (2 * k) and s * (2 * k + 1). if ever an operation - would result in two such chunks, they are combined into one chunk with - length 2 * s and start 2 * s * k. - -a "chunk info page" is divided into 512 64-bit integers describing up to 255 -chunks and a pointer to another chunk info page. for each n from 0 to 254: - if the (2 * n)'th integer (where the first integer is the 0th one) is 0: - the (2 * n + 1)'th integer is unused. - if the (2 * n)'th integer is not 0: - the (2 * n)'th integer describes the length of a chunk - the (2 * n + 1)'th integer describes the start of the same chunk -the 510th integer is a pointer to the next chunk info page, and the 511th -integer is never used. - -when a program calls new or malloc with needed size s, we find a free chunk of -length at least s + 8. we then remove that chunk from the list of free chunks, -and add back in whatever is left after the first s + 8 bytes, if anything. -in the first 8 bytes of the original chunk, we store the value s + 8. the -remainder of the orginal chunk is returned to the program. - -during that process, if there isn't a chunk with the needed size, one or more -new pages are requested from the kernel to create such a chunk. - -when a program calls delete or free with pointer ptr, we read the integer in -the 8 bytes starting at ptr - 8 into a variable s. we then add the region -starting at ptr - 8 with length s to the free memory. diff --git a/documentation/kernel-interfaces/syscalls.txt b/documentation/kernel-interfaces/syscalls.txt index ca93b56..399eb19 100644 --- a/documentation/kernel-interfaces/syscalls.txt +++ b/documentation/kernel-interfaces/syscalls.txt @@ -20,6 +20,7 @@ stream result: 12 = other end closed 13 = already exists 14 = not sized + 15 = not readable encode color: rax in: 0 @@ -173,3 +174,27 @@ get other end process handle: rdi in: stream handle rax out: stream result rdi out: process handle (if rax = success) + +start thread: + rax in: 20 + rdi in: entry point + rsi in: argument + at entry, rsp is set to the top of a new stack, rdi is set + to the argument, and the other registers are undefined. + +clear socket read queue: + rax in: 21 + rdi in: stream handle + rax out: number of bytes cleared (0 on bad handle or not a socket) + +get environment variable length: + rax in: 22 + rdi in: pointer to variable name + rsi in: variable name length + rax out: variable value length, or -1 if unset + +get environment variable value: + rax in: 23 + rdi in: pointer to variable name + rsi in: variable name length + rdx in: pointer to buffer for variable value diff --git a/documentation/kernel/panics.txt b/documentation/kernel/panics.txt index e1b6ec1..5c09490 100644 --- a/documentation/kernel/panics.txt +++ b/documentation/kernel/panics.txt @@ -3,6 +3,7 @@ panic. the following are the defined colors so far: #48a6ed - failed to get root node of initfs #5f8860 - no initfs module was given #7e874d - failed to look up /bin/init + #8077a6 - pure virtual function called (i think this should never happen?) #9af5e6 - an unimplemented path in the kernel #ba40bb - cpu exception occurred #c39db3 - failed to parse /bin/init diff --git a/euler/include/algorithm b/euler/include/algorithm deleted file mode 100644 index 08702f9..0000000 --- a/euler/include/algorithm +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -namespace std { - - template <class t> - void swap(t &a, t &b) { - t tmp = a; - a = b; - b = tmp; - } - -} diff --git a/euler/include/cctype b/euler/include/cctype new file mode 100644 index 0000000..2afc53d --- /dev/null +++ b/euler/include/cctype @@ -0,0 +1,3 @@ +#pragma once + +extern "C" int isspace(int ch); diff --git a/euler/include/cstdio b/euler/include/cstdio index 75472c1..0dc42d0 100644 --- a/euler/include/cstdio +++ b/euler/include/cstdio @@ -1,20 +1,16 @@ #pragma once #include <euler/stream.hpp> -#include <stddef.h> +#include <cstddef> -namespace std { +typedef euler::stream FILE; - typedef euler::stream FILE; +extern "C" FILE *fopen(const char *filename, const char *mode); +extern "C" void fclose(FILE *stream); - FILE *fopen(const char *filename, const char *mode); - int fclose(FILE *stream); +#define SEEK_CUR 2 +#define SEEK_END 1 +#define SEEK_SET 0 - int fseek(FILE *stream, long offset, int origin); - #define SEEK_SET 0 - #define SEEK_CUR 2 - #define SEEK_END 1 - - size_t fread(void *buffer, size_t size, size_t count, FILE *stream); - -} +extern "C" int fseek(FILE *stream, long offset, int origin); +extern "C" size_t fread(void *buffer, size_t size, size_t count, FILE *stream); diff --git a/euler/include/cstring b/euler/include/cstring index 45bc94e..cba5eb2 100644 --- a/euler/include/cstring +++ b/euler/include/cstring @@ -1,8 +1,9 @@ #pragma once -#include <stddef.h> +#include <cstddef> -namespace std { - size_t strlen(const char *str); - void *memcpy(void *dest, const void *src, size_t count); -} +extern "C" void *memset(void *dest, int ch, size_t count); +extern "C" void *memcpy(void *dest, const void *src, size_t count); + +extern "C" int strcmp(const char *lhs, const char *rhs); +extern "C" size_t strlen(const char *str); diff --git a/euler/include/euler/heap.hpp b/euler/include/euler/heap.hpp index e2a4852..ce94eef 100644 --- a/euler/include/euler/heap.hpp +++ b/euler/include/euler/heap.hpp @@ -1,8 +1,10 @@ #pragma once -#include <stdint.h> +#include <cstddef> + +namespace euler::heap { + + void *get_block(size_t length); + void return_block(void *start, size_t length); -namespace euler { - void *alloc(uint64_t bytes); - void dealloc(void *start, uint64_t bytes); } diff --git a/euler/include/euler/start_process.hpp b/euler/include/euler/start_process.hpp deleted file mode 100644 index 00ebbab..0000000 --- a/euler/include/euler/start_process.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include <euler/syscall.hpp> -#include <vector> - -namespace euler { - - struct start_process { - - private: - const char *path; - uint64_t path_len; - std::vector<__euler_env_var_spec> env_var_specs; - std::vector<__euler_gift_stream_spec> gift_stream_specs; - - public: - //path needs to stay valid through any call to start - start_process(const char *path); - - //name and value need to stay valid through any call to start - void add_env_variable(const char *name, const char *value); - - void gift_stream( - __euler_stream_handle to_gifter, __euler_stream_handle to_giftee); - - __euler_stream_result start(__euler_process_handle &handle_out); - - }; - -} diff --git a/euler/include/euler/stream.hpp b/euler/include/euler/stream.hpp index a364ec4..741d863 100644 --- a/euler/include/euler/stream.hpp +++ b/euler/include/euler/stream.hpp @@ -7,41 +7,42 @@ namespace euler { class stream { public: - virtual ~stream(); - virtual bool try_read(void *into, uint64_t bytes) = 0; - virtual bool try_seek(__euler_seek_from from, int64_t offset) = 0; + virtual ~stream() {} - }; + virtual syscall::stream_result seek( + euler::syscall::seek_from from, int64_t offset) = 0; - class file_stream : public stream { + virtual std::pair<uint64_t, syscall::stream_result> + read(uint64_t bytes, void *into) = 0; - private: + virtual void close() = 0; - __euler_stream_handle handle; - bool is_readable; - bool is_writable; - uint64_t length; - uint64_t offset; + }; - uint8_t *buffer; - uint64_t buffer_offset; - uint64_t buffer_size; - bool buffer_dirty; + class file_stream : public stream { - void write_buffer(); + syscall::stream_handle handle; + bool may_read; - public: + bool buffer_loaded; + uint64_t buffer_start; + uint8_t buffer[1024]; - bool good; + uint64_t length; + uint64_t position; + public: file_stream( - __euler_stream_handle handle, bool is_readable, bool is_writable, - bool clear, bool seek_to_end); + syscall::stream_handle handle, bool may_read, + uint64_t length, uint64_t position); + + virtual syscall::stream_result seek( + syscall::seek_from from, int64_t offset) override; - ~file_stream(); + virtual std::pair<uint64_t, syscall::stream_result> + read(uint64_t bytes, void *into) override; - bool try_read(void *into, uint64_t bytes) override; - bool try_seek(__euler_seek_from from, int64_t offset) override; + virtual void close() override; }; diff --git a/euler/include/euler/syscall.hpp b/euler/include/euler/syscall.hpp index 981925f..64456ae 100644 --- a/euler/include/euler/syscall.hpp +++ b/euler/include/euler/syscall.hpp @@ -1,102 +1,136 @@ #pragma once -#include <stdint.h> - -enum __euler_stream_result : uint64_t { - __EULER_SR_SUCCESS = 0, - __EULER_SR_BAD_HANDLE, - __EULER_SR_IO_ERROR, - __EULER_SR_OUT_OF_BOUNDS, - __EULER_SR_DOES_NOT_EXIST, - __EULER_SR_NOT_A_REGULAR_FILE, - __EULER_SR_NOT_AN_EXECUTABLE, - __EULER_SR_NOT_WRITABLE, - __EULER_SR_NOT_SEEKABLE, - __EULER_SR_SOCKET_ID_ALREADY_IN_USE, - __EULER_SR_SOCKET_ID_NOT_IN_USE, - __EULER_SR_SOCKET_LISTENER_CLOSED, - __EULER_SR_OTHER_END_CLOSED, - __EULER_SR_ALREADY_EXISTS, - __EULER_SR_NOT_SIZED -}; - -enum __euler_seek_from : uint8_t { - __EULER_SF_BEGINNING = 0, - __EULER_SF_END, - __EULER_SF_CURRENT_POSITION -}; - -typedef uint64_t __euler_stream_handle; -typedef uint64_t __euler_process_handle; - -extern "C" __euler_stream_result __euler_open_file( - const char *path, uint64_t path_len, __euler_stream_handle &handle_out, - bool allow_creation, bool only_allow_creation); - -extern "C" [[noreturn]] void __euler_end_this_thread(int32_t exit_code); - -extern "C" __euler_stream_result __euler_seek_stream( - __euler_stream_handle handle, __euler_seek_from from, int64_t offset); - -extern "C" __euler_stream_result __euler_set_stream_length( - __euler_stream_handle handle, uint64_t new_length); - -extern "C" void __euler_close_stream(__euler_stream_handle handle); - -extern "C" __euler_stream_result __euler_get_stream_length( - __euler_stream_handle handle, uint64_t &length_out); - -extern "C" void *__euler_get_new_pages(uint64_t count); - -extern "C" __euler_stream_result __euler_write_to_stream( - __euler_stream_handle handle, uint64_t count, const void *buffer); - -extern "C" __euler_stream_result __euler_read_from_stream( - __euler_stream_handle handle, uint64_t count, void *buffer); - -extern "C" uint32_t *__euler_get_framebuffer( - uint32_t &width_out, uint32_t &height_out, uint32_t &pitch_out); - -extern "C" uint32_t __euler_encode_color(uint8_t r, uint8_t g, uint8_t b); - -enum __euler_mouse_buttons : uint8_t { - __EULER_MB_LEFT = 1, - __EULER_MB_RIGHT = 2, - __EULER_MB_MIDDLE = 4 -}; - -enum __euler_input_packet_type : uint8_t { - __EULER_IPT_MOUSE = 1, - __EULER_IPT_KEYBOARD = 2 -}; - -extern "C" __euler_input_packet_type __euler_get_input_packet( - __euler_mouse_buttons &buttons_out, int16_t &x_change_out, - int16_t &y_change_out, uint32_t &keyboard_packet_out); - -struct [[gnu::packed]] __euler_env_var_spec { - uint64_t name_len; - const char *name; - uint64_t value_len; - const char *value; -}; - -struct [[gnu::packed]] __euler_gift_stream_spec { - __euler_stream_handle stream_handle_to_gifter; - __euler_stream_handle stream_handle_to_giftee; -}; - -struct [[gnu::packed]] __euler_process_start_info { - uint64_t file_path_length; - const char *file_path; - uint64_t env_var_count; - const __euler_env_var_spec *env_vars; - uint64_t gift_stream_count; - const __euler_gift_stream_spec *gift_streams; -}; - -extern "C" __euler_stream_result __euler_start_process( - const __euler_process_start_info &info, __euler_process_handle &handle_out); - -extern "C" __euler_stream_result __euler_get_other_end_process_handle( - __euler_stream_handle handle_in, __euler_process_handle &handle_out); +#include <optional> +#include <cstdint> +#include <utility> +#include <variant> +#include <string> +#include <vector> + +namespace euler::syscall { + + enum class stream_result : uint64_t { + success, + bad_handle, + io_error, + out_of_bounds, + does_not_exist, + not_a_regular_file, + not_an_executable, + not_writable, + not_seekable, + socket_id_already_used, + socket_id_not_in_use, + socket_listener_closed, + other_end_closed, + already_exists, + not_sized, + not_readable + }; + + enum class seek_from : uint8_t { + beginning, + end, + current_position + }; + + struct mouse_packet { + bool left_button_down; + bool right_button_down; + bool middle_button_down; + int16_t x_changed; + int16_t y_changed; + }; + + struct key_packet { + bool was_key_up_event; + bool num_lock; + bool caps_lock; + bool right_win; + bool left_win; + bool right_alt; + bool left_alt; + bool right_ctrl; + bool left_ctrl; + bool right_shift; + bool left_shift; + uint8_t key_code; + }; + + typedef uint32_t encoded_color; + typedef uint64_t stream_handle; + typedef uint64_t listener_handle; + typedef uint64_t process_handle; + + encoded_color encode_color( + uint8_t r, uint8_t g, uint8_t b); + + void get_framebuffer( + encoded_color *&buffer_out, uint32_t &width_out, + uint32_t &height_out, uint32_t &pitch_out); + + stream_result open_file( + const std::string &file_path, bool allow_creation, + bool only_allow_creation, stream_handle &handle_out); + + [[noreturn]] void end_this_thread(int32_t exit_code); + + void *get_new_pages(uint64_t n_pages); + + std::variant<mouse_packet, key_packet> get_input_packet(); + + void create_private_socket( + stream_handle &end_1_out, stream_handle &end_2_out); + + stream_result create_socket_listener( + const std::string &id, listener_handle &handle_out); + + void stop_socket_listener(listener_handle handle); + + stream_result accept_socket_connection( + listener_handle listener, stream_handle &stream_out); + + stream_result connect_to_socket( + const std::string &id, stream_handle &handle_out); + + void close_stream(stream_handle handle); + + stream_result seek_stream( + stream_handle handle, seek_from from, int64_t offset); + + stream_result read_from_stream( + stream_handle handle, uint64_t bytes, void *into); + + stream_result write_to_stream( + stream_handle handle, uint64_t bytes, const void *from); + + stream_result get_stream_length( + stream_handle handle, uint64_t &length_out); + + stream_result start_process( + const std::string &file_path, + const std::vector<std::pair<std::string, std::string>> &environment_variables, + const std::vector<std::pair<stream_handle, stream_result>> &gifted_streams, + process_handle &handle_out); + + [[noreturn]] void end_this_process(int32_t exit_code); + + stream_result set_stream_length( + stream_handle handle, uint64_t new_length); + + stream_result get_other_end_process_handle( + stream_handle stream, process_handle &process_out); + + //entry_point must not return + void start_thread(void (*entry_point)(uint64_t), uint64_t arg); + + //entry_point must not return + void start_thread(void (*entry_point)()); + + //return value is number of bytes cleared + uint64_t clear_socket_read_queue(stream_handle handle); + + std::optional<std::string> try_get_environment_variable( + const std::string &name); + +} diff --git a/euler/include/std/allocator.hpp b/euler/include/std/allocator.hpp new file mode 100644 index 0000000..32ba005 --- /dev/null +++ b/euler/include/std/allocator.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include <euler/heap.hpp> + +namespace std { + + template <class T> + struct allocator { + + using value_type = T; + + constexpr allocator() noexcept {} + constexpr allocator(const allocator &other) noexcept {} + + template <class U> + constexpr allocator(const allocator<U> &other) noexcept {} + + constexpr ~allocator() {} + + constexpr T *allocate(size_t n) { + return (T *)euler::heap::get_block(n * sizeof(T)); + } + + constexpr void deallocate(T *p, size_t n) { + euler::heap::return_block(p, n * sizeof(T)); + } + + }; + +} diff --git a/euler/include/std/string.hpp b/euler/include/std/string.hpp new file mode 100644 index 0000000..7ccdbc2 --- /dev/null +++ b/euler/include/std/string.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include <cstddef> +#include <vector> + +namespace std { + + class string { + + std::vector<char> characters; + + public: + constexpr string() : characters({'\0'}) {} + + constexpr string(const string &other) + : characters(other.characters) {} + + constexpr string(string &&other) noexcept + : characters(std::move(other.characters)) { + other.characters.push_back('\0'); + } + + constexpr string(const char *s) { + size_t count = 0; + while (s[count] != '\0') + ++count; + characters.resize(count + 1); + for (size_t i = 0; i <= count; ++i) + characters[i] = s[i]; + } + + constexpr ~string() {} + + constexpr string &operator =(const string &str) { + characters = str.characters; + return *this; + } + + constexpr string &operator =(string &&str) noexcept { + characters = std::move(str.characters); + str.characters.push_back('\0'); + return *this; + } + + constexpr size_t size() const noexcept { + return characters.size() - 1; + } + + constexpr void resize(size_t count) { + if (count < characters.size() - 1) { + characters.resize(count + 1); + characters[count] = '\0'; + } + else if (count > characters.size() - 1) + characters.resize(count + 1); + } + + constexpr void clear() noexcept { + resize(0); + } + + constexpr const char *data() const noexcept { + return characters.data(); + } + + constexpr char *data() noexcept { + return characters.data(); + } + + constexpr char &operator[](size_t pos) { + return characters[pos]; + } + + constexpr const char &operator[](size_t pos) const { + return characters[pos]; + } + + }; + +} diff --git a/euler/include/std/vector.hpp b/euler/include/std/vector.hpp new file mode 100644 index 0000000..a1ac21d --- /dev/null +++ b/euler/include/std/vector.hpp @@ -0,0 +1,191 @@ +#pragma once + +#include <memory> + +namespace std { + + template <class T, class Allocator = std::allocator<T>> + class vector { + + public: + using size_type = size_t; + using value_type = T; + using reference = value_type &; + using const_reference = const value_type &; + + private: + Allocator _alloc; + size_type _capacity; + size_type _size; + T *_data; + + constexpr void expand_capacity_to(size_type at_least) { + + size_type new_capacity = _capacity == 0 ? 1 : _capacity; + while (new_capacity < at_least) + new_capacity *= 2; + if (new_capacity == _capacity) + return; + + T *new_data = _alloc.allocate(new_capacity); + for (size_type i = 0; i < _size; ++i) { + std::construct_at(new_data + i, std::move(_data[i])); + std::destroy_at(_data + i); + } + if (_capacity > 0) + _alloc.deallocate(_data, _capacity); + + _capacity = new_capacity; + _data = new_data; + + } + + public: + constexpr vector() noexcept(noexcept(Allocator())) + : _alloc(), _capacity(0), _size(0), _data(0) {} + + constexpr vector(const vector &other) + : _alloc(), _capacity(other._size), _size(other._size), + _data(_alloc.allocate(_capacity)) { + for (size_type i = 0; i < _size; ++i) + std::construct_at(_data + i, other._data[i]); + } + + constexpr vector(vector &&other) noexcept + : _alloc(std::move(other._alloc)), _capacity(other._capacity), + _size(other._size), _data(other._data) { + other._capacity = 0; + other._size = 0; + } + + constexpr vector( + std::initializer_list<T> init, const Allocator &alloc = Allocator()) + : _alloc(alloc), _capacity(init.size()), + _size(init.size()), _data(_alloc.allocate(_capacity)) { + for (size_type i = 0; i < _size; ++i) + std::construct_at(_data + i, init.begin()[i]); + } + + explicit vector(size_type count) + : _alloc(), _capacity(count), _size(count), + _data(_alloc.allocate(_capacity)) { + for (size_type i = 0; i < _size; ++i) + std::construct_at(_data + i); + } + + constexpr ~vector() { + if (_capacity > 0) { + for (size_type i = 0; i < _size; ++i) + std::destroy_at(_data + i); + _alloc.deallocate(_data, _capacity); + } + } + + constexpr vector &operator =(const vector &other) { + + if (&other == this) + return *this; + + expand_capacity_to(other._size); + + for (size_type i = 0; i < _size; ++i) + std::destroy_at(_data + i); + + for (size_type i = 0; i < other._size; ++i) { + std::construct_at(_data + i, std::move(other._data[i])); + std::destroy_at(other._data + i); + } + + _size = other._size; + return *this; + + } + + constexpr vector &operator =(vector &&other) + noexcept( + std::allocator_traits<Allocator>:: + propagate_on_container_move_assignment::value || + std::allocator_traits<Allocator>:: + is_always_equal::value) { + + if (&other == this) + return *this; + + if (_capacity > 0) { + for (size_type i = 0; i < _size; ++i) + std::destroy_at(_data + i); + _alloc.deallocate(_data, _capacity); + } + + _alloc = std::move(other._alloc); + _capacity = other._capacity; + _size = other._size; + _data = other._data; + + other._capacity = 0; + other._size = 0; + + return *this; + + } + + constexpr size_type size() const noexcept { + return _size; + } + + constexpr T *data() noexcept { + return _data; + } + + constexpr const T *data() const noexcept { + return _data; + } + + constexpr reference operator[](size_type pos) { + return _data[pos]; + } + + constexpr const_reference operator[](size_type pos) const { + return _data[pos]; + } + + constexpr reference back() { + return _data[_size - 1]; + } + + constexpr const_reference back() const { + return _data[_size - 1]; + } + + constexpr void resize(size_type count) { + + if (count < _size) { + for (size_type i = count; i < _size; ++i) + std::destroy_at(_data + i); + _size = count; + } + + else if (count > _size) { + expand_capacity_to(count); + for (size_type i = _size; i < count; ++i) + std::construct_at(_data + i); + _size = count; + } + + } + + constexpr void push_back(const T &value) { + expand_capacity_to(_size + 1); + std::construct_at(_data + _size, value); + ++_size; + } + + constexpr void push_back(T &&value) { + expand_capacity_to(_size + 1); + std::construct_at(_data + _size, std::move(value)); + ++_size; + } + + }; + +} diff --git a/euler/include/string b/euler/include/string new file mode 100644 index 0000000..ce42b9e --- /dev/null +++ b/euler/include/string @@ -0,0 +1,13 @@ +#pragma once + +#include <std/string.hpp> + +namespace std { + + int stoi(const std::string &str, size_t *pos = 0, int base = 10); + + std::string to_string(int value); + + std::string operator +(std::string &&lhs, std::string &&rhs); + +} diff --git a/euler/include/type_traits b/euler/include/type_traits deleted file mode 100644 index fcea013..0000000 --- a/euler/include/type_traits +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -namespace std { - - template <class t> - struct remove_reference { - typedef t type; - }; - - template <class t> - struct remove_reference<t &> { - typedef t type; - }; - - template <class t> - struct remove_reference<t &&> { - typedef t type; - }; - - template <class t> - using remove_reference_t = typename remove_reference<t>::type; - -} diff --git a/euler/include/utility b/euler/include/utility deleted file mode 100644 index 23648c4..0000000 --- a/euler/include/utility +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include <type_traits> - -namespace std { - - template <class t> - constexpr std::remove_reference_t<t> &&move(t &&x) { - return static_cast<std::remove_reference_t<t> &&>(x); - } - -} diff --git a/euler/include/vector b/euler/include/vector index f5083ed..2184475 100644 --- a/euler/include/vector +++ b/euler/include/vector @@ -1,62 +1,3 @@ #pragma once -#include <stddef.h> -#include <utility> - -namespace std { - - template <class t> - class vector { - - t *buffer; - size_t buffer_length;//always positive - size_t count; - - public: - vector() : buffer(new t[16]), buffer_length(16), count(0) {} - - ~vector() { - delete[] buffer; - } - - t &operator[](size_t pos) { - return buffer[pos]; - } - - const t &operator[](size_t pos) const { - return buffer[pos]; - } - - t *data() { - return buffer; - } - - const t *data() const { - return buffer; - } - - size_t size() const { - return count; - } - - void reserve(size_t new_length) { - if (new_length <= buffer_length) - return; - t *new_buffer = new t[new_length]; - for (size_t i = 0; i < count; ++i) - new_buffer[i] = std::move(buffer[i]); - delete[] buffer; - buffer = new_buffer; - buffer_length = new_length; - } - - void push_back(t &&value) { - if (count == buffer_length) - reserve(count * 2); - buffer[count++] = std::move(value); - } - - //TODO - }; - -} +#include <std/vector.hpp> diff --git a/euler/makefile b/euler/makefile index 1767d75..62716f6 100644 --- a/euler/makefile +++ b/euler/makefile @@ -1,7 +1,6 @@ -LIBSTDCPP_SOURCES = euler/stream.cpp strings/strlen.cpp euler/syscall.asm \ - euler/entry.cpp io/fopen.cpp euler/gcc.asm memory/delete.cpp euler/heap.cpp \ - memory/new.cpp io/fclose.cpp io/fread.cpp strings/memcpy.cpp io/fseek.cpp \ - euler/start_process.cpp +LIBC_SOURCES = \ + entry.cpp std/string.cpp std/cstring.cpp syscall.cpp std/cstdlib.cpp \ + heap.cpp syscall.asm std/cctype.cpp std/cstdio.cpp stream.cpp clean: rm -rf build @@ -12,12 +11,12 @@ build/%.asm.o: source/%.asm build/%.cpp.o: source/%.cpp @mkdir -p $(@D) - $(HILBERT_CC) -c $^ -o $@ + $(HILBERT_CC) -ffreestanding -c $^ -o $@ build/crt0.o: build/empty.asm.o cp $^ $@ -build/libc.a: build/empty.asm.o +build/libc.a: ${LIBC_SOURCES:%=build/%.o} $(HILBERT_AR) rcs $@ $^ build/libg.a: build/empty.asm.o @@ -25,6 +24,3 @@ build/libg.a: build/empty.asm.o build/libm.a: build/empty.asm.o $(HILBERT_AR) rcs $@ $^ - -build/libstdc++.a: ${LIBSTDCPP_SOURCES:%=build/%.o} - $(HILBERT_AR) rcs $@ $^ diff --git a/euler/source/entry.cpp b/euler/source/entry.cpp new file mode 100644 index 0000000..e79209c --- /dev/null +++ b/euler/source/entry.cpp @@ -0,0 +1,28 @@ +#include <euler/syscall.hpp> +#include <cstdlib> +#include <string> + +int main(int argc, char **argv); + +extern "C" [[noreturn]] void _start() { + + auto argc_raw = euler::syscall::try_get_environment_variable("ARGC"); + int argc = argc_raw.has_value() ? std::stoi(argc_raw.value()) : 0; + + std::vector<std::string> argv; + + for (int i = 0; i < argc; ++i) { + std::string arg_name = std::string("ARGV") + std::to_string(i); + auto arg_raw = euler::syscall::try_get_environment_variable(arg_name); + argv.push_back(arg_raw.has_value() ? arg_raw.value() : ""); + } + + std::vector<char *> c_argv(argc); + for (int i = 0; i < argc; ++i) + c_argv[i] = argv[i].data(); + + int exit_code = main(argc, c_argv.data()); + + euler::syscall::end_this_process(exit_code); + +} diff --git a/euler/source/euler/entry.cpp b/euler/source/euler/entry.cpp deleted file mode 100644 index 69611b7..0000000 --- a/euler/source/euler/entry.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include <euler/syscall.hpp> - -int main(int argc, char **argv); - -extern "C" [[noreturn]] void _start() { - //TODO: call global constructors - //TODO: get argc, argv from environment - int exit_code = main(0, 0); - //TODO: call global destructors - __euler_end_this_thread(exit_code); -} diff --git a/euler/source/euler/gcc.asm b/euler/source/euler/gcc.asm deleted file mode 100644 index 6fc6fd5..0000000 --- a/euler/source/euler/gcc.asm +++ /dev/null @@ -1,53 +0,0 @@ -bits 64 - -global strlen - -section .text - -strlen: - xor rax, rax -.loop: - mov rdx, qword [rdi] - test edx, 0xff - jz .plus0 - test edx, 0xff00 - jz .plus1 - test edx, 0xff0000 - jz .plus2 - test edx, 0xff000000 - jz .plus3 - shr rdx, 32 - test edx, 0xff - jz .plus4 - test edx, 0xff00 - jz .plus5 - test edx, 0xff0000 - jz .plus6 - test edx, 0xff000000 - jz .plus7 - add rax, 8 - add rdi, 8 - jmp .loop -.plus0: - ret -.plus1: - add rax, 1 - ret -.plus2: - add rax, 2 - ret -.plus3: - add rax, 3 - ret -.plus4: - add rax, 4 - ret -.plus5: - add rax, 5 - ret -.plus6: - add rax, 6 - ret -.plus7: - add rax, 7 - ret diff --git a/euler/source/euler/heap.cpp b/euler/source/euler/heap.cpp deleted file mode 100644 index f7d407f..0000000 --- a/euler/source/euler/heap.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include <euler/syscall.hpp> -#include <euler/heap.hpp> - -namespace euler { - - static uint64_t *last_chunk_info_page = 0; - - void add_record(uint64_t start, uint64_t length) { - uint64_t dual_start = start ^ length; - for (uint64_t *cip = last_chunk_info_page; cip; cip = (uint64_t *)cip[510]) - for (int i = 0; i < 255; ++i) - if (cip[i * 2] == length && cip[i * 2 + 1] == dual_start) { - cip[i * 2] = length * 2; - cip[i * 2 + 1] &= start; - return; - } - for (uint64_t *cip = last_chunk_info_page; cip; cip = (uint64_t *)cip[510]) - for (int i = 0; i < 255; ++i) - if (cip[i * 2] == 0) { - cip[i * 2] = length; - cip[i * 2 + 1] = start; - return; - } - uint64_t *new_cip = (uint64_t *)__euler_get_new_pages(1); - new_cip[0] = length; - new_cip[1] = start; - for (int i = 1; i < 255; ++i) - new_cip[i * 2] = 0; - new_cip[510] = (uint64_t)last_chunk_info_page; - last_chunk_info_page = new_cip; - } - - void *alloc(uint64_t bytes) { - for (uint64_t *cip = last_chunk_info_page; cip; cip = (uint64_t *)cip[510]) - for (int i = 0; i < 255; ++i) - if (cip[i * 2] >= bytes) { - uint64_t start = cip[i * 2 + 1]; - uint64_t extra_length = cip[i * 2] - bytes; - cip[i * 2] = 0; - if (extra_length > 0) - dealloc((void *)(start + bytes), extra_length); - return (void *)start; - } - uint64_t pages_needed = (bytes - 1) / 4096 + 1; - uint64_t start = (uint64_t)__euler_get_new_pages(pages_needed); - if (bytes != pages_needed * 4096) - dealloc((void *)(start + bytes), pages_needed * 4096 - bytes); - return (void *)start; - } - - //this implementation is a little lazy - void dealloc(void *start, uint64_t bytes) { - uint64_t s = (uint64_t)start; - while (bytes > 0) { - uint64_t l = 1; - do - l *= 2; - while (l <= bytes && s % l == 0); - l /= 2; - add_record(s, l); - s += l; - bytes -= l; - } - } - -} diff --git a/euler/source/euler/start_process.cpp b/euler/source/euler/start_process.cpp deleted file mode 100644 index 64e16c5..0000000 --- a/euler/source/euler/start_process.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include <euler/start_process.hpp> -#include <cstring> - -namespace euler { - - start_process::start_process(const char *path) - : path(path), path_len(std::strlen(path)) {} - - void start_process::add_env_variable(const char *name, const char *value) { - env_var_specs.push_back({ - .name_len = std::strlen(name), .name = name, - .value_len = std::strlen(value), .value = value }); - } - - void start_process::gift_stream( - __euler_stream_handle to_gifter, __euler_stream_handle to_giftee) { - gift_stream_specs.push_back({ - .stream_handle_to_gifter = to_gifter, - .stream_handle_to_giftee = to_giftee }); - } - - __euler_stream_result start_process::start( - __euler_process_handle &handle_out) { - __euler_process_start_info info = { - .file_path_length = path_len, - .file_path = path, - .env_var_count = env_var_specs.size(), - .env_vars = env_var_specs.data(), - .gift_stream_count = gift_stream_specs.size(), - .gift_streams = gift_stream_specs.data(), - }; - return __euler_start_process(info, handle_out); - } - -} diff --git a/euler/source/euler/stream.cpp b/euler/source/euler/stream.cpp deleted file mode 100644 index 950f3c5..0000000 --- a/euler/source/euler/stream.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include <euler/stream.hpp> -#include <cstring> - -namespace euler { - - stream::~stream() {} - - void file_stream::write_buffer() { - if (__euler_write_to_stream(handle, buffer_size, buffer) - != __EULER_SR_SUCCESS) - good = false;//TODO: more precise error reporting - else - buffer_dirty = false; - } - - file_stream::file_stream( - __euler_stream_handle handle, bool is_readable, bool is_writable, - bool clear, bool seek_to_end) - - : handle(handle), is_readable(is_readable), is_writable(is_writable), - buffer(0), good(true) { - - if (clear) { - if (__euler_set_stream_length(handle, 0) != __EULER_SR_SUCCESS) { - good = false; - return; - } - length = 0; - } - - else if (__euler_get_stream_length(handle, length) != __EULER_SR_SUCCESS) { - good = false; - return; - } - - if (seek_to_end) { - if (__euler_seek_stream(handle, __EULER_SF_END, 0) - != __EULER_SR_SUCCESS) { - good = false; - return; - } - offset = length; - } - - else - offset = 0; - - } - - file_stream::~file_stream() { - if (buffer) { - if (buffer_dirty) - write_buffer(); - delete[] buffer; - } - __euler_close_stream(handle); - } - - bool file_stream::try_read(void *into, uint64_t bytes) { - - if (bytes == 0) - return true; - - if (offset + bytes > length) - return false; - - if (buffer) { - uint64_t start = offset > buffer_offset ? offset : buffer_offset; - uint64_t end = offset + bytes < buffer_offset + buffer_size - ? offset + bytes : buffer_offset + buffer_size; - if (end > start) { - - uint64_t real_end = offset + bytes; - - std::memcpy( - (uint8_t *)into + start - offset, buffer + start - buffer_offset, - end - start); - if (start != offset) - if (!try_read(into, start - offset)) - return false; - - if (end != real_end) { - if (!try_seek(__EULER_SF_BEGINNING, end)) - return false; - if (!try_read((uint8_t *)into + end, real_end - end)) - return false; - } - - else if (offset != real_end) - if (!try_seek(__EULER_SF_BEGINNING, real_end)) - return false; - - return true; - - } - } - - if (buffer_dirty) { - write_buffer(); - if (!good) - return false; - } - - //1024 is arbitrary - buffer_size = length - offset < 1024 ? length - offset : 1024; - - if (buffer != 0) - delete[] buffer; - - buffer = new uint8_t[buffer_size]; - buffer_dirty = false; - buffer_offset = offset; - - if (__euler_read_from_stream(handle, buffer_size, buffer) - != __EULER_SR_SUCCESS) { - delete[] buffer; - buffer = 0; - return false; - } - - uint64_t read_this_buffer = buffer_size < bytes ? buffer_size : bytes; - std::memcpy(into, buffer, read_this_buffer); - offset += read_this_buffer; - - if (read_this_buffer == bytes) - return true; - - return try_read( - (uint8_t *)into + read_this_buffer, bytes - read_this_buffer); - - } - - bool file_stream::try_seek(__euler_seek_from from, int64_t offset) { - if (__euler_seek_stream(handle, from, offset) != __EULER_SR_SUCCESS) - return false; - switch (from) { - case __EULER_SF_BEGINNING: - this->offset = offset; - return true; - case __EULER_SF_END: - this->offset = length + offset; - return true; - case __EULER_SF_CURRENT_POSITION: - this->offset += offset; - return true; - default: - return false; - } - } - -} diff --git a/euler/source/euler/syscall.asm b/euler/source/euler/syscall.asm deleted file mode 100644 index 64f08f8..0000000 --- a/euler/source/euler/syscall.asm +++ /dev/null @@ -1,144 +0,0 @@ -bits 64 - -global __euler_open_file -global __euler_close_stream -global __euler_get_new_pages -global __euler_write_to_stream -global __euler_seek_stream -global __euler_get_stream_length -global __euler_set_stream_length -global __euler_end_this_thread -global __euler_read_from_stream -global __euler_get_framebuffer -global __euler_encode_color -global __euler_get_input_packet -global __euler_start_process -global __euler_get_other_end_process_handle - -section .text - -__euler_open_file: - mov rax, 2 - push rdx - xor rdx, rdx - or rdx, r8 - shl r9, 1 - or rdx, r9 - syscall - pop rdx - mov qword [rdx], rdi - ret - -__euler_close_stream: - mov rax, 11 - syscall - ret - -__euler_get_new_pages: - mov rax, 4 - syscall - ret - -__euler_write_to_stream: - mov rax, 14 - syscall - ret - -__euler_seek_stream: - mov rax, 12 - syscall - ret - -__euler_get_stream_length: - push rsi - mov rax, 15 - syscall - pop rsi - mov qword [rsi], rdi - ret - -__euler_set_stream_length: - mov rax, 18 - syscall - ret - -__euler_end_this_thread: - mov rax, 3 - syscall - -__euler_read_from_stream: - mov rax, 13 - syscall - ret - -__euler_get_framebuffer: - push rdi - push rsi - push rdx - mov rax, 1 - syscall - pop rdx - mov dword [rdx], esi - pop rdx - pop rsi - mov dword [rsi], edi - shr rdi, 32 - mov dword [rdx], edi - ret - -__euler_encode_color: - xor ah, ah - mov al, dl - shl ax, 8 - mov al, sil - shl eax, 8 - mov al, dil - mov edi, eax - xor rax, rax - syscall - ret - -__euler_get_input_packet: - push rdi - push rsi - push rdx - push rcx - mov rax, 5 - syscall - - test al, 0x80 - jnz .mouse_packet - - pop rcx - mov dword [rcx], edi - add rsp, 24 - mov al, 2 - ret - -.mouse_packet: - add rsp, 8 - pop rdx - mov word [rdx], si - pop rdx - mov word [rdx], di - pop rdx - and al, 0x7f - mov byte [rdx], al - mov al, 1 - ret - -__euler_start_process: - push rsi - mov rax, 16 - syscall - pop rsi - mov qword [rsi], rdi - ret - -__euler_get_other_end_process_handle: - push rsi - mov rax, 19 - syscall - pop rsi - mov qword [rsi], rdi - ret diff --git a/euler/source/heap.cpp b/euler/source/heap.cpp new file mode 100644 index 0000000..dc92e5d --- /dev/null +++ b/euler/source/heap.cpp @@ -0,0 +1,155 @@ +#include <euler/syscall.hpp> +#include <euler/heap.hpp> +#include <cstdint> + +namespace euler::heap { + + struct heap_aligned_entry { + size_t start_vaddr; + size_t length; + heap_aligned_entry *prev; + heap_aligned_entry *next; + }; + + struct unused_heap_entries_page { + heap_aligned_entry *unused[510]; + unused_heap_entries_page *prev; + uint16_t unused_in_this_page; + }; + + heap_aligned_entry *first_entry = 0; + heap_aligned_entry *last_entry = 0; + unused_heap_entries_page *last_page = 0; + + void mark_entry_unused(heap_aligned_entry *entry) { + + for (unused_heap_entries_page *page = last_page; page != 0; page = page->prev) { + if (page->unused_in_this_page == 510) + continue; + for (uint16_t i = 0; i < 510; ++i) + if (page->unused[i] == 0) { + page->unused[i] = entry; + ++page->unused_in_this_page; + return; + } + } + + unused_heap_entries_page *page = (unused_heap_entries_page *)euler::syscall::get_new_pages(1); + page->prev = last_page; + last_page = page; + + page->unused_in_this_page = 1; + page->unused[0] = entry; + + for (uint16_t i = 1; i < 510; ++i) + page->unused[i] = 0; + + } + + void remove_entry(heap_aligned_entry *entry) { + + if (entry->prev) + entry->prev->next = entry->next; + else + first_entry = entry->next; + if (entry->next) + entry->next->prev = entry->prev; + else + last_entry = entry->prev; + + mark_entry_unused(entry); + + } + + heap_aligned_entry *get_new_entry() { + + for (unused_heap_entries_page *page = last_page; page != 0; page = page->prev) { + if (page->unused_in_this_page == 0) + continue; + for (uint16_t i = 0; i < 510; ++i) + if (page->unused[i] != 0) { + heap_aligned_entry *entry = page->unused[i]; + page->unused[i] = 0; + --page->unused_in_this_page; + return entry; + } + } + + heap_aligned_entry *new_entries = (heap_aligned_entry *)euler::syscall::get_new_pages(1); + + for (uint8_t i = 1; i < 128; ++i) + mark_entry_unused(new_entries + i); + + return new_entries; + + } + + void *get_block(size_t length) { + + for (heap_aligned_entry *entry = first_entry; entry != 0; entry = entry->next) { + if (entry->length == length) { + void *start = (void *)entry->start_vaddr; + remove_entry(entry); + return start; + } + if (entry->length > length) { + void *start = (void *)entry->start_vaddr; + entry->start_vaddr += length; + entry->length -= length; + return start; + } + } + + size_t pages = (length - 1) / 4096 + 1; + void *new_memory = euler::syscall::get_new_pages(pages); + if (pages * 4096 != length) + return_block((uint8_t *)new_memory + length, pages * 4096 - length); + return new_memory; + + } + + void return_block(void *start, size_t length) { + + heap_aligned_entry *after = first_entry; + while (after != 0 && after->start_vaddr < (size_t)start + length) + after = after->next; + + heap_aligned_entry *before; + if (after == 0) + before = last_entry; + else + before = after->prev; + + heap_aligned_entry *us; + + if (after && after->start_vaddr == (size_t)start + length) { + after->start_vaddr -= length; + after->length += length; + us = after; + } + + else { + us = get_new_entry(); + us->prev = before; + us->next = after; + us->start_vaddr = (size_t)start; + us->length = length; + if (before) + before->next = us; + else + first_entry = us; + if (after) + after->prev = us; + else + last_entry = us; + } + + if (before && before->start_vaddr + before->length == (size_t)start) { + us->start_vaddr -= before->length; + us->length += before->length; + remove_entry(before); + } + + } + +} diff --git a/euler/source/io/fclose.cpp b/euler/source/io/fclose.cpp deleted file mode 100644 index 6f43f85..0000000 --- a/euler/source/io/fclose.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include <cstdio> - -namespace std { - - int fclose(FILE *stream) { - delete stream; - return 0; - } - -} diff --git a/euler/source/io/fopen.cpp b/euler/source/io/fopen.cpp deleted file mode 100644 index 8d47bf0..0000000 --- a/euler/source/io/fopen.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include <stdint.h> -#include <cstring> -#include <cstdio> - -namespace std { - - FILE *fopen(const char *filename, const char *mode) { - - bool read = false; - bool write = false; - bool append = false; - bool extended = false; - bool create = false; - - for (const char *p = mode; *p; ++p) - switch (*p) { - case 'r': - read = true; - continue; - case 'w': - write = true; - continue; - case 'a': - append = true; - continue; - case '+': - extended = true; - continue; - case 'x': - create = true; - continue; - default: - continue; - } - - __euler_stream_handle handle; - __euler_stream_result res = __euler_open_file( - filename, strlen(filename), handle, write || append, create); - - if (res != __EULER_SR_SUCCESS) - return 0; - - euler::file_stream *f = new euler::file_stream(handle, read || extended, - write || extended, write && !append, append); - - if (f->good) - return f; - - delete f; - return 0; - - } - -} diff --git a/euler/source/io/fread.cpp b/euler/source/io/fread.cpp deleted file mode 100644 index e2d05b6..0000000 --- a/euler/source/io/fread.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include <cstdio> - -namespace std { - - size_t fread(void *buffer, size_t size, size_t count, FILE *stream) { - return stream->try_read(buffer, size * count) ? count : 0; - } - -} diff --git a/euler/source/io/fseek.cpp b/euler/source/io/fseek.cpp deleted file mode 100644 index 3254468..0000000 --- a/euler/source/io/fseek.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include <cstdio> - -namespace std { - - int fseek(FILE *stream, long offset, int origin) { - if (origin < 0 || origin > 2) - return 1; - return stream->try_seek((__euler_seek_from)origin, offset) ? 0 : 2; - } - -} diff --git a/euler/source/memory/delete.cpp b/euler/source/memory/delete.cpp deleted file mode 100644 index e4cc288..0000000 --- a/euler/source/memory/delete.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include <euler/heap.hpp> - -void operator delete(void *ptr) { - euler::dealloc((uint8_t *)ptr - 8, *(uint64_t *)((uint8_t *)ptr - 8)); -} - -void operator delete(void *ptr, uint64_t) { - euler::dealloc((uint8_t *)ptr - 8, *(uint64_t *)((uint8_t *)ptr - 8)); -} - -void operator delete[](void *ptr) { - euler::dealloc((uint8_t *)ptr - 8, *(uint64_t *)((uint8_t *)ptr - 8)); -} - -void operator delete[](void *ptr, uint64_t) { - euler::dealloc((uint8_t *)ptr - 8, *(uint64_t *)((uint8_t *)ptr - 8)); -} diff --git a/euler/source/memory/new.cpp b/euler/source/memory/new.cpp deleted file mode 100644 index 931328f..0000000 --- a/euler/source/memory/new.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include <euler/heap.hpp> - -void *operator new(uint64_t size) { - void *ptr = euler::alloc(size + 8); - *(uint64_t *)ptr = size + 8; - return (uint8_t *)ptr + 8; -} - -void *operator new[](uint64_t size) { - void *ptr = euler::alloc(size + 8); - *(uint64_t *)ptr = size + 8; - return (uint8_t *)ptr + 8; -} diff --git a/euler/source/std/cctype.cpp b/euler/source/std/cctype.cpp new file mode 100644 index 0000000..a35d1a5 --- /dev/null +++ b/euler/source/std/cctype.cpp @@ -0,0 +1,7 @@ +#include <cctype> + +extern "C" int isspace(int ch) { + return + ch == ' ' || ch == '\f' || ch == '\n' || + ch == '\r' || ch == '\t' || ch == '\v'; +} diff --git a/euler/source/std/cstdio.cpp b/euler/source/std/cstdio.cpp new file mode 100644 index 0000000..8c12a7c --- /dev/null +++ b/euler/source/std/cstdio.cpp @@ -0,0 +1,59 @@ +#include <cstdio> + +extern "C" FILE *fopen(const char *filename, const char *mode) { + + bool r = false, w = false, a = false, p = false, x = false; + for (size_t i = 0; mode[i] != '\0'; ++i) + if (mode[i] == 'r') r = true; + else if (mode[i] == 'w') w = true; + else if (mode[i] == 'a') a = true; + else if (mode[i] == 'p') p = true; + else if (mode[i] == 'x') x = true; + + euler::syscall::stream_handle handle; + if (euler::syscall::open_file(filename, !r, x, handle) != + euler::syscall::stream_result::success) + return 0; + + if (w && euler::syscall::set_stream_length(handle, 0) != + euler::syscall::stream_result::success) + return 0; + + if (a && euler::syscall::seek_stream( + handle, euler::syscall::seek_from::end, 0) != + euler::syscall::stream_result::success) { + euler::syscall::close_stream(handle); + return 0; + } + + uint64_t length; + if (euler::syscall::get_stream_length(handle, length) != + euler::syscall::stream_result::success) { + euler::syscall::close_stream(handle); + return 0; + } + + return new euler::file_stream(handle, r || p, length, a ? length : 0); + +} + +extern "C" void fclose(FILE *stream) { + stream->close(); + delete stream; +} + +extern "C" int fseek(FILE *stream, long offset, int origin) { + + if (origin < 0 || origin > 2) + return -1; + + return (int)stream->seek((euler::syscall::seek_from)origin, offset); + +} + +extern "C" size_t fread( + void *buffer, size_t size, size_t count, FILE *stream) { + + return (stream->read(size * count, buffer).first - 1) / size + 1; + +} diff --git a/euler/source/std/cstdlib.cpp b/euler/source/std/cstdlib.cpp new file mode 100644 index 0000000..cfb4b48 --- /dev/null +++ b/euler/source/std/cstdlib.cpp @@ -0,0 +1,19 @@ +#include <euler/heap.hpp> +#include <cstdlib> + +extern "C" [[noreturn]] void abort() noexcept { + //TODO + while (1) + ; +} + +extern "C" void *malloc(size_t size) { + size_t *block = (size_t *)euler::heap::get_block(size + 8); + *block = size; + return block + 1; +} + +extern "C" void free(void *ptr) { + size_t *block = (size_t *)ptr - 1; + euler::heap::return_block(block, *block + 8); +} diff --git a/euler/source/std/cstring.cpp b/euler/source/std/cstring.cpp new file mode 100644 index 0000000..3e5a56c --- /dev/null +++ b/euler/source/std/cstring.cpp @@ -0,0 +1,36 @@ +#include <cstring> + +extern "C" void *memset(void *dest, int ch, size_t count) { + unsigned char c = static_cast<unsigned char>(ch); + unsigned char *d = (unsigned char *)dest; + for (size_t i = 0; i < count; ++i) + d[i] = c; + return dest; +} + +extern "C" void *memcpy(void *dest, const void *src, size_t count) { + unsigned char *d = (unsigned char *)dest; + const unsigned char *s = (const unsigned char *)src; + for (size_t i = 0; i < count; ++i) + d[i] = s[i]; + return dest; +} + +extern "C" int strcmp(const char *lhs, const char *rhs) { + const unsigned char *l = (const unsigned char *)lhs; + const unsigned char *r = (const unsigned char *)rhs; + while (*l == *r) { + if (*l == 0) + return 0; + ++l; + ++r; + } + return *l < *r ? -1 : 1; +} + +extern "C" size_t strlen(const char *str) { + size_t len = 0; + while (str[len] != '\0') + ++len; + return len; +} diff --git a/euler/source/std/string.cpp b/euler/source/std/string.cpp new file mode 100644 index 0000000..31c47a5 --- /dev/null +++ b/euler/source/std/string.cpp @@ -0,0 +1,86 @@ +#include <cctype> +#include <string> + +namespace std { + + int stoi(const std::string &str, size_t *pos, int base) { + //TODO: exceptions + + size_t i = 0; + while (isspace(str[i])) + ++i; + + bool is_negative = false; + if (str[i] == '-') { + is_negative = true; + ++i; + } + else if (str[i] == '+') + ++i; + + if ((base == 16 || base == 0) && str[i] == '0' && + (str[i + 1] == 'x' || str[i + 1] == 'X')) { + base = 16; + i += 2; + } + + else if ((base == 8 || base == 0) && str[i] == '0') { + base = 8; + ++i; + } + + else if (base == 0) + base = 10; + + int value = 0; + + while (true) { + char c = str[i]; + if (c >= '0' && c < '0' + base) + value = value * base + c - '0'; + else if (c >= 'a' && c < 'a' + base - 10) + value = value * base + c - 'a' + 10; + else if (c >= 'A' && c < 'A' + base - 10) + value = value * base + c - 'A' + 10; + else + break; + } + + if (pos != 0) + *pos = i; + + return is_negative ? -value : value; + + } + + std::string to_string(int value) { + + int max_place = 1; + int places = 1; + while (max_place <= value / 10) { + max_place *= 10; + ++places; + } + + std::string s; + s.resize(places); + + for (int i = 0; i < places; ++i) { + s[i] = (value / max_place) % 10 + '0'; + max_place /= 10; + } + + return s; + + } + + std::string operator +(std::string &&lhs, std::string &&rhs) { + std::string s = std::move(lhs); + s.resize(lhs.size() + rhs.size()); + for (size_t i = 0; i < rhs.size(); ++i) + s[lhs.size() + i] = rhs[i]; + rhs.clear(); + return s; + } + +} diff --git a/euler/source/stream.cpp b/euler/source/stream.cpp new file mode 100644 index 0000000..faf2907 --- /dev/null +++ b/euler/source/stream.cpp @@ -0,0 +1,106 @@ +#include <euler/stream.hpp> +#include <cstring> + +namespace euler { + + file_stream::file_stream( + syscall::stream_handle handle, bool may_read, + uint64_t length, uint64_t position) + : handle(handle), may_read(may_read), buffer_loaded(false), + length(length), position(position) {} + + syscall::stream_result file_stream::seek( + syscall::seek_from from, int64_t offset) { + + int64_t new_position = offset + + (from == syscall::seek_from::beginning ? 0 : + from == syscall::seek_from::end ? length : position); + + if (new_position < 0 || (uint64_t)new_position > length) + return syscall::stream_result::out_of_bounds; + + position = new_position; + return syscall::stream_result::success; + + } + + std::pair<uint64_t, syscall::stream_result> + file_stream::read(uint64_t bytes, void *into) { + + if (!may_read) + return {0, syscall::stream_result::not_readable}; + + uint64_t have_read = 0; + syscall::stream_result result = syscall::stream_result::success; + + uint64_t end = position + bytes; + + if (end > length) { + end = length; + result = syscall::stream_result::out_of_bounds; + } + + uint64_t block_start = (position / 1024) * 1024; + + while (position < end) { + + uint64_t length_in_this_block = + std::min(end, block_start + 1024) - position; + + if (buffer_loaded && buffer_start == block_start) + memcpy(into, buffer + position - block_start, length_in_this_block); + + else if (length_in_this_block == 1024) { + + syscall::stream_result seek_result = + syscall::seek_stream( + handle, syscall::seek_from::beginning, block_start); + if (seek_result != syscall::stream_result::success) + return {have_read, seek_result}; + + syscall::stream_result read_result = + syscall::read_from_stream(handle, 1024, into); + if (read_result != syscall::stream_result::success) + return {have_read, read_result}; + + } + + else { + + syscall::stream_result seek_result = + syscall::seek_stream( + handle, syscall::seek_from::beginning, block_start); + if (seek_result != syscall::stream_result::success) + return {have_read, seek_result}; + + uint64_t buffer_length = std::min(1024UL, length - block_start); + + syscall::stream_result read_result = + syscall::read_from_stream(handle, buffer_length, buffer); + if (read_result != syscall::stream_result::success) { + buffer_loaded = false; + return {have_read, read_result}; + } + + buffer_loaded = true; + buffer_start = block_start; + memcpy(into, buffer + position - block_start, length_in_this_block); + + } + + into = (uint8_t *)into + length_in_this_block; + have_read += length_in_this_block; + position += length_in_this_block; + block_start += 1024; + + } + + return {have_read, result}; + + } + + void file_stream::close() { + syscall::close_stream(handle); + } + +} diff --git a/euler/source/strings/memcpy.cpp b/euler/source/strings/memcpy.cpp deleted file mode 100644 index d5a1d6c..0000000 --- a/euler/source/strings/memcpy.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include <stdint.h> -#include <cstring> - -namespace std { - - void *memcpy(void *dest, const void *src, size_t count) { - uint8_t *d8 = (uint8_t *)dest; - const uint8_t *s8 = (const uint8_t *)src; - for (size_t i = 0; i < count; ++i) - d8[i] = s8[i]; - return dest; - } - -} diff --git a/euler/source/strings/strlen.cpp b/euler/source/strings/strlen.cpp deleted file mode 100644 index 7a6fe2a..0000000 --- a/euler/source/strings/strlen.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include <cstring> - -namespace std { - - size_t strlen(const char *str) { - size_t len = 0; - while (str[len]) - ++len; - return len; - } - -} diff --git a/euler/source/syscall.asm b/euler/source/syscall.asm new file mode 100644 index 0000000..d4515bb --- /dev/null +++ b/euler/source/syscall.asm @@ -0,0 +1,32 @@ +bits 64 + +section .text + +global __euler_do_syscall +__euler_do_syscall: + + push rdi + push rsi + push rdx + push rcx + + mov rax, qword [rdi] + mov rdi, qword [rsi] + mov rsi, qword [rdx] + mov rdx, qword [rcx] + + syscall + + pop rcx + mov qword [rcx], rdx + + pop rdx + mov qword [rdx], rsi + + pop rsi + mov qword [rsi], rdi + + pop rdi + mov qword [rdi], rax + + ret diff --git a/euler/source/syscall.cpp b/euler/source/syscall.cpp new file mode 100644 index 0000000..b3ed3a8 --- /dev/null +++ b/euler/source/syscall.cpp @@ -0,0 +1,397 @@ +#include <euler/syscall.hpp> + +extern "C" void __euler_do_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx); + +namespace euler::syscall { + + encoded_color encode_color( + uint8_t r, uint8_t g, uint8_t b) { + + uint64_t rax = 0; + uint64_t rdi = (uint32_t)r | ((uint32_t)g << 8) | ((uint32_t)b << 16); + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return (encoded_color)(rax & 0xffffffff); + + } + + void get_framebuffer( + encoded_color *&buffer_out, uint32_t &width_out, + uint32_t &height_out, uint32_t &pitch_out) { + + uint64_t rax = 1; + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + buffer_out = (encoded_color *)rax; + width_out = rdi & 0xffffffff; + height_out = rdi >> 32; + pitch_out = rsi & 0xffffffff; + + } + + stream_result open_file( + const std::string &file_path, bool allow_creation, + bool only_allow_creation, stream_handle &handle_out) { + + uint64_t rax = 2; + uint64_t rdi = (uint64_t)file_path.data(); + uint64_t rsi = file_path.size(); + uint64_t rdx = + ( allow_creation ? 0x1 : 0x0) | + (only_allow_creation ? 0x2 : 0x0); + + __euler_do_syscall(rax, rdi, rsi, rdx); + + handle_out = rdi; + return (stream_result)rax; + + } + + [[noreturn]] void end_this_thread(int32_t exit_code) { + + uint64_t rax = 3; + uint64_t rdi = (uint32_t)exit_code; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + __builtin_unreachable(); + + } + + void *get_new_pages(uint64_t n_pages) { + + uint64_t rax = 4; + uint64_t rdi = n_pages; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return (void *)rax; + + } + + std::variant<mouse_packet, key_packet> get_input_packet() { + + uint64_t rax = 5; + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + if (rax & 0x80) + return (mouse_packet){ + . left_button_down = (rax & 0x01) != 0, + . right_button_down = (rax & 0x02) != 0, + .middle_button_down = (rax & 0x04) != 0, + .x_changed = (int16_t)(rdi & 0xffff), + .y_changed = (int16_t)(rsi & 0xffff) + }; + + return (key_packet){ + .was_key_up_event = (rdi & 0x40000) != 0, + . num_lock = (rdi & 0x20000) != 0, + . caps_lock = (rdi & 0x10000) != 0, + . right_win = (rdi & 0x8000) != 0, + . left_win = (rdi & 0x4000) != 0, + . right_alt = (rdi & 0x2000) != 0, + . left_alt = (rdi & 0x1000) != 0, + . right_ctrl = (rdi & 0x800) != 0, + . left_ctrl = (rdi & 0x400) != 0, + . right_shift = (rdi & 0x200) != 0, + . left_shift = (rdi & 0x100) != 0, + . key_code = (uint8_t)(rdi & 0xff) + }; + + } + + void create_private_socket( + stream_handle &end_1_out, stream_handle &end_2_out) { + + uint64_t rax = 6; + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + end_1_out = rax; + end_2_out = rdi; + + } + + stream_result create_socket_listener( + const std::string &id, listener_handle &handle_out) { + + uint64_t rax = 7; + uint64_t rdi = (uint64_t)id.data(); + uint64_t rsi = id.size(); + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + handle_out = rdi; + return (stream_result)rax; + + } + + void stop_socket_listener(listener_handle handle) { + + uint64_t rax = 8; + uint64_t rdi = handle; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + } + + stream_result accept_socket_connection( + listener_handle listener, stream_handle &stream_out) { + + uint64_t rax = 9; + uint64_t rdi = listener; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + stream_out = rdi; + return (stream_result)rax; + + } + + stream_result connect_to_socket( + const std::string &id, stream_handle &handle_out) { + + uint64_t rax = 10; + uint64_t rdi = (uint64_t)id.data(); + uint64_t rsi = id.size(); + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + handle_out = rdi; + return (stream_result)rax; + + } + + void close_stream(stream_handle handle) { + + uint64_t rax = 11; + uint64_t rdi = handle; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + } + + stream_result seek_stream( + stream_handle handle, seek_from from, int64_t offset) { + + uint64_t rax = 12; + uint64_t rdi = (uint64_t)handle; + uint64_t rsi = (uint8_t)from; + uint64_t rdx = (uint64_t)offset; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return (stream_result)rax; + + } + + stream_result read_from_stream( + stream_handle handle, uint64_t bytes, void *into) { + + uint64_t rax = 13; + uint64_t rdi = (uint64_t)handle; + uint64_t rsi = bytes; + uint64_t rdx = (uint64_t)into; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return (stream_result)rax; + + } + + stream_result write_to_stream( + stream_handle handle, uint64_t bytes, const void *from) { + + uint64_t rax = 14; + uint64_t rdi = (uint64_t)handle; + uint64_t rsi = bytes; + uint64_t rdx = (uint64_t)from; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return (stream_result)rax; + + } + + stream_result get_stream_length( + stream_handle handle, uint64_t &length_out) { + + uint64_t rax = 15; + uint64_t rdi = (uint64_t)handle; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + length_out = rdi; + return (stream_result)rax; + + } + + stream_result start_process( + const std::string &file_path, + const std::vector<std::pair<std::string, std::string>> &environment_variables, + const std::vector<std::pair<stream_handle, stream_result>> &gifted_streams, + process_handle &handle_out) { + + std::vector<uint64_t> ev_structs(environment_variables.size() * 4); + for (size_t i = 0; i < environment_variables.size(); ++i) { + ev_structs[i * 4] = environment_variables[i]. first.size(); + ev_structs[i * 4 + 1] = (uint64_t)environment_variables[i]. first.data(); + ev_structs[i * 4 + 2] = environment_variables[i].second.size(); + ev_structs[i * 4 + 3] = (uint64_t)environment_variables[i].second.data(); + } + + std::vector<uint64_t> gs_structs(gifted_streams.size() * 2); + for (size_t i = 0; i < environment_variables.size(); ++i) { + gs_structs[i * 2] = (uint64_t)gifted_streams[i]. first; + gs_structs[i * 2 + 1] = (uint64_t)gifted_streams[i].second; + } + + uint64_t psi_struct[] = { + file_path.size(), (uint64_t) file_path.data(), + environment_variables.size(), (uint64_t)ev_structs.data(), + gifted_streams.size(), (uint64_t)gs_structs.data() + }; + + uint64_t rax = 16; + uint64_t rdi = (uint64_t)psi_struct; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + handle_out = rdi; + return (stream_result)rax; + + } + + [[noreturn]] void end_this_process(int32_t exit_code) { + + uint64_t rax = 17; + uint64_t rdi = (uint32_t)exit_code; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + __builtin_unreachable(); + + } + + stream_result set_stream_length( + stream_handle handle, uint64_t new_length) { + + uint64_t rax = 18; + uint64_t rdi = (uint64_t)handle; + uint64_t rsi = new_length; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return (stream_result)rax; + + } + + stream_result get_other_end_process_handle( + stream_handle stream, process_handle &process_out) { + + uint64_t rax = 19; + uint64_t rdi = (uint64_t)stream; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + process_out = (process_handle)rdi; + return (stream_result)rax; + + } + + //entry_point must not return + void start_thread(void (*entry_point)(uint64_t), uint64_t arg) { + + uint64_t rax = 20; + uint64_t rdi = (uint64_t)entry_point; + uint64_t rsi = arg; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + } + + //entry_point must not return + void start_thread(void (*entry_point)()) { + + uint64_t rax = 20; + uint64_t rdi = (uint64_t)entry_point; + uint64_t rsi = 0; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + } + + //return value is number of bytes cleared + uint64_t clear_socket_read_queue(stream_handle handle) { + + uint64_t rax = 21; + uint64_t rdi = handle; + uint64_t rsi; + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + return rax; + + } + + //this function has a race condition if the variable is changed + //or unset by another thread between the two syscalls. + std::optional<std::string> try_get_environment_variable( + const std::string &name) { + + uint64_t rax = 22; + uint64_t rdi = (uint64_t)name.data(); + uint64_t rsi = name.size(); + uint64_t rdx; + + __euler_do_syscall(rax, rdi, rsi, rdx); + + if (rax == (uint64_t)-1) + return {}; + + std::string s; + s.resize(rax); + + rax = 23; + rdi = (uint64_t)name.data(); + rsi = name.size(); + rdx = (uint64_t)s.data(); + + __euler_do_syscall(rax, rdi, rsi, rdx); + //do i need to tell gcc that s is modified? + + return s; + + } + +} diff --git a/kernel/include/hilbert/kernel/application.hpp b/kernel/include/hilbert/kernel/application.hpp index 56c2a15..a4b59ff 100644 --- a/kernel/include/hilbert/kernel/application.hpp +++ b/kernel/include/hilbert/kernel/application.hpp @@ -22,7 +22,8 @@ namespace hilbert::kernel::application { socket_listener_closed, other_end_closed, already_exists, - not_sized + not_sized, + not_readable }; struct [[gnu::packed]] cpu_state { @@ -57,10 +58,10 @@ namespace hilbert::kernel::application { class process; class thread; - class file_stream; - class socket; - class socket_stream_end; - class socket_listener; + struct file_stream; + struct socket; + struct socket_stream_end; + struct socket_listener; struct generic_stream_ptr { bool is_socket; @@ -93,7 +94,7 @@ namespace hilbert::kernel::application { //saves running thread state, and switches to new task. when the thread is //resumed, returns to caller. - extern "C" void yield(); + extern "C" void yield(cpu_state &save_state_into); extern "C" [[noreturn]] void resume_next_thread(); diff --git a/kernel/include/hilbert/kernel/panic.hpp b/kernel/include/hilbert/kernel/panic.hpp index 0478142..3aadddb 100644 --- a/kernel/include/hilbert/kernel/panic.hpp +++ b/kernel/include/hilbert/kernel/panic.hpp @@ -1,5 +1,5 @@ #pragma once namespace hilbert::kernel { - [[noreturn]] void panic(uint32_t code); + extern "C" [[noreturn]] void panic(uint32_t code); } diff --git a/kernel/include/hilbert/kernel/utility.hpp b/kernel/include/hilbert/kernel/utility.hpp index 0247fee..b7f1a1b 100644 --- a/kernel/include/hilbert/kernel/utility.hpp +++ b/kernel/include/hilbert/kernel/utility.hpp @@ -381,6 +381,10 @@ namespace hilbert::kernel::utility { count = written; } + void clear() { + count = 0; + } + }; template <class t> diff --git a/kernel/makefile b/kernel/makefile index 9ec5be6..6d32537 100644 --- a/kernel/makefile +++ b/kernel/makefile @@ -10,7 +10,8 @@ build/%.asm.o: source/%.asm build/%.cpp.o: source/%.cpp @mkdir -p $(@D) - $(HILBERT_CC) -c -ffreestanding -mcmodel=kernel -I ${LIMINE_DIR} $^ -o $@ + $(HILBERT_CC) -c -ffreestanding -fno-exceptions -fno-rtti \ + -mcmodel=kernel -I ${LIMINE_DIR} -I ${MINTSUKI_HEADERS_DIR} $^ -o $@ build/kernel.elf: $(SOURCES:%=build/%.o) $(HILBERT_LD) -T link.ld $^ -o $@ diff --git a/kernel/source/application.asm b/kernel/source/application.asm index 632822f..fb19826 100644 --- a/kernel/source/application.asm +++ b/kernel/source/application.asm @@ -140,6 +140,7 @@ extern copy_syscall_stack ;returns: pointer to copy extern resume_next_thread +extern running_thread global yield yield: diff --git a/kernel/source/application.cpp b/kernel/source/application.cpp index 0c3fd36..8070019 100644 --- a/kernel/source/application.cpp +++ b/kernel/source/application.cpp @@ -239,7 +239,7 @@ namespace hilbert::kernel::application { void thread::wait_for_socket_stream(socket_stream_end *the_socket_stream) { waiting_for_socket_stream = the_socket_stream; the_socket_stream->waiting_to_read.insert(this); - yield(); + yield(saved_state); waiting_for_socket_stream = 0; } @@ -247,7 +247,7 @@ namespace hilbert::kernel::application { socket_listener *the_socket_listener) { waiting_to_accept_from = the_socket_listener; the_socket_listener->waiting_to_accept.insert(this); - yield(); + yield(saved_state); waiting_to_accept_from = 0; return new_socket_stream_id; } @@ -256,15 +256,15 @@ namespace hilbert::kernel::application { socket_listener *the_socket_listener) { waiting_to_connect_to = the_socket_listener; the_socket_listener->waiting_to_connect.insert(this); - yield(); + yield(saved_state); waiting_to_connect_to = 0; return new_socket_stream_id; } void thread::wait_for_input() { - waiting_for_input = false; + waiting_for_input = true; input::waiting_for_input->insert(this); - yield(); + yield(saved_state); waiting_for_input = false; } diff --git a/kernel/source/syscall.cpp b/kernel/source/syscall.cpp index 6b0f13f..f2a6801 100644 --- a/kernel/source/syscall.cpp +++ b/kernel/source/syscall.cpp @@ -700,10 +700,44 @@ namespace hilbert::kernel::syscall { } - typedef void (*syscall_handler)( - uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx); + void start_thread_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) { + + uint64_t entry_point = rdi; + uint64_t argument = rsi; + set_zero(rax, rdi, rsi, rdx); + + auto *p = application::running_thread->owner; + auto *t = new application::thread(p, entry_point); + t->saved_state.rdi = argument; + p->add_thread(t); + application::paused_threads->insert(t); + + } + + void clear_socket_read_queue_syscall( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) { + + unsigned handle = (unsigned)rdi; + set_zero(rax, rdi, rsi, rdx); + + auto stream = application::running_thread->owner->get_stream(handle); + + if (stream.is_null() || !stream.is_socket) { + rax = 0; + return; + } + + auto &queue = stream.as_socket_stream->read_queue; + + rax = queue.count; + queue.clear(); + + } + + void (*handlers[])( + uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx) = { - syscall_handler handlers[] = { &encode_color_syscall, &get_framebuffer_syscall, &open_file_syscall, @@ -723,10 +757,13 @@ namespace hilbert::kernel::syscall { &start_process_syscall, &end_this_process_syscall, &set_stream_length_syscall, - &get_other_end_process_handle_syscall + &get_other_end_process_handle_syscall, + &start_thread_syscall, + &clear_socket_read_queue_syscall + }; - static constexpr int max_syscall_number = 19; + static constexpr int max_syscall_number = 20; } diff --git a/libraries/daguerre/include/daguerre.hpp b/libraries/daguerre/include/daguerre.hpp deleted file mode 100644 index 9468826..0000000 --- a/libraries/daguerre/include/daguerre.hpp +++ /dev/null @@ -1,209 +0,0 @@ -#pragma once - -#include <algorithm> -#include <stdint.h> -#include <cstring> -#include <cstdio> - -namespace daguerre { - - typedef uint32_t hilbert_color; - - struct rgb24 { - uint8_t r; - uint8_t g; - uint8_t b; - }; - - template <class color_t> - static inline void default_overlay(color_t &dest, const color_t &src) { - dest = src; - } - - static inline void default_overlay(hilbert_color &dest, const rgb24 &src) { - dest = __euler_encode_color(src.r, src.g, src.b); - } - - static inline void default_overlay(rgb24 &dest, const bool &src) { - dest.r = src ? 255 : 0; - dest.g = src ? 255 : 0; - dest.b = src ? 255 : 0; - } - - template <class color_t> - class image { - - public: - bool delete_buffer_on_destruct; - color_t *buffer; - unsigned width; - unsigned height; - unsigned pitch;//in sizeof(color_t) - - image() - : delete_buffer_on_destruct(false), buffer(0), width(0), height(0), - pitch(0) {} - - image(unsigned width, unsigned height) - : delete_buffer_on_destruct(true), buffer(new color_t[width * height]), - width(width), height(height), pitch(width) {} - - image( - color_t *buffer, unsigned width, unsigned height, unsigned pitch, - bool delete_buffer_on_destruct) - : delete_buffer_on_destruct(delete_buffer_on_destruct), buffer(buffer), - width(width), height(height), pitch(pitch) {} - - ~image() { - if (delete_buffer_on_destruct && buffer) - delete[] buffer; - } - - template <class other_color_t, - void overlay(color_t &, const other_color_t &) = default_overlay> - image(const image<other_color_t> &other) - : delete_buffer_on_destruct(true), - buffer(new color_t[other.width * other.height]), width(other.width), - height(other.height), pitch(other.width) { - overlay_from<other_color_t, overlay>(other, 0, 0, 0, 0, width, height); - } - - image(image<color_t> &&other) - : delete_buffer_on_destruct(other.delete_buffer_on_destruct), - buffer(other.buffer), width(other.width), height(other.height), - pitch(other.pitch) { - other.buffer = 0; - } - - template <class other_color_t, - void overlay(color_t &, const other_color_t &) = default_overlay> - image<color_t> &operator =(const image<other_color_t> &other) { - if (delete_buffer_on_destruct && buffer) - delete[] buffer; - delete_buffer_on_destruct = true; - width = other.width; - height = other.height; - pitch = width; - buffer = new color_t[width * height]; - overlay_from<other_color_t, overlay>(other, 0, 0, 0, 0, width, height); - return *this; - } - - image<color_t> &operator =(image<color_t> &&other) { - if (delete_buffer_on_destruct && buffer) - delete[] buffer; - delete_buffer_on_destruct = other.delete_buffer_on_destruct; - buffer = other.buffer; - width = other.width; - height = other.height; - pitch = other.pitch; - other.buffer = 0; - return *this; - } - - color_t get(unsigned x, unsigned y) const { - return buffer[y * pitch + x]; - } - - void set(unsigned x, unsigned y, const color_t &c) { - buffer[y * pitch + x] = c; - } - - void copy_from( - const image<color_t> &other, unsigned to_x, unsigned to_y, - unsigned from_x, unsigned from_y, unsigned width, unsigned height) { - - color_t *to_start = buffer + pitch * to_y + to_x; - const color_t *from_start = other.buffer + other.pitch * from_y + from_x; - - for (unsigned y = 0; y < height; ++y) - std::memcpy( - to_start + pitch * y, from_start + other.pitch * y, - width * sizeof(color_t)); - - } - - template <class other_color_t, - void overlay(color_t &, const other_color_t &) = default_overlay> - void overlay_from( - const image<other_color_t> &other, unsigned to_x, unsigned to_y, - unsigned from_x, unsigned from_y, unsigned width, unsigned height) { - - color_t *to_start = buffer + pitch * to_y + to_x; - const other_color_t *from_start = - other.buffer + other.pitch * from_y + from_x; - - for (unsigned y = 0; y < height; ++y) - for (unsigned x = 0; x < width; ++x) - overlay(to_start[pitch * y + x], from_start[other.pitch * y + x]); - - } - - }; - - template <class color_t> - void swap(image<color_t> &a, image<color_t> &b) { - std::swap(a.delete_buffer_on_destruct, b.delete_buffer_on_destruct); - std::swap(a.buffer, b.buffer); - std::swap(a.width, b.width); - std::swap(a.height, b.height); - std::swap(a.pitch, b.pitch); - } - - image<hilbert_color> get_hilbert_framebuffer(); - - bool try_load_ppm(std::FILE *input, image<rgb24> &into); - - static inline bool try_load_ppm(const char *path, image<rgb24> &into) { - std::FILE *f = std::fopen(path, "r"); - if (!f) - return false; - bool success = try_load_ppm(f, into); - std::fclose(f); - return success; - } - - //TODO: unicode - template <class color_t> - class fixed_bitmap_font { - - public: - unsigned width; - unsigned height; - image<color_t> glyphs[128]; - - template <class target_color_t, - void overlay(target_color_t &dest, const color_t &src) = default_overlay> - void overlay_text( - image<target_color_t> &target, unsigned x, - unsigned y, const char *text) const { - - while (1) { - uint8_t ch = (uint8_t)*text; - if (ch == 0) - return; - if (ch < 128) { - target.template overlay_from<color_t, overlay>( - glyphs[ch], x, y, 0, 0, width, height); - x += width; - } - ++text; - } - - } - - }; - - bool try_load_psf(std::FILE *input, fixed_bitmap_font<bool> &into); - - static inline bool try_load_psf( - const char *path, fixed_bitmap_font<bool> &into) { - std::FILE *f = std::fopen(path, "r"); - if (!f) - return false; - bool success = try_load_psf(f, into); - std::fclose(f); - return success; - } - -} diff --git a/libraries/daguerre/include/daguerre/fixed-font.hpp b/libraries/daguerre/include/daguerre/fixed-font.hpp new file mode 100644 index 0000000..471163a --- /dev/null +++ b/libraries/daguerre/include/daguerre/fixed-font.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include <daguerre/general.hpp> + +//TODO: support more than just ascii + +namespace daguerre { + + //forward declare since these depend on each other + template <class color_t> + class image; + + template <class color_t> + class fixed_font { + + public: + int glyph_width; + int glyph_height; + image<color_t> glyphs[128]; + + //initializes every glyph to an empty image + fixed_font(); + + //initializes each glyph to have the right dimensions and + //a valid buffer, but does nothing to their contents. + fixed_font(int glyph_width, int glyph_height); + + //moves the other font's glyphs here, then + //sets the other font to have empty images. + fixed_font(fixed_font<color_t> &&other); + + //moves the other font's glyphs here, then + //sets the other font to have empty images. + fixed_font<color_t> &operator =(fixed_font<color_t> &&other); + + //use one of the two constructors below instead + fixed_font(const fixed_font<color_t> &other) = delete; + + //copies the other font's images here, passing them through conversion. + template <class other_color_t> + fixed_font(const fixed_font<other_color_t> &other, + converter_t<color_t, other_color_t> *conversion = &default_conversion); + + //directly calls memcpy on the images' buffers. the second argument + //does nothing except distinguish this from the other copy constructor. + fixed_font(const fixed_font<color_t> &other, bool); + + //use copy constructor and move assignment instead + fixed_font<color_t> &operator =(const fixed_font<color_t> &other) = delete; + + ~fixed_font() = default; + + }; + +} + +#include <daguerre/impl/fixed-font.hpp> diff --git a/libraries/daguerre/include/daguerre/framebuffer.hpp b/libraries/daguerre/include/daguerre/framebuffer.hpp new file mode 100644 index 0000000..c9a5226 --- /dev/null +++ b/libraries/daguerre/include/daguerre/framebuffer.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include <daguerre/image.hpp> + +namespace daguerre { + + image<hilbert_color> get_hilbert_framebuffer(); + +} diff --git a/libraries/daguerre/include/daguerre/general.hpp b/libraries/daguerre/include/daguerre/general.hpp new file mode 100644 index 0000000..78c6919 --- /dev/null +++ b/libraries/daguerre/include/daguerre/general.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include <euler/syscall.hpp> +#include <stdint.h> + +namespace daguerre { + + typedef euler::syscall::encoded_color hilbert_color; + + struct rgb24 { + uint8_t r; + uint8_t g; + uint8_t b; + }; + + template <class dest_t, class src_t> + using converter_t = void (dest_t &, const src_t &); + + //copies src to dest + template <class color_t> + static inline void default_conversion(color_t &dest, const color_t &src) { + dest = src; + } + + //encodes src and copies that to dest + static inline void default_conversion(hilbert_color &dest, const rgb24 &src) { + dest = euler::syscall::encode_color(src.r, src.g, src.b); + } + + template <class dest_t, class src_t, class param_t> + using param_converter_t = void (dest_t &, const src_t &, const param_t &); + + //if src is true, copies param to dest. + //if src is false, does nothing. + template <class color_t> + static inline void default_conversion( + color_t &dest, const bool &src, const color_t ¶m) { + if (src) + dest = param; + } + + //intersects [to, to + length) with [min, max) at stores the result in + //[to, to + length). if anything was removed from either end, the same + //amount is removed from the same end(s) of [from, from + length) + static inline void make_safe( + int &to, int &from, int &length, int min, int max) { + + if (to < min) { + from += min - to; + length -= min - to; + to = min; + } + + if (to + length > max) + length = max - to; + + } + +} diff --git a/libraries/daguerre/include/daguerre/image.hpp b/libraries/daguerre/include/daguerre/image.hpp new file mode 100644 index 0000000..4c44dd0 --- /dev/null +++ b/libraries/daguerre/include/daguerre/image.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include <daguerre/general.hpp> + +namespace daguerre { + + //forward declare since these depend on each other + template <class color_t> + class fixed_font; + + template <class color_t> + class image { + + public: + bool delete_buffer_on_destruct; + int width; + int height; + color_t *buffer; + int buffer_pitch;//in sizeof(color_t) + + //makes empty image + image(); + + //make image with specified dimensions, contents uninitialized + image(int width, int height); + + //make image with specified dimensions and buffer + image( + int width, int height, color_t *buffer, + int buffer_pitch, bool delete_buffer_on_destruct); + + //moves the other image's data here, and + //then sets the other image to an empty one. + image(image<color_t> &&other); + + //moves the other image's data here, and + //then sets the other image to an empty one. + image<color_t> &operator =(image<color_t> &&other); + + //use one of the two constructors below instead + image(const image<color_t> &other) = delete; + + //copies the other image's data here, passing it through conversion + template <class other_color_t> + image(const image<other_color_t> &other, + converter_t<color_t, other_color_t> *conversion = &default_conversion); + + //directly calls memcpy on the buffer. the second argument does + //nothing except distinguish this from the other copy constructor. + image(const image<color_t> &other, bool); + + //use copy constructor and move assignment instead + image<color_t> &operator =(const image<color_t> &other) = delete; + + ~image(); + + void fill(const color_t &color); + + //does not check bounds + color_t &at(int x, int y); + + //does not check bounds + const color_t &at(int x, int y) const; + + image<color_t> stretch(int new_width, int new_height); + + //copies the region here via memcpy. does not check bounds. + void copy_from( + const image<color_t> &other, int to_x, int to_y, + int from_x, int from_y, int width, int height); + + //copies the entire image here via memcpy. does not check bounds. + void copy_from(const image<color_t> &other, int to_x, int to_y); + + //copies the region here through conversion. does not check bounds. + template <class other_color_t> + void convert_from( + const image<other_color_t> &other, int to_x, int to_y, + int from_x, int from_y, int width, int height, + converter_t<color_t, other_color_t> *conversion = &default_conversion); + + //copies the entire image here through conversion. does not check bounds. + template <class other_color_t> + void convert_from( + const image<other_color_t> &other, int to_x, int to_y, + converter_t<color_t, other_color_t> *conversion = &default_conversion); + + //copies the region here through conversion. does not check bounds. + template <class other_color_t, class param_t> + void convert_from( + const param_t ¶m, const image<other_color_t> &other, int to_x, + int to_y, int from_x, int from_y, int width, int height, + param_converter_t<color_t, other_color_t, param_t> *conversion = + &default_conversion); + + //copies the entire image here through conversion. does not check bounds. + template <class other_color_t, class param_t> + void convert_from(const param_t ¶m, + const image<other_color_t> &other, int to_x, int to_y, + param_converter_t<color_t, other_color_t, param_t> *conversion = + &default_conversion); + + //does not check bounds or wrap text + template <class font_color_t, class param_t> + void render_text( + const fixed_font<font_color_t> &font, + const param_t ¶rm, int x, int y, const char *text, + param_converter_t<color_t, font_color_t, param_t> *conversion = + &default_conversion); + + }; + + template <class color_t> + void swap(image<color_t> &a, image<color_t> &b); + +} + +#include <daguerre/impl/image.hpp> diff --git a/libraries/daguerre/include/daguerre/impl/fixed-font.hpp b/libraries/daguerre/include/daguerre/impl/fixed-font.hpp new file mode 100644 index 0000000..dbff798 --- /dev/null +++ b/libraries/daguerre/include/daguerre/impl/fixed-font.hpp @@ -0,0 +1,59 @@ +#pragma once + +//this file should only be included from <daguerre/fixed-font.hpp> + +#include <daguerre/image.hpp> +#include <utility> + +namespace daguerre { + + template<class color_t> + fixed_font<color_t>::fixed_font() : glyph_width(0), glyph_height(0) {} + + template <class color_t> + fixed_font<color_t>::fixed_font(int glyph_width, int glyph_height) + : glyph_width(glyph_width), glyph_height(glyph_height) { + for (int i = 0; i < 128; ++i) + glyphs[i] = image<color_t>(glyph_width, glyph_height); + } + + template <class color_t> + fixed_font<color_t>::fixed_font(fixed_font<color_t> &&other) + : glyph_width(other.glyph_width), glyph_height(other.glyph_height) { + for (int i = 0; 0 < 128; ++i) + glyphs[i] = std::move(other.glyphs[i]); + other.glyph_width = 0; + other.glyph_height = 0; + } + + template <class color_t> + fixed_font<color_t> &fixed_font<color_t>::operator =( + fixed_font<color_t> &&other) { + glyph_width = other.glyph_width; + glyph_height = other.glyph_height; + for (int i = 0; 0 < 128; ++i) + glyphs[i] = std::move(other.glyphs[i]); + other.glyph_width = 0; + other.glyph_height = 0; + } + + template <class color_t> + template <class other_color_t> + fixed_font<color_t>::fixed_font( + const fixed_font<other_color_t> &other, + converter_t<color_t, other_color_t> *conversion) + : glyph_width(other.glyph_width), glyph_height(other.glyph_height) { + for (int i = 0; i < 128; ++i) + glyphs[i] = image<color_t>(other.glyphs[i], conversion); + } + + template <class color_t> + fixed_font<color_t>::fixed_font(const fixed_font<color_t> &other, bool) + : glyph_width(other.glyph_width), glyph_height(other.glyph_height) { + for (int i = 0; i < 128; ++i) + glyphs[i] = image<color_t>(other.glyphs[i], true); + } + +} + +#include <daguerre/image.hpp> diff --git a/libraries/daguerre/include/daguerre/impl/image.hpp b/libraries/daguerre/include/daguerre/impl/image.hpp new file mode 100644 index 0000000..1264879 --- /dev/null +++ b/libraries/daguerre/include/daguerre/impl/image.hpp @@ -0,0 +1,217 @@ +#pragma once + +//this file should only be included from <daguerre/image.hpp> + +#include <daguerre/fixed-font.hpp> +#include <algorithm> +#include <cstring> + +namespace daguerre { + + template <class color_t> + image<color_t>::image() + : delete_buffer_on_destruct(false), width(0), + height(0), buffer(0), buffer_pitch(0) {} + + template <class color_t> + image<color_t>::image(int width, int height) + : delete_buffer_on_destruct(true), width(width), height(height), + buffer(new color_t[width * height]), buffer_pitch(width) {} + + template <class color_t> + image<color_t>::image( + int width, int height, color_t *buffer, + int buffer_pitch, bool delete_buffer_on_destruct) + : delete_buffer_on_destruct(delete_buffer_on_destruct), width(width), + height(height), buffer(buffer), buffer_pitch(buffer_pitch) {} + + template <class color_t> + image<color_t>::image(image<color_t> &&other) + : delete_buffer_on_destruct(other.delete_buffer_on_destruct), + width(other.width), height(other.height), buffer(other.buffer), + buffer_pitch(other.buffer_pitch) { + + other.width = 0; + other.height = 0; + other.buffer = 0; + + } + + template<class color_t> + image<color_t> &image<color_t>::operator =(image<color_t> &&other) { + + if (delete_buffer_on_destruct && buffer) + delete[] buffer; + + delete_buffer_on_destruct = other.delete_buffer_on_destruct; + width = other.width; + height = other.height; + buffer = other.buffer; + buffer_pitch = other.buffer_pitch; + + other.width = 0; + other.height = 0; + other.buffer = 0; + + return *this; + + } + + template <class color_t> + template <class other_color_t> + image<color_t>::image(const image<other_color_t> &other, + converter_t<color_t, other_color_t> *conversion) + : delete_buffer_on_destruct(true), width(other.width), + height(other.height), buffer(new color_t[other.width * other.height]), + buffer_pitch(other.width) { + + for (int y = 0; y < height; ++y) + for (int x = 0; x < width; ++x) + conversion( + buffer[y * buffer_pitch + x], + other.buffer[y * other.buffer_pitch + x]); + + } + + template <class color_t> + image<color_t>::image(const image<color_t> &other, bool) + : delete_buffer_on_destruct(true), width(width), height(other.height), + buffer(new color_t[other.width * other.height]), + buffer_pitch(other.width) { + + if (buffer_pitch == other.buffer_pitch) + memcpy(buffer, other.buffer, (height - 1) * buffer_pitch + width); + + else + for (int y = 0; y < height; ++y) + memcpy(buffer + y * buffer_pitch, + other.buffer + y * other.buffer_pitch, + sizeof(color_t) * width); + + } + + template <class color_t> + image<color_t>::~image() { + if (delete_buffer_on_destruct && buffer) + delete[] buffer; + } + + template <class color_t> + void image<color_t>::fill(const color_t &color) { + for (int y = 0; y < height; ++y) + for (int x = 0; x < width; ++x) + buffer[y * buffer_pitch + x] = color; + } + + template <class color_t> + color_t &image<color_t>::at(int x, int y) { + return buffer[y * buffer_pitch + x]; + } + + template <class color_t> + const color_t &image<color_t>::at(int x, int y) const { + return buffer[y * buffer_pitch + x]; + } + + template <class color_t> + image<color_t> image<color_t>::stretch(int new_width, int new_height) { + image<color_t> im(new_width, new_height); + for (int y = 0; y < new_height; ++y) + for (int x = 0; x < new_width; ++x) + im.at(x, y) = at(x * width / new_width, y * height / new_height); + return im; + } + + template <class color_t> + void image<color_t>::copy_from( + const image<color_t> &other, int to_x, int to_y, + int from_x, int from_y, int width, int height) { + + color_t *to = buffer + to_y * buffer_pitch + to_x; + const color_t *from = other.buffer + from_y * other.buffer_pitch + from_x; + + for (int y = 0; y < height; ++y) + memcpy( + to + y * buffer_pitch, + from + y * other.buffer_pitch, + width * sizeof(color_t)); + + } + + template <class color_t> + void image<color_t>::copy_from( + const image<color_t> &other, int to_x, int to_y) { + copy_from(other, to_x, to_y, 0, 0, other.width, other.height); + } + + template <class color_t> + template <class other_color_t> + void image<color_t>::convert_from( + const image<other_color_t> &other, int to_x, int to_y, + int from_x, int from_y, int width, int height, + converter_t<color_t, other_color_t> *conversion) { + + for (int y = 0; y < height; ++y) + for (int x = 0; x < width; ++x) + conversion(at(to_x + x, to_y + y), other.at(from_x + x, from_y + y)); + + } + + template <class color_t> + template <class other_color_t> + void image<color_t>::convert_from( + const image<other_color_t> &other, int to_x, int to_y, + converter_t<color_t, other_color_t> *conversion) { + convert_from(other, to_x, to_y, 0, 0, other.width, other.y, conversion); + } + + template <class color_t> + template <class other_color_t, class param_t> + void image<color_t>::convert_from( + const param_t ¶m, const image<other_color_t> &other, int to_x, + int to_y, int from_x, int from_y, int width, int height, + param_converter_t<color_t, other_color_t, param_t> *conversion) { + + for (int y = 0; y < height; ++y) + for (int x = 0; x < width; ++x) + conversion( + at(to_x + x, to_y + y), other.at(from_x + x, from_y + y), param); + + } + + template <class color_t> + template <class other_color_t, class param_t> + void image<color_t>::convert_from( + const param_t ¶m, const image<other_color_t> &other, int to_x, + int to_y, param_converter_t<color_t, other_color_t, param_t> *conversion) { + convert_from( + param, other, to_x, to_y, 0, 0, other.width, other.y, conversion); + } + + template <class color_t> + template <class font_color_t, class param_t> + void image<color_t>::render_text( + const fixed_font<font_color_t> &font, + const param_t ¶m, int x, int y, const char *text, + param_converter_t<color_t, font_color_t, param_t> *conversion) { + + while (*text) { + int ch = *text; + if (ch >= 0 && ch < 128) + convert_from(param, font.glyphs[ch], x, y, conversion); + ++text; + x += font.glyph_width; + } + + } + + template <class color_t> + void swap(image<color_t> &a, image<color_t> &b) { + std::swap(a.delete_buffer_on_destruct, b.delete_buffer_on_destruct); + std::swap(a.width, b.width); + std::swap(a.height, b.height); + std::swap(a.buffer, b.buffer); + std::swap(a.buffer_pitch, b.buffer_pitch); + } + +} diff --git a/libraries/daguerre/include/daguerre/ppm.hpp b/libraries/daguerre/include/daguerre/ppm.hpp new file mode 100644 index 0000000..5249c98 --- /dev/null +++ b/libraries/daguerre/include/daguerre/ppm.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <daguerre/image.hpp> +#include <optional> +#include <cstdio> + +namespace daguerre { + + std::optional<image<rgb24>> try_load_ppm(FILE *input); + + static inline std::optional<image<rgb24>> try_load_ppm(const char *path) { + FILE *f = fopen(path, "r"); + if (!f) + return {}; + auto result = try_load_ppm(f); + fclose(f); + return result; + } + +} diff --git a/libraries/daguerre/include/daguerre/psf.hpp b/libraries/daguerre/include/daguerre/psf.hpp new file mode 100644 index 0000000..e38cdc4 --- /dev/null +++ b/libraries/daguerre/include/daguerre/psf.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <daguerre/fixed-font.hpp> +#include <optional> +#include <cstdio> + +namespace daguerre { + + std::optional<fixed_font<bool>> try_load_psf(FILE *input); + + static inline std::optional<fixed_font<bool>> try_load_psf(const char *path) { + FILE *f = fopen(path, "r"); + if (!f) + return {}; + auto result = try_load_psf(f); + fclose(f); + return result; + } + +} diff --git a/libraries/daguerre/makefile b/libraries/daguerre/makefile index 3505d35..f428193 100644 --- a/libraries/daguerre/makefile +++ b/libraries/daguerre/makefile @@ -1,5 +1,5 @@ SOURCES = \ - daguerre.cpp + framebuffer.cpp ppm.cpp psf.cpp build/%.cpp.o: source/%.cpp @mkdir -p $(@D) diff --git a/libraries/daguerre/source/daguerre.cpp b/libraries/daguerre/source/daguerre.cpp deleted file mode 100644 index 2f7fe4d..0000000 --- a/libraries/daguerre/source/daguerre.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include <daguerre.hpp> - -namespace daguerre { - - image<hilbert_color> get_hilbert_framebuffer() { - uint32_t width, height, pitch; - hilbert_color *ptr = __euler_get_framebuffer(width, height, pitch); - return image<hilbert_color>(ptr, width, height, pitch, false); - } - - //TODO: make this more robust - unsigned read_text_int(std::FILE *input) { - unsigned n = 0; - char ch; - std::fread(&ch, 1, 1, input); - if (ch == '#') { - do - std::fread(&ch, 1, 1, input); - while (ch != '\n'); - std::fread(&ch, 1, 1, input); - } - do { - n = n * 10 + ch - '0'; - std::fread(&ch, 1, 1, input); - } while (ch >= '0' && ch <= '9'); - return n; - } - - //only supports p6 format - bool try_load_ppm(std::FILE *input, image<rgb24> &into) { - - char header[3]; - if (std::fread(header, 1, 3, input) != 3) - return false; - - if (header[0] != 'P' || header[1] != '6' || header[2] != '\n') - return false; - - unsigned width = read_text_int(input); - unsigned height = read_text_int(input); - unsigned max = read_text_int(input); - - into = image<rgb24>(width, height); - - for (unsigned y = 0; y < height; ++y) - for (unsigned x = 0; x < width; ++x) { - if (std::fread(&into.buffer[y * width + x].r, 1, 1, input) != 1) - return false; - if (std::fread(&into.buffer[y * width + x].g, 1, 1, input) != 1) - return false; - if (std::fread(&into.buffer[y * width + x].b, 1, 1, input) != 1) - return false; - } - - if (max != 255) - for (unsigned v = 0; v < width * height; ++v) { - into.buffer[v].r = ((uint16_t)into.buffer[v].r * 255) / max; - into.buffer[v].g = ((uint16_t)into.buffer[v].g * 255) / max; - into.buffer[v].b = ((uint16_t)into.buffer[v].b * 255) / max; - } - - return true; - - } - - //assumes the font is in psf2 format, and has a unicode table - bool try_load_psf(std::FILE *input, fixed_bitmap_font<bool> &into) { - - uint32_t header[8]; - if (std::fread(header, 4, 8, input) != 8) - return false; - - const uint32_t glyphs_start = header[2]; - const uint32_t glyph_count = header[4]; - const uint32_t glyph_length = header[5]; - into.height = header[6]; - into.width = header[7]; - - const uint32_t unicode_start = glyphs_start + glyph_count * glyph_length; - std::fseek(input, unicode_start, SEEK_SET); - - uint32_t indices[128]; - - for (uint32_t index = 0; index < glyph_count; ++index) { - uint8_t ch; - std::fread(&ch, 1, 1, input); - if (ch < 128) - indices[ch] = index; - do - std::fread(&ch, 1, 1, input); - while (ch != 0xff); - } - - for (uint8_t ch = 0; ch < 128; ++ch) { - std::fseek(input, glyphs_start + glyph_length * indices[ch], SEEK_SET); - into.glyphs[ch] = image<bool>(into.width, into.height); - for (unsigned h = 0; h < into.height; ++h) - for (unsigned wb = 0; wb < into.width; wb += 8) { - uint8_t byte; - std::fread(&byte, 1, 1, input); - for (unsigned x = 0; x < 8 && wb + x < into.width; ++x) - into.glyphs[ch].set(wb + x, h, (byte >> (7 - x)) & 1); - } - } - - return true; - - } - -} diff --git a/libraries/daguerre/source/framebuffer.cpp b/libraries/daguerre/source/framebuffer.cpp new file mode 100644 index 0000000..d4282ad --- /dev/null +++ b/libraries/daguerre/source/framebuffer.cpp @@ -0,0 +1,12 @@ +#include <daguerre/framebuffer.hpp> + +namespace daguerre { + + image<hilbert_color> get_hilbert_framebuffer() { + hilbert_color *ptr; + uint32_t width, height, pitch; + euler::syscall::get_framebuffer(ptr, width, height, pitch); + return image<hilbert_color>(width, height, ptr, pitch, false); + } + +} diff --git a/libraries/daguerre/source/ppm.cpp b/libraries/daguerre/source/ppm.cpp new file mode 100644 index 0000000..a4a7e27 --- /dev/null +++ b/libraries/daguerre/source/ppm.cpp @@ -0,0 +1,60 @@ +#include <daguerre/ppm.hpp> + +namespace daguerre { + + //TODO: make this more robust + static unsigned read_text_int(FILE *input) { + unsigned n = 0; + char ch; + fread(&ch, 1, 1, input); + if (ch == '#') { + do + fread(&ch, 1, 1, input); + while (ch != '\n'); + fread(&ch, 1, 1, input); + } + do { + n = n * 10 + ch - '0'; + fread(&ch, 1, 1, input); + } while (ch >= '0' && ch <= '9'); + return n; + } + + //TODO: this only supports p6 format, and assumes max < 256 + std::optional<image<rgb24>> try_load_ppm(FILE *input) { + + char header[3]; + if (fread(header, 1, 3, input) != 3) + return {}; + + if (header[0] != 'P' || header[1] != '6' || header[2] != '\n') + return {}; + + unsigned width = read_text_int(input); + unsigned height = read_text_int(input); + unsigned max = read_text_int(input); + + image<rgb24> im(width, height); + + for (unsigned y = 0; y < height; ++y) + for (unsigned x = 0; x < width; ++x) { + if (fread(&im.buffer[y * width + x].r, 1, 1, input) != 1) + return {}; + if (fread(&im.buffer[y * width + x].g, 1, 1, input) != 1) + return {}; + if (fread(&im.buffer[y * width + x].b, 1, 1, input) != 1) + return {}; + } + + if (max != 255) + for (unsigned v = 0; v < width * height; ++v) { + im.buffer[v].r = ((uint16_t)im.buffer[v].r * 255) / max; + im.buffer[v].g = ((uint16_t)im.buffer[v].g * 255) / max; + im.buffer[v].b = ((uint16_t)im.buffer[v].b * 255) / max; + } + + return im; + + } + +} diff --git a/libraries/daguerre/source/psf.cpp b/libraries/daguerre/source/psf.cpp new file mode 100644 index 0000000..81f722a --- /dev/null +++ b/libraries/daguerre/source/psf.cpp @@ -0,0 +1,51 @@ +#include <daguerre/psf.hpp> + +namespace daguerre { + + //TODO: this assumes the font is in psf2 format, and has a unicode table + std::optional<fixed_font<bool>> try_load_psf(FILE *input) { + + uint32_t header[8]; + if (fread(header, 4, 8, input) != 8) + return {}; + + const uint32_t glyphs_start = header[2]; + const uint32_t glyph_count = header[4]; + const uint32_t glyph_length = header[5]; + const uint32_t height = header[6]; + const uint32_t width = header[7]; + + fixed_font<bool> font(width, height); + + const uint32_t unicode_start = glyphs_start + glyph_count * glyph_length; + fseek(input, unicode_start, SEEK_SET); + + uint32_t indices[128]; + + for (uint32_t index = 0; index < glyph_count; ++index) { + uint8_t ch; + fread(&ch, 1, 1, input); + if (ch < 128) + indices[ch] = index; + do + if (fread(&ch, 1, 1, input) != 1) + return {}; + while (ch != 0xff); + } + + for (uint8_t ch = 0; ch < 128; ++ch) { + fseek(input, glyphs_start + glyph_length * indices[ch], SEEK_SET); + for (unsigned h = 0; h < height; ++h) + for (unsigned wb = 0; wb < width; wb += 8) { + uint8_t byte; + fread(&byte, 1, 1, input); + for (unsigned x = 0; x < 8 && wb + x < width; ++x) + font.glyphs[ch].at(wb + x, h) = (byte >> (7 - x)) & 1; + } + } + + return font; + + } + +} @@ -1,29 +1,32 @@ LIMINE_DIR = $(abspath dependencies/limine) +MINTSUKI_HEADERS_DIR = $(abspath dependencies/mintsuki-headers) TOOLCHAIN_DIR = $(abspath toolchain) +EXTRA_CC_ARGS = -Wall -Wextra -Og -ggdb -fno-exceptions + HILBERT_NASM = nasm -f elf64 -HILBERT_CC = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-c++ -std=c++17 -Wall \ - -Wextra -O3 -ggdb -static -fno-exceptions -fno-rtti -mno-sse -I include \ - -I $(abspath dependencies/mintsuki-headers) -I $(abspath euler/include) \ - -I $(abspath libraries/daguerre/include) +HILBERT_CC = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-c++ -std=c++20 \ + ${EXTRA_CC_ARGS} -static -mno-sse -I include -I $(abspath euler/include) \ + -I $(abspath libraries/daguerre/include) -I ${MINTSUKI_HEADERS_DIR} HILBERT_AR = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-ar HILBERT_LD = ${TOOLCHAIN_DIR}/usr/bin/x86_64-elf-ld -z noexecstack .EXPORT_ALL_VARIABLES: -LIB_DIR = toolchain/usr/x86_64-elf/lib/ +LIB_DIR = ${TOOLCHAIN_DIR}/usr/x86_64-elf/lib LIMINE_DEP = dependencies/.limine-done MINTSUKI_HEADERS_DEP = dependencies/.mintsuki-headers-done BINUTILS_DEP = toolchain/.binutils-done GCC_DEP = toolchain/.gcc-done LIBGCC_DEP = toolchain/.libgcc-done +LIBSTDCPP_DEP = toolchain/.libstdcpp-done EULER_DEP = toolchain/.euler-done DAGUERRE_DEP = toolchain/.daguerre-done -APP_DEPS = ${GCC_DEP} ${LIBGCC_DEP} ${EULER_DEP} ${MINTSUKI_HEADERS_DEP} -LIBRARY_DEPS = ${GCC_DEP} ${MINTSUKI_HEADERS_DEP} +APP_DEPS = ${GCC_DEP} ${LIBGCC_DEP} ${LIBSTDCPP_DEP} ${EULER_DEP} +LIBRARY_DEPS = ${GCC_DEP} ${LIBSTDCPP_DEP} .PHONY: default run clean clean-dependencies @@ -53,7 +56,10 @@ ${LIMINE_DEP}: ${MINTSUKI_HEADERS_DEP}: mkdir -p dependencies - test -e dependencies/mintsuki-headers || git clone --depth 1 https://github.com/mintsuki/freestanding-headers dependencies/mintsuki-headers + test -e dependencies/mintsuki-headers || git clone --depth 1 https://github.com/osdev0/freestanding-headers dependencies/mintsuki-headers + cd dependencies/mintsuki-headers && git fetch --depth=1 origin dd3abd2d7147efc4170dff478d3b7730bed14147 + cd dependencies/mintsuki-headers && git checkout dd3abd2d7147efc4170dff478d3b7730bed14147 + patch dependencies/mintsuki-headers/stddef.h patches/mintsuki-stddef.patch touch $@ ${BINUTILS_DEP}: @@ -71,8 +77,10 @@ ${GCC_DEP}: ${BINUTILS_DEP} test -e dependencies/gcc || git clone --depth 1 -b releases/gcc-14.1.0 https://gcc.gnu.org/git/gcc dependencies/gcc mkdir -p dependencies/gcc/build cd dependencies/gcc/build && ../configure --disable-fixed-point \ - --disable-gcov --disable-multilib --disable-shared --enable-languages=c++ \ - --target=x86_64-elf --prefix=${TOOLCHAIN_DIR}/usr --without-headers + --disable-gcov --disable-multilib --disable-shared \ + --disable-hosted-libstdcxx \ + --enable-languages=c++ --target=x86_64-elf --enable-cstdio=stdio_pure \ + --prefix=${TOOLCHAIN_DIR}/usr --without-headers --enable-cxx-flags=-mno-sse +make -C dependencies/gcc/build all-gcc +make -C dependencies/gcc/build install-gcc touch $@ @@ -82,17 +90,22 @@ ${LIBGCC_DEP}: ${GCC_DEP} +make -C dependencies/gcc/build install-target-libgcc touch $@ -${EULER_DEP}: ${GCC_DEP} ${MINTSUKI_HEADERS_DEP} - +make -C euler build/crt0.o build/libc.a build/libg.a build/libm.a \ - build/libstdc++.a - mkdir -p toolchain/usr/x86_64-elf/lib - cp euler/build/crt0.o euler/build/libc.a euler/build/libg.a \ - euler/build/libm.a euler/build/libstdc++.a ${LIB_DIR} +${LIBSTDCPP_DEP}: ${GCC_DEP} + +make -C dependencies/gcc/build all-target-libstdc++-v3 + +make -C dependencies/gcc/build install-target-libstdc++-v3 + patch toolchain/usr/x86_64-elf/include/c++/14.1.0/memory patches/gcc-memory.patch + touch $@ + +${EULER_DEP}: ${LIBRARY_DEPS} + +make -C euler build/crt0.o build/libc.a build/libg.a build/libm.a + mkdir -p ${LIB_DIR} + cp euler/build/crt0.o euler/build/libc.a \ + euler/build/libg.a euler/build/libm.a ${LIB_DIR}/ touch $@ ${DAGUERRE_DEP}: ${LIBRARY_DEPS} +make -C libraries/daguerre build/libdaguerre.a - cp libraries/daguerre/build/libdaguerre.a ${LIB_DIR} + cp libraries/daguerre/build/libdaguerre.a ${LIB_DIR}/ touch $@ kernel/build/kernel.elf: ${GCC_DEP} ${MINTSUKI_HEADERS_DEP} ${LIMINE_DEP} @@ -101,7 +114,7 @@ kernel/build/kernel.elf: ${GCC_DEP} ${MINTSUKI_HEADERS_DEP} ${LIMINE_DEP} applications/init/build/init.elf: ${APP_DEPS} +make -C applications/init build/init.elf -applications/goldman/build/goldman.elf: ${APP_DEPS} +applications/goldman/build/goldman.elf: ${APP_DEPS} ${DAGUERRE_DEP} +make -C applications/goldman build/goldman.elf build/initfs.tgz: applications/init/build/init.elf \ diff --git a/patches/gcc-memory.patch b/patches/gcc-memory.patch new file mode 100644 index 0000000..9cdc9e2 --- /dev/null +++ b/patches/gcc-memory.patch @@ -0,0 +1,2 @@ +67a68 +> #include <std/allocator.hpp> diff --git a/patches/mintsuki-stddef.patch b/patches/mintsuki-stddef.patch new file mode 100644 index 0000000..4f98cc2 --- /dev/null +++ b/patches/mintsuki-stddef.patch @@ -0,0 +1,14 @@ +diff --git a/stddef.h b/stddef.h +index 376eb75..fb1e99d 100644 +--- a/stddef.h ++++ b/stddef.h +@@ -50,4 +50,9 @@ typedef decltype(nullptr) nullptr_t; + # define unreachable() __builtin_unreachable() + #endif + ++typedef struct { ++ long long ll __attribute__((__aligned__(__alignof__(long long)))); ++ long double ld __attribute__((__aligned__(__alignof__(long double)))); ++} max_align_t; ++ + #endif @@ -1,5 +1,6 @@ target remote | qemu-system-x86_64 -gdb stdio -cdrom build/disk.iso -boot d symbol-file kernel/build/kernel.elf +add-symbol-file applications/goldman/build/goldman.elf set disassembly-flavor intel set print asm-demangle on layout src @@ -22,6 +22,8 @@ acknowledgements (any under "dependencies" are downloaded during build): dependencies/gcc/COPYING3 (gnu gpl v3) dependencies/gcc/COPYING.RUNTIME (gcc runtime library exception v3.1) homepage: https://gcc.gnu.org/ + i patch the output <memory> header file from libstdc++ with + patches/gcc-memory.patch to include my std::allocator implementation. - dependencies/limine (limine bootloader v7.5.1) copyright 2019 - 2024 mintsuki and contributors @@ -31,7 +33,11 @@ acknowledgements (any under "dependencies" are downloaded during build): - dependencies/minstuki-headers copyright 2022 - 2024 mintsuki and contributors license: dependencies/mintsuki-headers/LICENSE (bsd zero-clause) - homepage: https://github.com/mintsuki/freestanding-headers/ + homepage: https://github.com/osdev0/freestanding-headers/ + i patch the <stddef.h> header file with patches/minstuki-stddef.patch + to add a max_align_t type to make libstdc++ happy. i use the commit + dd3abd2d7147efc4170dff478d3b7730bed14147 so i don't have to worry + about that file changing in a future commit. - skeleton/assets/burden.ppm ("selective focus photography snowflakes" by aaron burden) @@ -71,8 +77,8 @@ project structure: some descriptions of things that have not been created yet. - euler: - (a minimal start to) a c/c++ standard library and runtime. the plan is to - follow the c++17 standard, and only add things as i need them. + (a minimal start to) a c++20 standard library, plus some hilbert + specific functions. this uses the freestanding part of libstdc++. - kernel: the kernel. @@ -80,5 +86,8 @@ project structure: - libraries/daguerre: an image loading / rendering library. + - patches: + a couple patches that are applied to dependencies + - skeleton: files that are copied directly to the initfs. diff --git a/skeleton/assets/background.ppm b/skeleton/assets/background.ppm new file mode 120000 index 0000000..f6bb84e --- /dev/null +++ b/skeleton/assets/background.ppm @@ -0,0 +1 @@ +burden.ppm
\ No newline at end of file |