From 812e8fe9726d63bddd79393bf4fcdcce43f7263a Mon Sep 17 00:00:00 2001 From: noriyotcp Date: Sun, 24 Dec 2017 15:07:39 +0900 Subject: [PATCH] Reborn --- .gitignore | 16 +- .gitmodules | 4 +- LICENSE-APACHE | 2 +- LICENSE-MIT | 2 +- Makefile | 161 +- README.md | 44 +- REFERENCES.md | 3 + asm/boot.asm | 190 + asm/interrupt.asm | 147 + asm/long_mode_init.asm | 20 + asm/multiboot_header.asm | 22 + build/musl/Makefile | 34 - doc/utero 2017-12-24.gif | Bin 0 -> 21578 bytes ext/.gitignore | 1 + ext/Makefile | 41 + grub/grub.cfg | 7 + linker.ld | 23 + musl | 1 + spec/lib_cr/ctype_spec.cr | 240 - spec/lib_cr/string_spec.cr | 161 - spec/spec_helper.cr | 10 - src/arch/x86_64/boot.asm | 263 - src/arch/x86_64/c/idt.c | 56 - src/arch/x86_64/c/include/asm/idt.h | 77 - src/arch/x86_64/c/include/asm/io.h | 20 - src/arch/x86_64/c/include/asm/isrs.h | 20 - src/arch/x86_64/c/include/asm/multiboot.h | 135 - src/arch/x86_64/c/include/asm/processor.h | 108 - src/arch/x86_64/c/include/asm/stddef.h | 84 - src/arch/x86_64/c/include/asm/tasks.h | 31 - src/arch/x86_64/c/include/asm/tasks_types.h | 16 - src/arch/x86_64/c/isrs.c | 249 - src/arch/x86_64/c/tasks.c | 75 - src/arch/x86_64/grub.cfg | 21 - src/arch/x86_64/linker.ld | 49 - src/arch/x86_64/long_mode_init.asm | 221 - src/arch/x86_64/multiboot_header.asm | 27 - src/c_kernel/dummy_exception.c | 17 - src/c_kernel/hello.c | 18 - src/c_kernel/include/bits/alltypes.h | 28 - src/c_kernel/include/config.h | 26 - src/c_kernel/include/errno.h | 151 - src/c_kernel/include/processor.h | 21 - src/c_kernel/include/stddef.h | 27 - src/c_kernel/include/stdint.h | 119 - src/c_kernel/include/stdio.h | 26 - src/c_kernel/include/stdlib.h | 22 - src/c_kernel/include/string.h | 19 - src/c_kernel/include/tasks.h | 28 - src/c_kernel/include/tasks_types.h | 75 - src/c_kernel/make_string.c | 24 - src/c_kernel/make_string.h | 17 - src/c_kernel/memory.c | 31 - src/c_kernel/tasks.c | 309 -- src/c_kernel/utero_init.c | 72 - src/core/bool.cr | 62 - src/core/boot.c | 168 + src/core/box.cr | 28 - src/core/check.c | 14 + src/core/cmos.c | 106 + src/core/comparable.cr | 54 - src/core/gc.cr | 13 - src/core/gc/boehm.cr | 152 - src/core/idt.c | 31 + src/core/indexable.cr | 544 --- src/core/int.cr | 740 --- src/core/isr.c | 220 + src/core/iterable.cr | 48 - src/core/lib_cr.cr | 35 - .../lib_cr/x86_64-linux-musl/c/bits/io.cr | 11 - src/core/lib_cr/x86_64-linux-musl/c/ctype.cr | 25 - .../c/no_bind/libstring.cr.unused | 44 - src/core/lib_cr/x86_64-linux-musl/c/stddef.cr | 18 - src/core/lib_cr/x86_64-linux-musl/c/stdint.cr | 10 - src/core/lib_cr/x86_64-linux-musl/c/stdlib.cr | 24 - src/core/lib_cr/x86_64-linux-musl/c/string.cr | 25 - .../lib_cr/x86_64-linux-musl/c/sys/types.cr | 61 - src/core/macros.cr | 130 - src/core/named_tuple.cr | 490 -- src/core/nil.cr | 116 - src/core/number.cr | 12 - src/core/object.cr | 1192 ----- src/core/pointer.cr | 528 -- src/core/ports.c | 21 + src/core/prelude.cr | 35 - src/core/reference.cr | 133 - src/core/static_array.cr | 247 - src/core/string.cr | 4346 ----------------- src/core/timer.c | 30 + src/core/tuple.cr | 511 -- src/core_test/comparable_test.cr | 18 - src/core_test/pointer_test.cr | 39 - src/core_test/prelude.cr | 3 - src/core_test/string_test.cr | 67 - src/core_test/test.cr | 7 - src/drivers/keyboard.c | 26 + src/drivers/screen.c | 111 + src/drivers/serial.c | 73 + src/include/core/boot.h | 135 + src/include/core/check.h | 6 + src/include/core/cmos.h | 34 + src/include/core/debug.h | 14 + src/include/core/idt.h | 38 + src/include/core/isr.h | 93 + src/include/core/ports.h | 11 + src/include/core/timer.h | 9 + src/include/drivers/keyboard.h | 8 + src/include/drivers/screen.h | 32 + src/include/drivers/serial.h | 31 + src/include/kernel/kmain.h | 25 + src/include/kernel/panic.h | 11 + src/include/libk/ctype.h | 8 + src/include/libk/itoa.h | 8 + src/include/libk/mem.h | 9 + src/include/libk/printfk.h | 12 + src/include/libk/strrev.h | 8 + src/include/libk/ulltoa.h | 8 + src/include/mmu/mmap.h | 19 + src/include/mmu/mmu.h | 9 + src/include/mmu/paging.h | 59 + src/include/unused.h | 6 + src/kernel/kmain.c | 80 + src/kernel/lib_u.cr | 40 - .../x86_64-linux-musl/c/dummy_exception.cr | 14 - src/kernel/lib_u/x86_64-linux-musl/c/hello.cr | 14 - src/kernel/lib_u/x86_64-linux-musl/c/idt.cr | 14 - src/kernel/lib_u/x86_64-linux-musl/c/isrs.cr | 14 - .../lib_u/x86_64-linux-musl/c/make_string.cr | 14 - src/kernel/main.cr | 41 +- src/kernel/panic.c | 18 + src/kernel/prelude.cr | 18 - src/kernel/scrn.cr | 195 - src/kernel/scrn_h.cr | 29 - src/kernel/tasks.cr | 16 - src/kernel/utero_init.cr | 24 - src/libk/ctype.c | 6 + src/libk/itoa.c | 31 + src/libk/mem.c | 11 + src/libk/printfk.c | 103 + src/libk/strrev.c | 12 + src/libk/ulltoa.c | 31 + src/mmu/mmap.c | 126 + src/mmu/mmu.c | 7 + src/mmu/paging.c | 182 + src/musl | 1 - 145 files changed, 2589 insertions(+), 13224 deletions(-) create mode 100644 asm/boot.asm create mode 100644 asm/interrupt.asm create mode 100644 asm/long_mode_init.asm create mode 100644 asm/multiboot_header.asm delete mode 100644 build/musl/Makefile create mode 100644 doc/utero 2017-12-24.gif create mode 100644 ext/.gitignore create mode 100644 ext/Makefile create mode 100644 grub/grub.cfg create mode 100644 linker.ld create mode 160000 musl delete mode 100644 spec/lib_cr/ctype_spec.cr delete mode 100644 spec/lib_cr/string_spec.cr delete mode 100644 spec/spec_helper.cr delete mode 100644 src/arch/x86_64/boot.asm delete mode 100644 src/arch/x86_64/c/idt.c delete mode 100644 src/arch/x86_64/c/include/asm/idt.h delete mode 100644 src/arch/x86_64/c/include/asm/io.h delete mode 100644 src/arch/x86_64/c/include/asm/isrs.h delete mode 100644 src/arch/x86_64/c/include/asm/multiboot.h delete mode 100644 src/arch/x86_64/c/include/asm/processor.h delete mode 100644 src/arch/x86_64/c/include/asm/stddef.h delete mode 100644 src/arch/x86_64/c/include/asm/tasks.h delete mode 100644 src/arch/x86_64/c/include/asm/tasks_types.h delete mode 100644 src/arch/x86_64/c/isrs.c delete mode 100644 src/arch/x86_64/c/tasks.c delete mode 100644 src/arch/x86_64/grub.cfg delete mode 100644 src/arch/x86_64/linker.ld delete mode 100644 src/arch/x86_64/long_mode_init.asm delete mode 100644 src/arch/x86_64/multiboot_header.asm delete mode 100644 src/c_kernel/dummy_exception.c delete mode 100644 src/c_kernel/hello.c delete mode 100644 src/c_kernel/include/bits/alltypes.h delete mode 100644 src/c_kernel/include/config.h delete mode 100644 src/c_kernel/include/errno.h delete mode 100644 src/c_kernel/include/processor.h delete mode 100644 src/c_kernel/include/stddef.h delete mode 100644 src/c_kernel/include/stdint.h delete mode 100644 src/c_kernel/include/stdio.h delete mode 100644 src/c_kernel/include/stdlib.h delete mode 100644 src/c_kernel/include/string.h delete mode 100644 src/c_kernel/include/tasks.h delete mode 100644 src/c_kernel/include/tasks_types.h delete mode 100644 src/c_kernel/make_string.c delete mode 100644 src/c_kernel/make_string.h delete mode 100644 src/c_kernel/memory.c delete mode 100644 src/c_kernel/tasks.c delete mode 100644 src/c_kernel/utero_init.c delete mode 100644 src/core/bool.cr create mode 100644 src/core/boot.c delete mode 100644 src/core/box.cr create mode 100644 src/core/check.c create mode 100644 src/core/cmos.c delete mode 100644 src/core/comparable.cr delete mode 100644 src/core/gc.cr delete mode 100644 src/core/gc/boehm.cr create mode 100644 src/core/idt.c delete mode 100644 src/core/indexable.cr delete mode 100644 src/core/int.cr create mode 100644 src/core/isr.c delete mode 100644 src/core/iterable.cr delete mode 100644 src/core/lib_cr.cr delete mode 100644 src/core/lib_cr/x86_64-linux-musl/c/bits/io.cr delete mode 100644 src/core/lib_cr/x86_64-linux-musl/c/ctype.cr delete mode 100644 src/core/lib_cr/x86_64-linux-musl/c/no_bind/libstring.cr.unused delete mode 100644 src/core/lib_cr/x86_64-linux-musl/c/stddef.cr delete mode 100644 src/core/lib_cr/x86_64-linux-musl/c/stdint.cr delete mode 100644 src/core/lib_cr/x86_64-linux-musl/c/stdlib.cr delete mode 100644 src/core/lib_cr/x86_64-linux-musl/c/string.cr delete mode 100644 src/core/lib_cr/x86_64-linux-musl/c/sys/types.cr delete mode 100644 src/core/macros.cr delete mode 100644 src/core/named_tuple.cr delete mode 100644 src/core/nil.cr delete mode 100644 src/core/number.cr delete mode 100644 src/core/object.cr delete mode 100644 src/core/pointer.cr create mode 100644 src/core/ports.c delete mode 100644 src/core/prelude.cr delete mode 100644 src/core/reference.cr delete mode 100644 src/core/static_array.cr delete mode 100644 src/core/string.cr create mode 100644 src/core/timer.c delete mode 100644 src/core/tuple.cr delete mode 100644 src/core_test/comparable_test.cr delete mode 100644 src/core_test/pointer_test.cr delete mode 100644 src/core_test/prelude.cr delete mode 100644 src/core_test/string_test.cr delete mode 100644 src/core_test/test.cr create mode 100644 src/drivers/keyboard.c create mode 100644 src/drivers/screen.c create mode 100644 src/drivers/serial.c create mode 100644 src/include/core/boot.h create mode 100644 src/include/core/check.h create mode 100644 src/include/core/cmos.h create mode 100644 src/include/core/debug.h create mode 100644 src/include/core/idt.h create mode 100644 src/include/core/isr.h create mode 100644 src/include/core/ports.h create mode 100644 src/include/core/timer.h create mode 100644 src/include/drivers/keyboard.h create mode 100644 src/include/drivers/screen.h create mode 100644 src/include/drivers/serial.h create mode 100644 src/include/kernel/kmain.h create mode 100644 src/include/kernel/panic.h create mode 100644 src/include/libk/ctype.h create mode 100644 src/include/libk/itoa.h create mode 100644 src/include/libk/mem.h create mode 100644 src/include/libk/printfk.h create mode 100644 src/include/libk/strrev.h create mode 100644 src/include/libk/ulltoa.h create mode 100644 src/include/mmu/mmap.h create mode 100644 src/include/mmu/mmu.h create mode 100644 src/include/mmu/paging.h create mode 100644 src/include/unused.h create mode 100644 src/kernel/kmain.c delete mode 100644 src/kernel/lib_u.cr delete mode 100644 src/kernel/lib_u/x86_64-linux-musl/c/dummy_exception.cr delete mode 100644 src/kernel/lib_u/x86_64-linux-musl/c/hello.cr delete mode 100644 src/kernel/lib_u/x86_64-linux-musl/c/idt.cr delete mode 100644 src/kernel/lib_u/x86_64-linux-musl/c/isrs.cr delete mode 100644 src/kernel/lib_u/x86_64-linux-musl/c/make_string.cr create mode 100644 src/kernel/panic.c delete mode 100644 src/kernel/prelude.cr delete mode 100644 src/kernel/scrn.cr delete mode 100644 src/kernel/scrn_h.cr delete mode 100644 src/kernel/tasks.cr delete mode 100644 src/kernel/utero_init.cr create mode 100644 src/libk/ctype.c create mode 100644 src/libk/itoa.c create mode 100644 src/libk/mem.c create mode 100644 src/libk/printfk.c create mode 100644 src/libk/strrev.c create mode 100644 src/libk/ulltoa.c create mode 100644 src/mmu/mmap.c create mode 100644 src/mmu/mmu.c create mode 100644 src/mmu/paging.c delete mode 160000 src/musl diff --git a/.gitignore b/.gitignore index eb76e9d..2681481 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,13 @@ -# Compiled files +/build/ *.o - -# Executables +*.a *.bin +# A generated file when builing a Crystal program with --cross-compile option +bc_flags + # Crystal version .crystal-version -# Generated by crystal build -/target/ - -# Generated files -*.iso -*.log -*.a - # vscode config .vscode/ diff --git a/.gitmodules b/.gitmodules index 81cf3f7..7cd958f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ -[submodule "src/musl"] - path = src/musl +[submodule "musl"] + path = musl url = git://git.musl-libc.org/musl ignore = dirty diff --git a/LICENSE-APACHE b/LICENSE-APACHE index cd482d8..59362d0 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -186,7 +186,7 @@ file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright [yyyy] [name of copyright owner] +Copyright (c) 2016-2017 Utero OS Project Developers Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/LICENSE-MIT b/LICENSE-MIT index a12df3d..1c25d66 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2016 Utero OS Project Developers +Copyright (c) 2016-2017 Utero OS Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/Makefile b/Makefile index 7d48f5e..2857a42 100644 --- a/Makefile +++ b/Makefile @@ -1,101 +1,100 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. -# -# The part of this file was taken from: -# https://github.com/phil-opp/blog_os/blob/set_up_rust/Makefile +EXT_MUSL = $(EXT_DIR)/musl -arch ?= x86_64 -target ?= $(arch)-unknown-linux-musl -kernel := build/kernel-$(arch).bin -iso := build/utero-$(arch).iso -top_dir := $(shell pwd) -INCLUDE := -I$(top_dir)/src/c_kernel/include -I$(top_dir)/src/arch/x86_64/c/include -CFLAGS := -O2 -ffreestanding -nostdinc -Wno-implicit +CC = $(EXT_MUSL)/bin/musl-gcc +LD ?= ld +AR ?= ar +NASM ?= nasm -libcr := src/musl/lib/libcr.a -libu := build/arch/$(arch)/c/libu.a -libu_fullpath := $(subst build/,$(shell pwd)/build/,$(libu)) +OS_NAME = utero +BUILD_DIR = build +BUILD_DIRS = $(dir $(OBJECTS) $(SOURCES) $(CRYSTAL_SOURCES)) +EXT_DIR = ext +LINKER = linker.ld +ISO_DIR = $(BUILD_DIR)/isofiles +KERNEL_DIR = $(ISO_DIR)/boot +GRUB_DIR = $(KERNEL_DIR)/grub +KERNEL = $(KERNEL_DIR)/kernel.bin +ISO = $(BUILD_DIR)/$(OS_NAME).iso +LIB = $(BUILD_DIR)/lib$(OS_NAME).a +CRYSTAL_OS = $(filter %main.o,$(CRYSTAL_SOURCES)) -c_source_files := $(wildcard src/arch/$(arch)/c/*.c) -c_object_files := $(patsubst src/arch/$(arch)/c/%.c, \ - build/arch/$(arch)/c/%.o, $(c_source_files)) +ARCH ?= x86_64 +TARGET ?= $(ARCH)-unknown-linux-musl -c_kernel_source_files := $(wildcard src/c_kernel/*.c) -c_kernel_object_files := $(patsubst src/c_kernel/%.c, \ - build/c_kernel/%.o, $(c_kernel_source_files)) +OBJECTS := $(patsubst %.asm,build/%.o,$(shell find asm -name '*.asm')) +SOURCES := $(patsubst %.c,build/%.o,$(shell find src -name '*.c')) +CRYSTAL_SOURCES := $(patsubst %.cr,build/%.o,$(shell find src -name '*.cr')) -assembly_source_files := $(wildcard src/arch/$(arch)/*.asm) -assembly_object_files := $(patsubst src/arch/$(arch)/%.asm, \ - build/arch/$(arch)/%.o, $(assembly_source_files)) -crystal_files := $(shell find ./ -name *.cr) +CFLAGS = -W -Wall -pedantic -std=c11 -O2 -ffreestanding -nostdinc \ + -fno-builtin -fno-stack-protector \ + -mno-red-zone \ + -I src/include/ -I ext/ -c_object_files_fullpath := $(subst build/,$(shell pwd)/build/,$(c_object_files)) +default: iso -OBJS = $(assembly_object_files) $(c_object_files) $(c_kernel_object_files) +kernel: $(KERNEL) +.PHONY: kernel -crystal_os := target/$(target)/debug/main.o +sources: + @echo $(CRYSTAL_SOURCES) + @echo $(CRYSTAL_OS) -linker_script := src/arch/$(arch)/linker.ld -grub_cfg := src/arch/$(arch)/grub.cfg +$(KERNEL): musl build_dirs $(CRYSTAL_OS) $(OBJECTS) $(LIB) + mkdir -p $(KERNEL_DIR) + $(LD) --nmagic --output=$@ --script=$(LINKER) $(CRYSTAL_OS) $(OBJECTS) $(LIB) $(EXT_MUSL)/lib/libc.a -.PHONY: all test clean run iso +build/asm/%.o: asm/%.asm + $(NASM) -f elf64 $< -o $@ -all: $(kernel) +build/src/%.o: src/%.c + $(CC) $(CFLAGS) -c $< -o $@ -test: - @crystal spec -v +$(CRYSTAL_OS): $(CRYSTAL_SOURCES) + crystal build src/kernel/main.cr --cross-compile --target $(TARGET) --prelude=empty --verbose + mv -f main.o $@ -clean: - @rm -f $(kernel) $(iso) $(OBJS) $(libu) - @rm -rf target/ - $(MAKE) -C build/musl clean - -cleanobjs: - @rm -f $(OBJS) - @rm -rf target/ - -run: $(iso) - @qemu-system-$(arch) -cdrom $(iso) -monitor stdio +$(LIB): $(SOURCES) + $(AR) rcs $@ $^ -iso: $(iso) +build_dirs: + mkdir -p $(BUILD_DIRS) +.PHONY: build_dirs -$(iso): $(kernel) $(grub_cfg) - @mkdir -p build/isofiles/boot/grub - @cp $(kernel) build/isofiles/boot/kernel.bin - @cp $(grub_cfg) build/isofiles/boot/grub - @grub-mkrescue -o $(iso) build/isofiles 2> /dev/null - @rm -r build/isofiles +musl: + $(MAKE) -C $(EXT_DIR) +.PHONY: musl -$(kernel): $(linker_script) $(libcr) $(libu) $(crystal_os) - @echo Creating $@... - @ld -n -nostdlib -melf_$(arch) --gc-sections --build-id=none -T $(linker_script) -o $@ $(crystal_os) $(libu) $(libcr) +iso: $(ISO) +.PHONY: iso -$(crystal_os): $(libu) $(crystal_files) - @mkdir -p $(shell dirname $(crystal_os)) - @crystal build src/kernel/main.cr --target=$(target) --prelude=empty --emit=obj --verbose --link-flags $(libu_fullpath) - @rm main - @mv -f main.o target/$(target)/debug/ +$(ISO): cleansrcs $(KERNEL) + mkdir -p $(GRUB_DIR) + cp -R grub/* $(GRUB_DIR) + grub-mkrescue -o $@ $(ISO_DIR) -$(libcr): - $(MAKE) -C build/musl +run: $(ISO) + qemu-system-x86_64 -cdrom $< +.PHONY: run -$(libu): $(OBJS) - @ld -n -nostdlib -melf_$(arch) --build-id=none -r -T $(linker_script) -o $@ $(c_kernel_object_files) $(c_object_files) $(assembly_object_files) +debug: CFLAGS += -DENABLE_KERNEL_DEBUG +debug: cleaniso $(ISO) + qemu-system-x86_64 -cdrom $(ISO) -serial file:/tmp/serial.log +.PHONY: debug -build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm - @mkdir -p $(shell dirname $@) - @nasm -felf64 $< -o $@ - -build/arch/$(arch)/c/%.o: src/arch/$(arch)/c/%.c - @mkdir -p $(shell dirname $@) - @cc $(CFLAGS) $(INCLUDE) -o $@ -c $< - -build/c_kernel/%.o: src/c_kernel/%.c - @mkdir -p $(shell dirname $@) - @cc $(CFLAGS) $(INCLUDE) -o $@ -c $< +clean: + rm -f $(OBJECTS) $(SOURCES) $(CRYSTAL_OS) $(KERNEL) $(ISO) $(LIB) + rm -rf $(BUILD_DIR) +.PHONY: clean + +cleansrcs: + rm -f $(SOURCES) +.PHONY: cleansrcs + +cleaniso: + rm -f $(ISO) +.PHONY: cleaniso + +cleanmusl: + rm -rf $(EXT_MUSL) + $(MAKE) -C $(EXT_DIR) clean +.PHONY: cleanmusl diff --git a/README.md b/README.md index 255967e..60dee74 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # utero -![utero screenshot 2017-05-08 13-43](https://cloud.githubusercontent.com/assets/5820754/25790536/5d692c88-33f4-11e7-862d-8eaa602f7e61.gif) +utero 2017-12-24 **Utero** is an operating system (for x86_64) written in [Crystal](https://crystal-lang.org/) *as much as possible*. @@ -19,19 +19,57 @@ This is the *work in progress*. ## Usage To make an ISO file and run on Qemu + ``$ make run`` To make an ISO file + ``$ make iso`` To make a binary file of the kernel + ``$ make`` or ``$ make all`` +To compile the OS in DEBUG mode and run on Qemu + +``$ make debug`` + +**Note:** in DEBUG mode, logging uses the serial port `COM1` to write various +debugging information. `qemu` is configured to write the output of this serial +port to `/tmp/serial.log`. + To clean up + ``$ make clean`` -To test (crystal spec) -``$ make test`` +### Troubleshooting on Qemu +On a system that uses EFI boot, like dual boot macOS and Ubuntu(16.04) on MacBook Pro + +Error on Qemu like this + +```sh +Could not read from CDROM (code 0009) +``` + +The solution may be: + +```sh +$ sudo apt-get install grub-pc-bin +``` + +After the install `grub-pc-bin`, you will need to recreate the ISO file like this: + +```sh +$ make clean +$ make # or make iso +$ make run +``` + +The following links saved my life: + +https://github.com/Microsoft/WSL/issues/1043 + +http://intermezzos.github.io/book/appendix/troubleshooting.html#Could%20not%20read%20from%20CDROM%20%28code%200009%29 ## Contributing diff --git a/REFERENCES.md b/REFERENCES.md index b76844f..277b476 100644 --- a/REFERENCES.md +++ b/REFERENCES.md @@ -18,6 +18,9 @@ https://github.com/redcap97/cyanurus ### eduOS https://github.com/RWTH-OS/eduOS +### willOS +https://github.com/willdurand/willOS/tree/f8989809ba93437abe52ee14b0a0623adc0abea1 + ----- ### Create Your Own Operating System: Build, deploy, and test your very own operating systems for the Internet of Things and other devices Kindle Edition diff --git a/asm/boot.asm b/asm/boot.asm new file mode 100644 index 0000000..337ea59 --- /dev/null +++ b/asm/boot.asm @@ -0,0 +1,190 @@ +; https://intermezzos.github.io/book/hello-world.html + +global start +extern long_mode_start + +%define MULTIBOOT2_MAGIC_VALUE 0x36d76289 + +section .text +bits 32 +start: + mov esp, stack_top + ; `ebx` points to a boot information structure. + ; We move it to `esi` to pass it to our kernel. + mov esi, ebx + ; `eax` should contain the multiboot2 magic number. + mov edi, eax + + call check_multiboot + call check_cpuid + call check_long_mode + + call set_up_page_tables + call enable_paging + + lgdt [gdt64.pointer] + + jmp gdt64.code:long_mode_start + + ; should not be reached + hlt + +check_multiboot: + cmp eax, MULTIBOOT2_MAGIC_VALUE + jne .no_multiboot + ret +.no_multiboot: + mov al, "0" + jmp error + +; http://wiki.osdev.org/Setting_Up_Long_Mode#Detection_of_CPUID +check_cpuid: + ; Check if CPUID is supported by attempting to flip the ID bit (bit 21) + ; in the FLAGS register. If we can flip it, CPUID is available. + + ; Copy FLAGS in to EAX via stack + pushfd + pop eax + + ; Copy to ECX as well for comparing later on + mov ecx, eax + + ; Flip the ID bit + xor eax, 1 << 21 + + ; Copy EAX to FLAGS via the stack + push eax + popfd + + ; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported) + pushfd + pop eax + + ; Restore FLAGS from the old version stored in ECX (i.e. flipping the + ; ID bit back if it was ever flipped). + push ecx + popfd + + ; Compare EAX and ECX. If they are equal then that means the bit + ; wasn't flipped, and CPUID isn't supported. + cmp eax, ecx + je .no_cpuid + ret +.no_cpuid: + mov al, "1" + jmp error + +check_long_mode: + ; test if extended processor info in available + mov eax, 0x80000000 ; implicit argument for cpuid + cpuid ; get highest supported argument + cmp eax, 0x80000001 ; it needs to be at least 0x80000001 + jb .no_long_mode ; if it's less, the CPU is too old for long mode + + ; use extended info to test if long mode is available + mov eax, 0x80000001 ; argument for extended processor info + cpuid ; returns various feature bits in ecx and edx + test edx, 1 << 29 ; test if the LM-bit is set in the D-register + jz .no_long_mode ; If it's not set, there is no long mode + ret +.no_long_mode: + mov al, "2" + jmp error + +set_up_page_tables: + ; http://os.phil-opp.com/modifying-page-tables.html + ; required to implement recursive mapping (paging) + mov eax, p4_table + or eax, 0b11 ; present + writable + mov [p4_table + 511 * 8], eax + + ; Point the first entry of the level 4 page table to the first entry in the + ; p3 table + mov eax, p3_table + or eax, 0b11 + mov dword [p4_table + 0], eax + + ; Point the first entry of the level 3 page table to the first entry in the + ; p2 table + mov eax, p2_table + or eax, 0b11 + mov dword [p3_table + 0], eax + + ; point each page table level two entry to a page + mov ecx, 0 ; counter variable +.map_p2_table: + mov eax, 0x200000 ; 2MiB + mul ecx + or eax, 0b10000011 + mov [p2_table + ecx * 8], eax + + inc ecx + cmp ecx, 512 + jne .map_p2_table + + ret + +enable_paging: + ; move page table address to cr3 + mov eax, p4_table + mov cr3, eax + + ; enable PAE + mov eax, cr4 + or eax, 1 << 5 + mov cr4, eax + + ; set the long mode bit + mov ecx, 0xC0000080 + rdmsr + or eax, 1 << 8 + wrmsr + + ; enable paging + mov eax, cr0 + or eax, 1 << 31 + or eax, 1 << 16 + mov cr0, eax + + ret + +; Prints `ERR: ` and the given error code to screen and hangs. +; parameter: error code (in ascii) in al +; +; 0 = no multiboot +; 1 = no CPUID +; 2 = no long mode +error: + mov dword [0xb8000], 0x4f524f45 + mov dword [0xb8004], 0x4f3a4f52 + mov dword [0xb8008], 0x4f204f20 + mov byte [0xb800a], al + hlt + +; block started by symbol +section .bss +align 4096 +p4_table: + ; `resb` means 'reserves bytes' + resb 4096 +p3_table: + resb 4096 +p2_table: + resb 4096 +; http://os.phil-opp.com/allocating-frames.html +; the stack now has 16kB (four pages) +stack_bottom: + resb 4096 * 4 +stack_top: + +section .rodata +gdt64: + ; `dq` means 'define quad-word' + dq 0 +.code: equ $ - gdt64 + dq (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) +.data: equ $ - gdt64 + dq (1<<44) | (1<<47) | (1<<41) +.pointer: + dw .pointer - gdt64 - 1 + dq gdt64 diff --git a/asm/interrupt.asm b/asm/interrupt.asm new file mode 100644 index 0000000..d26cdcd --- /dev/null +++ b/asm/interrupt.asm @@ -0,0 +1,147 @@ +; https://github.com/ghaiklor/ghaiklor-os-gcc +; https://github.com/tmathmeyer/sos + +global interrupt +extern isr_handler +extern irq_handler + +%macro def_isr_handler 1 + global isr%1 + isr%1: + cli + mov rdi, dword %1 + jmp isr_common_stub +%endmacro + +%macro def_irq_handler 1 + global irq%1 + irq%1: + cli + mov rdi, dword (32 + %1) + jmp irq_common_stub +%endmacro + +isr_common_stub: + ; save registers + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + mov rsi, rsp + + ; call handler + call isr_handler + + ; restore registers + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + sti + iretq + +; define interruptions +; should be keep in sync with src/core/isr.h +def_isr_handler 0 +def_isr_handler 1 +def_isr_handler 2 +def_isr_handler 3 +def_isr_handler 4 +def_isr_handler 5 +def_isr_handler 6 +def_isr_handler 7 +def_isr_handler 8 +def_isr_handler 9 +def_isr_handler 10 +def_isr_handler 11 +def_isr_handler 12 +def_isr_handler 13 +def_isr_handler 14 +def_isr_handler 15 +def_isr_handler 16 +def_isr_handler 17 +def_isr_handler 18 +def_isr_handler 19 +def_isr_handler 20 +def_isr_handler 21 +def_isr_handler 22 +def_isr_handler 23 +def_isr_handler 24 +def_isr_handler 25 +def_isr_handler 26 +def_isr_handler 27 +def_isr_handler 28 +def_isr_handler 29 +def_isr_handler 30 +def_isr_handler 31 + +irq_common_stub: + ; save registers + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + mov rsi, rsp + + ; call handler + call irq_handler + + ; restore registers + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + sti + iretq + +; define hardware interruptions +; should be keep in sync with src/core/isr.h +def_irq_handler 0 +def_irq_handler 1 +def_irq_handler 2 +def_irq_handler 3 +def_irq_handler 4 diff --git a/asm/long_mode_init.asm b/asm/long_mode_init.asm new file mode 100644 index 0000000..589ee11 --- /dev/null +++ b/asm/long_mode_init.asm @@ -0,0 +1,20 @@ +global long_mode_start +extern main +; extern kmain + +section .text +bits 64 +long_mode_start: + ; load 0 into all data segment registers + mov ax, 0 + mov ss, ax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + call main + ; call kmain + + ; should not happen + hlt diff --git a/asm/multiboot_header.asm b/asm/multiboot_header.asm new file mode 100644 index 0000000..d9a37df --- /dev/null +++ b/asm/multiboot_header.asm @@ -0,0 +1,22 @@ +; https://intermezzos.github.io/book/multiboot-headers.html +section .multiboot_header + +%define MULTIBOOT2_MAGIC_NUMBER 0xe85250d6 +%define PROTECTED_MODE_CODE 0 + +; it is a label +header_start: + ; `dd` means 'define double word' + dd MULTIBOOT2_MAGIC_NUMBER + dd PROTECTED_MODE_CODE + dd header_end - header_start ; header length + + ; checksum + dd 0x100000000 - (MULTIBOOT2_MAGIC_NUMBER + 0 + (header_end - header_start)) + + ; required end tag + ; `dw` means 'define word' (word = 16 bits on x86_64) + dw 0 ; type + dw 0 ; flags + dd 8 ; size +header_end: diff --git a/build/musl/Makefile b/build/musl/Makefile deleted file mode 100644 index f661024..0000000 --- a/build/musl/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -ROOT_DIR = ../.. -BUILD_DIR = .. -ROOT_DIR_REALPATH = $(shell echo -n `cd $(ROOT_DIR); pwd`) - -# include $(BUILD_DIR)/config.mak - -# -w Suppress all warnings -MUSL_CFLAGS = -march=x86-64 -m64 -w -MUSL_OPTION = --disable-shared --target=x86_64-unknown-linux-musl --prefix=$(ROOT_DIR_REALPATH)/src/musl - -CC = clang - -SRC_DIR = $(ROOT_DIR)/src/musl -VPATH = $(SRC_DIR) - -.PHONY: all clean clobber - -all: lib/libcr.a - -clean: clobber - -clobber: - $(MAKE) -C $(SRC_DIR) clean - rm -f $(SRC_DIR)/config.mak - -lib/libcr.a: lib/libc.a - @cp $(VPATH)/$< $(VPATH)/$@ - -lib/libc.a: MAKEOVERRIDES= -lib/libc.a: config.mak - $(MAKE) -C $(SRC_DIR) - -config.mak: - cd $(SRC_DIR); CROSS_COMPILE="llvm-" CFLAGS="$(MUSL_CFLAGS)" CC="$(CC)" ./configure $(MUSL_OPTION) diff --git a/doc/utero 2017-12-24.gif b/doc/utero 2017-12-24.gif new file mode 100644 index 0000000000000000000000000000000000000000..0be4d860dc83abc15a8332e383d364ed49221323 GIT binary patch literal 21578 zcmeFYLwhA$7p|L%t%{S1D^|t!iftzq+qP}3*tVS&+eyW&*fv(3_uH+rf57RT*_e&l z8P_%a+~XON(h}S}zvm%qzP&*FuMxsvQNmzRpb*hvQ3`S~v$L`Ba)Liv+)811VlM`WjO^^rP)Mf1ptb?(yIJ&Tq3G`lBxnys$62a zJd*l6vSxgW)&k0ALYj7x5~8v)00ns|HAw{}MQHf3)q4F8M# zzheRc4~D=XgTyk0g@UD^BT}nK42nklPNf!z*JwNvf`rd*YpFyLf?{UNFkj{!d!+W{Qy0gjW3-QTB%|_9!H=$OJt1^G?&62B@x@{N;wlkku~~hl}xT! zCSRIPM5kg7fW|6!PP2SEhHennuacV7u$#;LsqZYXhEc6tYu=1ldcIJuQs+dSo)&$7 ztvz5e0>Z}cu#=5-dY~)A_as*+48)}Z({oZS&NyU~&vxTdDop_!jmxaF>`sTY^^0d? zI4#7m{RL*%l1jL6#KiHY@f3Qvosh7@H5*uRB%BZCi)Hf_db^%3{?N}m#`k`>UGKz! zLlP7ydfxEI=f0B!Y?UJ63je_7{IYMzJzpG{6)pUaRyGgtj%a5}_>bs%mga5j;J> zWrLf(!Yy5fHY493pDwZI{Jkn#^4^_X133&pUoNT9paIZG3nNru)p5ma36aJcNc5U< zUocwVIaLDuU_`2IUDs%^t2R%Q-st`=Pk$aO(%M}l!W9-{-~D*bS0uJm7E>p-Iar<4 zL!+(Z(E3JodDU$yGU71!F$i7P`z{*G*n{jQdpkrR*<;-QJezUTWu7jl({v*GVKKzQ z>~#dq$q3WbiuvR6cIIJ2*8!9>8p$@Y=AZLR4E|=bFUaLB&bi4f1VQ~G#xRqzbiSj=U<5MPg@ zHe@%EJBax1ilf1qd3kY&D<;xmJW?@`rQ`3t7uOML^AEQ@sBYQLSW&TQtnt}4c;2== zlWy`+49b~Kg`BQwr#{~$JBN$?_ipwH)8ZMI=qN>o^S{I8_Un1fo6t!sa72L+ka>2g zH7iSmEMJl;FYE%48;T^3Ey}>d`bldfVn)%4`y8HfS+ttq=K5e{MJkjvW2dQJti@PIdyBG6*t~ z;|MtX4H(WCWjPOvMPS%C-vcsUb3iAZAWn82SBP-0)CCn%#*kP&bVWL7jO z4RwmP$K85TEgivUmxzeQFXZsW6n1S;6KKj@$*6(7h<`J@F;=^l2EId5G%cIEjE>N$*h}^%kMNjF=9cOCy5IUGRZtpRFJ__ z_Djt#$JZ-@=9M6vTuMksh^DQLn7eSRVXOVMg!&J^61K{;;Do`l5RWq{bW!yukNin= z@?Kk-(p<*Iur+HdXIcpKx6DzjVD>O{=sG4-Z&9sajb>hfN~6QtGUdQ;$JP+=YNpVx zGu6;kq`{q3Sai-D{m{485OBk$b#PjU5;@L`bzNyRJWe%QkFc6!{`ZXzc9ENcP!jnS z*KEL}32V~Uxyh!MID36*O^%P1N#g0B`cdE-8=dTt70MBMHXa+su~)6~h6UQ(i5k6W z1jVvXm!HK)jCQ|2RQnFyJ4t^@sYP%0m$#&rNXlQY_u~N*3PxC%#hI2XG}U`gu~b^< z9W0w|^cH8$8utNLF8F*4M_n3Tw;VU zKfh5mgp$~1of)8!AwWP9TC3Xx%#AMm0!ZPCc$dR?~&@@P9a%&@O&A0PGob$%){H2OtX-T@dxOIxsB7bdZ zby%?hv~2@*ewk@uvNeT3-*(XQn1O8;MXRfcu=D41DvhIR3`TQYijBQ~;MLb?mxwDT z#XXffM9CiIL8P^N=G_}71P?s!C{9AE(-aQTn~zuEclX5FrM-9^t(jwvyNDnLW9v zj)C8abk`~E8zIY-_G#AApc$pwh^&1NQ1Q~o+=T}d!eS3oiPo0NFYj}7kxV~0H5!M^ znS8^tlTO@$ZT)@@z!KtpTSbb)sXG9%Sl$qS^Ku-+>ZAZw=i1v&d3^KJ^gi zFnap9{jlR@aWCa~&dc9=$%FuzVX>@908w(vo z?-zu&XX4jJ7!Uq&X%q?{+mA^3EF32p3*B2LuuhXM94jvehp@JWvP1kW;bzZ-uKIZ~ zZ;M-rZb#jBg%9-k>}9fOmwlvy-gX@8Y5x_wRg#m_%-dJn_p9%?jvM>r`I>3M>-?2c zBd-nUP&1{wY2kU%11i~l?E=L4kXcA9HZyfDnB8E`&Skd33w^eRcH>6;<151@z6K@z zyNx6PP1WprRE2L9RO8r%k0A;6f# zgDVMEfnaMkNNA038?F#Dr5q?CkT$}eQLPXN{V*Qr|7~YaH%ZO!1rWM{9*Pj(e$K<% zXu@F`nRx8k&91<>@0r+%IRA6U;RD)7W2;dwTQGF!2){7q|HLga7c)Ne+62<1puaS~!IHW! zhC&#x+8D#ZDD_9J!eOqbdytSpvN*!I6?jm%8@u1a!jT$#^g~IxemSm;cn`!VQUq^9dby zjy(-Mlk%&3o_AJHV59mm@xC0K8&jd1C03S<%{+UkDXFw#LL=UIrfJ$c=s|*L}bUd!a!PWHeC7Seg%nW4w{fE};=3k3XQX&_m7p4plG z;Zg3j7cieT?;#_9?TmA;QXEzWIMkN!y&bx7cZ3_Vf`BtJp(%sVfyg1@PWzDvVc-a7Cj1Ri7{=D|zt2@( zWK`j|&dArOe3@Zj3-&ij-oj5F49Uq1_(Uai#BxuW@|BLr2DW%<`~o>x@G zGe?E}np{k4A!U`z32nJ8Vg^-oxiexxU{=9XW~B#RrA3lbDq`iqdPU~4{JdM`Lq}1W zb)|PzMQl{=OJ!w#Ro;+!{_X^X<#}eVRRKF~313&V@k#UxHFW=^QU802nRW_4L5&Gl z5m`j)1Sg!V5{#XaF)sSI-w1Z@j?k3-K?FuT#!Ao_+{Pq<5KM`-^EhxAi6Ktb8jM}J zb}|(-1eNQNWkgl^xEobwJeaSO`B{}5|27PvvWvdd^B;7o{I&B@dCR{#D;>NG(kcsL zq^byb8)Kc zWYq2C-RaKORo&5zjPLme+V!CbjSfB;XatQ!=he~KiFXp|LERl@_yG6erX>3I&*;ST z>iiJvJe%|KyiB-Fmo7a3bPY{$H6QfLn7TM!U^QQN-AB~g5CjKLw-P#RT{VQsdZPn2 zBtiic0v-&85kvsCmX{j@C5t9%L*5?>&Hhmcgod7fVDGLzpTe)7^?7euJA8TIw{HBd zl!lM|^^d$r-t?2|-lOg~oby5{pKfyiD83qWy$L#7&(B)!t+(p11AzSa`t+9b9&P%* zG4zY;fadA?XATRIkh|e*`$;w$VQTs@bO&&HKnRl{1mC`Aq=5$A#y6V*irD^J#QuJp z4q~D{u+N|rQr|($fLQk+{?;JcOW!@hKnN*ljZJ_dMpGx#IO=6H2YpR zobIq>Pq%cA%~v&Ot=a||4{E|3)-{%HSVQ(HpnC?EMo0DEEPX%${`N7hJRT(A2XiNGX>u>_Rq$ChY(}Xw?T7t%!6T|j%cz&ZlvjRvix%(nqjiw zvY&!zwuE8q8hK``dakr*+K#BBiDR@ZgQg^fpd5qq+KR|A>H)k%+b`Uam zj zaBEIdde zZlL&l#VBr-T7M?sYD~;-AV+FBLSQk2e?eFvuZoBjk`+(rt@QeN*LHo0Xyy+OU1{!gNXBKbitzfI)J&CQqr z-+X-*cvh{kAa}wg>FCP>`3N<#vwgcb2av zM)bGZ^!wd^Y_#Yv_QdUgJ~teD*9UWU-+Oo8b0m||7zkLVX}G{$R?q?e&Viu9z7X-DDC)kL!J!1xA-?~i4Cqj9=TLp?kZ*cd z8vIAxU|$8iEA4-zG5c2mbR>VZCrx~;C3vhOxTnpuZvZ+lM%}Zm+n4u0HUl3CgAXkY zj&M*m<7j-)q{lu4pf|tVvm!tld<9U~p_Gf|(m$Tr@dE%=@!K*(8S1Z?Neg0P)psTH& zs~zyw9`SW--qpUr^|Al;Y2NiY=(=|H`Wk$FOMG)Lc+;M8bop z;#rX6V^HF$Wc{Nw8iZ`ZsUpdl)b68#(32X;lcK|un$V@P(7A@gqg4H=;oPHi{*%7L zW18TziqKOG@pJO^b3W+F2JOXx`9;p~u~6aB6Yb0#?a9yZ$)oStpX4uYE_7;Y9m?d7aDb1y1`h88dNIr`~f@2m5*ohx#mOhwPz|PTSa&Y z<_s|!KNT7!Jm^~1(`okFe1aCQXkS=nKmbBC0|GTi&B-)I!!}>+2lLrn#&R%BT*l=Z z$m+YInN7#F#Ip2LT{w|;qYlWyYDge%)9PTc+2^}?&&w~zGLIW?i99_X^_i($ArE4m zGOO;QfH@-2N}JPim%ePodN1$o!GX)c54j(7H&>v|?;gE)es|YLg}Y-3L;!#ASEqr{ z90+1}XqNNO7WoXz6G&_t1QSMa+xCboCl6X3%&rrGa`CAd>VX};qPhO0XdIr*V*3Ta zzT2UNF~_)+mb*x{$|~KU8H;cN6(Rt2Fgf$}&hb`Kqy!CuH#3aMK7q z%8ubhZ8-rL+lS!+OnuO7a=*qXo8{Q%#J^KBFPS#WbM1z*De#;ef*pi-Z-&_v1zrxD z=NMNX*_1@!ezquyJ+(Y20dUP)lqE?3s7g}Qr7bG5%s<(c<+zSoR27AeR8*BDf3~Wr zD%MUyt7)3Gs%uK3?5S&8l(uS!n6z?e>U$ii%jx;RaBBUIc)HXyP5^Lfn>n$%Yg^=p zrLUS7m$vEH$mTxl*tH+6EZBC#aOpWce7fto%mIMY&P!(P`W|n(FZy05rK?Sz7b9GT ze%W0fh5`S!+YJNZs59!}6X2V4(Xg3KJxH{!G28D%^-E#Hh&TNfSOm$eL7nX&YdhePs+OO zyKhGM9C}}lyB+%eG0C;ZWSFBk4xvf%JB{F4^f--?wCg%Xz}Gh&PLhrByUcK%^tj9k z_37S+(I|F0&MQg^xGig1^gi_fG$UP8+X<3_ zUPnM7kVmlT%^l0WU5)|unU#g0&sF;g$fq}#S;rbwf@Uy$QS~C|_q1x!_mwmd0V2O2 z63u^~IQI|=_3tYP}gal)E2*a{o2WW{I`9ym3;;YAVVAvYL za&(BGZd?W#43Ub-jTN8v!b|=T|Ba~LA)4%$XbUv?jcmp(MsPkIW*zby#jQh(_!-@l zD>NIyk6WA^M>$+LHVz}LL!8PQJ!;rW7pv4Wvwg%oT636Oy$XJW!F?g-@eCiLk6VJ( z8RKUvy$L~Y2jI4HA7cbum$${*Vm& zZA>^EEjT8Wavl?3TE@UOIc>OPh$&NA0mn8a7rL}h5MM@R#U`~>ytG>(Q${1tCapH~ zXNLj4td4?voCdI6jXJ+98fm$xpm z&RQNmuBgkDw|BPA-iEd)1>q|=^IGQ|iJuhCW-7QNTIXJcp5%phPJ6DXTE$UP!C!SM z0%n>sKA|i4?xg1ean$mlzRE?gy{QAvT?)~1RmBY9m7_t@Mcg%(5=*WNq}G3mE%qz0 zg?Lq1!v4fk0xHwxHbzt1uz2ayie=5bNiuev%DCJJMSn!A<{hWfzU5ddhMi|--8)rC z&d4YeMyZuqE0>GdS*xYItHsJJS1F*Fh}L&4Z?j{@>A}$B0q>U^(y^;jC@Qp<-^J7G zo~mwKtCju|Xy(nP)p506=$mDMHGA7q>-_*{hTr&922fXu!#HduFtW9xnbI54+%8S9 z3A870Qk&9XPR)LGE6&)r<>fvx@QV=YxW}iroU&h8O=RjU2dA{w9v$2Kj*(xVP0wm= zWn_2C)-}IQZy$ZUatOlL`+J|)*t6= zLf<^PHYHq#xD8)zio+82n#tCFtn%p1v8&hEB~&UZ)dW2pQ7;hh8GJ^!C3)^S1jt@2 zLm=U`LWAmqzOt8d>DKaA+#1Nph<=MGWDNSX+=ks{{6?8e8v5aW5FwHyfsv3A`;+qy zS2<^&sV-wg%k?g%9M1%dFPSvTVZIsX#G5W|zFmo!_lqI<@M~|`HZF-r}MQ)BrmbsqC($3vwbdShF zwrsu3RpBV-j^UTUjdqGVXH`Dix`haJ?0ncWWdShyRE9Wf0m9|67*J^`Eel*slW@t% zj6D-E%vvgpeJnr2wpN~s`BSPDDE3@EXeaPnO>G-r1S>-SCZlJ#!^|Bfcfc|CodZl6PX* z(}ywVXrOp>)Q}Q8Y0Bs9*EGG3)bcpSDKZ}XU|t>*sgsXS;I|^@>MU2vb9PA2IdX7+ zn>r$L$y{CE)2-_q!`>Fpd)Yeq>h_wQviwyjnRE)U*jz~R=PYIJwUzhaT`M4ds+G?@ z*KU4au1t5U6^gsici31Rh-Yt1(6=<>;+vn(bC0dfy~1+&*!+p((a9Hk?WVc5eHYKt zdy{MA_rli?LE;&Jl6U*g`uX=R&2wZa<_;bCQOEc(A0Qhhg#g-<&3Eyd+LhuLKj1f` zb9h@&1RG4>n=F*c=O;ah=V>Lt6<&-)KUA1jvPv8kU$z!~R6c_xL?m9WWOv_p^9@{& zZ6+fl=NQ*g;$Hs!CkcOre3VL%RA-Zb2U?pyEkN14CAkRN7JBPTo^Bm$n6KZp_& z>4_7j6dkjS7NWx+6qEyT@dKE!29%IR?q7vjO~n?HtchCWQM0ghqx+lFDx&tbY6Kwn1R57#7$SP8pOeuvLN zl#&6qVIe6j2}6bv>sU-xN)ZBnfQy=>!SIMDk)*>k>V*%simfmtQK2>iF#)(xFhG1D z084CrO@yIEh`vWy^sx`lc03|BM&djU#j6j>cEs;+m?%n0E;hU4P9%C*^bcF|>%kZU zL!p03znLqJUk5gu?0IYUhVbih@WB)#x`WWQV5_4kCPtWd_mIQUiidQ&>jwx{7t zx{gl9UuLovv8A-6tVDc>ReEszw&$N#0)kq6fgEThWjy+DQY2NhI(EVcI<|&?A~JXq zU^N+YnB3YT2=tY4kBzT#?bZAoZv7n5I|Rwp@HeZ;>RL_Z_)b*~^EWbpe%p%8Moujb z$s#StT5nC&(MXM?PRFrM%ivFsz)SnrV1}#phlY+P?@dagXiS>H!7s?kYyjc z`6Gzrk?!Sep$n`Y`FGf63gzTC#e0`?2A_1LLWpJ&5s3r;0FZ0sRq&^2za<4-=6+`% zd~Y6z8k}*Po{|!uPTd+^gOc>{RZ#jfIOaMtXF0f`JIY3+*wr)IH9v6eD(G{V881z4 z$1kF~J}t*kfK>t_Efh}}7K!MZg%29-{G)ieK08%8_wm;AomKiCTCV6&UYvg}&R6N# zR+21}Ot?5}&5?Ddpyc>JR#-#2@xu)?4qcqzY_J+t>Hv?{Jlz%d&NJ(Jw? z$51D8FGr}EFzH1tXdxda$qN~0G` z^@!hAzMRKQ%xy-A+)witf|N(8OjwRI&{$J=YjPS~qWRgc;@C>yb5wQgs>IVWjwm{@ zCpV+gD)3i4SyLQcl@arnEkBZ@pT1-nNkD3(A2UjSc^+9kdPDB)a*Y6aZh>^&u|)w= z`VaZEpoF>*iJ#)s7!4;S^po(-y?C1+OFABYTMKWGZRKw8w(fOuS7!fcI(_fR-t?UvyIG&aI94NEfJZq z(tW9f?d#B*jn(Uea7yAETO}~%bjzN4U3^zNuZB@_HpJwYo^2OFei~G*Dk$53zJ*QG zV~gtKEJ((!zV-+XFs?SGZM1Q$w1@p6Y1IP*p4N@X*Vc2?V;=isdY6|j*G%~3H7Ut{ zEpPn9Abw}ljAWc0+6HLxkGtDy(W$MB=&kymt@bWyD3NVh=SCly8AQ1mR0Qsvg{7aj z?xaocR8Dkd+f6fZNRf7~eyc&K&jA77ms|9bIlt*v@ardWZ$7E3EZS@YpY6<#j1iV< zGan7yZ7XjAb~~;N>zt-vX0{mFcd%v*yX6c_&kPD!HeX=`@2V3Z%gCX2^lHhgRz;_&=hobUqj`XIgrW$16&*7*G2iPXz`Yi{~tD9Xk# zk3NcQ~Q6jyUH)w^@WU=JG;&6R1z)9pFhrD7^OW)wr;NvCDfhi2S(cO9b_+%Qd@ znQB#H;RO83V`X~UrOVNWu#=)@(Bn)$JZ4mvY197vv7?)_R>3$t8LFH2Wn}jEfWgjC z4y-7GdZ;{dXw6?u-lH}z(4RK|RwtN^ebp$_SqC~8(M)`Yy z{qOM4K6(W+B5Zx^T9dxoJ-j$GY5^6~KY!JIHzxSC6z$E})OActO*v7_a|n+~%@V&x zOh9Qs6?1cIi6gl*lOO)(`rzq9I{gi8kU7WDI_r*ky|QU}?v`)e$qA)iub)_$po)v@ zF*~eQDb(4@r-74oTp^j zRQ+%0&$B1LWN!_9`MRw%jTxOU%k+4YKdxq)SS!#gIG6Hr4_Fm+ALJzxiWp#%3j8Ax znd82pl+ESkN*tM*S++V(!t$LRlW!Ld#OaMx7tuTyCBH6O(s9}Zvs&FR^nxxr>e4#P zY~ngEVjC}d+|zoGZ7{!VI*=}-$N~q_GrqH2M%-Ntn=6dkXNszbolnvY~jRg2W4)jMC&xie_hn~g!j5H{6_H^Oh*k3^?mZALi{4aV63 z|1Kw3KY{%SJ?WQ+BELf&>i`=~hP?Z-{N!+)kEU#pJou}5e(O&pwxYtyH`eb?BuSgZ zTfQ6Hgym>w6W4aMU2P(*^Ww;Rx!Jc?Fpp7v5wssI`Tq7$F!1gi+MiVa{tW-4b8pZm z&SqD^&0S}CUKboN%HPxFw!iKLHOIu`Xz(;ccJ6-T%k;IddzGJmO(?MYwzn{%?cM98 zvFntUIkp{)Z2nBBeeh(lXY~pLR~U>&z)2K3s7K8^uT5e+k1{@ectxz>InIFtKF6usaHMe^5CtFcZN zfZHHicPGQxGMAP+KaDTls!X0KxbiUc8|MUNxNR_%LB>v6Mp>jnl~r-}!mk%IfR4gh zd4acq_F)$Ov)6G^?4YYfvht`Gb!5afLS<1Dv7}Xr>@n?0knQ4{RRMmIH*E>)s1!-L z@#6YvHTb_&ky+t#ruBIx4#Ij_Nj0YSsTbR`w~bh{zvq6bFqL(6mAvztaid%vk44QN zjcz>sZUcC_h;A4yG9Nsc4@URBCX3Pv*yMHGAXBh}ZugC7RB6=R3Q}=zLfgx3@5PR! zZrlni!)-q_uI=prJax_OATrnG?GU;o!`(2JrtRGbo<+^wD3QnI-56;E!~HlV(Dr_U zro85UlA-S)2dMf@nwtM#Oh_+^Q86# zyyN?F^|BiX$Mm`vifjM+HWc8(5>r_xl_TibU4Nih$x_iR zCfh%jP|2UkNmVVT?_2sa#Z>~7>6WtMLrXbPXcb&07jo~hiUq%D0U>m1k(jL&$O3dK zXjOkoQClh%zNpoTyXGoYp7Jz$&-t6HmOY$Xt1mw3wB~468snR5%)h8~mbzwIo1U`l z{Vv!K=vFNjTkEEJ=nWDoS9?*L>;1oe8bNhW4PZW}hYMUXe6L>9WNmA#_GK_tq+Oqg zZ)!^aqBLjgo|tidPR{$fq!gpykW6lCiQ#9oMyuRdMr~@XYX$$XHKFfbUtDo(>#aR^ zuGU?;Yilq1V07f8-ul~gT|D}O&N;>A&k-wE<*Xl*YBBwGxk`JNN-vXpb=CHjaclR^ zHl63t#qwP;SIw~iv-VQ;PIFUxueBeu?u+h8l`IhWr5!DjXAo~s`G#cT-0u-Av# z(H|_p5{y*62Sd>|fNH=HN^!Y{pvv8Z17a~_tNA zsc}?CwBKtdHE;;hn~7woT;A+-!OnAo496H8WB;9cijV-8zE-}8w`dCh!yU1`DhUJh}#EYo;@S0-G>vuZ%sB1t!UnUD0D zyrTS4-U?oY*pF$2l&<75LAND|TI=Y>u4POgjv6wm3vFt)_A1By$$F4 z{EME@F-)y+7p?obk2lvb$~Aiz@AG*;iq<&}mQ=V;&i*|7k5x}<&fceY^EXUD%5|C3 zDI;f|7&3pOb;CIa#_bW65hbV9xPq9ml}}GIHnyCF-(4z4eAx~+c;@0L3Bkjr=SnB= zD|{nv#qD9IYG|vA?N3>}>H?}7?Hfz2#BOaNNR*t^d~55V#ZIUnds)?QN^Lt&yxw#Q zUiAc}=jzoiKVo76fS7^cr{|eF3*l^0{xEnnx}gK2*b98kzi>x{quCd?(Ft+K%Bo}q zKA^Mjn5)x(-RB~oq$kEnemp|_9Len*y)hl1#eWJ_9YPbLQ>=dV23^&repXtw1X!n6 zQYA<9bcSD(y%CmGAFF8p)B+GR%2dEG-dF8WH$$+3*Mxl^Os!KF*UmWkxAwwR<~D^p z)c&c*t(5OT{p*|?erU0$g%_5Xf9u9Pj-nLjFZ7ryfxf|B^F{8&n9QAv?x)F~E6RZw zk?u0>0ib<=6)t1+s>i5tkcT8W(zq}FzEq`(|I&#kn}_>dgdf1z9pN=#QH53k7`$xZ zO3@MY{q$$3^N;Ko*Iz(i_d%ZJ7wX9r-~0PuJj=jt+z=YP;JRdLM$h2)cE5iXq2_S@ zIvPHDG`WDKA4)3UEqec*Sby^?le%c}Uzia}%7GI5$Om0}b9tiQmcicZscNc4 zHxQ|R62UQorxc@wJ@M*1&CS)f7JG%`ZUy)$RT%U8uS?&!E4`;%+qi3aNAx6~Cz&ZG z5co5&DK4ThmQ6K?7!cpJ76Y~By(Q%l=uFyX5w|a;>J8^I+avs2XADB8OJYSZ2EHO1j}9fJ{S?kwNK<_CdtM8i%LptfV*R4pp&C6UI#NBNs<`1E}HwOf-{uUVZOr+z#7=xGV!AL?g}L z#AEPjt;mC^$;IC!!?>U#k5%K-aKp6l!h*^p=7W6;p)xNcUHMHzbG`iJA~Q(w;xQ|M z?;ZZ?&ROW(iTawE!1e5)NID(_pmuVGJYEJwBuRcuT_{%8WlvG`@=-RHPb zraf5zX)DwH%kw?p7~Eg--n=3bGl|T(XZj zu|Z>zDMvv4B$rpbGXCFXxa$gOmU>6`UNl~m3_M=g(M5m8Q%stQL>Egu5OV_%N%*Ak zzy$B&B2fic(TPM=;ckc;FK6U;tEqOcq)Dql<>>q%+DfDH2zQz6t^2a8x3JxT^pA|()l*3sn5ldrE=A2M1-?S1;!Iv z&vU86!tKWsMv^Nrvl8kSymNGN$Il}V-U>|C65wbUy1dl)yb454<1QzHnhO=dY(CAr-Oc=b&bDXeIiA%RSS_nEEe*;EzkJGAm5Wc|i|;~PekQfR zU}et)`DmBt2A^bK>tri+)UA2dsnTUXjOYID_TY^u{f*cb$eV`w8mKa5K0HvXw^=G` zV~332%;Qt_x{Bq|o#6k`etsVB^>MB{PUMS@-%N_;jvDAp(&Wv_+X!;y%_5gIgHElg_VC-{s!7iln8~W~ zrOT7`Oi$CTVv}kOlWn*nXarkz-<#5&>Ek0p$ zF@b&Bz!ka-cdzWDtlS!_`rK20`t|5@Lg1`ShSh23x6Lp*`kKy|q5-~MOU>T780$6N zgz1m$!SmiM=}f51oEonNibcOF*|LB1wevo;ejV9xpY_F}eL6OMC-iiITD28j?Tzpp zBn<5xqWv-v#g|%PZ1KUx9U)+Py zlWn<&wf8(j{TIEz82TMOd(O^758j2D*ZWyN`|p9(V=?)j9eF6)5ztwbg8V)GQzLKL z!yg~DD>lRCE2+sD(d_RjRml~7h%qr$MbsA&Vn{`}=fSSFMY5~}#l%^I5}oCdFq|FqtchODS1`aJR#l4ZJaE^85C$R<`4ItX~-8 zpBhp6FRd{{GM_ZtjwZcHo?i!lYSNn3=hQ`1vF$n^wHG7cm^HRDo=jGgek-Qib^XT^ zI$SPx^a(ityWfL^u&+}+RS)En9t`TZXq}H_3i--s)V*Df*Gv?5?N{EFZ$t*b)Ma_S zpHBB&`NjWw1YJKCZpx#%6oouL|7?Bbfk@myivONqX!Zi3aV(AOsQ}a%zO1dos&ec< zX#R#`X;}UZ!!xh=8&2eY`Zt0!oM!*O0=o(W|Lj&cfexfiY&7n2G+}*@Ono9F1Mc%yfC|%Dy!c37~f%eE> zm+|x<&C*?Z7sx1)S(I(^gU3AfuX?4qp6#HOYKC{41XZrwPLzJ0)bkl;z8YBjAUXJ2 z;xs1_q-|N0%7kcHoMB#dR+fXfVicMiPIq2WlKvk$s3@yCuc~P~uc`t&%Me(38zYXM z2DsC`o||>c_q>Hg{-8P@H@7{u?y8umW=-%`>9s4l%8|Q8^_ra8B z+P6R<5ju9hPJG-BVp4D2{>Kw~a6$=Y{xpUx1=XyUlb8%QO7b3f0q!jYwX)CDnlXF096T1|=zF9ZJnXvrzbG?(EN;bzBqAad$-fis5 z?LcRUD#t=?Zsxu|UDB7zIL*+UXB;Emlw%=yO5)YKWU05quZCmCYtlok_Vpy!;mhsM zny5e3@pRX9$1%z_)5~5huHovrH#JGm%G7SItGGlts=%P$&4|acGd;=8{Yt3<&1E%>L&OzIG);3;3W9a1gLNDvlSD`&1=q{JNRSf$$ppFDoqT#!iNE zk_g+gC+`rrb+HZEj3fMa#=&P}Cf1ugVC*u+ko#8|(KuGZEkC4Dc#ENv6+u2rna*y^#Hcb0t;&o?K37PH}0E2r9II6a~ew{ip`}m3u6cpTvT9y zR3y;GkqX;Mim$qe-EHy|j@Cv3h{XPV*~kzr8*t2@UrbAuI+vnrQi|JV`w3uspVe?r zcKdqV7Q)aGQc`Xrf22TT&?gW{vR*9Y9yz758WCl|;YiJ$u9Od%oZOaYOcR^Y`-%Jh z2S0i-@GK>Ykc(GIJkKdM;Ic|!rt1$t-XTGB+EDGOW9crkwTwB3R3C0)@p1&i!vcvy zK8kKN&Rw|yp-T@unzb4oeb>nP^H58ma@m)W)0zBoS6n?qxU}u8N$RP}z($PT(rsoXN@laQ zM)%S-t6q8=T)i@Ij}xco_uW~sYlI>`p$o62*73JYc{`c9$FQ5VOL6zw3>9v7$d|o) zx##x&;#AKBfLVJ+Yx|cSFwp_aLGFNX6%7#>7msi&@@vxY?LDp2U7W@^cXA9?%%fkQ zk=%;ewiIP*VL%i4(D5Kdug!q9Dq8N2PFqtQ$Dz�Y~xZ5ovyzN_`~rv1n(fYuU4n zeT*K~nLM*<7gx5WoTAR@LwJ4nCy=dF5jAVINN!?Ua!c?pu(_tmVrE{!gpZ?a$=XG`2n^|z!HjsOR% z^WG-{u*}X_tppsUyeL9ZOHCDzPF97KxptfO>AF_+e1b+sWm|QXS9Q5=RUH`Bwt|=v z>pE{%1opievt0Yo=l0S9`c?~i=gQ5|_+m?QYHnBC-Vvzl?q$=$Ul)1uh+`D8f8BG` zact9Q$Tg>u$HRmpK{*nW!p>WV$jaF_c4w5uaYDj*xSN-AsZI=87B|uze zsgvz0am`|5qZ(qeRCd6FBL2ERj`e9-$eR5!Y9golU}rt~8K1g#N*YR_OtU{K%9ir+ zb~4#X6A>D6m)(x0*!iKt0d$XF*$3Ny=JAVZ8`l>I8YSX;eJ>G|?DB25N4HDID>j0l zuuDl@23H$~egRx4GNLZzuB+)>oOk~GTfmD89YN_yO@?uurL_y}mDno}p~}j?t%Tg( zNEhPUdS%64&FpJtXoOa@W1Zsz;vS)1khcUZKdKe%+h$qVe$3YHIjvE9jAgZN!wi&8 z{oL(GanIj8Mw7%Nkl4i!k`0aItaUt(j@ZDsw`7u7Gvi>uL-c){kW1&NqV^rFvOTkK=b^;W-{$@H^6wkXbDSAEhg{2q|8Gm)}VB-R9c13K{bd1~ky z()U<{_XbbME3m&+XUI!$UosNrF6>uT8ahLDH?^>x&IlQ!c=6vl#PkOdk@kuV|GHUN zc_~ciMCf__a0mwSnSu~(4?Q`essfDAa_~~mjL;%R=!`_16$hWkyPi#f%a@q>Xu!{n zgq$flzx2WHW4ntU34WOu7~lX8H7!53kj!2*)a?5Vj}K(_`fJArkVf zJ|avm03(B>8pOf^!g#q2hMa5@c~4PgErpTZ&tbQ<)OB~cSkH9$qDErHfNodBVh;=3 zNg&C;W(69xQ&{42BqZw~&Uh8)JP4o$XXCE?!fKNGJ8zg=^Y_T(T zpBlnW1HHnV*3BJC&I~L}P3VbH!QqmCyeaqmk^*^Bdc}hQdoJs;B7u$I7Oym`@+y;1 z#8^96B)lS=gTT|yW(9vA*8!sh)qbw47$)4RfsDhi%Xgyg{LxUqYEkLlf+s?dp^#+uo_ zf{XEzCQY@h*LV=YrnjkXo?7jY$~BLM8I3BP5Ypop)-W6AyWcH&6{Dd?lSb$0J!qC) zVmv_z<&#C=_gLMJCMh-_G&dqBt`BeF(4!Si2xr_%M)=)Qm!uX(SonQxH!p=*n(USi{pq3do}Nd zxVG|w%e`&h9LGW2f%N}86l8Rz$I(=>}Sj6aPdFJb@1hg zPd-O@9aPJ2#1f>Er2C$X$?B$HJ+INb4;kHxIpb#5bC={Wxu)xnjlnKK+4|7@uJd|j zNAt=eGjp(d*E7?O58SgpH@3%jGg>L`Ma+$?aO=ft%=24_U%Ya1Mu&}LpA0u9L*C9y zhz~PcL@3TdDm42W@L}D2qLRud&g)Md#nXUu*C0rm!&{{^z4Nc!Y*dP@D0f89&YgMa zl>9!cKU_{>T0}8E=u9m~sZ z8ZCID!_D>tl3qlOFBpdD<%@&F42?O-nFh4Gdy`n5n8KAzej!QKh*-U|74BXS72mKd z4>WdJgHka#+@aSar|(ErT~nNPQbTX)Px%Q&`48rqq;~ss_};`06OV9n!Wct~igV24IjVPSyCLsQsln!@$Wi|On0jKU+XMJfAaCK;-`jhd>rp?sG4m1!bjr~ zMRI%04j(_So}_gqOJB)j27KY;F$?k+KDOkX+u@_7J9BV{k7PLf3m?(-i6&3#7qV+S z=O=ot92X~^M(`zV1`$_iqpq;$#DP7B0;k!YcU|Kf&g9l$Y<~r@0-Sr9*!HK`jL` z|B%S2n6GSJ-{Ip^ZiO8_ibCK0D<6l&MHZn^Ii%sw9 z=NF$WcknqMeNVBfYCiNEL3!u8j`^iF4xS%OXYr-Ff74mi{s)~U&B9}go(ZnpV&wWS zHcqVGm%PETYuEqj|N8y^|6`pe|I7Nm|Lngz*1liw|7DwgkTU*s7Qe&H4^rO`QpUG! n>f?;uVeuCh%5j+R8|qctT@d=Bb`tvw{*Bt1$YspM#i9K-Cibn4 literal 0 HcmV?d00001 diff --git a/ext/.gitignore b/ext/.gitignore new file mode 100644 index 0000000..bb5ac97 --- /dev/null +++ b/ext/.gitignore @@ -0,0 +1 @@ +musl diff --git a/ext/Makefile b/ext/Makefile new file mode 100644 index 0000000..7134aa6 --- /dev/null +++ b/ext/Makefile @@ -0,0 +1,41 @@ +ROOT_DIR = .. +# BUILD_DIR = .. +ROOT_DIR_REALPATH = $(shell echo -n `cd $(ROOT_DIR); pwd`) +PREFIX := $(ROOT_DIR_REALPATH)/ext/musl +# include $(BUILD_DIR)/config.mak + +# -w Suppress all warnings +MUSL_CFLAGS = -march=x86-64 -m64 -w -std=c11 +MUSL_LDFLAGS = -nostdlib -z nodefaultlib +MUSL_OPTION = --target=x86_64-unknown-linux-musl --prefix=$(PREFIX) +# MUSL_OPTION = --disable-shared --target=x86_64-unknown-linux-musl --prefix=$(ROOT_DIR_REALPATH)/ext/musl + +CC = gcc + +SRC_DIR = $(ROOT_DIR)/musl +VPATH = $(SRC_DIR) + +.PHONY: all clean clobber + +# all: lib/libc.a +all: MAKEOVERRIDES= +all: $(SRC_DIR)/config.mak + $(MAKE) -C $(SRC_DIR) -j8 + $(MAKE) -C $(SRC_DIR) install -j8 + +clean: clobber + +clobber: + $(MAKE) -C $(SRC_DIR) clean + rm -f $(SRC_DIR)/config.mak + +# lib/libcr.a: lib/libc.a +# @cp $(VPATH)/$< $(VPATH)/$@ + +# lib/libc.a: MAKEOVERRIDES= +# lib/libc.a: config.mak +# $(MAKE) -C $(SRC_DIR) + +$(SRC_DIR)/config.mak: + cd $(SRC_DIR); CROSS_COMPILE="$(ARCH)-" CFLAGS="$(MUSL_CFLAGS)" CC="$(CC)" ./configure $(MUSL_OPTION) + # cd $(SRC_DIR); CROSS_COMPILE="$(ARCH)-" CFLAGS="$(MUSL_CFLAGS)" CC="$(CC)" LDFLAGS="$(MUSL_LDFLAGS)" ./configure $(MUSL_OPTION) diff --git a/grub/grub.cfg b/grub/grub.cfg new file mode 100644 index 0000000..c2f0d88 --- /dev/null +++ b/grub/grub.cfg @@ -0,0 +1,7 @@ +set timeout=1 +set default=0 + +menuentry "utero" { + multiboot2 /boot/kernel.bin + boot +} diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..d92c7cb --- /dev/null +++ b/linker.ld @@ -0,0 +1,23 @@ +/* https://intermezzos.github.io/book/hello-world.html */ + +ENTRY(start) + +SECTIONS { + . = 1M; + + .boot : + { + /* ensure that the multiboot header is at the beginning */ + *(.multiboot_header) + } + + .text : + { + *(.text) + } + + .rodata : + { + *(.rodata .rodata.*) + } +} diff --git a/musl b/musl new file mode 160000 index 0000000..d5029bb --- /dev/null +++ b/musl @@ -0,0 +1 @@ +Subproject commit d5029bb88a4ff91b007a19c7d9efb790aab63246 diff --git a/spec/lib_cr/ctype_spec.cr b/spec/lib_cr/ctype_spec.cr deleted file mode 100644 index b424024..0000000 --- a/spec/lib_cr/ctype_spec.cr +++ /dev/null @@ -1,240 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -require "../spec_helper" -require "../../src/core/lib_cr/x86_64-linux-musl/c/ctype" - -describe "LibCR" do - describe "fun isalpha(c : Char) : Int" do - context "Returns non-zero when an English character" do - it { LibCR.isalpha('a'.ord).should_not eq 0 } - it { LibCR.isalpha('z'.ord).should_not eq 0 } - end - - context "Returns zero when a non-English character" do - it { LibCR.isalpha(' '.ord).should eq 0 } - it { LibCR.isalpha('1'.ord).should eq 0 } - it { LibCR.isalpha('@'.ord).should eq 0 } - end - - context "Returns non-zero when a Japanese character (Hiragana)" do - it { LibCR.isalpha('あ'.ord).should_not eq 0 } - it { LibCR.isalpha('っ'.ord).should_not eq 0 } - end - - context "Returns zero when a Japanese character (without Hiragana)" do - it { LibCR.isalpha('ア'.ord).should eq 0 } - it { LibCR.isalpha('亜'.ord).should eq 0 } - end - - context "Returns zero when a Japanese character (Historical kana orthography)" do - # Historical kana orthography - # https://en.wikipedia.org/wiki/Historical_kana_orthography - # 'i' - it { LibCR.isalpha('ゐ'.ord).should eq 0 } - it { LibCR.isalpha('ヰ'.ord).should eq 0 } - # 'e' - it { LibCR.isalpha('ゑ'.ord).should eq 0 } - it { LibCR.isalpha('ヱ'.ord).should eq 0 } - end - end - - describe "fun isalnum(c : Char) : Int" do - context "Return non-zero when a character or a digit" do - char_or_digits = ['a', 'z', 'A', 'Z', '0', '9'] - char_or_digits.each do |c| - it { LibCR.isalnum(c.ord).should_not eq 0 } - end - end - - context "Return zero when a non-character or a non-digit" do - it { LibCR.isalnum(' '.ord).should eq 0 } - it { LibCR.isalnum('\n'.ord).should eq 0 } - it { LibCR.isalnum('@'.ord).should eq 0 } - end - end - - describe "fun isblank(c : Char) : Int" do - context "Returns non-zero when a white-space or a tab character" do - it { LibCR.isblank(' '.ord).should_not eq 0 } - it { LibCR.isblank('\t'.ord).should_not eq 0 } - end - - context "Returns zero when not a white-space or a tab character" do - it { LibCR.isblank('\v'.ord).should eq 0 } - it { LibCR.isblank('\r'.ord).should eq 0 } - it { LibCR.isblank('x'.ord).should eq 0 } - it { LibCR.isblank('@'.ord).should eq 0 } - end - - context "Returns zero when a Zenkaku white-space (due to unsupport locales?)" do - it { LibCR.isblank(' '.ord).should eq 0 } - end - end - - describe "fun iscntrl(c : Char) : Int" do - context "Returns non-zero when a control character" do - # Crystal does not support '\a' ? - # https://github.com/crystal-lang/crystal/issues/3078#issuecomment-238630121 - # it { LibCR.iscntrl('\a'.ord).should_not eq 0 } # Bell - it { LibCR.iscntrl(0x07).should_not eq 0 } # Bell - - control_chars = ['\0', '\b', '\t', '\n', '\v', '\f', '\r'] - control_chars.each do |c| - it { LibCR.iscntrl(c.ord).should_not eq 0 } - end - end - - context "Returns zero when not a control character" do - it { LibCR.iscntrl(' '.ord).should eq 0 } - end - end - - describe "fun isdigit(c : Char) : Int" do - context "Returns non-zero when a digit" do - it { LibCR.isdigit('0'.ord).should_not eq 0 } - it { LibCR.isdigit('9'.ord).should_not eq 0 } - end - - context "Returns zero when a non-digit" do - it { LibCR.isdigit(' '.ord).should eq 0 } - it { LibCR.isdigit('a'.ord).should eq 0 } - it { LibCR.isdigit('@'.ord).should eq 0 } - end - end - - describe "fun isgraph(c : Char) : Int" do - context "Returns non-zero when a printable character without a space" do - (0x21..0x7e).each do |c| - it { LibCR.isgraph(c).should_not eq 0 } - end - end - - context "Returns zero when a control character" do - (0x00..0x1f).each do |c| - it { LibCR.isgraph(c).should eq 0 } - end - end - - context "Returns zero when a space" do - it { LibCR.isgraph(0x20).should eq 0 } - end - - context "Returns zero when a DELETE" do - # 0x7f (DELETE) - it { LibCR.isgraph(0x7f).should eq 0 } - end - end - - describe "fun islower(c : Char) : Int" do - context "Returns non-zero when lowercase" do - it { LibCR.islower('a'.ord).should_not eq 0 } - it { LibCR.islower('z'.ord).should_not eq 0 } - end - - context "Returns zero when non-lowercase" do - non_lowercases = ['A', 'Z', ' ', '@'] - non_lowercases.each do |c| - it { LibCR.islower(c.ord).should eq 0 } - end - end - end - - describe "fun isprint(c : Char) : Int" do - context "Returns non-zero when a printable character (including a space)" do - (0x20..0x7e).each do |c| - it { LibCR.isprint(c).should_not eq 0 } - end - end - - context "Returns zero when a control character" do - (0x00..0x1f).each do |c| - it { LibCR.isprint(c).should eq 0 } - end - end - - context "Returns zero when a DELETE" do - # 0x7f (DELETE) - it { LibCR.isprint(0x7f).should eq 0 } - end - end - - describe "fun ispunct(c : Char) : Int" do - context "Returns non-zero when a punctuation character" do - (0x21..0x29).each do |c| - it { LibCR.ispunct(c).should_not eq 0 } - end - - (0x2a..0x2f).each do |c| - it { LibCR.ispunct(c).should_not eq 0 } - end - - (0x3a..0x3f).each do |c| - it { LibCR.ispunct(c).should_not eq 0 } - end - - it { LibCR.ispunct(0x40).should_not eq 0 } - - (0x5b..0x60).each do |c| - it { LibCR.ispunct(c).should_not eq 0 } - end - - (0x7b..0x7d).each do |c| - it { LibCR.ispunct(c).should_not eq 0 } - end - end - end - - # checks for white-space characters. In the "C" and "POSIX" - # locales, these are: space, form-feed ('\f'), newline ('\n'), - # carriage return ('\r'), horizontal tab ('\t'), and vertical tab - # ('\v'). - describe "fun isspace(c : Char) : Int" do - context "Returns non-zero when a white-space character" do - white_space_chars = [' ', '\f', '\n', '\r', '\t', '\v'] - white_space_chars.each do |c| - it { LibCR.isspace(c.ord).should_not eq 0 } - end - end - - context "Returns zero when without a white-space character" do - it { LibCR.isspace('a'.ord).should eq 0 } - end - end - - describe "fun isupper(c : Char) : Int" do - context "Returns non-zero when uppercase" do - it { LibCR.isupper('A'.ord).should_not eq 0 } - it { LibCR.isupper('Z'.ord).should_not eq 0 } - end - - context "Returns zero when non-uppercase" do - non_uppercases = ['a', 'z', ' ', '@'] - non_uppercases.each do |c| - it { LibCR.isupper(c.ord).should eq 0 } - end - end - end - - describe "fun isxdigit(c : Char) : Int" do - context "Returns non-zero when a hexadicimal digit" do - hex_digits = ['0', '9', 'a', 'f', 'A', 'F'] - hex_digits.each do |c| - it { LibCR.isxdigit(c.ord).should_not eq 0 } - end - end - - context "Returns zero when not a hexadicimal digit" do - non_hex_digits = ['g', 'G', '@', ' '] - non_hex_digits.each do |c| - it { LibCR.isxdigit(c.ord).should eq 0 } - end - end - end -end diff --git a/spec/lib_cr/string_spec.cr b/spec/lib_cr/string_spec.cr deleted file mode 100644 index 395e7dc..0000000 --- a/spec/lib_cr/string_spec.cr +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -require "../spec_helper" -require "../../src/core/lib_cr/x86_64-linux-musl/c/string" -# FIXME: or KILLME -# require "../../src/lib_cr/no_bind/libstring" -# include NoBind - -describe "LibCR" do - context "memcmp" do - ptr1 = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] - ptr2 = Pointer.malloc(4) { |i| i + 11 } # [11, 12, 13, 14] - it { LibCR.memcmp(ptr1, ptr2, 4).should be < 0 } - it { LibCR.memcmp(ptr2, ptr1, 4).should be > 0 } - it { LibCR.memcmp(ptr1, ptr1, 4).should eq 0 } - end - - context "memcmp - given strings" do - str1 = "abcx" - str2 = "abcd" - it { LibCR.memcmp(str1, str2, 4).should be > 0 } - it { LibCR.memcmp(str2, str1, 4).should be < 0 } - it { LibCR.memcmp(str1, str1, 4).should eq 0 } - end - - context "memcmp - given two strings with different sizes" do - str1 = "abcdefghijk" - str2 = "abcd" - it { LibCR.memcmp(str1, str2, 4).should eq 0 } - it { LibCR.memcmp(str2, str1, 4).should eq 0 } - end - - describe "memset" do - ptr = Pointer.malloc(4) { |i| i + 10 } # [10, 11, 12, 13] - - context "assign 0 to len" do - it "does not change the value of pointer" do - LibCR.memset(ptr, 0_i32, 0_u64) - ptr.to_slice(4).should eq Slice[10, 11, 12, 13] - end - end - - context "assign 1 to size" do - it "sets zero to the first value of pointer" do - LibCR.memset(ptr, 0_i32, 1_u64) - ptr.to_slice(4).should eq Slice[0, 11, 12, 13] - end - end - end - - context "strlen" do - str1 = "abcde" - str2 = "あいうえお" - it { LibCR.strlen(str1).should eq 5 } - it { LibCR.strlen(str2).should eq 15 } - end - - context "strcmp" do - it { LibCR.strcmp("abcde", "abcde").should eq 0 } - it { LibCR.strcmp("abcde", "abcdx").should be < 0 } - it { LibCR.strcmp("abcdx", "abcde").should be > 0 } - it { LibCR.strcmp("", "abcde").should be < 0 } - it { LibCR.strcmp("abcde", "").should be > 0 } - it { LibCR.strcmp("abcde", "abcd#{'\u{00fc}'}").should be < 0 } # "abcd#{'\u{00fc}'}" == abcdü" - - # Comparing two strings with different sizes - it { LibCR.strcmp("abcde", "abcdef").should be < 0 } - it { LibCR.strcmp("abcdef", "abcde").should be > 0 } - - # Comparing one byte characters and triple-byte characters - it { LibCR.strcmp("abcde", "あいうえお").should be < 0 } - it { LibCR.strcmp("あいうえお", "abcde").should be > 0 } - end - - describe "strstr" do - str = "abcabcabcdabcde" - - puts abcabcabcdabcde_ptr = LibCR.strstr(str, "abcabcabcdabcde") # equal to the pointer of s[0] - - it { LibCR.strstr(str, "x").should eq Pointer(UInt8).null } - it { LibCR.strstr(str, "xyz").should eq Pointer(UInt8).null } - it { LibCR.strstr(str, "a").should eq abcabcabcdabcde_ptr } - it { LibCR.strstr(str, "abc").should eq abcabcabcdabcde_ptr } - it { LibCR.strstr(str, "abcd").should eq abcabcabcdabcde_ptr + 6 } - it { LibCR.strstr(str, "abcde").should eq abcabcabcdabcde_ptr + 10 } - end - - describe "strchr" do - abccd = "abccd" - # pass the code point of 'a' to 2nd argument - abccd_ptr = LibCR.strchr(abccd, 'a'.ord) # equal to the pointer of abccd[0] - - it { LibCR.strchr(abccd, 'x'.ord).should eq Pointer(UInt8).null } - it { LibCR.strchr(abccd, 'a'.ord).should eq abccd_ptr } - it { LibCR.strchr(abccd, 'd'.ord).should eq abccd_ptr + 4 } - it { LibCR.strchr(abccd, '\0'.ord).should eq abccd_ptr + 5 } - it { LibCR.strchr(abccd, 'c'.ord).should eq abccd_ptr + 2 } - end - - describe "strncmp" do - abcde = "abcde" - abcdx = "abcdx" - cmpabcde = "abcde\u{0}f" - cmpabcd_ = "abcde\u{fc}" # \u{fc} == ü - empty = "" - x = "x" - - it { LibCR.strncmp(abcde, cmpabcde, 5).should eq 0 } - it { LibCR.strncmp(abcde, cmpabcde, 10).should eq 0 } - it { LibCR.strncmp(abcde, abcdx, 5).should be < 0 } - it { LibCR.strncmp(abcdx, abcde, 5).should be > 0 } - it { LibCR.strncmp(empty, abcde, 5).should be < 0 } - it { LibCR.strncmp(abcde, empty, 5).should be > 0 } - it { LibCR.strncmp(abcde, abcdx, 4).should eq 0 } - it { LibCR.strncmp(abcde, x, 0).should eq 0 } - it { LibCR.strncmp(abcde, x, 1).should be < 0 } - it { LibCR.strncmp(abcde, cmpabcd_, 6).should be < 0 } - - # Comparing one byte characters and triple-byte characters - it { LibCR.strncmp("abcde", "あいうえお", 15).should be < 0 } - it { LibCR.strncmp("あいうえお", "abcde", 15).should be > 0 } - - # Comparing the both of triple-byte characters - it { LibCR.strncmp("あいうえお", "あいうえか", 15).should be < 0 } - it { LibCR.strncmp("あいうえか", "あいうえお", 15).should be > 0 } - it { LibCR.strncmp("あいうえお", "あいうえか", 12).should eq 0 } - end - - describe "strchrnul" do - blank = "" - blank_ptr = LibCR.strchrnul(blank, '\u{0}'.ord) - str = "abcabc" - str_ptr = LibCR.strchrnul(str, 'a'.ord) - - it { LibCR.strchrnul(blank, 'A'.ord).should eq blank_ptr } - it { LibCR.strchrnul(blank, '\u{0}'.ord).should eq blank_ptr } - - it { LibCR.strchrnul(str, 'A'.ord).should eq str_ptr + 6 } - it { LibCR.strchrnul(str, 'a'.ord).should eq str_ptr + 0 } - it { LibCR.strchrnul(str, 'c'.ord).should eq str_ptr + 2 } - it { LibCR.strchrnul(str, '\u{0}'.ord).should eq str_ptr + 6 } - end - - describe "memchr" do - abcde = "abcde" - abcde_ptr = LibCR.memchr(abcde, 'a'.ord, 1) # equal to the pointer of abcde[0] - - it { LibCR.memchr(abcde, 'c'.ord, 5).should eq abcde_ptr + 2 } - it { LibCR.memchr(abcde, 'a'.ord, 1).should eq abcde_ptr } - it { LibCR.memchr(abcde, 'a'.ord, 0).should eq Pointer(Void).null } - it { LibCR.memchr(abcde, '\0'.ord, 5).should eq Pointer(Void).null } - it { LibCR.memchr(abcde, '\0'.ord, 6).should eq abcde_ptr + 5 } - end -end diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr deleted file mode 100644 index d5aa24f..0000000 --- a/spec/spec_helper.cr +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -require "spec" diff --git a/src/arch/x86_64/boot.asm b/src/arch/x86_64/boot.asm deleted file mode 100644 index eaf8971..0000000 --- a/src/arch/x86_64/boot.asm +++ /dev/null @@ -1,263 +0,0 @@ -; Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -; file at the top-level directory of this distribution. -; -; Licensed under the Apache License, Version 2.0 or the MIT license -; , at your -; option. This file may not be copied, modified, or distributed -; except according to those terms. -; -; The part of this file was taken from: -; https://github.com/phil-opp/blog_os/blob/set_up_rust/src/arch/x86_64/multiboot_header.asm - -global start -global stack_top -extern long_mode_start -extern kernel_start ; Defined in linker script -extern kernel_end ; Defined in linker script - -section .text -bits 32 -start: - mov esp, stack_top - add esp, (8<<10) - 16 - ; http://os.phil-opp.com/allocating-frames.html#the-multiboot-information-structure - ; mov edi, ebx ; Move Multiboot info pointer to edi - - ; Interpret multiboot information - mov dword [mb_info], ebx - - call check_multiboot - call cpu_init - call check_cpuid - call check_long_mode - - call set_up_page_tables - call enable_paging - call set_up_SSE - - ; push kernel_end - ; push kernel_start - - pop ebx ; restore pointer to multiboot structure - - ; load the 64-bit GDT - lgdt [gdt64.pointer] - - jmp gdt64.code:long_mode_start - -set_up_page_tables: - ; map first P4 entry to P3 table - mov eax, p3_table - or eax, 0b11 ; present + writable - mov [p4_table], eax - - ; map first P3 entry to P2 table - mov eax, p2_table - or eax, 0b11 ; present + writable - mov [p3_table], eax - - ; map each P2 entry to a huge 2MiB page - mov ecx, 0 ; counter variable - -.map_p2_table: - ; map ecx-th P2 entry to a huge page that starts at address 2MiB*ecx - mov eax, 0x200000 ; 2MiB - mul ecx ; start address of ecx-th page - or eax, 0b10000011 ; present + writable + huge - mov [p2_table + ecx * 8], eax ; map ecx-th entry - - inc ecx ; increase counter - cmp ecx, 512 ; if counter == 512, the whole P2 table is mapped - jne .map_p2_table ; else map the next entry - - ret - -enable_paging: - ; load P4 to cr3 register (cpu uses this to access the P4 table) - mov eax, p4_table - mov cr3, eax - - ; enable PAE-flag in cr4 (Physical Addresss Extension) - mov eax, cr4 - or eax, 1 << 5 - mov cr4, eax - - ; set the long mode bit in the EFER MSR (model specific register) - mov ecx, 0xC0000080 - rdmsr - or eax, 1 << 8 - wrmsr - - ; enable paging in the cr0 register - mov eax, cr0 - or eax, 1 << 31 - mov cr0, eax - - ret - -; Prints `ERR: ` and the given error code to screen and hangs -; parameter: error code (in ascii) in al -error: - mov dword [0xb8000], 0x4f524f45 - mov dword [0xb8004], 0x4f3a4f52 - mov dword [0xb8008], 0x4f204f20 - mov byte [0xb800a], al - hlt - -; Throw error 0 if eax doesn't contain the Multiboot 2 magic value (0x36d76289) -check_multiboot: - cmp eax, 0x36d76289 - jne .no_multiboot - ret -.no_multiboot: - mov al, "0" - jmp error - -; This will set up the x86 control registers: -; Caching and the floating point unit are enabled -; Bootstrap page tables are loaded and page size -; extensions (huge pages) enabled. -cpu_init: - ; initialize page tables - - ; map multiboot info 1:1 - push edi - mov eax, dword [mb_info] ; map multiboot info - and eax, 0xFFFFF000 ; page align lower half - mov edi, eax - shr edi, 9 ; (edi >> 12) * 8 (index for boot_pgt) - add edi, boot_pgt - or eax, 0x101 ; set present and global bits - mov dword [edi], eax - pop edi - - ; map kernel 1:1 - push edi - push ebx - push ecx - mov ecx, kernel_start - mov ebx, kernel_end - add ebx, 0x1000 -L0: cmp ecx, ebx - jae L1 - mov eax, ecx - and eax, 0xFFFFF000 ; page align lower half - mov edi, eax - shr edi, 9 ; (edi >> 12) * 8 (index for boot_pgt) - add edi, boot_pgt - or eax, 0x103 ; set present, global and writable bits - mov dword [edi], eax - add ecx, 0x1000 - jmp L0 -L1: - pop ecx - pop ebx - pop edi - ret - -check_cpuid: - ; Check if CPUID is supported by attempting to flip the ID bit (bit 21) - ; in the FLAGS register. If we can flip it, CPUID is available. - - ; Copy FLAGS in to EAX via stack - pushfd - pop eax - - ; Copy to ECX as well for comparing later on - mov ecx, eax - - ; Flip the ID bit - xor eax, 1 << 21 - - ; Copy EAX to FLAGS via the stack - push eax - popfd - - ; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported) - pushfd - pop eax - - ; Restore FLAGS from the old version stored in ECX (i.e. flipping the ID bit - ; back if it was ever flipped). - push ecx - popfd - - ; Compare EAX and ECX. If they are equal then that means the bit wan't - ; flipped, and CPUID isn't supported - cmp eax, ecx - je .no_cpuid - ret -.no_cpuid: - mov al, "1" - jmp error - -; Throw error 2 if the CPU doesn't support Long Mode -check_long_mode: - ; test if extended processor info in available - mov eax, 0x80000000 ; implicit argument for cpuid - cpuid ; get highest supported argument - cmp eax, 0x80000001 ; it needs to be at least 0x80000001 - jb .no_long_mode ; if it's less, the CPU is too old for long mode - - ; use extended info to test if long mode is available - mov eax, 0x80000001 ; argument for extended processor info - cpuid ; returns various feature bits in ecx - test edx, 1 << 29 - jz .no_long_mode - ret -.no_long_mode: - mov al, "2" - jmp error - -; Check for SSE and enable it. If it's not supported throw error "a". -set_up_SSE: - ; check for SSE - mov eax, 0x1 - cpuid - test edx, 1<<25 - jz .no_SSE - - ; enable SSE - mov eax, cr0 - and ax, 0xFFFB ; clear coproccessor emulation CR0.EM - or ax, 0x2 ; set coproccessor monitoring CR0.MP - mov cr0, eax - mov eax, cr4 - or ax, 3 << 9 ; set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time - mov cr4, eax - - ret -.no_SSE: - mov al, "a" - jmp error - -section .data -global mb_info: -ALIGN 8 -mb_info: - dq 0 - -boot_pgt: - times 512 dq 0 - -section .bss -align 4096 -p4_table: - resb 4096 -p3_table: - resb 4096 -p2_table: - resb 4096 -stack_bottom: - resb 4096 * 4 -stack_top: - -section .rodata -gdt64: - dq 0 ; zero entry -.code: equ $ - gdt64 - dq (1<<44) | (1<<47) | (1<<43) | (1<<53) ; code segment -.pointer: - dw $ - gdt64 -1 - dq gdt64 diff --git a/src/arch/x86_64/c/idt.c b/src/arch/x86_64/c/idt.c deleted file mode 100644 index fccc90f..0000000 --- a/src/arch/x86_64/c/idt.c +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/kernel/idt.c - -#include - -static idt_entry_t idt[256] = {[0 ... 255] = {0, 0, 0, 0, 0, 0, 0}}; -static idt_ptr_t idtp; - -void configure_idt_entry(idt_entry_t *dest_entry, size_t base, - unsigned short sel, unsigned char flags) -{ - /* The interrupt routine's base address */ - dest_entry->base_lo = (base & 0xFFFF); - dest_entry->base_hi = (base >> 16) & 0xFFFF; - - /* The segment or 'selector' that this IDT entry will use - * is set here, along with any access flags */ - dest_entry->sel = sel; - dest_entry->always0 = 0; - dest_entry->flags = flags; -} - -/* - * Use this function to set an entry in the IDT. Alot simpler - * than twiddling with the GDT ;) - */ -void idt_set_gate(unsigned char num, size_t base, unsigned short sel, - unsigned char flags) -{ - configure_idt_entry(&idt[num], base, sel, flags); -} - -/* Installs the IDT */ -void idt_install(void) -{ - static int initialized = 0; - - if (!initialized) { - initialized = 1; - - /* Sets the special IDT pointer up, just like in 'gdt.c' */ - idtp.limit = (sizeof(idt_entry_t) * 256) - 1; - idtp.base = (size_t)&idt; - } - /* Points the processor's internal register to the new IDT */ - asm volatile("lidt %0" : : "m" (idtp)); -} diff --git a/src/arch/x86_64/c/include/asm/idt.h b/src/arch/x86_64/c/include/asm/idt.h deleted file mode 100644 index da5ec82..0000000 --- a/src/arch/x86_64/c/include/asm/idt.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/include/asm/idt.h - -#ifndef IDT_H -#define IDT_H - -#include "stddef.h" - -// This bit shall be set to 0 if the IDT slot is empty -#define IDT_FLAG_PRESENT 0x80 -// Interrupt can be called from within RING0 -#define IDT_FLAG_RING0 0x00 -// Interrupt can be called from within RING1 and lower -#define IDT_FLAG_RING1 0x20 -// Interrupt can be called from within RING2 and lower -#define IDT_FLAG_RING2 0x40 -// Interrupt can be called from within RING3 and lower -#define IDT_FLAG_RING3 0x60 -// Size of gate is 16 bit -#define IDT_FLAG_16BIT 0x00 -// Size of gate is 32 bit -#define IDT_FLAG_32BIT 0x08 -// The entry describes an interrupt gate -#define IDT_FLAG_INTTRAP 0x06 -// The entry describes a trap gate -#define IDT_FLAG_TRAPGATE 0x07 -// The entry describes a task gate -#define IDT_FLAG_TASKGATE 0x05 - -/* - * This is not IDT-flag related. It's the segment selectors for kernel code and data. - */ -#define KERNEL_CODE_SELECTOR 0x08 -#define KERNEL_DATA_SELECTOR 0x10 - -typedef struct { - // Handler function's lower 16 address bits - uint16_t base_lo; - // Handler function's segment selector. - uint16_t sel; - // These bits are reserved by Intel - uint8_t always0; - // These 8 bits contain flags. Exact use depends on the type of interrupt gate. - uint8_t flags; - // Higher 16 bits of handler function's base address - uint16_t base_hi; - // In 64 bit mode, the "highest" 32 bits of the handler function's base address - uint32_t base_hi64; - // reserved entries - uint32_t reserved; -} __attribute__ ((packed)) idt_entry_t; - -typedef struct { - // Size of the IDT in bytes (not the number of entries!) - uint16_t limit; - // Base address of the IDT - size_t base; -} __attribute__ ((packed)) idt_ptr_t; - -void idt_install(void); - -void idt_set_gate(unsigned char num, size_t base, unsigned short sel, - unsigned char flags); - -void configure_idt_entry(idt_entry_t *dest_entry, size_t base, - unsigned short sel, unsigned char flags); - -#endif /* end of include guard: IDT_H */ diff --git a/src/arch/x86_64/c/include/asm/io.h b/src/arch/x86_64/c/include/asm/io.h deleted file mode 100644 index 4da3d54..0000000 --- a/src/arch/x86_64/c/include/asm/io.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// Functions are taken from musl/arch/x86_64/bits/io.h - -#ifndef IO_H -#define IO_H - -static __inline void outb(unsigned char __val, unsigned short __port) -{ - __asm__ volatile ("outb %0,%1" : : "a" (__val), "dN" (__port)); -} - -#endif /* end of include guard: IO_H */ diff --git a/src/arch/x86_64/c/include/asm/isrs.h b/src/arch/x86_64/c/include/asm/isrs.h deleted file mode 100644 index 34f66cc..0000000 --- a/src/arch/x86_64/c/include/asm/isrs.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/include/asm/isrs.h - -#ifndef ISRS_H -#define ISRS_H - -#include "stddef.h" - -void isrs_install(void); - -#endif /* end of include guard: ISRS_H */ diff --git a/src/arch/x86_64/c/include/asm/multiboot.h b/src/arch/x86_64/c/include/asm/multiboot.h deleted file mode 100644 index 4dd70c3..0000000 --- a/src/arch/x86_64/c/include/asm/multiboot.h +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https : //github.com/RWTH-OS/eduOS/blob/master/arch/x86/include/asm/multiboot.h -// Utero OS is able to use Multiboot (http://www.gnu.org/software/grub/manual/multiboot/) - -// TODO: This is almost copy, reviewing later is required - -#ifndef __ARCH_MULTIBOOT_H__ -#define __ARCH_MULTIBOOT_H__ - -#include "stddef.h" - -/// Does the bootloader provide mem_* fields? -#define MULTIBOOT_INFO_MEM (1 << 0) -/// Does the bootloader provide the command-line? -#define MULTIBOOT_INFO_CMDLINE (1 << 2) -/// Does the bootloader provide a list of modules? -#define MULTIBOOT_INFO_MODS (1 << 3) -/// Does the bootloader provide a full memory map? -#define MULTIBOOT_INFO_MEM_MAP (1 << 6) - -typedef uint16_t multiboot_uint16_t; -typedef uint32_t multiboot_uint32_t; -typedef uint64_t multiboot_uint64_t; - -/* The symbol table for a.out. */ -struct multiboot_aout_symbol_table -{ - multiboot_uint32_t tabsize; - multiboot_uint32_t strsize; - multiboot_uint32_t addr; - multiboot_uint32_t reserved; -}; -typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; - -/* The section header table for ELF. */ -struct multiboot_elf_section_header_table -{ - multiboot_uint32_t num; - multiboot_uint32_t size; - multiboot_uint32_t addr; - multiboot_uint32_t shndx; -}; -typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; - -struct multiboot_info -{ - /** Multiboot info version number */ - multiboot_uint32_t flags; - - /** Available memory from BIOS */ - multiboot_uint32_t mem_lower; - multiboot_uint32_t mem_upper; - - /** "root" partition */ - multiboot_uint32_t boot_device; - - /** Kernel command line */ - multiboot_uint32_t cmdline; - - /** Boot-Module list */ - multiboot_uint32_t mods_count; - multiboot_uint32_t mods_addr; - - union { - multiboot_aout_symbol_table_t aout_sym; - multiboot_elf_section_header_table_t elf_sec; - } u; - - /** Memory Mapping buffer */ - multiboot_uint32_t mmap_length; - multiboot_uint32_t mmap_addr; - - /** Drive Info buffer */ - multiboot_uint32_t drives_length; - multiboot_uint32_t drives_addr; - - /** ROM configuration table */ - multiboot_uint32_t config_table; - - /** Boot Loader Name */ - multiboot_uint32_t boot_loader_name; - - /** APM table */ - multiboot_uint32_t apm_table; - - /** Video */ - multiboot_uint32_t vbe_control_info; - multiboot_uint32_t vbe_mode_info; - multiboot_uint16_t vbe_mode; - multiboot_uint16_t vbe_interface_seg; - multiboot_uint16_t vbe_interface_off; - multiboot_uint16_t vbe_interface_len; -}; - -typedef struct multiboot_info multiboot_info_t; - -struct multiboot_mmap_entry -{ - multiboot_uint32_t size; - multiboot_uint64_t addr; - multiboot_uint64_t len; -#define MULTIBOOT_MEMORY_AVAILABLE 1 -#define MULTIBOOT_MEMORY_RESERVED 2 - multiboot_uint32_t type; -} __attribute__((packed)); -typedef struct multiboot_mmap_entry multiboot_memory_map_t; - -struct multiboot_mod_list -{ - /** the memory used goes from bytes ’mod start’ to ’mod end-1’ inclusive */ - multiboot_uint32_t mod_start; - multiboot_uint32_t mod_end; - - /** Module command line */ - multiboot_uint32_t cmdline; - - /** padding to take it to 16 bytes (must be zero) */ - multiboot_uint32_t pad; -}; -typedef struct multiboot_mod_list multiboot_module_t; - -/// Pointer to multiboot structure -/// This pointer is declared at set by boot.asm -extern multiboot_info_t *mb_info; - -#endif diff --git a/src/arch/x86_64/c/include/asm/processor.h b/src/arch/x86_64/c/include/asm/processor.h deleted file mode 100644 index 84b59ee..0000000 --- a/src/arch/x86_64/c/include/asm/processor.h +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/include/asm/processor.h - -#ifndef ASM_PROCESSOR_H -#define ASM_PROCESSOR_H - -#include - -// rdtsc (Read-time stamp counter) -// return the 64-bit time stamp value -inline static uint64_t -rdtsc(void) -{ - uint64_t lo, hi; - asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); - return (hi << 32 | lo); -} - -// wbinvd asm instruction(Write back and invalidate cache) -inline static void -flush_cache(void) -{ - asm volatile("wbinvd" ::: "memory"); -} - -// invd asm instruction (Invalidate internal caches - without writing back) -inline static void -invalid_cache(void) -{ - asm volatile("invd"); -} - -// NOTE: mb, rmb, wmb will be moved to arch/x86_64/c/processor.c -// and used via extern func_memory_barrier (mb|rmb|wmb) - -// mb (Memory barrier) -// mfence asm instruction (Memory Fence - Serializes load and store operations) -inline static void -mb(void) -{ - asm volatile("mfence" ::: "memory"); -} - -// rmb (Read memory barrier) -// lfence asm instruction (Load Fence - Serializes load operations) -inline static void -rmb(void) -{ - asm volatile("lfence" ::: "memory"); -} - -// wmb (Write memory barrier) -// sfence asm instruction (Store Fence - Serializes store operations) -inline static void -wmb(void) -{ - asm volatile("sfence" ::: "memory"); -} - -// search the most significant bit -// bsr (Bit Scan Reverse) asm instruction -static inline size_t -msb(size_t i) -{ - size_t ret; - - if (!i) { - return (sizeof(size_t) * 8); - } - asm volatile("bsr %1, %0" : "=r"(ret) : "r"(i) : "cc"); - - return ret; -} - -// search the least significant bit -// bsf (Bit Scan Forward) asm instruction -static inline size_t -lsb(size_t i) -{ - size_t ret; - - if (!i) { - return (sizeof(size_t) * 8); - } - asm volatile("bsf %1, %0" : "=r"(ret) : "r"(i) : "cc"); - - return ret; -} - -// A one-instruction-do-nothing -#define NOP1 asm volatile("nop") -// A two-instruction-do-nothing -#define NOP2 asm volatile("nop;nop") -// A four-instruction-do-nothing -#define NOP4 asm volatile("nop;nop;nop;nop") -// A eight-instruction-do-nothing -#define NOP8 asm volatile("nop;nop;nop;nop;nop;nop;nop;nop") - -#endif /* end of include guard: ASM_PROCESSOR_H */ diff --git a/src/arch/x86_64/c/include/asm/stddef.h b/src/arch/x86_64/c/include/asm/stddef.h deleted file mode 100644 index c7d0918..0000000 --- a/src/arch/x86_64/c/include/asm/stddef.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/include/asm/stddef.h - -#ifndef ASM_STDDEF_H -#define ASM_STDDEF_H - -// Unsigned 64 bit integer -typedef unsigned long long uint64_t; -// Signed 64 bit integer -typedef long long int64_t; -// Unsigned 32 bit integer -typedef unsigned int uint32_t; -// Signed 32 bit integer -typedef int int32_t; -// Unsigned 16 bit integer -typedef unsigned short uint16_t; -// Signed 16 bit integer -typedef short int16_t; -// Unsigned 8 bit integer (/char) -typedef unsigned char uint8_t; -// Signed 8 bit integer (/char) -typedef char int8_t; - -// A popular type for addresses -typedef unsigned long long size_t; - -// This defines what the stack looks like after the task context is saved. -struct state -{ - // R15 register - uint64_t r15; - // R14 register - uint64_t r14; - // R13 register - uint64_t r13; - // R12 register - uint64_t r12; - // R11 register - uint64_t r11; - // R10 register - uint64_t r10; - // R9 register - uint64_t r9; - // R8 register - uint64_t r8; - // RDI register - uint64_t rdi; - // RSI register - uint64_t rsi; - // RBP register - uint64_t rbp; - // (pseudo) RSP register - uint64_t rsp; - // RBX register - uint64_t rbx; - // RDX register - uint64_t rdx; - // RCX register - uint64_t rcx; - // RAX register - uint64_t rax; - - // Interrupt number - uint64_t int_no; - - // pushed by the processor automatically - uint64_t error; - uint64_t rip; - uint64_t cs; - uint64_t rflags; - uint64_t userrsp; - uint64_t ss; -}; - -#endif /* end of include guard: ASM_STDDEF_H */ diff --git a/src/arch/x86_64/c/include/asm/tasks.h b/src/arch/x86_64/c/include/asm/tasks.h deleted file mode 100644 index d373c3e..0000000 --- a/src/arch/x86_64/c/include/asm/tasks.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/include/asm/tasks.h - -#ifndef ASM_TASKS_H -#define ASM_TASKS_H - -#include - -// Switch the current task -// stack: Pointer to the old stack pointer -void switch_context(size_t** stack); - -// Setup a default frame for a new task -// task: Pointer to the task structure -// ep: The entry point for code execution -// arg: Arguments list pointer for the task's stack -// return -// - 0 on success -// - -EINVAL (-22) on failure -int create_default_frame(task_t* task, entry_point_t ep, void* arg); - -#endif /* end of include guard: ASM_TASKS_H */ \ No newline at end of file diff --git a/src/arch/x86_64/c/include/asm/tasks_types.h b/src/arch/x86_64/c/include/asm/tasks_types.h deleted file mode 100644 index e754cbc..0000000 --- a/src/arch/x86_64/c/include/asm/tasks_types.h +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/include/asm/tasks_types.h - -#ifndef ASM_TASKS_TYPES_H -#define ASM_TASKS_TYPES_H - -#include -#include - -#endif /* end of include guard: ASM_TASKS_TYPES_H */ \ No newline at end of file diff --git a/src/arch/x86_64/c/isrs.c b/src/arch/x86_64/c/isrs.c deleted file mode 100644 index 594b1dc..0000000 --- a/src/arch/x86_64/c/isrs.c +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/kernel/isrs.c - -#include -#include -#include -#include - -/* - * These are function prototypes for all of the exception - * handlers: The first 32 entries in the IDT are reserved - * by Intel and are designed to service exceptions! - */ -extern void isr0(void); -extern void isr1(void); -extern void isr2(void); -extern void isr3(void); -extern void isr4(void); -extern void isr5(void); -extern void isr6(void); -extern void isr7(void); -extern void isr8(void); -extern void isr9(void); -extern void isr10(void); -extern void isr11(void); -extern void isr12(void); -extern void isr13(void); -extern void isr14(void); -extern void isr15(void); -extern void isr16(void); -extern void isr17(void); -extern void isr18(void); -extern void isr19(void); -extern void isr20(void); -extern void isr21(void); -extern void isr22(void); -extern void isr23(void); -extern void isr24(void); -extern void isr25(void); -extern void isr26(void); -extern void isr27(void); -extern void isr28(void); -extern void isr29(void); -extern void isr30(void); -extern void isr31(void); -extern int eputs(const char*); -extern int eprint(const char*); - -void fault_handler(struct state* s); -// static void fault_handler(struct state *s); -// fqu_handler - -/* - * This is a very repetitive function... it's not hard, it's - * just annoying. As you can see, we set the first 32 entries - * in the IDT to the first 32 ISRs. We can't use a for loop - * for this, because there is no way to get the function names - * that correspond to that given entry. We set the access - * flags to 0x8E. This means that the entry is present, is - * running in ring 0 (kernel level), and has the lower 5 bits - * set to the required '14', which is represented by 'E' in - * hex. - */ -void -isrs_install(void) -{ - int i; - - idt_set_gate(0, (size_t)isr0, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(1, (size_t)isr1, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(2, (size_t)isr2, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(3, (size_t)isr3, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(4, (size_t)isr4, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(5, (size_t)isr5, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(6, (size_t)isr6, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(7, (size_t)isr7, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(8, (size_t)isr8, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(9, (size_t)isr9, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(10, (size_t)isr10, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(11, (size_t)isr11, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(12, (size_t)isr12, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(13, (size_t)isr13, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(14, (size_t)isr14, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(15, (size_t)isr15, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(16, (size_t)isr16, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(17, (size_t)isr17, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(18, (size_t)isr18, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(19, (size_t)isr19, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(20, (size_t)isr20, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(21, (size_t)isr21, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(22, (size_t)isr22, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(23, (size_t)isr23, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(24, (size_t)isr24, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(25, (size_t)isr25, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(26, (size_t)isr26, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(27, (size_t)isr27, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(28, (size_t)isr28, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(29, (size_t)isr29, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(30, (size_t)isr30, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - idt_set_gate(31, (size_t)isr31, KERNEL_CODE_SELECTOR, - IDT_FLAG_PRESENT | IDT_FLAG_RING0 | IDT_FLAG_32BIT | - IDT_FLAG_INTTRAP); - - // install the default handler - - // set hanlder for fpu exceptions -} - -/** @brief Exception messages - * - * This is a simple string array. It contains the message that - * corresponds to each and every exception. We get the correct - * message by accessing it like this: - * exception_message[interrupt_number] - */ -static const char* exception_messages[] = { "Division By Zero", - "Debug", - "Non Maskable Interrupt", - "Breakpoint", - "Into Detected Overflow", - "Out of Bounds", - "Invalid Opcode", - "No Coprocessor", - "Double Fault", - "Coprocessor Segment Overrun", - "Bad TSS", - "Segment Not Present", - "Stack Fault", - "General Protection Fault", - "Page Fault", - "Unknown Interrupt", - "Coprocessor Fault", - "Alignment Check", - "Machine Check", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved" }; - -/* - * All of our Exception handling Interrupt Service Routines will - * point to this function. This will tell us what exception has - * occured! Right now, we simply abort the current task. - * All ISRs disable interrupts while they are being - * serviced as a 'locking' mechanism to prevent an IRQ from - * happening and messing up kernel data structures - */ -#define N 256 -void -fault_handler(struct state* s) -// static void fault_handler(struct state *s) -{ - char message[N] = { '\0' }; - if (s->int_no < 32) { - sprintf( - message, - " Exception (%llu) at 0x%llx:0x%llx, error code 0x%llx, rflags 0x%llx\n", - s->int_no, s->cs, s->rip, s->error, s->rflags); - eputs(exception_messages[s->int_no]); - eprint(message); - - outb(0x20, 0x20); - - // irq_enable(); - // abort(); - for (;;) - ; - } -} diff --git a/src/arch/x86_64/c/tasks.c b/src/arch/x86_64/c/tasks.c deleted file mode 100644 index 170f072..0000000 --- a/src/arch/x86_64/c/tasks.c +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/kernel/tasks.c - -#include -#include -#include -#include -#include -#include - -size_t* -get_current_stack(void) -{ - task_t* curr_task = current_task; - - return curr_task->last_stack_pointer; -} - -int -create_default_frame(task_t* task, entry_point_t ep, void* arg) -{ - size_t* stack; - struct state* stptr; - size_t state_size; - - if (BUILTIN_EXPECT(!task, 0)) { - return -EINVAL; - } - - if (BUILTIN_EXPECT(!task->stack, 0)) { - return -EINVAL; - } - - memset(task->stack, 0xcd, KERNEL_STACK_SIZE); - // => stack is 16byte aligned - stack = (size_t*)(task->stack + KERNEL_STACK_SIZE - 16); - // Only marker for debugging purposes, ... - *stack-- = 0xdeadbeef; - // and the "caller" we shall return to - // This procedure cleans the task after exit - *stack = (size_t)leave_kernel_task; - // Next bunch on the stack is the initial register state - // The stack must look like the stack of a task - // which was scheduled away previously - state_size = sizeof(struct state); - stack = (size_t*)((size_t)stack - state_size); - stptr = (struct state*)stack; - memset(stptr, 0x00, state_size); - stptr->rsp = (size_t)stack + state_size; - // The first-function-to-be-called's arguments, ... - stptr->rdi = (size_t)arg; - stptr->int_no = 0xb16b00b5; - stptr->error = 0xc03db4b3; - // The instruction pointer shall be set on the first function - // to be called after IRETing - stptr->rip = (size_t)ep; - stptr->cs = 0x08; - stptr->ss = 0x10; - stptr->rflags = 0x1202; - stptr->userrsp = stptr->rsp; - // Set the task's stack pointer entry to the stack - // we have crafted right now - task->last_stack_pointer = (size_t*)stack; - - return 0; -} diff --git a/src/arch/x86_64/grub.cfg b/src/arch/x86_64/grub.cfg deleted file mode 100644 index 082c277..0000000 --- a/src/arch/x86_64/grub.cfg +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. -# -# The part of this file was taken from: -# https://github.com/phil-opp/blog_os/blob/set_up_rust/src/arch/x86_64/grub.cfg - -# To 'blink' the cursor on multiboot2 -# https://www.reddit.com/r/osdev/comments/5m1ekv/text_mode_cursor/dc42t7k/ -set timeout=1 -set default=0 - -menuentry "utero" { - multiboot2 /boot/kernel.bin - boot -} diff --git a/src/arch/x86_64/linker.ld b/src/arch/x86_64/linker.ld deleted file mode 100644 index bd0fc83..0000000 --- a/src/arch/x86_64/linker.ld +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -file at the top-level directory of this distribution. - -Licensed under the Apache License, Version 2.0 or the MIT license -, at your -option. This file may not be copied, modified, or distributed -except according to those terms. - -The part of this file was taken from: -https://github.com/phil-opp/blog_os/blob/set_up_rust/src/arch/x86_64/linker.ld -*/ - -ENTRY(start) - -SECTIONS { - . = 1M; - kernel_start = .; - - .rodata : - { - /* ensure that the multiboot header is at the beginning */ - KEEP(*(.multiboot_header)) - *(.rodata .rodata.*) - . = ALIGN(4K); - } - - .text : - { - *(.text .text.*) - . = ALIGN(4K); - } - - .data : - { - *(.data .data.*) - . = ALIGN(4K); - } - - .bss : - { - bss_start = .; - *(.bss .bss.*) - . = ALIGN(4K); - bss_end = .; - } - kernel_end = .; -} diff --git a/src/arch/x86_64/long_mode_init.asm b/src/arch/x86_64/long_mode_init.asm deleted file mode 100644 index 25a91b4..0000000 --- a/src/arch/x86_64/long_mode_init.asm +++ /dev/null @@ -1,221 +0,0 @@ -; Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -; file at the top-level directory of this distribution. -; -; Licensed under the Apache License, Version 2.0 or the MIT license -; , at your -; option. This file may not be copied, modified, or distributed -; except according to those terms. -; -; The part of this file was taken from: -; https://github.com/phil-opp/blog_os/blob/set_up_rust/src/arch/x86_64/long_mode_init.asm -; https://github.com/RWTH-OS/eduOS/blob/master/arch/x86/kernel/entry.asm - -global long_mode_start -extern stack_top - -section .text -bits 64 -long_mode_start: - ; load 0 into all data segment registers - xor ax, ax - mov ss, ax - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - - ; set default stack pointer - mov rsp, stack_top - add rsp, (8<<10) - 16 - - ; Pass kernel_start, kernel_end, multiboot info - ; to the following function - ; mov rdi, rbx ; Move Multiboot info pointer to rdi - - ; extern early_info - ; call early_info - ; These following lines call main.cr - extern main - call main - - ; jmp $ - hlt - -; The first 32 interrupt service routines (ISR) entries correspond to exceptions. -; Some exceptions will push an error code onto the stack which is specific to -; the exception caused. To decrease the complexity, we handle this by pushing a -; Dummy error code of 0 onto the stack for any ISR that doesn't push an error -; code already. -; -; ISRs are registered as "Interrupt Gate". -; Therefore, the interrupt flag (IF) is already cleared. - -; NASM macro which pushs also an pseudo error code -%macro isrstub_pseudo_error 1 - global isr%1 - isr%1: - push byte 0 ; pseudo error code - push byte %1 - jmp common_stub -%endmacro - -; Similar to isrstub_pseudo_error, but without pushing -; a pseudo error code => The error code is already -; on the stack. -%macro isrstub 1 - global isr%1 - isr%1: - push byte %1 - jmp common_stub -%endmacro - -; Create isr entries, where the number after the -; pseudo error code represents following interrupts: -; 0: Divide By Zero Exception -; 1: Debug Exception -; 2: Non Maskable Interrupt Exception -; 3: Int 3 Exception -; 4: INTO Exception -; 5: Out of Bounds Exception -; 6: Invalid Opcode Exception -; 7: Coprocessor Not Available Exception -%assign i 0 -%rep 8 - isrstub_pseudo_error i -%assign i i+1 -%endrep - -; 8: Double Fault Exception (With Error Code!) -isrstub 8 - -; 9: Coprocessor Segment Overrun Exception -isrstub_pseudo_error 9 - -; 10: Bad TSS Exception (With Error Code!) -; 11: Segment Not Present Exception (With Error Code!) -; 12: Stack Fault Exception (With Error Code!) -; 13: General Protection Fault Exception (With Error Code!) -; 14: Page Fault Exception (With Error Code!) -%assign i 10 -%rep 5 - isrstub i -%assign i i+1 -%endrep - -; 15: Reserved Exception -; 16: Floating Point Exception -; 17: Alignment Check Exception -; 18: Machine Check Exceptio -; 19-31: Reserved -%assign i 15 -%rep 17 - isrstub_pseudo_error i -%assign i i+1 -%endrep - -extern fault_handler -; extern irq_handler -extern get_current_stack -extern finish_task_switch - -global switch_context -ALIGN 8 -switch_context: - ; create on the stack a pseudo interrupt - ; afterwards, we switch to the task with iret - mov rax, rdi ; rdi contains the address to store the old rsp - pushf ; RFLAGS - push QWORD 0x08 ; CS - push QWORD rollback ; RIP - push QWORD 0x00 ; Interrupt number - push QWORD 0x00edbabe ; Error code - push rax - push rcx - push rdx - push rbx - push rsp - push rbp - push rsi - push rdi - push r8 - push r9 - push r10 - push r11 - push r12 - push r13 - push r14 - push r15 - - jmp common_switch - -ALIGN 8 -rollback: - ret - -ALIGN 8 -common_stub: - push rax - push rcx - push rdx - push rbx - push rsp - push rbp - push rsi - push rdi - push r8 - push r9 - push r10 - push r11 - push r12 - push r13 - push r14 - push r15 - - ; use the same handler for interrupts and exceptions - mov rdi, rsp - call fault_handler - ; call irq_handler - - cmp rax, 0 - je no_context_switch - ; jmp no_context_switch - -common_switch: - mov [rax], rsp ; store old rsp - call get_current_stack ; get new rsp - xchg rax, rsp - - ; set task switched flag - mov rax, cr0 - or eax, 8 - mov cr0, rax - - ; set rsp0 in the task state segment - ; TODO: It seems to be defined in gdt.c - ; extern set_kernel_stack - ; call set_kernel_stack - - ; call cleanup code - call finish_task_switch - -no_context_switch: - pop r15 - pop r14 - pop r13 - pop r12 - pop r11 - pop r10 - pop r9 - pop r8 - pop rdi - pop rsi - pop rbp - add rsp, 8 - pop rbx - pop rdx - pop rcx - pop rax - - add rsp, 16 - iretq diff --git a/src/arch/x86_64/multiboot_header.asm b/src/arch/x86_64/multiboot_header.asm deleted file mode 100644 index b80011a..0000000 --- a/src/arch/x86_64/multiboot_header.asm +++ /dev/null @@ -1,27 +0,0 @@ -; Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -; file at the top-level directory of this distribution. -; -; Licensed under the Apache License, Version 2.0 or the MIT license -; , at your -; option. This file may not be copied, modified, or distributed -; except according to those terms. -; -; The part of this file was taken from: -; https://github.com/phil-opp/blog_os/blob/set_up_rust/src/arch/x86_64/multiboot_header.asm - -section .multiboot_header -header_start: - dd 0xe85250d6 ; magic number (multiboot 2) - dd 0 ; architecture 0 (protected mode i386) - dd header_end - header_start ; header length - ; checksum - dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start)) - - ; insert optional multiboot tags here - - ; required end tag - dw 0 ; type - dw 0 ; flags - dd 8 ; size -header_end: diff --git a/src/c_kernel/dummy_exception.c b/src/c_kernel/dummy_exception.c deleted file mode 100644 index cd0febe..0000000 --- a/src/c_kernel/dummy_exception.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "make_string.h" -#define N 256 - -char *dummy_exception() -{ - static char *str[N]; - int error_num = 7; - long long int a = (long long int)1 << 63; - long long int b = (long long int)1 << 63; - long long int c = (long long int)1 << 63; - long long int d = (long long int)1 << 63; - - *str = make_string("Dummy Exception (%d) at 0x%llx:0x%llx, error code 0x%llx, rflags 0x%llx\n", - error_num, a, b, c, d); - - return *str; -} diff --git a/src/c_kernel/hello.c b/src/c_kernel/hello.c deleted file mode 100644 index c3f5f70..0000000 --- a/src/c_kernel/hello.c +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#include "make_string.h" -#define N 256 - -char *hello_from_c(void) -{ - static char *str[N]; - *str = make_string("%s\n", "Hello from C"); - return *str; -} diff --git a/src/c_kernel/include/bits/alltypes.h b/src/c_kernel/include/bits/alltypes.h deleted file mode 100644 index 07a07a4..0000000 --- a/src/c_kernel/include/bits/alltypes.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// musl/obj/include/bits/alltypes.h -#define _Addr long -#define _Int64 long -#define _Reg long - -#if defined(__NEED_va_list) && !defined(__DEFINED_va_list) -typedef __builtin_va_list va_list; -#define __DEFINED_va_list -#endif - -#if defined(__NEED_uintptr_t) && !defined(__DEFINED_uintptr_t) -typedef unsigned _Addr uintptr_t; -#define __DEFINED_uintptr_t -#endif - -#undef _Addr -#undef _Int64 -#undef _Reg diff --git a/src/c_kernel/include/config.h b/src/c_kernel/include/config.h deleted file mode 100644 index 509d8ff..0000000 --- a/src/c_kernel/include/config.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/include/eduos/config.h.example - -#ifndef CONFIG_H -#define CONFIG_H - -#define MAX_TASKS 16 -#define VIDEO_MEM_ADDR 0xb8000 -#define CACHE_LINE 64 -#define KERNEL_STACK_SIZE (8 << 10) /* 8 KiB */ -#define DEFAULT_STACK_SIZE (16 * 1024) /* 16 KiB */ - -#define BUILTIN_EXPECT(exp, b) __builtin_expect((exp), (b)) -#define NORETURN __attribute__((noreturn)) -#define STDCALL __attribute__((stdcall)) - -#endif /* end of include guard: CONFIG_H */ \ No newline at end of file diff --git a/src/c_kernel/include/errno.h b/src/c_kernel/include/errno.h deleted file mode 100644 index 1c838f7..0000000 --- a/src/c_kernel/include/errno.h +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// This is taken from: -// src/musl/arch/mips64/bits/errno.h - -#ifndef ERRNO_H -#define ERRNO_H - -#define EPERM 1 -#define ENOENT 2 -#define ESRCH 3 -#define EINTR 4 -#define EIO 5 -#define ENXIO 6 -#define E2BIG 7 -#define ENOEXEC 8 -#define EBADF 9 -#define ECHILD 10 -#define EAGAIN 11 -#define ENOMEM 12 -#define EACCES 13 -#define EFAULT 14 -#define ENOTBLK 15 -#define EBUSY 16 -#define EEXIST 17 -#define EXDEV 18 -#define ENODEV 19 -#define ENOTDIR 20 -#define EISDIR 21 -#define EINVAL 22 -#define ENFILE 23 -#define EMFILE 24 -#define ENOTTY 25 -#define ETXTBSY 26 -#define EFBIG 27 -#define ENOSPC 28 -#define ESPIPE 29 -#define EROFS 30 -#define EMLINK 31 -#define EPIPE 32 -#define EDOM 33 -#define ERANGE 34 -#define ENOMSG 35 -#define EIDRM 36 -#define ECHRNG 37 -#define EL2NSYNC 38 -#define EL3HLT 39 -#define EL3RST 40 -#define ELNRNG 41 -#define EUNATCH 42 -#define ENOCSI 43 -#define EL2HLT 44 -#define EDEADLK 45 -#define ENOLCK 46 -#define EBADE 50 -#define EBADR 51 -#define EXFULL 52 -#define ENOANO 53 -#define EBADRQC 54 -#define EBADSLT 55 -#define EDEADLOCK 56 -#define EBFONT 59 -#define ENOSTR 60 -#define ENODATA 61 -#define ETIME 62 -#define ENOSR 63 -#define ENONET 64 -#define ENOPKG 65 -#define EREMOTE 66 -#define ENOLINK 67 -#define EADV 68 -#define ESRMNT 69 -#define ECOMM 70 -#define EPROTO 71 -#define EDOTDOT 73 -#define EMULTIHOP 74 -#define EBADMSG 77 -#define ENAMETOOLONG 78 -#define EOVERFLOW 79 -#define ENOTUNIQ 80 -#define EBADFD 81 -#define EREMCHG 82 -#define ELIBACC 83 -#define ELIBBAD 84 -#define ELIBSCN 85 -#define ELIBMAX 86 -#define ELIBEXEC 87 -#define EILSEQ 88 -#define ENOSYS 89 -#define ELOOP 90 -#define ERESTART 91 -#define ESTRPIPE 92 -#define ENOTEMPTY 93 -#define EUSERS 94 -#define ENOTSOCK 95 -#define EDESTADDRREQ 96 -#define EMSGSIZE 97 -#define EPROTOTYPE 98 -#define ENOPROTOOPT 99 -#define EPROTONOSUPPORT 120 -#define ESOCKTNOSUPPORT 121 -#define EOPNOTSUPP 122 -#define ENOTSUP EOPNOTSUPP -#define EPFNOSUPPORT 123 -#define EAFNOSUPPORT 124 -#define EADDRINUSE 125 -#define EADDRNOTAVAIL 126 -#define ENETDOWN 127 -#define ENETUNREACH 128 -#define ENETRESET 129 -#define ECONNABORTED 130 -#define ECONNRESET 131 -#define ENOBUFS 132 -#define EISCONN 133 -#define ENOTCONN 134 -#define EUCLEAN 135 -#define ENOTNAM 137 -#define ENAVAIL 138 -#define EISNAM 139 -#define EREMOTEIO 140 -#define ESHUTDOWN 143 -#define ETOOMANYREFS 144 -#define ETIMEDOUT 145 -#define ECONNREFUSED 146 -#define EHOSTDOWN 147 -#define EHOSTUNREACH 148 -#define EWOULDBLOCK EAGAIN -#define EALREADY 149 -#define EINPROGRESS 150 -#define ESTALE 151 -#define ECANCELED 158 -#define ENOMEDIUM 159 -#define EMEDIUMTYPE 160 -#define ENOKEY 161 -#define EKEYEXPIRED 162 -#define EKEYREVOKED 163 -#define EKEYREJECTED 164 -#define EOWNERDEAD 165 -#define ENOTRECOVERABLE 166 -#define ERFKILL 167 -#define EHWPOISON 168 -#define EDQUOT 1133 - -#endif /* end of include guard: ERRNO_H */ diff --git a/src/c_kernel/include/processor.h b/src/c_kernel/include/processor.h deleted file mode 100644 index 79ade15..0000000 --- a/src/c_kernel/include/processor.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/include/eduos/processor.h - -#ifndef PROCESSOR_H -#define PROCESSOR_H - -// This is no needed because of being included in stddef.h -// #include -#include -#include - -#endif /* end of include guard: PROCESSOR_H */ diff --git a/src/c_kernel/include/stddef.h b/src/c_kernel/include/stddef.h deleted file mode 100644 index eeec58a..0000000 --- a/src/c_kernel/include/stddef.h +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/include/eduos/stddef.h - -#ifndef STDDEF_H -#define STDDEF_H - -#include -#include - -#define NULL ((void*) 0) - -// represents a task identifier -typedef unsigned int tid_t; - -struct task; - -// pointer to the current (running) task -// NOTE: defined in tasks.c -extern struct task* current_task; - -#endif /* end of include guard: STDDEF_H */ \ No newline at end of file diff --git a/src/c_kernel/include/stdint.h b/src/c_kernel/include/stdint.h deleted file mode 100644 index 1a89daf..0000000 --- a/src/c_kernel/include/stdint.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef _STDINT_H -#define _STDINT_H - -#define __NEED_int8_t -#define __NEED_int16_t -#define __NEED_int32_t -#define __NEED_int64_t - -#define __NEED_uint8_t -#define __NEED_uint16_t -#define __NEED_uint32_t -#define __NEED_uint64_t - -#define __NEED_intptr_t -#define __NEED_uintptr_t - -#define __NEED_intmax_t -#define __NEED_uintmax_t - -#include "bits/alltypes.h" - -typedef int8_t int_fast8_t; -typedef int64_t int_fast64_t; - -typedef int8_t int_least8_t; -typedef int16_t int_least16_t; -typedef int32_t int_least32_t; -typedef int64_t int_least64_t; - -typedef uint8_t uint_fast8_t; -typedef uint64_t uint_fast64_t; - -typedef uint8_t uint_least8_t; -typedef uint16_t uint_least16_t; -typedef uint32_t uint_least32_t; -typedef uint64_t uint_least64_t; - -#define INT8_MIN (-1 - 0x7f) -#define INT16_MIN (-1 - 0x7fff) -#define INT32_MIN (-1 - 0x7fffffff) -#define INT64_MIN (-1 - 0x7fffffffffffffff) - -#define INT8_MAX (0x7f) -#define INT16_MAX (0x7fff) -#define INT32_MAX (0x7fffffff) -#define INT64_MAX (0x7fffffffffffffff) - -#define UINT8_MAX (0xff) -#define UINT16_MAX (0xffff) -#define UINT32_MAX (0xffffffffu) -#define UINT64_MAX (0xffffffffffffffffu) - -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST64_MIN INT64_MIN - -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST64_MIN INT64_MIN - -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST64_MAX INT64_MAX - -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MAX INT64_MAX - -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST64_MAX UINT64_MAX - -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -#define WINT_MIN 0U -#define WINT_MAX UINT32_MAX - -#if L'\0' - 1 > 0 -#define WCHAR_MAX (0xffffffffu + L'\0') -#define WCHAR_MIN (0 + L'\0') -#else -#define WCHAR_MAX (0x7fffffff + L'\0') -#define WCHAR_MIN (-1 - 0x7fffffff + L'\0') -#endif - -#define SIG_ATOMIC_MIN INT32_MIN -#define SIG_ATOMIC_MAX INT32_MAX - -// TODO: Let these lines be enable - -// #include - -// #define INT8_C(c) c -// #define INT16_C(c) c -// #define INT32_C(c) c - -// #define UINT8_C(c) c -// #define UINT16_C(c) c -// #define UINT32_C(c) c##U - -// #if UINTPTR_MAX == UINT64_MAX -// #define INT64_C(c) c##L -// #define UINT64_C(c) c##UL -// #define INTMAX_C(c) c##L -// #define UINTMAX_C(c) c##UL -// #else -// #define INT64_C(c) c##LL -// #define UINT64_C(c) c##ULL -// #define INTMAX_C(c) c##LL -// #define UINTMAX_C(c) c##ULL -// #endif - -#endif diff --git a/src/c_kernel/include/stdio.h b/src/c_kernel/include/stdio.h deleted file mode 100644 index 59e5379..0000000 --- a/src/c_kernel/include/stdio.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// Functions are taken from musl/include/stdio.h - -#ifndef STDIO_H -#define STDIO_H - -#define __NEED_va_list - -#include "bits/alltypes.h" - -#define va_start(v, l) __builtin_va_start(v, l) -#define va_end(v) __builtin_va_end(v) -#define va_arg(v, l) __builtin_va_arg(v, l) -#define va_copy(d, s) __builtin_va_copy(d, s) - -int sprintf(char *__restrict, const char *__restrict, ...); - -#endif /* end of include guard: STDIO_H */ diff --git a/src/c_kernel/include/stdlib.h b/src/c_kernel/include/stdlib.h deleted file mode 100644 index bec7406..0000000 --- a/src/c_kernel/include/stdlib.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/include/eduos/stdlib.h - -#ifndef STDLIB_H -#define STDLIB_H - -#include -#include - -// Implemented in memory.c -void* create_stack(tid_t id); - -#endif /* end of include guard: STDLIB_H */ \ No newline at end of file diff --git a/src/c_kernel/include/string.h b/src/c_kernel/include/string.h deleted file mode 100644 index ad0959b..0000000 --- a/src/c_kernel/include/string.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// Functions are taken from musl/include/string.h - -#ifndef STRING_H -#define STRING_H - -#include "bits/alltypes.h" - -void* memset(void*, int, size_t); - -#endif /* end of include guard: STRING_H */ diff --git a/src/c_kernel/include/tasks.h b/src/c_kernel/include/tasks.h deleted file mode 100644 index 22617c0..0000000 --- a/src/c_kernel/include/tasks.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/include/eduos/tasks.h - -#ifndef TASKS_H -#define TASKS_H - -#include -#include -#include - -int multitasking_init(void); - -int create_kernel_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio); - -void reschedule(void); - -void NORETURN leave_kernel_task(void); - -#endif /* end of include guard: TASKS_H */ \ No newline at end of file diff --git a/src/c_kernel/include/tasks_types.h b/src/c_kernel/include/tasks_types.h deleted file mode 100644 index fa27fef..0000000 --- a/src/c_kernel/include/tasks_types.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/include/eduos/tasks_types.h - -#ifndef TASKS_TYPES_H -#define TASKS_TYPES_H - -#include -#include - -#define TASK_INVALID 0 -#define TASK_READY 1 -#define TASK_RUNNING 2 -#define TASK_BLOCKED 3 -#define TASK_FINISHED 4 -#define TASK_IDLE 5 - -#define MAX_PRIO 31 -#define REALTIME_PRIO 31 -#define HIGH_PRIO 16 -#define NORMAL_PRIO 8 -#define LOW_PRIO 1 -#define IDLE_PRIO 0 - -typedef int (*entry_point_t)(void*); - -// Represents the Process Control Block -typedef struct task -{ - // task id = position in the task table - tid_t id __attribute__((aligned(CACHE_LINE))); - // task status - uint32_t status; - // copy of the stack pointer before a context switch - size_t* last_stack_pointer; - // starting address of the stack - void* stack; - // task priority - uint8_t prio; - // next task in the queue - struct task* next; - // previous task in the queue - struct task* prev; -} task_t; - -typedef struct -{ - task_t* first; - task_t* last; -} task_list_t; - -// Represents a queue for all runnable tasks -typedef struct -{ - // idle task - task_t* idle __attribute__((aligned(CACHE_LINE))); - // previous task - task_t* old_task; - // total number of tasks in the queue - uint32_t nr_tasks; - // indicates the used priority queues - uint32_t prio_bitmap; - // a queue for each priority - task_list_t queue[MAX_PRIO - 1]; -} readyqueues_t; - -#endif /* end of include guard: TASKS_TYPES_H */ \ No newline at end of file diff --git a/src/c_kernel/make_string.c b/src/c_kernel/make_string.c deleted file mode 100644 index 079f642..0000000 --- a/src/c_kernel/make_string.c +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#include -#include "make_string.h" -#define N 256 - -char *make_string(char *fmt, ...) -{ - static char s[N] = {'\0'}; - va_list ap; - - va_start(ap, fmt); - vsprintf(s, fmt, ap); - va_end(ap); - - return s; -} diff --git a/src/c_kernel/make_string.h b/src/c_kernel/make_string.h deleted file mode 100644 index d689052..0000000 --- a/src/c_kernel/make_string.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#ifndef MAKE_STRING_H -#define MAKE_STRING_H - -#include - -char *make_string(char *fmt, ...); - -#endif /* end of include guard: MAKE_STRING_H */ diff --git a/src/c_kernel/memory.c b/src/c_kernel/memory.c deleted file mode 100644 index ea2a4bf..0000000 --- a/src/c_kernel/memory.c +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/mm/memory.c - -#include -#include - -static char stack[MAX_TASKS - 1][KERNEL_STACK_SIZE]; - -void* -create_stack(tid_t id) -{ - // Idle task uses stack - if (BUILTIN_EXPECT(!id, 0)) { - return NULL; - } - // Do we have a valid task id? - if (BUILTIN_EXPECT(id >= MAX_TASKS, 0)) { - return NULL; - } - - return (void*)stack[id - 1]; -} diff --git a/src/c_kernel/tasks.c b/src/c_kernel/tasks.c deleted file mode 100644 index 67ebaa8..0000000 --- a/src/c_kernel/tasks.c +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// The part of this file was taken from: -// https://github.com/RWTH-OS/eduOS/blob/master/kernel/tasks.c - -#include -#include -#include -#include -#include -#include -extern void* stack_top; // defined in boot.asm -extern int eputs(const char*); -extern int eprint(const char*); - -// Array of task structures (PCB) -// A task's id will be its position in this array -static task_t task_table[MAX_TASKS] = - {[0] = { 0, TASK_IDLE, NULL, NULL, 0, NULL, NULL }, - [1 ... MAX_TASKS - 1] = { 0, TASK_INVALID, NULL, NULL, 0, NULL, NULL } }; -// readyqueues -// Task list is a queue for priority, total number is 30 -static readyqueues_t readyqueues = { task_table+0, NULL, 0, 0, {[0 ... MAX_PRIO-2] = {NULL, NULL}}}; - -task_t* current_task = task_table+0; - -// Return pointer to the task_t structure of current task -task_t * -get_current_task(void) -{ - return current_task; -} - -int multitasking_init(void) -{ - // if the first task status isn't idle (it's unlikely) - // puts message task 0 is not an idle task - if (BUILTIN_EXPECT(task_table[0].status != TASK_IDLE, 0)) { - // Equivalent to kputs("Task 0 is not an idle task\n"); - eputs("Task 0 is not an idle task"); - return -ENOMEM; - } - // At the first task_table - // Set prio to zero - task_table[0].prio = IDLE_PRIO; - // Set stack_top - 8192 to stack - task_table[0].stack = stack_top - 8192; - return 0; -} - -void -finish_task_switch(void) -{ - // pointer to old task and prio - task_t* old; - uint8_t prio; - // Old(previous) task in the ready queue is NOT NULL - if ((old = readyqueues.old_task) != NULL) { - // If old task's status is TASK_INVALID - if (old->status == TASK_INVALID) { - // Set old task's stack to NULL - old->stack = NULL; - // Set old task's last_stack_pointer to NULL - old->last_stack_pointer = NULL; - // Set old task in the queue to NULL - readyqueues.old_task = NULL; - - } else { // else if old task's status is valid - // Set old task's prio to (current) prio - prio = old->prio; - if (!readyqueues.queue[prio - 1].first) { // if a queue for current prio is not the first - // Set next and previous tasks against old task to NULL - old->next = old->prev = NULL; - // Set old task to the first and the last in task list - readyqueues.queue[prio - 1].first = readyqueues.queue[prio - 1].last = old; - - } else { // else if a queue for prio is the first - // Set next task against old to NULL - old->next = NULL; - // Set previous task against old to the last in task list - old->prev = readyqueues.queue[prio - 1].last; - // Set old to the next task of the last task in list - readyqueues.queue[prio - 1].last->next = old; - // Set old to the last task in list - readyqueues.queue[prio - 1].last = old; - } - // The previous task in ready queues is NULL; - readyqueues.old_task = NULL; - // Using |= operator, if there's prio_bitmap, assign itself - // but when no prio_bitmap, assign 1 << prio - readyqueues.prio_bitmap |= (1 << prio); - } - } -} - -#define N 256 // For message -// Procedures which are called by exiting tasks -static void NORETURN -do_exit(int arg) -{ - task_t* curr_task = current_task; - // Equivalent to kprintf("Terminate task: %u, return value %d\n", curr_task->id, arg); - char message[N] = { '\0' }; - sprintf(message, "Terminate task: %u, return value %d\n", curr_task->id, arg); - eprint(message); - - curr_task->status = TASK_FINISHED; - reschedule(); - // Equivalent to kprintf("Kernel panic: scheduler found no valid task\n"); - eputs("Kernel panic: scheduler found no valid task"); - while (1) - { - NOP8; - } -} - -// A procedure to be called by kernel tasks -void NORETURN -leave_kernel_task(void) -{ - int result; - result = 0; // get_return_value(); - do_exit(result); -} - -// Create a task with a specific entry point -// id: Pointer to a tid_t struct where the id shall be set -// ep: Pointer to the function the task shall start with -// arg: Arguments list -// prio: Desired priority of the new task -static int -create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio) -{ - int ret = -ENOMEM; - uint32_t i; - - // entry pointer is null - if (BUILTIN_EXPECT(!ep, 0)) { - return -EINVAL; - } - // the given prio equals IDLE_PRIO(0) - if (BUILTIN_EXPECT(prio == IDLE_PRIO, 0)) { - return -EINVAL; - } - // the given prio is higher than MAX_PRIO(31) - if (BUILTIN_EXPECT(prio > MAX_PRIO, 0)) { - return -EINVAL; - } - // Loop 0~15 - for(i=0; i < MAX_TASKS; i++) { - // Turn invalid-status task into ready-status - if (task_table[i].status == TASK_INVALID) { - // set i to id - task_table[i].id = i; - // set status to TASK_READY; - task_table[i].status == TASK_READY; - // last_stack_pointer = NULL; - task_table[i].last_stack_pointer = NULL; - task_table[i].stack = create_stack(i); - // Set the given prio to prio - task_table[i].prio = prio; - - if (id) { - // set i to id's pointer - *id = i; - } - - ret = create_default_frame(task_table+i, ep, arg); - // Add task in the readyqueues - readyqueues.prio_bitmap |= (1 << prio); - readyqueues.nr_tasks++; - // if there's no first one of queue - if (!readyqueues.queue[prio-1].first) { - // next and prev of task_table[i] = NULL; - task_table[i].next = task_table[i].prev = NULL; - // set task_table+i (address of task_table+i?) to first - readyqueues.queue[prio - 1].first = task_table + i; - // set task_table+i (address of task_table+i?) to last - readyqueues.queue[prio - 1].last = task_table + i; - } else { // else there's the first of queue - // set queue[prio-1].last to previous task in task_table - task_table[i].prev = readyqueues.queue[prio - 1].last; - // set NULL to next task in task_table - task_table[i].next = NULL; - // set task_table+i to the last of the queue to next task - readyqueues.queue[prio - 1].last->next = task_table + i; - // set task_table+i to the last of the queue - readyqueues.queue[prio - 1].last = task_table + i; - } - break; - } - } - - return ret; -} - -int -create_kernel_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio) -{ - // given prio is higher than MAX_PRIO(31) - if (prio > MAX_PRIO) { - // Set NORMAL_PRIO to prio - prio = NORMAL_PRIO; - } - return create_task(id, ep, args, prio); -} - -size_t** -scheduler(void) -{ - task_t* orig_task; - uint32_t prio; - - orig_task = current_task; - // signalizes that this task could be reused - // current_task is task_table+0 when initialized - if (current_task->status == TASK_FINISHED) { - current_task->status = TASK_INVALID; - readyqueues.old_task = current_task; - } else { // current_task is NOT FINISHED - // Reset old task - readyqueues.old_task = NULL; - } - // Determines highest priority (at this point) - prio = msb(readyqueues.prio_bitmap); - // if current highest prio is higher than MAX_PRIO - if (prio > MAX_PRIO) { - // Get task out - if ((current_task->status == TASK_RUNNING) || - (current_task->status == TASK_IDLE)) { - goto get_task_out; - } - // set readyqueues.idle (idle task) to current_task - current_task = readyqueues.idle; - - } else { // current highest prio is lower than MAX_PRIO - // Does the current task have an higher priority? => no task switch - // if current_task's prio is higher than current highest prio AND - // current_task's status is TASK_RUNNING - if ((current_task->prio > prio) && (current_task->status == TASK_RUNNING)) { - goto get_task_out; - } - // if current_task's status is equals to TASK_RUNNING - if (current_task->status == TASK_RUNNING) { - // Set current_task's status to TASK_READY - current_task->status = TASK_READY; - // Set current_task to readyqueues.old_task - readyqueues.old_task = current_task; - } - // Set readyqueues's queue[current prio].first to current_task - current_task = readyqueues.queue[prio - 1].first; - // if current_task's status equals to TASK_INVALID (unlikely) - if (BUILTIN_EXPECT(current_task->status == TASK_INVALID, 0)) { - // Equivalent to kprintf("Upps!!!!!! Got invalid task %d, orig task %d\n", - // current_task->id, orig_task->id); - char message[N] = { '\0' }; - sprintf(message, "Upps!!!!!! Got invalid task %d, orig task %d\n", - current_task->id, orig_task->id); - eprint(message); - } - // Set current_task's status to TASK_RUNNING - current_task->status = TASK_RUNNING; - // Remove new task from queue - // By the way, priority 0 is only used by the idle task and doesn't need own - // queue - readyqueues.queue[prio - 1].first = current_task->next; - // if there's no next task in the queue - if (!current_task->next) { - readyqueues.queue[prio - 1].last = NULL; - // ~ is Binary Ones Complement Operator has the effect of 'flipping' bits - // &= is bitwise AND operator which turns (1 AND 0) or (0 AND 0) into 0 - readyqueues.prio_bitmap &= ~(1 << prio); - } - // Set the next and the previous tasks against current_task to NULL - current_task->next = current_task->prev = NULL; - } - -get_task_out: - if (current_task != orig_task) { // current_task is not orig_task - // Equivalent to kprintf("schedule from %u to %u with prio %u\n", - // orig_task->id, - // current_task->id, (uint32_t)current_task->prio); - char message[N] = { '\0' }; - sprintf(message, "schedule from %u to %u with prio %u\n", orig_task->id, - current_task->id, (uint32_t)current_task->prio); - eprint(message); - // Returns the address of last_stack_pointer in orig_task - return (size_t**)&(orig_task->last_stack_pointer); - } - - return NULL; -} - -void -reschedule(void) -{ - // pointer to pointer of address of stack - size_t** stack; - if ((stack = scheduler())) { - switch_context(stack); - } -} diff --git a/src/c_kernel/utero_init.c b/src/c_kernel/utero_init.c deleted file mode 100644 index 00f1ef5..0000000 --- a/src/c_kernel/utero_init.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "make_string.h" -#include -#include -#include -#include -#include -#define N 256 - -// Parameters passed from assembly or linker -extern const void * kernel_start; -extern const void * kernel_end; -extern const void * bss_start; -extern const void * bss_end; -extern int eputs(const char*); -extern int eprint(const char*); - -// int early_info(unsigned int ks, unsigned int ke) -// { - // kernel_start = ks; - // kernel_end = ke; - - // return 0; -// } - -char *make_kernel_info() -{ - static char *str[N]; - char *fmt = "\ -Kernel starts at: %p\n\ -Kernel ends at: %p\n\ -Bss starts at: %p\n\ -Bss ends at: %p\n\ -multiboot info at: %p\n"; - - *str = make_string(fmt, - &kernel_start, - &kernel_end, - &bss_start, - &bss_end, - &mb_info); - return *str; -} - -static int -foo(void* arg) -{ - int i = 0; - - - for (i = 0; i < 5; i++) { - char *message; - sprintf(message, "hello from %s\n", (char*)arg); - eprint(message); - reschedule(); - } - - return 0; -} - -int create_foo(void) -{ - tid_t id1; - tid_t id2; - create_kernel_task(&id1, foo, "foo1", NORMAL_PRIO); - create_kernel_task(&id2, foo, "foo2", NORMAL_PRIO); - reschedule(); - - while (1) { - NOP8; - } - return 0; -} diff --git a/src/core/bool.cr b/src/core/bool.cr deleted file mode 100644 index c2ae573..0000000 --- a/src/core/bool.cr +++ /dev/null @@ -1,62 +0,0 @@ -# Bool has only two possible values: `true` and `false`. They are constructed using these literals: -# -# ``` -# true # A Bool that is true -# false # A Bool that is false -# ``` -struct Bool - # Bitwise OR. Returns `true` if this bool or *other* is `true`, otherwise returns `false`. - # - # ``` - # false | false # => false - # false | true # => true - # true | false # => true - # true | true # => true - # ``` - def |(other : Bool) - self ? true : other - end - - # Bitwise AND. Returns `true` if this bool and *other* are `true`, otherwise returns `false`. - # - # ``` - # false & false # => false - # false & true # => false - # true & false # => false - # true & true # => true - # ``` - def &(other : Bool) - self ? other : false - end - - # Exclusive OR. Returns `true` if this bool is different from *other*, otherwise returns `false`. - # - # ``` - # false ^ false # => false - # false ^ true # => true - # true ^ false # => true - # true ^ true # => false - # ``` - def ^(other : Bool) - self != other - end - - # Returns a hash value for this boolean: 0 for `false`, 1 for `true`. - def hash - self ? 1 : 0 - end - - # Returns `"true"` for `true` and `"false"` for `false`. - def to_s - self ? "true" : "false" - end - - # Appends `"true"` for `true` and `"false"` for `false` to the given IO. - # def to_s(io) - # io << to_s - # end - - def clone - self - end -end diff --git a/src/core/boot.c b/src/core/boot.c new file mode 100644 index 0000000..dd10077 --- /dev/null +++ b/src/core/boot.c @@ -0,0 +1,168 @@ +#include +#include +#include +#include + +bool multiboot_is_valid(unsigned long magic, unsigned long addr) +{ + if (magic != MULTIBOOT2_MAGIC_VALUE) { + printfk("Invalid magic value: 0x%x", magic); + return false; + } + + if (addr & 7) { + printfk("Unaligned MBI: 0x%x", addr); + return false; + } + + return true; +} + +void* find_multiboot_tag(multiboot_tag_t *tags, uint16_t type) +{ + multiboot_tag_t *tag; + + for ( + tag = tags; // points to the first tag of the multiboot_info_t struct + tag->type != MULTIBOOT_TAG_TYPE_END; + tag = (multiboot_tag_t *) ((uint8_t *) tag + ((tag->size + 7) & ~7)) + ) { + if (tag->type == type) { + return tag; + } + } + + return 0; +} + +reserved_areas_t read_multiboot_info(multiboot_info_t *mbi) +{ + multiboot_tag_t *tag; + reserved_areas_t reserved = { + .kernel_start = -1, + .kernel_end = 0, + .multiboot_start = (uint64_t) mbi, + .multiboot_end = 0 + }; + + DEBUG("announced MBI size 0x%x", mbi->size); + + for ( + tag = (multiboot_tag_t *) mbi->tags; + tag->type != MULTIBOOT_TAG_TYPE_END; + tag = (multiboot_tag_t *) ((uint8_t *) tag + ((tag->size + 7) & ~7)) + ) { + switch (tag->type) { + case MULTIBOOT_TAG_TYPE_CMDLINE: + DEBUG("command line = %s", ((multiboot_tag_string_t *) tag)->string); + break; + case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME: + DEBUG("boot loader name = %s", ((multiboot_tag_string_t *) tag)->string); + break; + case MULTIBOOT_TAG_TYPE_MODULE: + DEBUG( + "module at 0x%x-0x%x. command line %s", + ((multiboot_tag_module_t *) tag)->mod_start, + ((multiboot_tag_module_t *) tag)->mod_end, + ((multiboot_tag_module_t *) tag)->cmdline + ); + break; + case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: + DEBUG( + "mem_lower = %dKB, mem_upper = %dKB", + ((multiboot_tag_basic_meminfo_t *) tag)->mem_lower, + ((multiboot_tag_basic_meminfo_t *) tag)->mem_upper + ); + break; + case MULTIBOOT_TAG_TYPE_BOOTDEV: + DEBUG( + "boot device 0x%x,%u,%u", + ((multiboot_tag_bootdev_t *) tag)->biosdev, + ((multiboot_tag_bootdev_t *) tag)->slice, + ((multiboot_tag_bootdev_t *) tag)->part + ); + break; + case MULTIBOOT_TAG_TYPE_MMAP: + { + multiboot_mmap_entry_t *mmap; + for ( + mmap = ((multiboot_tag_mmap_t *) tag)->entries; + (uint8_t *) mmap < (uint8_t *) tag + tag->size; + mmap = (multiboot_mmap_entry_t *) ((unsigned long) mmap + ((multiboot_tag_mmap_t *) tag)->entry_size) + ) { + DEBUG( + "mmap base_addr = 0x%X, length = 0x%X, type = 0x%x", + mmap->addr, + mmap->len, + mmap->type + ); + } + } + break; + case MULTIBOOT_TAG_TYPE_FRAMEBUFFER: + DEBUG("%s", "framebuffer"); + break; + case MULTIBOOT_TAG_TYPE_APM: + DEBUG("%s", "apm"); + break; + case MULTIBOOT_TAG_TYPE_ACPI_OLD: + DEBUG("%s", "acpi old"); + break; + case MULTIBOOT_TAG_TYPE_ACPI_NEW: + DEBUG("%s", "acpi new"); + break; + case MULTIBOOT_TAG_TYPE_ELF_SECTIONS: + { + multiboot_elf_sections_entry_t *elf; + + uint32_t i; + for ( + i = 0, + elf = ((multiboot_tag_elf_sections_t *) tag)->sections; + i < ((multiboot_tag_elf_sections_t *) tag)->num; + elf = (multiboot_elf_sections_entry_t *) ((uint64_t) elf + ((multiboot_tag_elf_sections_t *) tag)->section_size), + i++ + ) { + DEBUG( + "elf section #%d addr = 0x%X, type = 0x%X, size = 0x%X, flags = 0x%X", + i, + elf->addr, + elf->type, + elf->size, + elf->flags + ); + + if (elf->type == MULTIBOOT_ELF_SECTION_TYPE_NULL) { + continue; + } + + if (((uint64_t) (elf->addr)) < reserved.kernel_start) { + reserved.kernel_start = (uint64_t) elf->addr; + } + + if (((uint64_t) (elf->addr)) + elf->size > reserved.kernel_end) { + reserved.kernel_end = (uint64_t) elf->addr; + reserved.kernel_end += elf->size; + } + } + } + break; + case MULTIBOOT_TAG_TYPE_NETWORK: + DEBUG("%s", "network"); + break; + case MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR: + DEBUG("%s", "load base addr"); + break; + + default: + DEBUG("tag 0x%x, size 0x%x", tag->type, tag->size); + } + } + + tag = (multiboot_tag_t *) ((uint8_t *) tag + ((tag->size + 7) & ~7)); + reserved.multiboot_end = (uint64_t) tag; + + DEBUG("total MBI size 0x%X", (uint64_t) tag - (uint64_t) mbi); + + return reserved; +} diff --git a/src/core/box.cr b/src/core/box.cr deleted file mode 100644 index 7416335..0000000 --- a/src/core/box.cr +++ /dev/null @@ -1,28 +0,0 @@ -# A Box allows turning any object to a `Void*` and back. -# -# A Box's purpose is passing data to C as a `Void*` and then converting that -# back to the original data type. -# -# For an example usage, see `Proc`'s explanation about sending Procs to C. -class Box(T) - # Returns the original object - # getter object : T - - # Creates a `Box` with the given object. - # - # This method isn't usually used directly. Instead, `Box.box` is used. - def initialize(@object : T) - end - - # Creates a Box for an object and returns it as a `Void*`. - def self.box(object) : Void* - new(object).as(Void*) - end - - # Unboxes a `Void*` into an object of type `T`. Note that for this you must - # specify T: `Box(T).unbox(data)`. - def self.unbox(pointer : Void*) : T - pointer.as(self).as(T) - # pointer.as(self).object - end -end diff --git a/src/core/check.c b/src/core/check.c new file mode 100644 index 0000000..3448ca3 --- /dev/null +++ b/src/core/check.c @@ -0,0 +1,14 @@ +#include +#include +#include +#include + +void check_interrupts() +{ + printfk("- checking interrupts... "); + + uint32_t tick = timer_tick(); + while (tick == timer_tick()) ; + + printfk("OK\n"); +} diff --git a/src/core/cmos.c b/src/core/cmos.c new file mode 100644 index 0000000..7d7c7cf --- /dev/null +++ b/src/core/cmos.c @@ -0,0 +1,106 @@ +#include +#include +#include + +uint8_t read_register(uint8_t reg); +bool update_in_progress(); +bool rtc_values_are_not_equal(cmos_rtc_t c1, cmos_rtc_t c2); + +cmos_rtc_t cmos_read_rtc() +{ + cmos_rtc_t rtc; + cmos_rtc_t last; + + // This uses the "read registers until you get the same values twice in a + // row" technique to avoid getting inconsistent values due to RTC updates + while (update_in_progress()) ; + + // read a first time + rtc.seconds = read_register(CMOS_REG_SECONDS); + rtc.minutes = read_register(CMOS_REG_MINUTES); + rtc.hours = read_register(CMOS_REG_HOURS); + rtc.weekdays = read_register(CMOS_REG_WEEKDAYS); + rtc.day = read_register(CMOS_REG_DAY); + rtc.month = read_register(CMOS_REG_MONTH); + rtc.year = read_register(CMOS_REG_YEAR); + rtc.century = read_register(CMOS_REG_CENTURY); + + do { + // prepare to read a second time + // Something's wrong when using musl libc's memcpy + // memcpy(&rtc, &last, sizeof(cmos_rtc_t)); + last.seconds = rtc.seconds; + last.minutes = rtc.minutes; + last.hours = rtc.hours; + last.weekdays = rtc.weekdays; + last.day = rtc.day; + last.month = rtc.month; + last.year = rtc.year; + last.century = rtc.century; + + while (update_in_progress()) ; + + // read a second time + rtc.seconds = read_register(CMOS_REG_SECONDS); + rtc.minutes = read_register(CMOS_REG_MINUTES); + rtc.hours = read_register(CMOS_REG_HOURS); + rtc.weekdays = read_register(CMOS_REG_WEEKDAYS); + rtc.day = read_register(CMOS_REG_DAY); + rtc.month = read_register(CMOS_REG_MONTH); + rtc.year = read_register(CMOS_REG_YEAR); + rtc.century = read_register(CMOS_REG_CENTURY); + } while (rtc_values_are_not_equal(rtc, last)); + + // Status Register B contains the formats of bytes + uint8_t reg_b = read_register(CMOS_REG_STATUS_B); + + if (!(reg_b & 0x04)) { + // convert BCD back into a "good" binary value + rtc.seconds = (rtc.seconds & 0x0F) + ((rtc.seconds / 16) * 10); + rtc.minutes = (rtc.minutes & 0x0F) + ((rtc.minutes / 16) * 10); + rtc.hours = ((rtc.hours & 0x0F) + (((rtc.hours & 0x70) / 16) * 10)) | (rtc.hours & 0x80); + rtc.weekdays = (rtc.weekdays & 0x0F) + ((rtc.weekdays / 16) * 10); + rtc.day = (rtc.day & 0x0F) + ((rtc.day / 16) * 10); + rtc.month = (rtc.month & 0x0F) + ((rtc.month / 16) * 10); + rtc.year = (rtc.year & 0x0F) + ((rtc.year / 16) * 10); + rtc.century = (rtc.century & 0x0F) + ((rtc.century / 16) * 10); + } + + // if the hour is pm, then the 0x80 bit is set on the hour byte + if (!(reg_b & 0x02) && (rtc.hours & 0x80)) { + // Convert 12 hour clock to 24 hour clock if necessary + rtc.hours = ((rtc.hours & 0x7F) + 12) % 24; + } + + // compute full year + rtc.year += (rtc.century * 100); + + return rtc; +} + +bool update_in_progress() +{ + port_byte_out(CMOS_COMMAND_PORT, CMOS_REG_STATUS_A); + // the RTC has an "Update in progress" flag (bit 7 of Status Register A) + return (port_byte_in(CMOS_DATA_PORT) & 0x80); +} + +uint8_t read_register(uint8_t reg) +{ + port_byte_out(CMOS_COMMAND_PORT, (1 << 7) | reg); + return port_byte_in(CMOS_DATA_PORT); +} + +bool rtc_values_are_not_equal(cmos_rtc_t c1, cmos_rtc_t c2) +{ + return ( + c1.seconds != c2.seconds || + c1.minutes != c2.minutes || + c1.hours != c2.hours || + c1.weekdays != c2.weekdays || + c1.day != c2.day || + c1.month != c2.month || + c1.year != c2.year || + c1.century != c2.century + ); +} diff --git a/src/core/comparable.cr b/src/core/comparable.cr deleted file mode 100644 index 2b8cf94..0000000 --- a/src/core/comparable.cr +++ /dev/null @@ -1,54 +0,0 @@ -# The `Comparable` mixin is used by classes whose objects may be ordered. -# -# Including types must provide an `<=>` method, which compares the receiver against -# another object, returning `-1`, `0`, or `+1` depending on whether the receiver is less than, -# equal to, or greater than the other object. -# -# `Comparable` uses `<=>` to implement the conventional comparison operators (`<`, `<=`, `==`, `>=`, and `>`). -module Comparable(T) - # Compares this object to *other* based on the receiver’s `<=>` method, returning `true` if it returns `-1`. - def <(other : T) - (self <=> other) < 0 - end - - # Compares this object to *other* based on the receiver’s `<=>` method, returning `true` if it returns `-1` or `0`. - def <=(other : T) - (self <=> other) <= 0 - end - - # Compares this object to *other* based on the receiver’s `<=>` method, returning `true` if it returns `0`. - # Also returns `true` if this and *other* are the same object. - def ==(other : T) - if self.is_a?(Reference) - # Need to do two different comparisons because the compiler doesn't yet - # restrict something like `other.is_a?(Reference) || other.is_a?(Nil)`. - # See #2461 - return true if other.is_a?(Reference) && self.same?(other) - return true if other.is_a?(Nil) && self.same?(other) - end - - (self <=> other) == 0 - end - - # Compares this object to *other* based on the receiver’s `<=>` method, returning `true` if it returns `1`. - def >(other : T) - (self <=> other) > 0 - end - - # Compares this object to *other* based on the receiver’s `<=>` method, returning `true` if it returns `1` or `0`. - def >=(other : T) - (self <=> other) >= 0 - end - - # Comparison operator. Returns `0` if the two objects are equal, - # a negative number if this object is considered less than *other*, - # or a positive number otherwise. - # - # Subclasses define this method to provide class-specific ordering. - # - # ``` - # # Sort in a descending way - # [4, 7, 2].sort { |x, y| y <=> x } # => [7, 4, 2] - # ``` - abstract def <=>(other : T) -end diff --git a/src/core/gc.cr b/src/core/gc.cr deleted file mode 100644 index e1abbad..0000000 --- a/src/core/gc.cr +++ /dev/null @@ -1,13 +0,0 @@ -module GC - def self.malloc(size : Int) - __crystal_malloc(size.to_u32) - end - - def self.malloc_atomic(size : Int) - __crystal_malloc_atomic(size.to_u32) - end - - def self.realloc(pointer : Void*, size : Int) - __crystal_realloc(pointer, size.to_u32) - end -end diff --git a/src/core/gc/boehm.cr b/src/core/gc/boehm.cr deleted file mode 100644 index ff21ded..0000000 --- a/src/core/gc/boehm.cr +++ /dev/null @@ -1,152 +0,0 @@ -# @[Link("pthread")] -# {% if flag?(:freebsd) %} -# @[Link("gc-threaded")] -# {% else %} - # @[Link("gc")] -# {% end %} - -lib LibGC - alias Int = LibCR::Int - alias SizeT = LibCR::SizeT - alias Word = LibCR::ULong - - fun init = GC_init - fun malloc = GC_malloc(size : SizeT) : Void* - fun malloc_atomic = GC_malloc_atomic(size : SizeT) : Void* - fun realloc = GC_realloc(ptr : Void*, size : SizeT) : Void* - fun free = GC_free(ptr : Void*) - fun collect_a_little = GC_collect_a_little : Int - fun collect = GC_gcollect - fun add_roots = GC_add_roots(low : Void*, high : Void*) - fun enable = GC_enable - fun disable = GC_disable - fun set_handle_fork = GC_set_handle_fork(value : Int) - - fun base = GC_base(displaced_pointer : Void*) : Void* - fun is_heap_ptr = GC_is_heap_ptr(pointer : Void*) : Int - fun general_register_disappearing_link = GC_general_register_disappearing_link(link : Void**, obj : Void*) : Int - - type Finalizer = Void*, Void* -> - fun register_finalizer = GC_register_finalizer(obj : Void*, fn : Finalizer, cd : Void*, ofn : Finalizer*, ocd : Void**) - fun register_finalizer_ignore_self = GC_register_finalizer_ignore_self(obj : Void*, fn : Finalizer, cd : Void*, ofn : Finalizer*, ocd : Void**) - fun invoke_finalizers = GC_invoke_finalizers : Int - - fun get_heap_usage_safe = GC_get_heap_usage_safe(heap_size : Word*, free_bytes : Word*, unmapped_bytes : Word*, bytes_since_gc : Word*, total_bytes : Word*) - fun set_max_heap_size = GC_set_max_heap_size(Word) - - fun get_start_callback = GC_get_start_callback : Void* - fun set_start_callback = GC_set_start_callback(callback : ->) - - fun set_push_other_roots = GC_set_push_other_roots(proc : ->) - fun get_push_other_roots = GC_get_push_other_roots : -> - - fun push_all_eager = GC_push_all_eager(bottom : Void*, top : Void*) - - $stackbottom = GC_stackbottom : Void* - - fun set_on_collection_event = GC_set_on_collection_event(cb : ->) - - $gc_no = GC_gc_no : LibCR::ULong - $bytes_found = GC_bytes_found : LibCR::Long - # GC_on_collection_event isn't exported. Can't collect totals without it. - # bytes_allocd, heap_size, unmapped_bytes are macros - - fun size = GC_size(addr : Void*) : LibCR::SizeT - - # Boehm GC requires to use GC_pthread_create and GC_pthread_join instead of pthread_create and pthread_join - fun pthread_create = GC_pthread_create(thread : LibCR::PthreadT*, attr : Void*, start : Void* ->, arg : Void*) : LibCR::Int - fun pthread_join = GC_pthread_join(thread : LibCR::PthreadT, value : Void**) : LibCR::Int - fun pthread_detach = GC_pthread_detach(thread : LibCR::PthreadT) : LibCR::Int -end - -# :nodoc: -fun __crystal_malloc(size : UInt32) : Void* - LibGC.malloc(size) -end - -# :nodoc: -fun __crystal_malloc_atomic(size : UInt32) : Void* - LibGC.malloc_atomic(size) -end - -# :nodoc: -fun __crystal_realloc(ptr : Void*, size : UInt32) : Void* - LibGC.realloc(ptr, size) -end - -module GC - def self.init - LibGC.set_handle_fork(1) - LibGC.init - end - - def self.collect - LibGC.collect - end - - def self.enable - LibGC.enable - end - - def self.disable - LibGC.disable - end - - def self.free(pointer : Void*) - LibGC.free(pointer) - end - - def self.add_finalizer(object : Reference) - add_finalizer_impl(object) - end - - def self.add_finalizer(object) - # Nothing - end - - private def self.add_finalizer_impl(object : T) forall T - LibGC.register_finalizer_ignore_self(object.as(Void*), - ->(obj, data) { obj.as(T).finalize }, - nil, nil, nil) - nil - end - - def self.add_root(object : Reference) - roots = @@roots ||= [] of Pointer(Void) - roots << Pointer(Void).new(object.object_id) - end - - def self.register_disappearing_link(pointer : Void**) - base = LibGC.base(pointer.value) - LibGC.general_register_disappearing_link(pointer, base) - end - - def self.is_heap_ptr(pointer : Void*) - LibGC.is_heap_ptr(pointer) != 0 - end - - record Stats, - # collections : LibCR::ULong, - # bytes_found : LibCR::Long, - heap_size : LibCR::ULong, - free_bytes : LibCR::ULong, - unmapped_bytes : LibCR::ULong, - bytes_since_gc : LibCR::ULong, - total_bytes : LibCR::ULong - - def self.stats - LibGC.get_heap_usage_safe(out heap_size, out free_bytes, out unmapped_bytes, out bytes_since_gc, out total_bytes) - # collections = LibGC.gc_no - 1 - # bytes_found = LibGC.bytes_found - - Stats.new( - # collections: collections, - # bytes_found: bytes_found, - heap_size: heap_size, - free_bytes: free_bytes, - unmapped_bytes: unmapped_bytes, - bytes_since_gc: bytes_since_gc, - total_bytes: total_bytes - ) - end -end diff --git a/src/core/idt.c b/src/core/idt.c new file mode 100644 index 0000000..04d0a12 --- /dev/null +++ b/src/core/idt.c @@ -0,0 +1,31 @@ +#include + +idt_gate_t idt[IDT_ENTRIES]; +idt_register_t idt_reg; + +void set_idt_gate(uint16_t n, uint64_t handler) +{ + idt[n].selector = 0x08; + idt[n].ptr_low = (uint16_t) handler; + idt[n].ptr_mid = (uint16_t) (handler >> 16); + idt[n].ptr_high = (uint32_t) (handler >> 32); + + idt[n].opts.stack_OK = 0; // do not switch stack + idt[n].opts.present = 1; // are we valid + idt[n].opts.DPL = 3; // priv to call int handler + idt[n].opts.gate_type = 0x01; // 1 = interrupt, 2 = trap + idt[n].opts.ONES = 0x07; + idt[n].opts.ZERO = 0; + idt[n].opts.ZEROS = 0; + + idt[n]._1_reserved = 0; + idt[n]._2_reserved = 0; + idt[n]._type = 0; +} + +void set_idt() +{ + idt_reg.base = (uint64_t) &idt; + idt_reg.length = IDT_ENTRIES * sizeof(idt_gate_t) - 1; + __asm__("lidt %0" : : "m"(idt_reg)); +} diff --git a/src/core/indexable.cr b/src/core/indexable.cr deleted file mode 100644 index 088ab3b..0000000 --- a/src/core/indexable.cr +++ /dev/null @@ -1,544 +0,0 @@ -# A container that allows accessing elements via a numeric index. -# -# Indexing starts at `0`. A negative index is assumed to be -# relative to the end of the container: `-1` indicates the last element, -# `-2` is the next to last element, and so on. -# -# Types including this module are typically `Array`-like types. -module Indexable(T) - include Iterable(T) - # TODO: Implement this - # include Enumerable(T) - - # Returns the number of elements in this container. - abstract def size - - # Returns the element at the given *index*, without doing any bounds check. - # - # `Indexable` makes sure to invoke this method with *index* in `0...size`, - # so converting negative indices to positive ones is not needed here. - # - # Clients never invoke this method directly. Instead, they access - # elements with `#[](index)` and `#[]?(index)`. - # - # This method should only be directly invoked if you are absolutely - # sure the index is in bounds, to avoid a bounds check for a small boost - # of performance. - abstract def unsafe_at(index : Int) - - # Returns the element at the given *index*, if in bounds, - # otherwise executes the given block and returns its value. - # - # ``` - # a = [:foo, :bar] - # a.at(0) { :baz } # => :foo - # a.at(2) { :baz } # => :baz - # ``` - def at(index : Int) - index = check_index_out_of_bounds(index) do - return yield - end - unsafe_at(index) - end - - # Returns the element at the given *index*, if in bounds, - # otherwise raises `IndexError`. - # - # ``` - # a = [:foo, :bar] - # a.at(0) # => :foo - # a.at(2) # raises IndexError - # ``` - # TODO: Implement this - # @[AlwaysInline] - # def at(index : Int) - # at(index) { raise IndexError.new } - # end - - # Returns the element at the given *index*. - # - # Negative indices can be used to start counting from the end of the array. - # Raises `IndexError` if trying to access an element outside the array's range. - # - # ``` - # ary = ['a', 'b', 'c'] - # ary[0] # => 'a' - # ary[2] # => 'c' - # ary[-1] # => 'c' - # ary[-2] # => 'b' - # - # ary[3] # raises IndexError - # ary[-4] # raises IndexError - # ``` - # TODO: Implement this - # @[AlwaysInline] - # def [](index : Int) - # at(index) - # end - - # Returns the element at the given *index*. - # - # Negative indices can be used to start counting from the end of the array. - # Returns `nil` if trying to access an element outside the array's range. - # - # ``` - # ary = ['a', 'b', 'c'] - # ary[0]? # => 'a' - # ary[2]? # => 'c' - # ary[-1]? # => 'c' - # ary[-2]? # => 'b' - # - # ary[3]? # nil - # ary[-4]? # nil - # ``` - # TODO: Implement this - # @[AlwaysInline] - # def []?(index : Int) - # at(index) { nil } - # end - - # By using binary search, returns the first element - # for which the passed block returns `true`. - # - # If the block returns `false`, the finding element exists - # behind. If the block returns `true`, the finding element - # is itself or exists infront. - # - # Binary search needs sorted array, so `self` has to be sorted. - # - # Returns `nil` if the block didn't return `true` for any element. - # - # ``` - # [2, 5, 7, 10].bsearch { |x| x >= 4 } # => 5 - # [2, 5, 7, 10].bsearch { |x| x > 10 } # => nil - # ``` - def bsearch - bsearch_index { |value| yield value }.try { |index| unsafe_at(index) } - end - - # By using binary search, returns the index of the first element - # for which the passed block returns `true`. - # - # If the block returns `false`, the finding element exists - # behind. If the block returns `true`, the finding element - # is itself or exists infront. - # - # Binary search needs sorted array, so `self` has to be sorted. - # - # Returns `nil` if the block didn't return `true` for any element. - # - # ``` - # [2, 5, 7, 10].bsearch_index { |x, i| x >= 4 } # => 1 - # [2, 5, 7, 10].bsearch_index { |x, i| x > 10 } # => nil - # ``` - def bsearch_index - (0...size).bsearch { |index| yield unsafe_at(index), index } - end - - # Calls the given block once for each element in `self`, passing that - # element as a parameter. - # - # ``` - # a = ["a", "b", "c"] - # a.each { |x| print x, " -- " } - # ``` - # - # produces: - # - # ```text - # a -- b -- c -- - # ``` - def each - each_index do |i| - yield unsafe_at(i) - end - end - - # Returns an `Iterator` for the elements of `self`. - # - # ``` - # a = ["a", "b", "c"] - # iter = a.each - # iter.next # => "a" - # iter.next # => "b" - # ``` - # - # The returned iterator keeps a reference to `self`: if the array - # changes, the returned values of the iterator change as well. - # TODO: Implement this - # def each - # ItemIterator(self, T).new(self) - # end - - # Calls the given block once for each index in `self`, passing that - # index as a parameter. - # - # ``` - # a = ["a", "b", "c"] - # a.each_index { |x| print x, " -- " } - # ``` - # - # produces: - # - # ```text - # 0 -- 1 -- 2 -- - # ``` - def each_index : Nil - i = 0 - while i < size - yield i - i += 1 - end - end - - # Returns an `Iterator` for each index in `self`. - # - # ``` - # a = ["a", "b", "c"] - # iter = a.each_index - # iter.next # => 0 - # iter.next # => 1 - # ``` - # - # The returned iterator keeps a reference to `self`. If the array - # changes, the returned values of the iterator will change as well. - def each_index - IndexIterator.new(self) - end - - # Returns `true` if `self` is empty, `false` otherwise. - # - # ``` - # ([] of Int32).empty? # => true - # ([1]).empty? # => false - # ``` - def empty? - size == 0 - end - - # Optimized version of `equals?` used when `other` is also an `Indexable`. - def equals?(other : Indexable) - return false if size != other.size - each_with_index do |item, i| - return false unless yield(item, other.unsafe_at(i)) - end - true - end - - # Determines if `self` equals *other* according to a comparison - # done by the given block. - # - # If `self`'s size is the same as *other*'s size, this method yields - # elements from `self` and *other* in tandem: if the block returns true - # for all of them, this method returns `true`. Otherwise it returns `false`. - # - # ``` - # a = [1, 2, 3] - # b = ["a", "ab", "abc"] - # a.equals?(b) { |x, y| x == y.size } # => true - # a.equals?(b) { |x, y| x == y } # => false - # ``` - def equals?(other) - return false if size != other.size - each_with_index do |item, i| - return false unless yield(item, other[i]) - end - true - end - - # Returns the first element of `self` if it's not empty, or raises `IndexError`. - # - # ``` - # ([1, 2, 3]).first # => 1 - # ([] of Int32).first # raises IndexError - # ``` - # TODO: Implement this! - # def first - # first { raise IndexError.new } - # end - - # Returns the first element of `self` if it's not empty, or the given block's value. - # - # ``` - # ([1, 2, 3]).first { 4 } # => 1 - # ([] of Int32).first { 4 } # => 4 - # ``` - # TODO: Implement this! - # def first - # size == 0 ? yield : unsafe_at(0) - # end - - # Returns the first element of `self` if it's not empty, or `nil`. - # - # ``` - # ([1, 2, 3]).first? # => 1 - # ([] of Int32).first? # => nil - # ``` - # TODO: Implement this! - # def first? - # first { nil } - # end - - # Returns a hash code based on `self`'s size and elements. - # - # See also: `Object#hash`. - def hash - reduce(31 * size) do |memo, elem| - 31 * memo + elem.hash - end - end - - # Returns the index of the first appearance of *value* in `self` - # starting from the given *offset*, or `nil` if the value is not in `self`. - # - # ``` - # [1, 2, 3, 1, 2, 3].index(2, offset: 2) # => 4 - # ``` - def index(object, offset : Int = 0) - index(offset) { |e| e == object } - end - - # Returns the index of the first object in `self` for which the block - # returns `true`, starting from the given *offset*, or `nil` if no match - # is found. - # - # ``` - # [1, 2, 3, 1, 2, 3].index(offset: 2) { |x| x < 2 } # => 3 - # ``` - def index(offset : Int = 0) - offset += size if offset < 0 - return nil if offset < 0 - - offset.upto(size - 1) do |i| - if yield unsafe_at(i) - return i - end - end - nil - end - - # Returns the last element of `self` if it's not empty, or raises `IndexError`. - # - # ``` - # ([1, 2, 3]).last # => 3 - # ([] of Int32).last # raises IndexError - # ``` - def last - last { raise IndexError.new } - end - - # Returns the last element of `self` if it's not empty, or the given block's value. - # - # ``` - # ([1, 2, 3]).last { 4 } # => 3 - # ([] of Int32).last { 4 } # => 4 - # ``` - def last - size == 0 ? yield : unsafe_at(size - 1) - end - - # Returns the last element of `self` if it's not empty, or `nil`. - # - # ``` - # ([1, 2, 3]).last? # => 3 - # ([] of Int32).last? # => nil - # ``` - def last? - last { nil } - end - - # Same as `#each`, but works in reverse. - def reverse_each(&block) : Nil - (size - 1).downto(0) do |i| - yield unsafe_at(i) - end - end - - # Returns an `Iterator` over the elements of `self` in reverse order. - # TODO: Implement this - # def reverse_each - # ReverseItemIterator(self, T).new(self) - # end - - # Returns the index of the last appearance of *value* in `self`, or - # `nil` if the value is not in `self`. - # - # If *offset* is given, it defines the position to _end_ the search - # (elements beyond this point are ignored). - # - # ``` - # [1, 2, 3, 2, 3].rindex(2) # => 3 - # [1, 2, 3, 2, 3].rindex(2, offset: 2) # => 1 - # ``` - def rindex(value, offset = size - 1) - rindex(offset) { |elem| elem == value } - end - - # Returns the index of the first object in `self` for which the block - # returns `true`, starting from the last object, or `nil` if no match - # is found. - # - # If *offset* is given, the search starts from that index towards the - # first elements in `self`. - # - # ``` - # [1, 2, 3, 2, 3].rindex { |x| x < 3 } # => 3 - # [1, 2, 3, 2, 3].rindex(offset: 2) { |x| x < 3 } # => 1 - # ``` - def rindex(offset = size - 1) - offset += size if offset < 0 - return nil if offset >= size - - offset.downto(0) do |i| - if yield unsafe_at(i) - return i - end - end - nil - end - - # Returns a random element from `self`, using the given *random* number generator. - # Raises `IndexError` if `self` is empty. - # - # ``` - # a = [1, 2, 3] - # a.sample # => 2 - # a.sample # => 1 - # a.sample(Random.new(1)) # => 3 - # ``` - # TODO: Implement this - # def sample(random = Random::DEFAULT) - # raise IndexError.new if size == 0 - # unsafe_at(random.rand(size)) - # end - - # Returns a `Tuple` populated with the elements at the given indexes. - # Raises `IndexError` if any index is invalid. - # - # ``` - # ["a", "b", "c", "d"].values_at(0, 2) # => {"a", "c"} - # ``` - # TODO: Implement this - # def values_at(*indexes : Int) - # indexes.map { |index| self[index] } - # end - - def zip(other : Indexable) - each_with_index do |elem, i| - yield elem, other[i] - end - end - # TODO: Implement this - # def zip(other : Indexable(U)) forall U - # pairs = Array({T, U}).new(size) - # zip(other) { |x, y| pairs << {x, y} } - # pairs - # end - # TODO: Implement this - # def zip?(other : Indexable) - # each_with_index do |elem, i| - # yield elem, other[i]? - # end - # end - # TODO: Implement this - # def zip?(other : Indexable(U)) forall U - # pairs = Array({T, U?}).new(size) - # zip?(other) { |x, y| pairs << {x, y} } - # pairs - # end - # TODO: Implement this - # private def check_index_out_of_bounds(index) - # check_index_out_of_bounds(index) { raise IndexError.new } - # end - - private def check_index_out_of_bounds(index) - index += size if index < 0 - if 0 <= index < size - index - else - yield - end - end - - # :nodoc: - # TODO: Implement this - # def self.range_to_index_and_count(range, collection_size) - # start_index = range.begin - # start_index += collection_size if start_index < 0 - # raise IndexError.new if start_index < 0 - - # end_index = range.end - # end_index += collection_size if end_index < 0 - # end_index -= 1 if range.excludes_end? - # count = end_index - start_index + 1 - # count = 0 if count < 0 - - # {start_index, count} - # end - # TODO: Implement this - # private class ItemIterator(A, T) - # include Iterator(T) - - # def initialize(@array : A, @index = 0) - # end - - # def next - # if @index >= @array.size - # stop - # else - # value = @array[@index] - # @index += 1 - # value - # end - # end - - # def rewind - # @index = 0 - # self - # end - # end - # TODO: Implement this - # private class ReverseItemIterator(A, T) - # include Iterator(T) - - # def initialize(@array : A, @index : Int32 = array.size - 1) - # end - - # def next - # if @index < 0 - # stop - # else - # value = @array[@index] - # @index -= 1 - # value - # end - # end - - # def rewind - # @index = @array.size - 1 - # self - # end - # end - # TODO: Implement this - # private class IndexIterator(A) - # include Iterator(Int32) - - # def initialize(@array : A, @index = 0) - # end - - # def next - # if @index >= @array.size - # stop - # else - # value = @index - # @index += 1 - # value - # end - # end - - # def rewind - # @index = 0 - # self - # end - # end -end diff --git a/src/core/int.cr b/src/core/int.cr deleted file mode 100644 index c1cc58c..0000000 --- a/src/core/int.cr +++ /dev/null @@ -1,740 +0,0 @@ -# Int is the base type of all integer types. -# -# There are four signed integer types: `Int8`, `Int16`, `Int32` and `Int64`, -# being able to represent numbers of 8, 16, 32 and 64 bits respectively. -# There are four unsigned integer types: `UInt8`, `UInt16`, `UInt32` and `UInt64`. -# -# An integer literal is an optional `+` or `-` sign, followed by -# a sequence of digits and underscores, optionally followed by a suffix. -# If no suffix is present, the literal's type is the lowest between `Int32`, `Int64` and `UInt64` -# in which the number fits: -# -# ``` -# 1 # Int32 -# -# 1_i8 # Int8 -# 1_i16 # Int16 -# 1_i32 # Int32 -# 1_i64 # Int64 -# -# 1_u8 # UInt8 -# 1_u16 # UInt16 -# 1_u32 # UInt32 -# 1_u64 # UInt64 -# -# +10 # Int32 -# -20 # Int32 -# -# 2147483648 # Int64 -# 9223372036854775808 # UInt64 -# ``` -# -# The underscore `_` before the suffix is optional. -# -# Underscores can be used to make some numbers more readable: -# -# ``` -# 1_000_000 # better than 1000000 -# ``` -# -# Binary numbers start with `0b`: -# -# ``` -# 0b1101 # == 13 -# ``` -# -# Octal numbers start with `0o`: -# -# ``` -# 0o123 # == 83 -# ``` -# -# Hexadecimal numbers start with `0x`: -# -# ``` -# 0xFE012D # == 16646445 -# 0xfe012d # == 16646445 -# ``` -struct Int - alias Signed = Int8 | Int16 | Int32 | Int64 - alias Unsigned = UInt8 | UInt16 | UInt32 | UInt64 - alias Primitive = Signed | Unsigned - - # Returns a `Char` that has the unicode codepoint of `self`. - # - # Raises `ArgumentError` if this integer's value doesn't fit a char's range (`0..0x10ffff`). - # - # ``` - # 97.chr # => 'a' - # ``` - # def chr - # unless 0 <= self <= Char::MAX_CODEPOINT - # raise ArgumentError.new("#{self} out of char range") - # end - # unsafe_chr - # end - - def ~ - self ^ -1 - end - - # Divides `self` by *other* using floored division. - # - # In floored division, given two integers x and y: - # * q = x / y is rounded toward negative infinity - # * r = x % y has the sign of the second argument - # * x == q*y + r - # - # For example: - # - # ```text - # x y x / y x % y - # 5 3 1 2 - # -5 3 -2 1 - # 5 -3 -2 -1 - # -5 -3 1 -2 - # ``` - # - # Raises if *other* is zero, or if *other* is -1 and - # `self` is signed and is the minimum value for that - # integer type. - def /(other : Int) - check_div_argument other - - div = unsafe_div other - mod = unsafe_mod other - div -= 1 if other > 0 ? mod < 0 : mod > 0 - div - end - - # Divides `self` by *other* using truncated division. - # - # In truncated division, given two integers x and y: - # * `q = x.tdiv(y)` is rounded toward zero - # * `r = x.remainder(y)` has the sign of the first argument - # * `x == q*y + r` - # - # For example: - # - # ```text - # x y x / y x % y - # 5 3 1 2 - # -5 3 -1 -2 - # 5 -3 -1 2 - # -5 -3 1 -2 - # ``` - # - # Raises if *other* is `0`, or if *other* is `-1` and - # `self` is signed and is the minimum value for that - # integer type. - def tdiv(other : Int) - check_div_argument other - - unsafe_div other - end - - private def check_div_argument(other) - if other == 0 - self - # TODO: Implement this! - # raise DivisionByZero.new - end - - {% begin %} - if self < 0 && self == {{@type}}::MIN && other == -1 - self - # TODO: Implement this! - # raise ArgumentError.new "Overflow: {{@type}}::MIN / -1" - end - {% end %} - end - - # def fdiv(other) - # to_f / other - # end - - # Returns `self` modulo *other*. - # - # This uses floored division. - # - # See `Int#/` for more details. - def %(other : Int) - if other == 0 - self - # TODO: Implement this! - # raise DivisionByZero.new - elsif (self ^ other) >= 0 - self.unsafe_mod(other) - else - me = self.unsafe_mod(other) - me == 0 ? me : me + other - end - end - - # Returns `self` remainder *other*. - # - # This uses truncated division. - # - # See `Int#div` for more details. - def remainder(other : Int) - if other == 0 - self - # TODO: Implement this! - raise DivisionByZero.new - else - unsafe_mod other - end - end - - # Returns the result of shifting this number's bits *count* positions to the right. - # Also known as arithmetic right shift. - # - # * If *count* is greater than the number of bits of this integer, returns 0 - # * If *count* is negative, a left shift is performed - # - # ``` - # 8000 >> 1 # => 4000 - # 8000 >> 2 # => 2000 - # 8000 >> 32 # => 0 - # 8000 >> -1 # => 16000 - # - # -8000 >> 1 # => -4000 - # ``` - def >>(count : Int) - if count < 0 - self << count.abs - elsif count < sizeof(self) * 8 - self.unsafe_shr(count) - else - self.class.zero - end - end - - # Returns the result of shifting this number's bits *count* positions to the left. - # - # * If *count* is greater than the number of bits of this integer, returns 0 - # * If *count* is negative, a right shift is performed - # - # ``` - # 8000 << 1 # => 16000 - # 8000 << 2 # => 32000 - # 8000 << 32 # => 0 - # 8000 << -1 # => 4000 - # ``` - def <<(count : Int) - if count < 0 - self >> count.abs - elsif count < sizeof(self) * 8 - self.unsafe_shl(count) - else - self.class.zero - end - end - - def abs - self >= 0 ? self : -self - end - - def ceil - self - end - - def floor - self - end - - def round - self - end - - def trunc - self - end - - # Returns the value of raising `self` to the power of *exponent*. - # - # Raises `ArgumentError` if *exponent* is negative: if this is needed, - # either use a float base or a float exponent. - # - # ``` - # 2 ** 3 # => 8 - # 2 ** 0 # => 1 - # 2 ** -1 # ArgumentError - # ``` - # def **(exponent : Int) : self - # if exponent < 0 - # raise ArgumentError.new "Cannot raise an integer to a negative integer power, use floats for that" - # end - - # result = self.class.new(1) - # k = self - # while exponent > 0 - # result *= k if exponent & 0b1 != 0 - # k *= k - # exponent = exponent.unsafe_shr(1) - # end - # result - # end - - # Returns the value of raising `self` to the power of *exponent*. - # - # ``` - # 2 ** 3.0 # => 8.0 - # 2 ** 0.0 # => 1.0 - # 2 ** -1.0 # => 0.5 - # ``` - # def **(exponent : Float) : Float64 - # to_f ** exponent - # end - - def ===(char : Char) - self === char.ord - end - - # Returns this number's *bit*th bit, starting with the least-significant. - # - # ``` - # 11.bit(0) # => 1 - # 11.bit(1) # => 1 - # 11.bit(2) # => 0 - # 11.bit(3) # => 1 - # 11.bit(4) # => 0 - # ``` - def bit(bit) - self >> bit & 1 - end - - def gcd(other : Int) - self == 0 ? other.abs : (other % self).gcd(self) - end - - def lcm(other : Int) - (self * other).abs / gcd(other) - end - - def divisible_by?(num) - self % num == 0 - end - - def even? - divisible_by? 2 - end - - def odd? - !even? - end - - def hash - self - end - - def succ - self + 1 - end - - def pred - self - 1 - end - - def times(&block : self ->) : Nil - i = self ^ self - while i < self - yield i - i += 1 - end - end - - # def times - # TimesIterator(typeof(self)).new(self) - # end - - def upto(to, &block : self ->) : Nil - x = self - while x <= to - yield x - x += 1 - end - end - - # def upto(to) - # UptoIterator(typeof(self), typeof(to)).new(self, to) - # end - - def downto(to, &block : self ->) : Nil - x = self - while x >= to - yield x - x -= 1 - end - end - - # def downto(to) - # DowntoIterator(typeof(self), typeof(to)).new(self, to) - # end - - def to(to, &block : self ->) : Nil - if self < to - upto(to) { |i| yield i } - elsif self > to - downto(to) { |i| yield i } - else - yield self - end - end - - def to(to) - self <= to ? upto(to) : downto(to) - end - - def modulo(other) - self % other - end - - private DIGITS_DOWNCASE = "0123456789abcdefghijklmnopqrstuvwxyz" - private DIGITS_UPCASE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - private DIGITS_BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - - # def to_s - # to_s(10) - # end - - # def to_s(io : IO) - # to_s(10, io) - # end - - # def to_s(base : Int, upcase : Bool = false) - # raise ArgumentError.new("Invalid base #{base}") unless 2 <= base <= 36 || base == 62 - # raise ArgumentError.new("upcase must be false for base 62") if upcase && base == 62 - - # case self - # when 0 - # return "0" - # when 1 - # return "1" - # end - - # internal_to_s(base, upcase) do |ptr, count| - # String.new(ptr, count, count) - # end - # end - - # def to_s(base : Int, io : IO, upcase : Bool = false) - # raise ArgumentError.new("Invalid base #{base}") unless 2 <= base <= 36 || base == 62 - # raise ArgumentError.new("upcase must be false for base 62") if upcase && base == 62 - - # case self - # when 0 - # io << '0' - # return - # when 1 - # io << '1' - # return - # end - - # internal_to_s(base, upcase) do |ptr, count| - # io.write_utf8 Slice.new(ptr, count) - # end - # end - - # private def internal_to_s(base, upcase = false) - # chars = uninitialized UInt8[65] - # ptr_end = chars.to_unsafe + 64 - # ptr = ptr_end - # num = self - - # neg = num < 0 - - # digits = (base == 62 ? DIGITS_BASE62 : (upcase ? DIGITS_UPCASE : DIGITS_DOWNCASE)).to_unsafe - - # while num != 0 - # ptr -= 1 - # ptr.value = digits[num.remainder(base).abs] - # num = num.tdiv(base) - # end - - # if neg - # ptr -= 1 - # ptr.value = '-'.ord.to_u8 - # end - - # count = (ptr_end - ptr).to_i32 - # yield ptr, count - # end - - # Writes this integer to the given *io* in the given *format*. - # - # See also: `IO#write_bytes`. - # def to_io(io : IO, format : IO::ByteFormat) - # format.encode(self, io) - # end - - # Reads an integer from the given *io* in the given *format*. - # - # See also: `IO#read_bytes`. - # def self.from_io(io : IO, format : IO::ByteFormat) : self - # format.decode(self, io) - # end - - # Counts `1`-bits in the binary representation of this integer. - # - # ``` - # 5.popcount # => 2 - # -15.popcount # => 29 - # ``` - # abstract def popcount - - # private class TimesIterator(T) - # include Iterator(T) - - # @n : T - # @index : Int32 - - # def initialize(@n : T, @index = 0) - # end - - # def next - # if @index < @n - # value = @index - # @index += 1 - # value - # else - # stop - # end - # end - - # def rewind - # @index = 0 - # self - # end - # end - - # private class UptoIterator(T, N) - # include Iterator(T) - - # @from : T - # @to : N - # @current : T - - # def initialize(@from : T, @to : N) - # @current = @from - # end - - # def next - # if @current > @to - # stop - # else - # value = @current - # @current += 1 - # value - # end - # end - - # def rewind - # @current = @from - # self - # end - # end - - # private class DowntoIterator(T, N) - # include Iterator(T) - - # @from : T - # @to : N - # @current : T - - # def initialize(@from : T, @to : N) - # @current = @from - # end - - # def next - # if @current < @to - # stop - # else - # value = @current - # @current -= 1 - # value - # end - # end - - # def rewind - # @current = @from - # self - # end - # end -end - -struct Int8 - MIN = -128_i8 - MAX = 127_i8 - - # Returns an `Int8` by invoking `to_i8` on *value*. - def self.new(value) - value.to_i8 - end - - def - - 0_i8 - self - end - - # def popcount - # Intrinsics.popcount8(self) - # end - - def clone - self - end -end - -struct Int16 - MIN = -32768_i16 - MAX = 32767_i16 - - # Returns an `Int16` by invoking `to_i16` on *value*. - def self.new(value) - value.to_i16 - end - - def - - 0_i16 - self - end - - # def popcount - # Intrinsics.popcount16(self) - # end - - def clone - self - end -end - -struct Int32 - MIN = -2147483648_i32 - MAX = 2147483647_i32 - - # Returns an `Int32` by invoking `to_i32` on *value*. - def self.new(value) - value.to_i32 - end - - def - - 0 - self - end - - # def popcount - # Intrinsics.popcount32(self) - # end - - def clone - self - end -end - -struct Int64 - MIN = -9223372036854775808_i64 - MAX = 9223372036854775807_i64 - - # Returns an `Int64` by invoking `to_i64` on *value*. - def self.new(value) - value.to_i64 - end - - def - - 0_i64 - self - end - - # def popcount - # Intrinsics.popcount64(self) - # end - - def clone - self - end -end - -struct UInt8 - MIN = 0_u8 - MAX = 255_u8 - - # Returns an `UInt8` by invoking `to_u8` on *value*. - def self.new(value) - value.to_u8 - end - - def abs - self - end - - # def popcount - # Intrinsics.popcount8(self) - # end - - def clone - self - end -end - -struct UInt16 - MIN = 0_u16 - MAX = 65535_u16 - - # Returns an `UInt16` by invoking `to_u16` on *value*. - def self.new(value) - value.to_u16 - end - - def abs - self - end - - # def popcount - # Intrinsics.popcount16(self) - # end - - def clone - self - end -end - -struct UInt32 - MIN = 0_u32 - MAX = 4294967295_u32 - - # Returns an `UInt32` by invoking `to_u32` on *value*. - def self.new(value) - value.to_u32 - end - - def abs - self - end - - # def popcount - # Intrinsics.popcount32(self) - # end - - def clone - self - end -end - -struct UInt64 - MIN = 0_u64 - MAX = 18446744073709551615_u64 - - # Returns an `UInt64` by invoking `to_u64` on *value*. - def self.new(value) - value.to_u64 - end - - def abs - self - end - - # def popcount - # Intrinsics.popcount64(self) - # end - - def clone - self - end -end diff --git a/src/core/isr.c b/src/core/isr.c new file mode 100644 index 0000000..0366670 --- /dev/null +++ b/src/core/isr.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include +#include +#include +#include + +#define NB_REGISTERS_PUSHED_BEFORE_CALL 15 + +stack_t* get_stack(uint64_t id, uint64_t stack); +void breakpoint_handler(stack_t *stack); + +isr_t interrupt_handlers[256]; +const char* exception_messages[] = { + "Division By Zero", + "Debug", + "Non Maskable Interrupt", + "Breakpoint", + "Into Detected Overflow", + "Out of Bounds", + "Invalid Opcode", + "No Coprocessor", + + "Double Fault", + "Coprocessor Segment Overrun", + "Bad TSS", + "Segment Not Present", + "Stack Fault", + "General Protection Fault", + "Page Fault", + "Unknown Interrupt", + + "Coprocessor Fault", + "Alignment Check", + "Machine Check", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved" +}; + +void isr_init() +{ + // start initialization + port_byte_out(PIC1, 0x11); + port_byte_out(PIC2, 0x11); + + // set IRQ base numbers for each PIC + port_byte_out(PIC1_DATA, IRQ_BASE); + port_byte_out(PIC2_DATA, IRQ_BASE + 8); + + // use IRQ number 2 to relay IRQs from the slave PIC + port_byte_out(PIC1_DATA, 0x04); + port_byte_out(PIC2_DATA, 0x02); + + // finish initialization + port_byte_out(PIC1_DATA, 0x01); + port_byte_out(PIC2_DATA, 0x01); + + // mask all IRQs + port_byte_out(PIC1_DATA, 0x00); + port_byte_out(PIC2_DATA, 0x00); + + set_idt_gate(0, (uint64_t) isr0); + set_idt_gate(1, (uint64_t) isr1); + set_idt_gate(2, (uint64_t) isr2); + set_idt_gate(3, (uint64_t) isr3); + set_idt_gate(4, (uint64_t) isr4); + set_idt_gate(5, (uint64_t) isr5); + set_idt_gate(6, (uint64_t) isr6); + set_idt_gate(7, (uint64_t) isr7); + set_idt_gate(8, (uint64_t) isr8); + set_idt_gate(9, (uint64_t) isr9); + set_idt_gate(10, (uint64_t) isr10); + set_idt_gate(11, (uint64_t) isr11); + set_idt_gate(12, (uint64_t) isr12); + set_idt_gate(13, (uint64_t) isr13); + set_idt_gate(14, (uint64_t) isr14); + set_idt_gate(15, (uint64_t) isr15); + set_idt_gate(16, (uint64_t) isr16); + set_idt_gate(17, (uint64_t) isr17); + set_idt_gate(18, (uint64_t) isr18); + set_idt_gate(19, (uint64_t) isr19); + set_idt_gate(20, (uint64_t) isr20); + set_idt_gate(21, (uint64_t) isr21); + set_idt_gate(22, (uint64_t) isr22); + set_idt_gate(23, (uint64_t) isr23); + set_idt_gate(24, (uint64_t) isr24); + set_idt_gate(25, (uint64_t) isr25); + set_idt_gate(26, (uint64_t) isr26); + set_idt_gate(27, (uint64_t) isr27); + set_idt_gate(28, (uint64_t) isr28); + set_idt_gate(29, (uint64_t) isr29); + set_idt_gate(30, (uint64_t) isr30); + set_idt_gate(31, (uint64_t) isr31); + + set_idt_gate(IRQ0, (uint64_t) irq0); + set_idt_gate(IRQ1, (uint64_t) irq1); + set_idt_gate(IRQ2, (uint64_t) irq2); + set_idt_gate(IRQ3, (uint64_t) irq3); + set_idt_gate(IRQ4, (uint64_t) irq4); + + // handlers for isr exceptions + register_interrupt_handler(EXCEPTION_BP, breakpoint_handler); + + set_idt(); +} + +void irq_init() +{ + __asm__("sti"); +} + +void irq_disable() +{ + __asm__("cli"); +} + +void isr_handler(uint64_t id, uint64_t stack_addr) +{ + stack_t *stack = get_stack(id, stack_addr); + + if (interrupt_handlers[id] != 0) { + isr_t handler = interrupt_handlers[id]; + handler(get_stack(id, stack_addr)); + + return; + } + + PANIC( + "Received interrupt: %d - %s\n\n" + " instruction_pointer = 0x%X\n" + " code_segment = 0x%X\n" + " cpu_flags = 0x%X\n" + " stack_pointer = 0x%X\n" + " stack_segment = 0x%X", + id, exception_messages[id], + stack->instruction_pointer, + stack->code_segment, + stack->cpu_flags, + stack->stack_pointer, + stack->stack_segment + ); +} + +void irq_handler(uint64_t id, uint64_t stack_addr) +{ + if (id >= 40) { + port_byte_out(PIC2, PIC_EOI); + } + + port_byte_out(PIC1, PIC_EOI); + + if (interrupt_handlers[id] != 0) { + isr_t handler = interrupt_handlers[id]; + handler(get_stack(id, stack_addr)); + } +} + +void register_interrupt_handler(uint64_t id, isr_t handler) +{ + interrupt_handlers[id] = handler; +} + +stack_t* get_stack(uint64_t id, uint64_t stack_addr) +{ + // https://github.com/0xAX/linux-insides/blob/master/interrupts/interrupts-3.md + // + // +------------+ + // +40 | %SS | + // +32 | %RSP | + // +24 | %RFLAGS | + // +16 | %CS | + // +8 | %RIP | + // 0 | ERROR CODE | <-- %RSP + // +------------+ + // + switch (id) { + case 8: + case 10: + case 11: + case 12: + case 13: + case 14: + case 17: + // skip error code, so that we always get the same stack_t + stack_addr += sizeof(uint64_t); + break; + } + + return (stack_t*) (stack_addr + (NB_REGISTERS_PUSHED_BEFORE_CALL * sizeof(uint64_t))); +} + +void breakpoint_handler(stack_t *stack) +{ + printfk( + "Exception: BREAKPOINT\n" + " instruction_pointer = 0x%X\n" + " code_segment = 0x%X\n" + " cpu_flags = 0x%X\n" + " stack_pointer = 0x%X\n" + " stack_segment = 0x%X\n", + stack->instruction_pointer, + stack->code_segment, + stack->cpu_flags, + stack->stack_pointer, + stack->stack_segment + ); +} diff --git a/src/core/iterable.cr b/src/core/iterable.cr deleted file mode 100644 index 765eb0a..0000000 --- a/src/core/iterable.cr +++ /dev/null @@ -1,48 +0,0 @@ -# The `Iterable` mixin provides convenience methods to collection classes -# that provide an `each` method that returns an `Iterator` over the collection. -module Iterable(T) - # Must return an `Iterator` over the elements in this collection. - abstract def each - - # Same as `each.cycle`. - def cycle - each.cycle - end - - # Same as `each.cycle(n)`. - def cycle(n) - each.cycle(n) - end - - # Returns an Iterator that enumerates over the items, chunking them together - # based on the return value of the block. - # - # ``` - # (0..7).chunk(&./(3)).to_a # => [{0, [0, 1, 2]}, {1, [3, 4, 5]}, {2, [6, 7]}] - # ``` - # - # See also: `Iterator#chunks`. - def chunk(reuse = false, &block : T -> U) forall U - each.chunk reuse, &block - end - - # Same as `each.slice(count, reuse)`. - def each_slice(count : Int, reuse = false) - each.slice(count, reuse) - end - - # Same as `each.cons(count)`. - def each_cons(count : Int, reuse = false) - each.cons(count, reuse) - end - - # Same as `each.with_index(offset)`. - def each_with_index(offset = 0) - each.with_index(offset) - end - - # Same as `each.with_object(obj)`. - def each_with_object(obj) - each.with_object(obj) - end -end diff --git a/src/core/lib_cr.cr b/src/core/lib_cr.cr deleted file mode 100644 index d803c6e..0000000 --- a/src/core/lib_cr.cr +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -# This was taken from crystal/src/lib_c.cr - -lib LibCR - alias Char = UInt8 - alias UChar = Char - alias SChar = Int8 - alias Short = Int16 - alias UShort = UInt16 - alias Int = Int32 - alias UInt = UInt32 - - {% if flag?(:x86_64) || flag?(:aarch64) %} - alias Long = Int64 - alias ULong = UInt64 - {% elsif flag?(:i686) || flag?(:arm) %} - alias Long = Int32 - alias ULong = UInt32 - {% end %} - - alias LongLong = Int64 - alias ULongLong = UInt64 - alias Float = Float32 - alias Double = Float64 - - $environ : Char** -end diff --git a/src/core/lib_cr/x86_64-linux-musl/c/bits/io.cr b/src/core/lib_cr/x86_64-linux-musl/c/bits/io.cr deleted file mode 100644 index 45c1d0d..0000000 --- a/src/core/lib_cr/x86_64-linux-musl/c/bits/io.cr +++ /dev/null @@ -1,11 +0,0 @@ -@[AlwaysInline] -def outb(port : UInt16, val : UInt8) - asm("outb $1, $0" :: "{dx}"(port), "{al}"(val) :: "volatile") -end - -@[AlwaysInline] -def inb(port : UInt16) : UInt8 - val = 0_u8 - asm("inb $1, $0" : "={al}"(val) : "{dx}"(port) :: "volatile") - val -end diff --git a/src/core/lib_cr/x86_64-linux-musl/c/ctype.cr b/src/core/lib_cr/x86_64-linux-musl/c/ctype.cr deleted file mode 100644 index b465016..0000000 --- a/src/core/lib_cr/x86_64-linux-musl/c/ctype.cr +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -require "./stddef" - -lib LibCR - fun isalpha(c : Char) : Int - fun isalnum(c : Char) : Int - fun isblank(c : Char) : Int - fun iscntrl(c : Char) : Int - fun isdigit(c : Char) : Int - fun isgraph(c : Char) : Int - fun islower(c : Char) : Int - fun isprint(c : Char) : Int - fun ispunct(c : Char) : Int - fun isspace(c : Char) : Int - fun isupper(c : Char) : Int - fun isxdigit(c : Char) : Int -end diff --git a/src/core/lib_cr/x86_64-linux-musl/c/no_bind/libstring.cr.unused b/src/core/lib_cr/x86_64-linux-musl/c/no_bind/libstring.cr.unused deleted file mode 100644 index 26e12aa..0000000 --- a/src/core/lib_cr/x86_64-linux-musl/c/no_bind/libstring.cr.unused +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -# require "../stddef" -# -# module NoBind -# struct LibString -# def self.strlen(str : LibCR::Char*) : LibCR::SizeT -# str[4].to_u64 # Byte size of str is in it -# end -# -# # Alternative -# # def self.strlen(str : LibCR::Char*) : LibCR::SizeT -# # ret = 0_u64 -# # # str[12] seems to be the starting point of string -# # while str[12 + ret] != 0 -# # ret += 1_u64 -# # end -# # ret -# # end -# -# def self.strcmp(x0 : LibCR::Char*, x1 : LibCR::Char*) : LibCR::Int -# # What a shit this is -# ret = x0 - x1 -# -# case ret -# when .> 0 -# ret = 1_i32 -# when .< 0 -# ret = -1_i32 -# else -# ret = 0_i32 -# end -# -# return ret.as(LibCR::Int) -# end -# end -# end diff --git a/src/core/lib_cr/x86_64-linux-musl/c/stddef.cr b/src/core/lib_cr/x86_64-linux-musl/c/stddef.cr deleted file mode 100644 index ecc4d41..0000000 --- a/src/core/lib_cr/x86_64-linux-musl/c/stddef.cr +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -# This was take from -# crystal/src/lib_c/x86_64-linux-musl/c/stddef.cr - -# Lookup core/lib_cr.cr -require "../../../lib_cr" - -lib LibCR - alias SizeT = ULong -end diff --git a/src/core/lib_cr/x86_64-linux-musl/c/stdint.cr b/src/core/lib_cr/x86_64-linux-musl/c/stdint.cr deleted file mode 100644 index 1fd2bd6..0000000 --- a/src/core/lib_cr/x86_64-linux-musl/c/stdint.cr +++ /dev/null @@ -1,10 +0,0 @@ -lib LibCR - alias Int8T = SChar - alias Int16T = Short - alias Int32T = Int - alias Int64T = Long - alias UInt8T = Char - alias UInt16T = UShort - alias UInt32T = UInt - alias UInt64T = ULong -end diff --git a/src/core/lib_cr/x86_64-linux-musl/c/stdlib.cr b/src/core/lib_cr/x86_64-linux-musl/c/stdlib.cr deleted file mode 100644 index 3b3e119..0000000 --- a/src/core/lib_cr/x86_64-linux-musl/c/stdlib.cr +++ /dev/null @@ -1,24 +0,0 @@ -require "./stddef" -# require "./sys/wait" - -lib LibCR - # struct DivT - # quot : Int - # rem : Int - # end - - # fun atof(x0 : Char*) : Double - # fun div(x0 : Int, x1 : Int) : DivT - fun exit(x0 : Int) : NoReturn - fun free(x0 : Void*) : Void - fun getenv(x0 : Char*) : Char* - fun malloc(x0 : SizeT) : Void* - fun mkstemp(x0 : Char*) : Int - fun putenv(x0 : Char*) : Int - fun realloc(x0 : Void*, x1 : SizeT) : Void* - fun realpath(x0 : Char*, x1 : Char*) : Char* - fun setenv(x0 : Char*, x1 : Char*, x2 : Int) : Int - fun strtof(x0 : Char*, x1 : Char**) : Float - # fun strtod(x0 : Char*, x1 : Char**) : Double - fun unsetenv(x0 : Char*) : Int -end diff --git a/src/core/lib_cr/x86_64-linux-musl/c/string.cr b/src/core/lib_cr/x86_64-linux-musl/c/string.cr deleted file mode 100644 index 0709966..0000000 --- a/src/core/lib_cr/x86_64-linux-musl/c/string.cr +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -require "./stddef" - -lib LibCR - fun memchr(x0 : Void*, c : Int, n : SizeT) : Void* - fun memcmp(x0 : Void*, x1 : Void*, x2 : SizeT) : Int - fun strcmp(x0 : Char*, x1 : Char*) : Int - # fun strerror(x0 : Int) : Char* - fun strlen(x0 : Char*) : SizeT - - # Additional functions - fun memset(dest : Void*, src : Int, len : SizeT) : Void* - fun strchr(x0 : Char*, x1 : Int) : Char* - fun strchrnul(x0 : Char*, x1 : Int) : Char* - fun strncmp(x0 : Char*, x1 : Char*, n : SizeT) : Int - fun strstr(x0 : Char*, x1 : Char*) : Char* -end diff --git a/src/core/lib_cr/x86_64-linux-musl/c/sys/types.cr b/src/core/lib_cr/x86_64-linux-musl/c/sys/types.cr deleted file mode 100644 index 8b9d046..0000000 --- a/src/core/lib_cr/x86_64-linux-musl/c/sys/types.cr +++ /dev/null @@ -1,61 +0,0 @@ -require "../stddef" -require "../stdint" - -lib LibCR - alias BlkcntT = Long - alias BlksizeT = Long - alias ClockT = Long - alias ClockidT = Int - alias DevT = ULong - alias GidT = UInt - alias IdT = UInt - alias InoT = ULong - alias ModeT = UInt - alias NlinkT = ULong - alias OffT = Long - alias PidT = Int - - union PthreadAttrTU - __i : StaticArray(Int, 14) - __vi : StaticArray(Int, 14) - __s : StaticArray(ULong, 7) - end - - struct PthreadAttrT - __u : PthreadAttrTU - end - - union PthreadCondTU - __i : StaticArray(Int, 12) - __vi : StaticArray(Int, 12) - __p : StaticArray(Void*, 6) - end - - struct PthreadCondT - __u : PthreadCondTU - end - - struct PthreadCondattrT - __attr : UInt - end - - union PthreadMutexTU - __i : StaticArray(Int, 10) - __vi : StaticArray(Int, 10) - __p : StaticArray(Void*, 5) - end - - struct PthreadMutexT - __u : PthreadMutexTU - end - - struct PthreadMutexattrT - __attr : UInt - end - - type PthreadT = Void* - alias SSizeT = Long - alias SusecondsT = Long - alias TimeT = Long - alias UidT = UInt -end diff --git a/src/core/macros.cr b/src/core/macros.cr deleted file mode 100644 index 4d8c5a0..0000000 --- a/src/core/macros.cr +++ /dev/null @@ -1,130 +0,0 @@ -# Defines a **`Struct`** with the given name and properties. -# -# The generated struct has a constructor with the given properties -# in the same order as declared. The struct only provides getters, -# not setters, making it immutable by default. -# -# The properties can be type declarations or assignments. -# -# You can pass a block to this macro, that will be inserted inside -# the struct definition. -# -# ``` -# record Point, x : Int32, y : Int32 -# -# Point.new 1, 2 # => # -# ``` -# -# An example with the block version: -# -# ``` -# record Person, first_name : String, last_name : String do -# def full_name -# "#{first_name} #{last_name}" -# end -# end -# -# person = Person.new "John", "Doe" -# person.full_name # => "John Doe" -# ``` -# -# An example with type declarations and default values: -# -# ``` -# record Point, x : Int32 = 0, y : Int32 = 0 -# -# Point.new # => # -# Point.new y: 2 # => # -# ``` -# -# An example with assignments (in this case the compiler must be able to -# infer the types from the default values): -# -# ``` -# record Point, x = 0, y = 0 -# -# Point.new # => # -# Point.new y: 2 # => # -# ``` -macro record(name, *properties) - struct {{name.id}} - {% for property in properties %} - {% if property.is_a?(Assign) %} - getter {{property.target.id}} - {% elsif property.is_a?(TypeDeclaration) %} - getter {{property.var}} : {{property.type}} - {% else %} - getter :{{property.id}} - {% end %} - {% end %} - - def initialize({{ - *properties.map do |field| - "@#{field.id}".id - end - }}) - end - - {{yield}} - - def clone - {{name.id}}.new({{ - *properties.map do |property| - if property.is_a?(Assign) - "@#{property.target.id}.clone".id - elsif property.is_a?(TypeDeclaration) - "@#{property.var.id}.clone".id - else - "@#{property.id}.clone".id - end - end - }}) - end - end -end - -# Prints a series of expressions together with their values. -# Useful for print style debugging. -# -# ``` -# a = 1 -# pp a # => "a # => 1" -# -# pp [1, 2, 3].map(&.to_s) # => "[1, 2, 3].map(&.to_s) # => ["1", "2", "3"]" -# ``` -# macro pp(*exps) -# {% if exps.size == 0 %} -# # Nothing -# {% elsif exps.size == 1 %} -# {% exp = exps.first %} -# %prefix = "#{{{ exp.stringify }}} # => " -# ::print %prefix -# %object = {{exp}} -# PrettyPrint.format(%object, STDOUT, width: 80 - %prefix.size, indent: %prefix.size) -# ::puts -# %object -# {% else %} -# %names = { {{*exps.map(&.stringify)}} } -# %max_size = %names.max_of &.size -# { -# {% for exp, i in exps %} -# begin -# %prefix = "#{%names[{{i}}].ljust(%max_size)} # => " -# ::print %prefix -# %object = {{exp}} -# PrettyPrint.format(%object, STDOUT, width: 80 - %prefix.size, indent: %prefix.size) -# ::puts -# %object -# end, -# {% end %} -# } -# {% end %} -# end - -# macro assert_responds_to(var, method) -# if {{var}}.responds_to?(:{{method}}) -# {{var}} -# else -# raise "Expected {{var}} to respond to :{{method}}, not #{ {{var}} }" -# end -# end diff --git a/src/core/named_tuple.cr b/src/core/named_tuple.cr deleted file mode 100644 index cd7468b..0000000 --- a/src/core/named_tuple.cr +++ /dev/null @@ -1,490 +0,0 @@ -# A named tuple is a fixed-size, immutable, stack-allocated mapping -# of a fixed set of keys to values. -# -# You can think of a `NamedTuple` as an immutable `Hash` whose keys (which -# are of type `Symbol`), and the types for each key, are known at compile time. -# -# A named tuple can be created with a named tuple literal: -# -# ``` -# language = {name: "Crystal", year: 2011} # NamedTuple(name: String, year: Int32) -# -# language[:name] # => "Crystal" -# language[:year] # => 2011 -# language[:other] # compile time error -# ``` -# -# The compiler knows what types are in each key, so when indexing a named tuple -# with a symbol literal the compiler will return the value for that key and -# with the expected type, like in the above snippet. Indexing with a symbol -# literal for which there's no key will give a compile-time error. -# -# Indexing with a symbol that is only known at runtime will return -# a value whose type is the union of all the types in the named tuple, -# and might raise `KeyError`. -struct NamedTuple - # Creates a named tuple that will contain the given arguments. - # - # This method is useful in macros and generic code because with it you can - # creates empty named tuples, something that you can't do with a tuple literal. - # - # ``` - # NamedTuple.new(name: "Crystal", year: 2011) #=> {name: "Crystal", year: 2011} - # NamedTuple.new # => {} - # {} # syntax error - # ``` - def self.new(**options : **T) - options - end - - # Creates a named tuple from the given hash, with elements casted to the given types. - # Here the Int32 | String union is cast to Int32. - # - # ``` - # num_or_str = 42.as(Int32 | String) - # NamedTuple(name: String, val: Int32).from({"name" => "number", "val" => num_or_str}) # => {name: "number", val: 42} - # - # num_or_str = "a string".as(Int32 | String) - # NamedTuple(name: String, val: Int32).from({"name" => "number", "val" => num_or_str}) # raises TypeCastError (cast from String to Int32 failed) - # ``` - # See also: `#from`. - def self.from(hash : Hash) : self - {% begin %} - NamedTuple.new(**{{T}}).from(hash) - {% end %} - end - - # Expects to be called on a named tuple whose values are types, creates a tuple from the given hash, - # with types casted appropriately. The hash keys must be either symbols or strings. - # - # This allows you to easily pass a hash as individual named arguments to a method. - # - # ``` - # require "json" - # - # def speak_about(thing : String, n : Int64) - # "I see #{n} #{thing}s" - # end - # - # data = JSON.parse(%({"thing": "world", "n": 2})).as_h - # speak_about(**{thing: String, n: Int64}.from(data)) # => "I see 2 worlds" - # ``` - def from(hash : Hash) - if size != hash.size - raise ArgumentError.new("Expected a hash with #{size} keys but one with #{hash.size} keys was given.") - end - - {% begin %} - NamedTuple.new( - {% for key, value in T %} - {{key.stringify}}: self[{{key.symbolize}}].cast(hash.fetch({{key.symbolize}}) { hash["{{key}}"] }), - {% end %} - ) - {% end %} - end - - # Returns the value for the given *key*, if there's such key, otherwise raises `KeyError`. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # - # key = :name - # tuple[key] # => "Crystal" - # - # key = "year" - # tuple[key] # => 2011 - # - # key = :other - # tuple[key] # raises KeyError - # ``` - def [](key : Symbol | String) - fetch(key) { raise KeyError.new "Missing named tuple key: #{key.inspect}" } - end - - # Returns the value for the given *key*, if there's such key, otherwise returns `nil`. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # - # key = :name - # tuple[key]? # => "Crystal" - # - # key = "year" - # tuple[key] # => 2011 - # - # key = :other - # tuple[key]? # => nil - # ``` - def []?(key : Symbol | String) - fetch(key, nil) - end - - # Returns the value for the given *key*, if there's such key, otherwise returns *default_value*. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.fetch(:name, "Unknown") # => "Crystal" - # tuple.fetch("year", 0) # => 2011 - # tuple.fetch(:other, 0) # => 0 - # ``` - def fetch(key : Symbol | String, default_value) - fetch(key) { default_value } - end - - # Returns the value for the given *key*, if there's such key, otherwise the value returned by the block. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.fetch(:name) { "Unknown" } # => "Crystal" - # tuple.fetch(:other) { 0 } # => 0 - # ``` - def fetch(key : Symbol, &block) - {% for key in T %} - return self[{{key.symbolize}}] if {{key.symbolize}} == key - {% end %} - yield - end - - # Returns the value for the given *key*, if there's such key, otherwise the value returned by the block. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.fetch("name") { "Unknown" } # => "Crystal" - # tuple.fetch("other") { 0 } # => 0 - # ``` - def fetch(key : String, &block) - {% for key in T %} - return self[{{key.symbolize}}] if {{key.stringify}} == key - {% end %} - yield - end - - # Returns a hash value based on this name tuple's size, keys and values. - # - # See also: `Object#hash`. - def hash - hash = 31 * size - {% for key in T.keys.sort %} - hash = 31 * hash + {{key.symbolize}}.hash - hash = 31 * hash + self[{{key.symbolize}}].hash - {% end %} - hash - end - - # Same as `to_s`. - def inspect - to_s - end - - # Returns a `Tuple` of symbols with the keys in this named tuple. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.keys # => {:name, :year} - # ``` - def keys - {% begin %} - Tuple.new( - {% for key in T %} - {{key.symbolize}}, - {% end %} - ) - {% end %} - end - - # Returns a `Tuple` with the values in this named tuple. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.values # => {"Crystal", 2011} - # ``` - def values - {% begin %} - Tuple.new( - {% for key in T %} - self[{{key.symbolize}}], - {% end %} - ) - {% end %} - end - - # Returns `true` if this named tuple has the given *key*, `false` otherwise. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.has_key?(:name) # => true - # tuple.has_key?(:other) # => false - # ``` - def has_key?(key : Symbol) : Bool - {% for key in T %} - return true if {{key.symbolize}} == key - {% end %} - false - end - - # ditto - def has_key?(key : String) : Bool - {% for key in T %} - return true if {{key.stringify}} == key - {% end %} - false - end - - # Appends a string representation of this named tuple to the given `IO`. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.to_s # => %({name: "Crystal", year: 2011}) - # ``` - def to_s(io) - io << "{" - {% for key, value, i in T %} - {% if i > 0 %} - io << ", " - {% end %} - key = {{key.stringify}} - if Symbol.needs_quotes?(key) - key.inspect(io) - else - io << key - end - io << ": " - self[{{key.symbolize}}].inspect(io) - {% end %} - io << "}" - end - - def pretty_print(pp) - pp.surround("{", "}", left_break: nil, right_break: nil) do - {% for key, value, i in T %} - {% if i > 0 %} - pp.comma - {% end %} - pp.group do - key = {{key.stringify}} - if Symbol.needs_quotes?(key) - pp.text key.inspect - else - pp.text key - end - pp.text ": " - pp.nest do - pp.breakable "" - self[{{key.symbolize}}].pretty_print(pp) - end - end - {% end %} - end - end - - # Yields each key and value in this named tuple. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.each do |key, value| - # puts "#{key} = #{value}" - # end - # ``` - # - # Output: - # - # ```text - # name = Crystal - # year = 2011 - # ``` - def each : Nil - {% for key in T %} - yield {{key.symbolize}}, self[{{key.symbolize}}] - {% end %} - end - - # Yields each key in this named tuple. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.each_key do |key| - # puts key - # end - # ``` - # - # Output: - # - # ```text - # name - # year - # ``` - def each_key : Nil - {% for key in T %} - yield {{key.symbolize}} - {% end %} - end - - # Yields each value in this named tuple. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.each_value do |value| - # puts value - # end - # ``` - # - # Output: - # - # ```text - # Crystal - # 2011 - # ``` - def each_value : Nil - {% for key in T %} - yield self[{{key.symbolize}}] - {% end %} - end - - # Yields each key and value, together with an index starting at *offset*, in this named tuple. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.each_with_index do |key, value, i| - # puts "#{i + 1}) #{key} = #{value}" - # end - # ``` - # - # Output: - # - # ```text - # 1) name = Crystal - # 2) year = 2011 - # ``` - def each_with_index(offset = 0) - i = offset - each do |key, value| - yield key, value, i - i += 1 - end - end - - # Returns an `Array` populated with the results of each iteration in the given block, - # which is given each key and value in this named tuple. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.map { |k, v| "#{k}: #{v}" } # => ["name: Crystal", "year: 2011"] - # ``` - def map - array = Array(typeof(yield first_key_internal, first_value_internal)).new(size) - each do |k, v| - array.push yield k, v - end - array - end - - # Returns a new `Array` of tuples populated with each key-value pair. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.to_a # => [{:name, "Crystal"}, {:year, 2011}] - # ``` - def to_a - ary = Array({typeof(first_key_internal), typeof(first_value_internal)}).new(size) - each do |key, value| - ary << {key.as(typeof(first_key_internal)), value.as(typeof(first_value_internal))} - end - ary - end - - # Returns a `Hash` with the keys and values in this named tuple. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.to_h # => {:name => "Crystal", :year => 2011} - # ``` - def to_h - {% if T.size == 0 %} - {% raise "Can't convert an empty NamedTuple to a Hash" %} - {% else %} - { - {% for key in T %} - {{key.symbolize}} => self[{{key.symbolize}}], - {% end %} - } - {% end %} - end - - # Returns the number of elements in this named tuple. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.size # => 2 - # ``` - def size - {{T.size}} - end - - # Returns `true` if this named tuple is empty. - # - # ``` - # tuple = {name: "Crystal", year: 2011} - # tuple.empty? # => false - # ``` - def empty? - size == 0 - end - - # Returns `true` if this tuple has the same keys as *other*, and values - # for each key are the same in `self` and *other*. - # - # ``` - # tuple1 = {name: "Crystal", year: 2011} - # tuple2 = {year: 2011, name: "Crystal"} - # tuple3 = {name: "Crystal", year: 2012} - # tuple4 = {name: "Crystal", year: 2011.0} - # - # tuple1 == tuple2 # => true - # tuple1 == tuple3 # => false - # tuple1 == tuple4 # => true - # ``` - def ==(other : self) - {% for key in T %} - return false unless self[{{key.symbolize}}] == other[{{key.symbolize}}] - {% end %} - true - end - - # ditto - def ==(other : NamedTuple) - compare_with_other_named_tuple(other) - end - - private def compare_with_other_named_tuple(other : U) forall U - {% if T.keys.sort == U.keys.sort %} - {% for key in T %} - return false unless self[{{key.symbolize}}] == other[{{key.symbolize}}] - {% end %} - - true - {% else %} - false - {% end %} - end - - # Returns a named tuple with the same keys but with cloned values, using the `clone` method. - def clone - {% begin %} - { - {% for key in T %} - {{key.stringify}}: self[{{key.symbolize}}].clone, - {% end %} - } - {% end %} - end - - private def first_key_internal - i = 0 - keys[i] - end - - private def first_value_internal - i = 0 - values[i] - end -end diff --git a/src/core/nil.cr b/src/core/nil.cr deleted file mode 100644 index d552a7f..0000000 --- a/src/core/nil.cr +++ /dev/null @@ -1,116 +0,0 @@ -# The `Nil` type has only one possible value: `nil`. -# -# `nil` is commonly used to represent the absence of a value. -# For example, `String#index` returns the position of the character or `nil` if it's not -# in the string: -# -# ``` -# str = "Hello world" -# str.index 'e' # => 1 -# str.index 'a' # => nil -# ``` -# -# In the above example, trying to invoke a method on the returned value will -# give a compile time error unless both `Int32` and `Nil` define that method: -# -# ``` -# str = "Hello world" -# idx = str.index 'e' -# idx + 1 # Error: undefined method '+' for Nil -# ``` -# -# The language and the standard library provide short, readable, easy ways to deal with `nil`, -# such as `Object#try` and `Object#not_nil!`: -# -# ``` -# str = "Hello world" -# -# # The index of 'e' in str or 0 if not found -# idx1 = str.index('e') || 0 -# -# idx2 = str.index('a') -# if idx2 -# # Compiles: idx2 can't be nil here -# idx2 + 1 -# end -# -# # Tell the compiler that we are sure the returned -# # value is not nil: raises a runtime exception -# # if our assumption doesn't hold. -# idx3 = str.index('o').not_nil! -# ``` -struct Nil - # Returns `0_u64`. Even though `Nil` is not a `Reference` type, it is usually - # mixed with them to form nilable types so it's useful to have an - # object id for `nil`. - def object_id - 0_u64 - end - - # :nodoc: - def crystal_type_id - 0 - end - - # Returns `true`: `Nil` has only one singleton value: `nil`. - def ==(other : Nil) - true - end - - # Returns `true`: `Nil` has only one singleton value: `nil`. - def same?(other : Nil) - true - end - - # Returns `false`. - # TODO: Implement this! - # def same?(other : Reference) - # false - # end - - # Returns `0`. - def hash - 0 - end - - # Returns an empty string. - def to_s - "" - end - - # Doesn't write anything to the given `IO`. - # TODO: Implement this! - # def to_s(io : IO) - # # Nothing to do - # end - - # Returns `"nil"`. - def inspect - "nil" - end - - # Writes `"nil"` to the given `IO`. - # TODO: Implement this! - # def inspect(io) - # io << "nil" - # end - - # Doesn't yield to the block. - # - # See also: `Object#try`. - def try(&block) - self - end - - # Raises an exception. - # - # See also: `Object#not_nil!`. - # TODO: Implement this! - # def not_nil! - # raise "Nil assertion failed" - # end - - def clone - self - end -end diff --git a/src/core/number.cr b/src/core/number.cr deleted file mode 100644 index 2ff35e3..0000000 --- a/src/core/number.cr +++ /dev/null @@ -1,12 +0,0 @@ -# The top-level number type. -struct Number - # TODO: Implement this! - # include Comparable(Number) - # TODO: Implement this! - # alias Primitive = Int::Primitive | Float::Primitive - alias Primitive = Int::Primitive - - def self.zero : self - new(0) - end -end diff --git a/src/core/object.cr b/src/core/object.cr deleted file mode 100644 index dc5818b..0000000 --- a/src/core/object.cr +++ /dev/null @@ -1,1192 +0,0 @@ -# `Object` is the base type of all Crystal objects. -class Object - # Returns `true` if this object is equal to *other*. - # - # Subclasses override this method to provide class-specific meaning. - # abstract def ==(other) - - # Returns `true` if this object is not equal to *other*. - # - # By default this method is implemented as `!(self == other)` - # so there's no need to override this unless there's a more efficient - # way to do it. - def !=(other) - !(self == other) - end - - # Shortcut to `!(self =~ other)`. - def !~(other) - !(self =~ other) - end - - # Case equality. - # - # The `===` method is used in a `case ... when ... end` expression. - # - # For example, this code: - # - # ``` - # case value - # when x - # # something when x - # when y - # # something when y - # end - # ``` - # - # Is equivalent to this code: - # - # ``` - # if x === value - # # something when x - # elsif y === value - # # something when y - # end - # ``` - # - # Object simply implements `===` by invoking `==`, but subclasses - # (notably `Regex`) can override it to provide meaningful case-equality semantics. - def ===(other) - self == other - end - - # Pattern match. - # - # Overridden by descendants (notably `Regex` and `String`) to provide meaningful - # pattern-match semantics. - def =~(other) - nil - end - - # Generates an `Int` hash value for this object. - # - # This method must have the property that `a == b` implies `a.hash == b.hash`. - # - # The hash value is used along with `==` by the `Hash` class to determine if two objects - # reference the same hash key. - # TODO: It must be implemented by Float32 - # abstract def hash - - # Returns a string representation of this object. - # - # Descendants must usually **not** override this method. Instead, - # they must override `to_s(io)`, which must append to the given - # IO object. - # def to_s - # String.build do |io| - # to_s io - # end - # end - - # Appends a `String` representation of this object - # to the given `IO` object. - # - # An object must never append itself to the io argument, - # as this will in turn call `to_s(io)` on it. - # TODO: It must be implemented by Reference - # abstract def to_s(io : IO) - - # Returns a `String` representation of this object. - # - # Similar to `to_s`, but usually returns more information about - # this object. - # - # Classes must usually **not** override this method. Instead, - # they must override `inspect(io)`, which must append to the - # given `IO` object. - # def inspect - # String.build do |io| - # inspect io - # end - # end - - # Appends a string representation of this object - # to the given `IO` object. - # - # Similar to `to_s(io)`, but usually appends more information - # about this object. - # def inspect(io : IO) - # to_s io - # end - - # Pretty prints `self` into the given printer. - # - # By default appends a text that is the result of invoking - # `#inspect` on `self`. Subclasses should override - # for custom pretty printing. - # def pretty_print(pp : PrettyPrint) : Nil - # pp.text(inspect) - # end - - # Returns a pretty printed version of `self`. - # def pretty_inspect(width = 79, newline = "\n", indent = 0) : String - # String.build do |io| - # PrettyPrint.format(self, io, width, newline, indent) - # end - # end - - # Yields `self` to the block, and then returns `self`. - # - # The primary purpose of this method is to "tap into" a method chain, - # in order to perform operations on intermediate results within the chain. - # - # ``` - # (1..10).tap { |x| puts "original: #{x.inspect}" } - # .to_a.tap { |x| puts "array: #{x.inspect}" } - # .select { |x| x % 2 == 0 }.tap { |x| puts "evens: #{x.inspect}" } - # .map { |x| x*x }.tap { |x| puts "squares: #{x.inspect}" } - # ``` - def tap - yield self - self - end - - # Yields `self`. `Nil` overrides this method and doesn't yield. - # - # This method is useful for dealing with nilable types, to safely - # perform operations only when the value is not `nil`. - # - # ``` - # # First program argument in downcase, or nil - # ARGV[0]?.try &.downcase - # ``` - def try - yield self - end - - # Returns `self`. `Nil` overrides this method and raises an exception. - def not_nil! - self - end - - # Return `self`. - # - # ``` - # str = "hello" - # str.itself.object_id == str.object_id # => true - # ``` - def itself - self - end - - # Returns a shallow copy of this object. - # - # As a convention, `clone` is the method used to create a deep copy of - # an object, but this logic isn't defined generically for every type - # because cycles could be involved, and the clone logic might not need - # to clone everything. - # - # Many types in the standard library, like `Array`, `Hash`, `Set` and - # `Deque`, and all primitive types, define `dup` and `clone`. - # TODO: It must be implemented by Int8 - # abstract def dup - - # Unsafely reinterprets the bytes of an object as being of another `type`. - # - # This method is useful to treat a type that is represented as a chunk of - # bytes as another type where those bytes convey useful information. As an - # example, you can check the individual bytes of an `Int32`: - # - # ``` - # 0x01020304.unsafe_as(StaticArray(UInt8, 4)) # => StaticArray[4, 3, 2, 1] - # ``` - # - # Or treat the bytes of a `Float64` as an `Int64`: - # - # ``` - # 1.234_f64.unsafe_as(Int64) # => 4608236261112822104 - # ``` - # - # This method is **unsafe** because it behaves unpredictably when the given - # `type` doesn't have the same bytesize as the receiver, or when the given - # `type` representation doesn't semantically match the underlying bytes. - # - # Also note that because `unsafe_as` is a regular method, unlike the pseudo-method - # `as`, you can't specify some types in the type grammar using a short notation, so - # specifying a static array must always be done as `StaticArray(T, N)`, a tuple - # as `Tuple(...)` and so on, never as `UInt8[4]` or `{Int32, Int32}`. - def unsafe_as(type : T.class) forall T - x = self - pointerof(x).as(T*).value - end - - {% for prefixes in { {"", "", "@"}, {"class_", "self.", "@@"} } %} - {% - macro_prefix = prefixes[0].id - method_prefix = prefixes[1].id - var_prefix = prefixes[2].id - %} - - # Defines getter methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # {{macro_prefix}}getter name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def {{method_prefix}}name - # {{var_prefix}}name - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # {{macro_prefix}}getter :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # {{macro_prefix}}getter name : String - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}name : String - # - # def {{method_prefix}}name : String - # {{var_prefix}}name - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # {{macro_prefix}}getter name : String = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}name : String = "John Doe" - # - # def {{method_prefix}}name : String - # {{var_prefix}}name - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferrable from the initial value: - # - # ``` - # class Person - # {{macro_prefix}}getter name = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}name = "John Doe" - # - # def {{method_prefix}}name : String - # {{var_prefix}}name - # end - # end - # ``` - # - # If a block is given to the macro, a getter is generated - # with a variable that is lazily initialized with - # the block's contents: - # - # ``` - # class Person - # {{macro_prefix}}getter(birth_date) { Time.now } - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def {{method_prefix}}birth_date - # {{var_prefix}}birth_date ||= Time.now - # end - # end - # ``` - macro {{macro_prefix}}getter(*names, &block) - \{% if block %} - \{% if names.size != 1 %} - \{{ raise "Only one argument can be passed to `getter` with a block" }} - \{% end %} - - \{% name = names[0] %} - - \{% if name.is_a?(TypeDeclaration) %} - {{var_prefix}}\{{name.var.id}} : \{{name.type}}? - - def {{method_prefix}}\{{name.var.id}} - {{var_prefix}}\{{name.var.id}} ||= \{{yield}} - end - \{% else %} - def {{method_prefix}}\{{name.id}} - {{var_prefix}}\{{name.id}} ||= \{{yield}} - end - \{% end %} - \{% else %} - \{% for name in names %} - \{% if name.is_a?(TypeDeclaration) %} - {{var_prefix}}\{{name}} - - def {{method_prefix}}\{{name.var.id}} : \{{name.type}} - {{var_prefix}}\{{name.var.id}} - end - \{% elsif name.is_a?(Assign) %} - {{var_prefix}}\{{name}} - - def {{method_prefix}}\{{name.target.id}} - {{var_prefix}}\{{name.target.id}} - end - \{% else %} - def {{method_prefix}}\{{name.id}} - {{var_prefix}}\{{name.id}} - end - \{% end %} - \{% end %} - \{% end %} - end - - # Defines raise-on-nil and nilable getter methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # {{macro_prefix}}getter! name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def {{method_prefix}}name? - # {{var_prefix}}name - # end - # - # def {{method_prefix}}name - # {{var_prefix}}name.not_nil! - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # {{macro_prefix}}getter! :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type, as nilable. - # - # ``` - # class Person - # {{macro_prefix}}getter! name : String - # end - # ``` - # - # is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}name : String? - # - # def {{method_prefix}}name? - # {{var_prefix}}name - # end - # - # def {{method_prefix}}name - # {{var_prefix}}name.not_nil! - # end - # end - # ``` - macro {{macro_prefix}}getter!(*names) - \{% for name in names %} - \{% if name.is_a?(TypeDeclaration) %} - {{var_prefix}}\{{name}}? - \{% name = name.var %} - \{% end %} - - def {{method_prefix}}\{{name.id}}? - {{var_prefix}}\{{name.id}} - end - - def {{method_prefix}}\{{name.id}} - {{var_prefix}}\{{name.id}}.not_nil! - end - \{% end %} - end - - # Defines query getter methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # {{macro_prefix}}getter? happy - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def {{method_prefix}}happy? - # {{var_prefix}}happy - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # {{macro_prefix}}getter? :happy, "famous" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # {{macro_prefix}}getter? happy : Bool - # end - # ``` - # - # is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}happy : Bool - # - # def {{method_prefix}}happy? : Bool - # {{var_prefix}}happy - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # {{macro_prefix}}getter? happy : Bool = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}happy : Bool = true - # - # def {{method_prefix}}happy? : Bool - # {{var_prefix}}happy - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferrable from the initial value: - # - # ``` - # class Person - # {{macro_prefix}}getter? happy = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}happy = true - # - # def {{method_prefix}}happy? - # {{var_prefix}}happy - # end - # end - # ``` - macro {{macro_prefix}}getter?(*names) - \{% for name in names %} - \{% if name.is_a?(TypeDeclaration) %} - {{var_prefix}}\{{name}} - - def {{method_prefix}}\{{name.var.id}}? : \{{name.type}} - {{var_prefix}}\{{name.var.id}} - end - \{% elsif name.is_a?(Assign) %} - {{var_prefix}}\{{name}} - - def {{method_prefix}}\{{name.target.id}}? - {{var_prefix}}\{{name.target.id}} - end - \{% else %} - def {{method_prefix}}\{{name.id}}? - {{var_prefix}}\{{name.id}} - end - \{% end %} - \{% end %} - end - - # Defines setter methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # {{macro_prefix}}setter name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def {{method_prefix}}name=({{var_prefix}}name) - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # {{macro_prefix}}setter :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # {{macro_prefix}}setter name : String - # end - # ``` - # - # is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}name : String - # - # def {{method_prefix}}name=({{var_prefix}}name : String) - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # {{macro_prefix}}setter name : String = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}name : String = "John Doe" - # - # def {{method_prefix}}name=({{var_prefix}}name : String) - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferrable from the initial value: - # - # ``` - # class Person - # {{macro_prefix}}setter name = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}name = "John Doe" - # - # def {{method_prefix}}name=({{var_prefix}}name) - # end - # end - # ``` - macro {{macro_prefix}}setter(*names) - \{% for name in names %} - \{% if name.is_a?(TypeDeclaration) %} - {{var_prefix}}\{{name}} - - def {{method_prefix}}\{{name.var.id}}=({{var_prefix}}\{{name.var.id}} : \{{name.type}}) - end - \{% elsif name.is_a?(Assign) %} - {{var_prefix}}\{{name}} - - def {{method_prefix}}\{{name.target.id}}=({{var_prefix}}\{{name.target.id}}) - end - \{% else %} - def {{method_prefix}}\{{name.id}}=({{var_prefix}}\{{name.id}}) - end - \{% end %} - \{% end %} - end - - # Defines property methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # {{macro_prefix}}property name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def {{method_prefix}}name=({{var_prefix}}name) - # end - # - # def {{method_prefix}}name - # {{var_prefix}}name - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # {{macro_prefix}}property :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # {{macro_prefix}}property name : String - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}name : String - # - # def {{method_prefix}}name=({{var_prefix}}name) - # end - # - # def {{method_prefix}}name - # {{var_prefix}}name - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # {{macro_prefix}}property name : String = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}name : String = "John Doe" - # - # def {{method_prefix}}name=({{var_prefix}}name : String) - # end - # - # def {{method_prefix}}name - # {{var_prefix}}name - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferrable from the initial value: - # - # ``` - # class Person - # {{macro_prefix}}property name = "John Doe" - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}name = "John Doe" - # - # def {{method_prefix}}name=({{var_prefix}}name : String) - # end - # - # def {{method_prefix}}name - # {{var_prefix}}name - # end - # end - # ``` - # - # If a block is given to the macro, a property is generated - # with a variable that is lazily initialized with - # the block's contents: - # - # ``` - # class Person - # {{macro_prefix}}property(birth_date) { Time.now } - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def {{method_prefix}}birth_date - # {{var_prefix}}birth_date ||= Time.now - # end - # - # def {{method_prefix}}birth_date=({{var_prefix}}birth_date) - # end - # end - # ``` - macro {{macro_prefix}}property(*names, &block) - \{% if block %} - \{% if names.size != 1 %} - \{{ raise "Only one argument can be passed to `property` with a block" }} - \{% end %} - - \{% name = names[0] %} - - {{macro_prefix}}setter \{{name}} - - \{% if name.is_a?(TypeDeclaration) %} - {{var_prefix}}\{{name.var.id}} : \{{name.type}}? - - def {{method_prefix}}\{{name.var.id}} - {{var_prefix}}\{{name.var.id}} ||= \{{yield}} - end - \{% else %} - def {{method_prefix}}\{{name.id}} - {{var_prefix}}\{{name.id}} ||= \{{yield}} - end - \{% end %} - \{% else %} - \{% for name in names %} - \{% if name.is_a?(TypeDeclaration) %} - {{var_prefix}}\{{name}} - - def {{method_prefix}}\{{name.var.id}} : \{{name.type}} - {{var_prefix}}\{{name.var.id}} - end - - def {{method_prefix}}\{{name.var.id}}=({{var_prefix}}\{{name.var.id}} : \{{name.type}}) - end - \{% elsif name.is_a?(Assign) %} - {{var_prefix}}\{{name}} - - def {{method_prefix}}\{{name.target.id}} - {{var_prefix}}\{{name.target.id}} - end - - def {{method_prefix}}\{{name.target.id}}=({{var_prefix}}\{{name.target.id}}) - end - \{% else %} - def {{method_prefix}}\{{name.id}} - {{var_prefix}}\{{name.id}} - end - - def {{method_prefix}}\{{name.id}}=({{var_prefix}}\{{name.id}}) - end - \{% end %} - \{% end %} - \{% end %} - end - - # Defines raise-on-nil property methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # {{macro_prefix}}property! name - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def {{method_prefix}}name=({{var_prefix}}name) - # end - # - # def {{method_prefix}}name? - # {{var_prefix}}name - # end - # - # def {{method_prefix}}name - # {{var_prefix}}name.not_nil! - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # {{macro_prefix}}property! :name, "age" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type, as nilable. - # - # ``` - # class Person - # {{macro_prefix}}property! name : String - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}name : String? - # - # def {{method_prefix}}name=({{var_prefix}}name) - # end - # - # def {{method_prefix}}name? - # {{var_prefix}}name - # end - # - # def {{method_prefix}}name - # {{var_prefix}}name.not_nil! - # end - # end - # ``` - macro {{macro_prefix}}property!(*names) - {{macro_prefix}}getter! \{{*names}} - - \{% for name in names %} - \{% if name.is_a?(TypeDeclaration) %} - def {{method_prefix}}\{{name.var.id}}=({{var_prefix}}\{{name.var.id}} : \{{name.type}}) - end - \{% else %} - def {{method_prefix}}\{{name.id}}=({{var_prefix}}\{{name.id}}) - end - \{% end %} - \{% end %} - end - - # Defines query property methods for each of the given arguments. - # - # Writing: - # - # ``` - # class Person - # {{macro_prefix}}property? happy - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # def {{method_prefix}}happy=({{var_prefix}}happy) - # end - # - # def {{method_prefix}}happy? - # {{var_prefix}}happy - # end - # end - # ``` - # - # The arguments can be string literals, symbol literals or plain names: - # - # ``` - # class Person - # {{macro_prefix}}property? :happy, "famous" - # end - # ``` - # - # If a type declaration is given, a variable with that name - # is declared with that type. - # - # ``` - # class Person - # {{macro_prefix}}property? happy : Bool - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}happy : Bool - # - # def {{method_prefix}}happy=({{var_prefix}}happy) - # end - # - # def {{method_prefix}}happy? - # {{var_prefix}}happy - # end - # - # def {{method_prefix}}happy - # {{var_prefix}}happy.not_nil! - # end - # end - # ``` - # - # The type declaration can also include an initial value: - # - # ``` - # class Person - # {{macro_prefix}}property? happy : Bool = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}happy : Bool = true - # - # def {{method_prefix}}happy=({{var_prefix}}happy : Bool) - # end - # - # def {{method_prefix}}happy? : Bool - # {{var_prefix}}happy - # end - # end - # ``` - # - # An assignment can be passed too, but in this case the type of the - # variable must be easily inferrable from the initial value: - # - # ``` - # class Person - # {{macro_prefix}}property? happy = true - # end - # ``` - # - # Is the same as writing: - # - # ``` - # class Person - # {{var_prefix}}happy = true - # - # def {{method_prefix}}happy=({{var_prefix}}happy) - # end - # - # def {{method_prefix}}happy? - # {{var_prefix}}happy - # end - # end - # ``` - macro {{macro_prefix}}property?(*names) - \{% for name in names %} - \{% if name.is_a?(TypeDeclaration) %} - {{var_prefix}}\{{name}} - - def {{method_prefix}}\{{name.var.id}}? : \{{name.type}} - {{var_prefix}}\{{name.var.id}} - end - - def {{method_prefix}}\{{name.var.id}}=({{var_prefix}}\{{name.var.id}} : \{{name.type}}) - end - \{% elsif name.is_a?(Assign) %} - {{var_prefix}}\{{name}} - - def {{method_prefix}}\{{name.target.id}}? - {{var_prefix}}\{{name.target.id}} - end - - def {{method_prefix}}\{{name.target.id}}=({{var_prefix}}\{{name.target.id}}) - end - \{% else %} - def {{method_prefix}}\{{name.id}}? - {{var_prefix}}\{{name.id}} - end - - def {{method_prefix}}\{{name.id}}=({{var_prefix}}\{{name.id}}) - end - \{% end %} - \{% end %} - end - {% end %} - - # Delegate *methods* to *to*. - # - # Note that due to current language limitations this is only useful - # when no captured blocks are involved. - # - # ``` - # class StringWrapper - # def initialize(@string : String) - # end - # - # delegate downcase, to: @string - # delegate gsub, to: @string - # delegate empty?, capitalize, to: @string - # end - # - # wrapper = StringWrapper.new "HELLO" - # wrapper.downcase # => "hello" - # wrapper.gsub(/E/, "A") # => "HALLO" - # wrapper.empty? # => false - # wrapper.capitalize # => "Hello" - # ``` - macro delegate(*methods, to object) - {% for method in methods %} - def {{method.id}}(*args, **options) - {{object.id}}.{{method.id}}(*args, **options) - end - - def {{method.id}}(*args, **options) - {{object.id}}.{{method.id}}(*args, **options) do |*yield_args| - yield *yield_args - end - end - {% end %} - end - - # Defines a `hash` method computed from the given fields. - # - # ``` - # class Person - # def initialize(@name, @age) - # end - # - # # Define a hash method based on @name and @age - # def_hash @name, @age - # end - # ``` - macro def_hash(*fields) - def hash - {% if fields.size == 1 %} - {{fields[0]}}.hash - {% else %} - hash = 0 - {% for field in fields %} - hash = 31 * hash + {{field}}.hash - {% end %} - hash - {% end %} - end - end - - # Defines an `==` method by comparing the given fields. - # - # The generated `==` method has a `self` restriction. - # - # ``` - # class Person - # def initialize(@name, @age) - # end - # - # # Define a `==` method that compares @name and @age - # def_equals @name, @age - # end - # ``` - macro def_equals(*fields) - def ==(other : self) - {% for field in fields %} - return false unless {{field.id}} == other.{{field.id}} - {% end %} - true - end - end - - # Defines `hash` and `==` method from the given fields. - # - # The generated `==` method has a `self` restriction. - # - # ``` - # class Person - # def initialize(@name, @age) - # end - # - # # Define a hash method based on @name and @age - # # Define a `==` method that compares @name and @age - # def_equals_and_hash @name, @age - # end - # ``` - macro def_equals_and_hash(*fields) - def_equals {{*fields}} - def_hash {{*fields}} - end - - # Forwards missing methods to *delegate*. - # - # ``` - # class StringWrapper - # def initialize(@string : String) - # end - # - # forward_missing_to @string - # end - # - # wrapper = StringWrapper.new "HELLO" - # wrapper.downcase # => "hello" - # wrapper.gsub(/E/, "A") # => "HALLO" - # ``` - macro forward_missing_to(delegate) - macro method_missing(call) - {{delegate}}.\{{call}} - end - end - - # Defines a `clone` method that returns a copy of this object with all - # instance variables cloned (`clone` is in turn invoked on them). - macro def_clone - # Returns a copy of `self` with all instance variables cloned. - def clone - clone = \{{@type}}.allocate - clone.initialize_copy(self) - clone - end - - protected def initialize_copy(other) - \{% for ivar in @type.instance_vars %} - @\{{ivar.id}} = other.@\{{ivar.id}}.clone - \{% end %} - end - end - - protected def self.set_crystal_type_id(ptr) - ptr.as(LibCR::SizeT*).value = LibCR::SizeT.new(crystal_instance_type_id) - # ptr.as(LibC::SizeT*).value = LibC::SizeT.new(crystal_instance_type_id) - ptr - end -end diff --git a/src/core/pointer.cr b/src/core/pointer.cr deleted file mode 100644 index ddb12b7..0000000 --- a/src/core/pointer.cr +++ /dev/null @@ -1,528 +0,0 @@ -# struct Pointer(T) - # def self.null - # new 0_u64 - # end - - # def +(other : Int) - # self + other.to_i64 - # end - - # def [](offset : Int) - # (self + offset).value - # end - - # def []=(offset : Int, value : T) - # (self + offset).value = value - # end -# end - -# require "c/string" -require "./lib_cr/x86_64-linux-musl/c/string" -require "./lib_cr/x86_64-linux-musl/c/stdlib" - -# A typed pointer to some memory. -# -# This is the only unsafe type in Crystal. If you are using a pointer, you are writing -# unsafe code because a pointer doesn't know where it's pointing to nor how much memory -# starting from it is valid. However, pointers make it possible to interface with C and -# to implement efficient data structures. For example, both `Array` and `Hash` are -# implemented using pointers. -# -# You can obtain pointers in four ways: `#new`, `#malloc`, `pointerof` and by calling a C -# function that returns a pointer. -# -# `pointerof(x)`, where *x* is a variable or an instance variable, returns a pointer to -# that variable: -# -# ``` -# x = 1 -# ptr = pointerof(x) -# ptr.value = 2 -# x # => 2 -# ``` -# -# Note that a pointer is *falsey* if it's null (if it's address is zero). -# -# When calling a C function that expects a pointer you can also pass `nil` instead of using -# `Pointer.null` to construct a null pointer. -# -# For a safe alternative, see `Slice`, which is a pointer with a size and with bounds checking. -struct Pointer(T) - # Unsafe wrapper around a `Pointer` that allows to write values to - # it while advancing the location and keeping track of how many elements - # were written. - # - # See also: `Pointer#appender`. - struct Appender(T) - def initialize(@pointer : Pointer(T)) - @start = @pointer - end - - def <<(value : T) - @pointer.value = value - @pointer += 1 - end - - def size - @pointer - @start - end - - def pointer - @pointer - end - end - - include Comparable(self) - - # Returns `true` if this pointer's address is zero. - # - # ``` - # a = 1 - # pointerof(a).null? # => false - # - # b = Pointer(Int32).new(0) - # b.null? # => true - # ``` - def null? - address == 0 - end - - # Returns a new pointer whose address is this pointer's address incremented by `other * sizeof(T)`. - # - # ``` - # ptr = Pointer(Int32).new(1234) - # ptr.address # => 1234 - # - # # An Int32 occupies four bytes - # ptr2 = ptr + 1 - # ptr2.address # => 1238 - # ``` - def +(other : Int) - self + other.to_i64 - end - - # Returns a new pointer whose address is this pointer's address decremented by `other * sizeof(T)`. - # - # ``` - # ptr = Pointer(Int32).new(1234) - # ptr.address # => 1234 - # - # # An Int32 occupies four bytes - # ptr2 = ptr - 1 - # ptr2.address # => 1230 - # ``` - def -(other : Int) - self + (-other) - end - - # Returns -1, 0 or 1 if this pointer's address is less, equal or greater than *other*'s address, - # respectively. - # - # See also: `Object#<=>`. - # NOTE: Is `#<=>` in Comparable, not in Object? - # def <=>(other : self) - # address <=> other.address - # end - - # Gets the value pointed at this pointer's address plus `offset * sizeof(T)`. - # - # ``` - # ptr = Pointer.malloc(4) { |i| i + 10 } - # ptr[0] # => 10 - # ptr[1] # => 11 - # ptr[2] # => 12 - # ptr[3] # => 13 - # ``` - def [](offset) - (self + offset).value - end - - # Sets the value pointed at this pointer's address plus `offset * sizeof(T)`. - # - # ``` - # ptr = Pointer(Int32).malloc(4) # [0, 0, 0, 0] - # ptr[1] = 42 - # - # ptr2 = ptr + 1 - # ptr2.value # => 42 - # ``` - def []=(offset, value : T) - (self + offset).value = value - end - - # Copies *count* elements from *source* into `self`. - # If *source* and `self` overlap, behaviour is undefined. - # Use `#move_from` if they overlap (slower but always works). - # - # ``` - # ptr1 = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] - # ptr2 = Pointer.malloc(4) { |i| i + 11 } # [11, 12, 13, 14] - # - # # ptr2 -> [11, 12, 13, 14] - # # ^---^ <- copy this - # # ptr1 -> [1, 2, 3, 4] - # # ^---^ <- here - # ptr1.copy_from(ptr2, 2) - # ptr1[0] # => 11 - # ptr1[1] # => 12 - # ptr1[2] # => 3 - # ptr1[3] # => 4 - # ``` - # def copy_from(source : Pointer(T), count : Int) - # source.copy_to(self, count) - # end - - # :nodoc: - # def copy_from(source : Pointer(NoReturn), count : Int) - # raise ArgumentError.new("Negative count") if count < 0 - - # We need this overload for cases when we have a pointer to unreachable - # data, like when doing Tuple.new.to_a - # self - # end - - # Copies *count* elements from `self` into *target*. - # If `self` and *target* overlap, behaviour is undefined. - # Use `#move_to` if they overlap (slower but always works). - # - # ``` - # ptr1 = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] - # ptr2 = Pointer.malloc(4) { |i| i + 11 } # [11, 12, 13, 14] - # - # # ptr1 -> [1, 2, 3, 4] - # # ^---^ <- copy this - # # ptr2 -> [11, 12, 13, 14] - # # ^---^ <- here - # ptr1.copy_to(ptr2, 2) - # ptr2[0] # => 1 - # ptr2[1] # => 2 - # ptr2[2] # => 13 - # ptr2[3] # => 14 - # ``` - # def copy_to(target : Pointer, count : Int) - # target.copy_from_impl(self, count) - # end - - # Copies *count* elements from *source* into `self`. - # *source* and `self` may overlap; the copy is always done in a non-destructive manner. - # - # ``` - # ptr1 = Pointer.malloc(4) { |i| i + 1 } # ptr1 -> [1, 2, 3, 4] - # ptr2 = ptr1 + 1 # ^--------- ptr2 - # - # # [1, 2, 3, 4] - # # ^-----^ <- copy this - # # ^------^ <- here - # ptr2.move_from(ptr1, 3) - # - # ptr1[0] # => 1 - # ptr1[1] # => 1 - # ptr1[2] # => 2 - # ptr1[3] # => 3 - # ``` - # def move_from(source : Pointer(T), count : Int) - # source.move_to(self, count) - # end - - # :nodoc: - # def move_from(source : Pointer(NoReturn), count : Int) - # raise ArgumentError.new("Negative count") if count < 0 - - # We need this overload for cases when we have a pointer to unreachable - # data, like when doing Tuple.new.to_a - # self - # end - - # Copies *count* elements from `self` into *source*. - # *source* and `self` may overlap; the copy is always done in a non-destructive manner. - # - # ``` - # ptr1 = Pointer.malloc(4) { |i| i + 1 } # ptr1 -> [1, 2, 3, 4] - # ptr2 = ptr1 + 1 # ^--------- ptr2 - # - # # [1, 2, 3, 4] - # # ^-----^ <- copy this - # # ^------^ <- here - # ptr1.move_to(ptr2, 3) - # - # ptr1[0] # => 1 - # ptr1[1] # => 1 - # ptr1[2] # => 2 - # ptr1[3] # => 3 - # ``` - # def move_to(target : Pointer, count : Int) - # target.move_from_impl(self, count) - # end - - # We use separate method in which we make sure that `source` - # is never a union of pointers. This is guaranteed because both - # copy_from/move_from/copy_to/move_to reverse self and caller, - # and so if either self or the arguments are unions a dispatch - # will happen and unions will disappear. - # protected def copy_from_impl(source : Pointer(T), count : Int) - # raise ArgumentError.new("Negative count") if count < 0 - - # if self.class == source.class - # Intrinsics.memcpy(self.as(Void*), source.as(Void*), (count * sizeof(T)).to_u32, 0_u32, false) - # else - # while (count -= 1) >= 0 - # self[count] = source[count] - # end - # end - # self - # end - - # protected def move_from_impl(source : Pointer(T), count : Int) - # raise ArgumentError.new("Negative count") if count < 0 - - # if self.class == source.class - # Intrinsics.memmove(self.as(Void*), source.as(Void*), (count * sizeof(T)).to_u32, 0_u32, false) - # else - # if source.address < address - # copy_from source, count - # else - # count.times do |i| - # self[i] = source[i] - # end - # end - # end - # self - # end - - # Compares *count* elements from this pointer and *other*, byte by byte. - # - # Returns 0 if both pointers point to the same sequence of *count* bytes. Otherwise - # returns the difference between the first two differing bytes (treated as UInt8). - # - # ``` - # ptr1 = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] - # ptr2 = Pointer.malloc(4) { |i| i + 11 } # [11, 12, 13, 14] - # - # ptr1.memcmp(ptr2, 4) # => -10 - # ptr2.memcmp(ptr1, 4) # => 10 - # ptr1.memcmp(ptr1, 4) # => 0 - # ``` - # def memcmp(other : Pointer(T), count : Int) - # # LibC.memcmp(self.as(Void*), (other.as(Void*)), (count * sizeof(T))) - # LibCR.memcmp(self.as(Void*), (other.as(Void*)), (count * sizeof(T))) - # end - - # Swaps the contents pointed at the offsets *i* and *j*. - # - # ``` - # ptr = Pointer.malloc(4) { |i| i + 1 } - # ptr[2] # => 3 - # ptr[3] # => 4 - # ptr.swap(2, 3) - # ptr[2] # => 4 - # ptr[3] # => 3 - # ``` - # def swap(i, j) - # self[i], self[j] = self[j], self[i] - # end - - # Returns the address of this pointer. - # - # ``` - # ptr = Pointer(Int32).new(1234) - # ptr.hash # => 1234 - # ``` - # TODO: This is in object.cr - # def_hash address - - # Appends a string representation of this pointer to the given `IO`, - # including its type and address in hexadecimal. - # - # ``` - # ptr1 = Pointer(Int32).new(1234) - # ptr1.to_s # => "Pointer(Int32)@0x4d2" - # - # ptr2 = Pointer(Int32).new(0) - # ptr2.to_s # => "Pointer(Int32).null" - # ``` - # def to_s(io : IO) - # io << "Pointer(" - # io << T.to_s - # io << ")" - # if address == 0 - # io << ".null" - # else - # io << "@0x" - # address.to_s(16, io) - # end - # end - - # Tries to change the size of the allocation pointed to by this pointer to *size*, - # and returns that pointer. - # - # Since the space after the end of the block may be in use, realloc may find it - # necessary to copy the block to a new address where more free space is available. - # The value of realloc is the new address of the block. - # If the block needs to be moved, realloc copies the old contents. - # - # Remember to always assign the value of realloc. - # - # ``` - # ptr = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] - # ptr = ptr.realloc(8) - # ptr # [1, 2, 3, 4, 0, 0, 0, 0] - # ``` - # def realloc(size : Int) - # realloc(size.to_u64) - # end - - # Shuffles *count* consecutive values pointed by this pointer. - # - # ``` - # ptr = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] - # ptr.shuffle!(4) - # ptr # [3, 4, 1, 2] - # ``` - # def shuffle!(count : Int, random = Random::DEFAULT) - # (count - 1).downto(1) do |i| - # j = random.rand(i + 1) - # swap(i, j) - # end - # self - # end - - # Sets *count* consecutive values pointed by this pointer to the - # values returned by the block. - # - # ``` - # ptr = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4] - # ptr.map!(4) { |value| value * 2 } - # ptr # [2, 4, 6, 8] - # ``` - # def map!(count : Int) - # count.times do |i| - # self[i] = yield self[i] - # end - # end - - # Like `map!`, but yield 2 arugments, the element and it's index - # def map_with_index!(count : Int, &block) - # count.times do |i| - # self[i] = yield self[i], i - # end - # self - # end - - # Returns a pointer whose memory address is zero. This doesn't allocate memory. - # - # When calling a C function you can also pass `nil` instead of constructing a - # null pointer with this method. - # - # ``` - # ptr = Pointer(Int32).null - # ptr.address # => 0 - # ``` - def self.null - new 0_u64 - end - - # Returns a pointer that points to the given memory address. This doesn't allocate memory. - # - # ``` - # ptr = Pointer(Int32).new(5678) - # ptr.address # => 5678 - # ``` - def self.new(address : Int) - new address.to_u64 - end - - # Allocates `size * sizeof(T)` bytes from the system's heap initialized - # to zero and returns a pointer to the first byte from that memory. - # The memory is allocated by the `GC`, so when there are - # no pointers to this memory, it will be automatically freed. - # - # ``` - # # Allocate memory for an Int32: 4 bytes - # ptr = Pointer(Int32).malloc - # ptr.value # => 0 - # - # # Allocate memory for 10 Int32: 40 bytes - # ptr = Pointer(Int32).malloc(10) - # ptr[0] # => 0 - # # ... - # ptr[9] # => 0 - # ``` - # def self.malloc(size : Int = 1) - # if size < 0 - # raise ArgumentError.new("Negative Pointer#malloc size") - # end - # malloc(size.to_u64) - # end - - # Allocates `size * sizeof(T)` bytes from the system's heap initialized - # to *value* and returns a pointer to the first byte from that memory. - # The memory is allocated by the `GC`, so when there are - # no pointers to this memory, it will be automatically freed. - # - # ``` - # # An Int32 occupies 4 bytes, so here we are requesting 8 bytes - # # initialized to the number 42 - # ptr = Pointer.malloc(2, 42) - # ptr[0] # => 42 - # ptr[1] # => 42 - # ``` - # def self.malloc(size : Int, value : T) - # ptr = Pointer(T).malloc(size) - # size.times { |i| ptr[i] = value } - # ptr - # end - - # Allocates `size * sizeof(T)` bytes from the system's heap initialized - # to the value returned by the block (which is invoked once with each index in the range `0...size`) - # and returns a pointer to the first byte from that memory. - # The memory is allocated by the `GC`, so when there are - # no pointers to this memory, it will be automatically freed. - # - # ``` - # # An Int32 occupies 4 bytes, so here we are requesting 16 bytes. - # # i is an index in the range 0 .. 3 - # ptr = Pointer.malloc(4) { |i| i + 10 } - # ptr[0] # => 10 - # ptr[1] # => 11 - # ptr[2] # => 12 - # ptr[3] # => 13 - # ``` - # def self.malloc(size : Int, &block : Int32 -> T) - # ptr = Pointer(T).malloc(size) - # size.times { |i| ptr[i] = yield i } - # ptr - # end - - # Returns a `Pointer::Appender` for this pointer. - def appender - Pointer::Appender.new(self) - end - - # Returns a `Slice` that points to this pointer and is bounded by the given *size*. - # - # ``` - # ptr = Pointer.malloc(6) { |i| i + 10 } # [10, 11, 12, 13, 14, 15] - # slice = ptr.to_slice(4) # => Slice[10, 11, 12, 13] - # slice.class # => Slice(Int32) - # ``` - # def to_slice(size) - # Slice.new(self, size) - # end - - # Clears (sets to "zero" bytes) a number of values pointed by this pointer. - # - # ``` - # ptr = Pointer.malloc(6) { |i| i + 10 } # [10, 11, 12, 13, 14, 15] - # ptr.clear(3) - # ptr.to_slice(6) # => Slice[0, 0, 0, 13, 14, 15] - # ``` - # def clear(count = 1) - # ptr = self.as(Pointer(Void)) - # TODO: Use LibC(R).memset ? - # Intrinsics.memset(self.as(Void*), 0_u8, (count * sizeof(T)).to_u32, 0_u32, false) - # end - - def clone - self - end -end diff --git a/src/core/ports.c b/src/core/ports.c new file mode 100644 index 0000000..428da37 --- /dev/null +++ b/src/core/ports.c @@ -0,0 +1,21 @@ +#include + +uint8_t port_byte_in(uint16_t port) { + uint8_t result; + __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); + return result; +} + +void port_byte_out(uint16_t port, uint8_t data) { + __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); +} + +uint16_t port_word_in(uint16_t port) { + uint16_t result; + __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); + return result; +} + +void port_word_out(uint16_t port, uint16_t data) { + __asm__("out %%ax, %%dx" : : "a" (data), "d" (port)); +} diff --git a/src/core/prelude.cr b/src/core/prelude.cr deleted file mode 100644 index 03973d6..0000000 --- a/src/core/prelude.cr +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -# Order-dependent list -require "./lib_cr" -require "./lib_cr/**" -require "./macros" -require "./object" -require "./comparable" -# require "exception" -require "./iterable" -# require "./iterator" -require "./indexable" -require "./string" - -# Alpha-sorted list -require "./bool" -require "./box" -require "./gc" -# require "./gc/null" -# require "./gc/boehm" -require "./int" -require "./named_tuple" -require "./nil" -require "./number" -require "./pointer" -require "./reference" -require "./static_array" -require "./tuple" diff --git a/src/core/reference.cr b/src/core/reference.cr deleted file mode 100644 index 0340e7e..0000000 --- a/src/core/reference.cr +++ /dev/null @@ -1,133 +0,0 @@ -# `Reference` is the base class of classes you define in your program. -# It is set as a class' superclass when you don't specify one: -# -# ``` -# class MyClass # < Reference -# end -# ``` -# -# A reference type is passed by reference: when you pass it to methods, -# return it from methods or assign it to variables, a pointer is actually passed. -# -# Invoking `new` on a `Reference` allocates a new instance on the heap. -# The instance's memory is automatically freed (garbage-collected) when -# the instance is no longer referred by any other entity in the program. -class Reference - # Returns `true` if this reference is the same as *other*. Invokes `same?`. - def ==(other : self) - same?(other) - end - - # Returns `false` (other can only be a `Value` here). - def ==(other) - false - end - - # Returns `true` if this reference is the same as *other*. This is only - # `true` if this reference's `object_id` is the same as *other*'s. - def same?(other : Reference) - object_id == other.object_id - end - - # Returns `false`: a reference is never `nil`. - def same?(other : Nil) - false - end - - # Returns a shallow copy of this object. - # - # This allocates a new object and copies the contents of - # `self` into it. - def dup - {% if @type.abstract? %} - # This shouldn't happen, as the type is abstract, - # but we need to avoid the allocate invocation below - raise "Can't dup {{@type}}" - {% else %} - dup = self.class.allocate - dup.as(Void*).copy_from(self.as(Void*), instance_sizeof(self)) - dup - {% end %} - end - - # Returns this reference's `object_id` as the hash value. - def hash - object_id - end - - # def inspect(io : IO) : Nil - # io << "#<" << {{@type.name.id.stringify}} << ":0x" - # object_id.to_s(16, io) - - # executed = exec_recursive(:inspect) do - # {% for ivar, i in @type.instance_vars %} - # {% if i > 0 %} - # io << "," - # {% end %} - # io << " @{{ivar.id}}=" - # @{{ivar.id}}.inspect io - # {% end %} - # end - # unless executed - # io << " ..." - # end - # io << ">" - # nil - # end - - # def pretty_print(pp) : Nil - # {% if @type.overrides?(Reference, "inspect") %} - # pp.text inspect - # {% else %} - # prefix = "#<#{{{@type.name.id.stringify}}}:0x#{object_id.to_s(16)}" - # executed = exec_recursive(:pretty_print) do - # pp.surround(prefix, ">", left_break: nil, right_break: nil) do - # {% for ivar, i in @type.instance_vars.map(&.name).sort %} - # {% if i == 0 %} - # pp.breakable - # {% else %} - # pp.comma - # {% end %} - # pp.group do - # pp.text "@{{ivar.id}}=" - # pp.nest do - # pp.breakable "" - # @{{ivar.id}}.pretty_print(pp) - # end - # end - # {% end %} - # end - # end - # unless executed - # pp.text "#{prefix} ...>" - # end - # {% end %} - # end - - # def to_s(io : IO) : Nil - # io << "#<" << self.class.name << ":0x" - # object_id.to_s(16, io) - # io << ">" - # nil - # end - - # :nodoc: - # module ExecRecursive - # def self.hash - # @@exec_recursive ||= {} of {UInt64, Symbol} => Bool - # end - # end - - # private def exec_recursive(method) - # hash = ExecRecursive.hash - # key = {object_id, method} - # if hash[key]? - # false - # else - # hash[key] = true - # value = yield - # hash.delete(key) - # true - # end - # end -end diff --git a/src/core/static_array.cr b/src/core/static_array.cr deleted file mode 100644 index 25ba47e..0000000 --- a/src/core/static_array.cr +++ /dev/null @@ -1,247 +0,0 @@ -# A fixed-size, stack allocated array. -struct StaticArray(T, N) - # include Indexable(T) - - # Create a new `StaticArray` with the given *args*. The type of the - # static array will be the union of the type of the given *args*, - # and its size will be the number of elements in *args*. - # - # ``` - # ary = StaticArray[1, 'a'] - # ary[0] # => 1 - # ary[1] # => 'a' - # ary.class # => StaticArray(Char | Int32, 2) - # ``` - # - # See also: `Number.static_array`. - macro [](*args) - %array = uninitialized StaticArray(typeof({{*args}}), {{args.size}}) - {% for arg, i in args %} - %array.to_unsafe[{{i}}] = {{arg}} - {% end %} - %array - end - - # Creates a new static array and invokes the - # block once for each index of the array, assigning the - # block's value in that index. - # - # ``` - # StaticArray(Int32, 3).new { |i| i * 2 } # => StaticArray[0, 2, 4] - # ``` - def self.new(&block : Int32 -> T) - array = uninitialized self - N.times do |i| - array.to_unsafe[i] = yield i - end - array - end - - # Creates a new static array filled with the given value. - # - # ``` - # StaticArray(Int32, 3).new(42) # => StaticArray[42, 42, 42] - # ``` - def self.new(value : T) - new { value } - end - - # Disallow creating an uninitialized StaticArray with new. - # If this is desired, one can use `array = uninitialized ...` - # which makes it clear that it's unsafe. - private def initialize - end - - # Equality. Returns `true` if each element in `self` is equal to each - # corresponding element in *other*. - # - # ``` - # array = StaticArray(Int32, 3).new 0 # => StaticArray[0, 0, 0] - # array2 = StaticArray(Int32, 3).new 0 # => StaticArray[0, 0, 0] - # array3 = StaticArray(Int32, 3).new 1 # => StaticArray[1, 1, 1] - # array == array2 # => true - # array == array3 # => false - # ``` - def ==(other : StaticArray) - return false unless size == other.size - each_with_index do |e, i| - return false unless e == other[i] - end - true - end - - # Equality with another object. Always returns `false`. - # - # ``` - # array = StaticArray(Int32, 3).new 0 # => StaticArray[0, 0, 0] - # array == nil # => false - # ``` - def ==(other) - false - end - - @[AlwaysInline] - def unsafe_at(index : Int) - to_unsafe[index] - end - - # Sets the given value at the given *index*. - # - # Negative indices can be used to start counting from the end of the array. - # Raises `IndexError` if trying to set an element outside the array's range. - # - # ``` - # array = StaticArray(Int32, 3).new { |i| i + 1 } # => StaticArray[1, 2, 3] - # array[2] = 2 # => 2 - # array # => StaticArray[1, 2, 2] - # array[4] = 4 # raises IndexError - # ``` - @[AlwaysInline] - def []=(index : Int, value : T) - index = check_index_out_of_bounds index - to_unsafe[index] = value - end - - # Yields the current element at the given index and updates the value - # at the given *index* with the block's value. - # Raises `IndexError` if trying to set an element outside the array's range. - # - # ``` - # array = StaticArray(Int32, 3).new { |i| i + 1 } # => StaticArray[1, 2, 3] - # array.update(1) { |x| x * 2 } # => 4 - # array # => StaticArray[1, 4, 3] - # array.update(5) { |x| x * 2 } # raises IndexError - # ``` - def update(index : Int) - index = check_index_out_of_bounds index - to_unsafe[index] = yield to_unsafe[index] - end - - # Returns the size of `self` - # - # ``` - # array = StaticArray(Int32, 3).new { |i| i + 1 } - # array.size # => 3 - # ``` - def size - N - end - - # Fills the array by substituting all elements with the given value. - # - # ``` - # array = StaticArray(Int32, 3).new { |i| i + 1 } - # array.[]= 2 # => nil - # array # => StaticArray[2, 2, 2] - # ``` - def []=(value : T) - size.times do |i| - to_unsafe[i] = value - end - end - - # Modifies `self` by randomizing the order of elements in the array - # using the given *random* number generator. Returns `self`. - # - # ``` - # a = StaticArray(Int32, 3).new { |i| i + 1 } # => StaticArray[1, 2, 3] - # a.shuffle!(Random.new(42)) # => StaticArray[3, 2, 1] - # a # => StaticArray[3, 2, 1] - # ``` - def shuffle!(random = Random::DEFAULT) - to_slice.shuffle!(random) - self - end - - # Invokes the given block for each element of `self`, replacing the element - # with the value returned by the block. Returns `self`. - # - # ``` - # array = StaticArray(Int32, 3).new { |i| i + 1 } - # array.map! { |x| x*x } # => StaticArray[1, 4, 9] - # ``` - def map! - to_unsafe.map!(size) { |e| yield e } - self - end - - # Like `map`, but the block gets passed both the element and its index. - def map_with_index!(&block : (T, Int32) -> T) - to_unsafe.map_with_index!(size) { |e, i| yield e, i } - self - end - - # Reverses the elements of this array in-place, then returns `self`. - # - # ``` - # array = StaticArray(Int32, 3).new { |i| i + 1 } - # array.reverse! # => StaticArray[3, 2, 1] - # ``` - def reverse! - to_slice.reverse! - self - end - - # Returns a slice that points to the elements of this static array. - # Changes made to the returned slice also affect this static array. - # - # ``` - # array = StaticArray(Int32, 3).new(2) - # slice = array.to_slice # => Slice[2, 2, 2] - # slice[0] = 3 - # array # => StaticArray[3, 2, 2] - # ``` - def to_slice - Slice.new(to_unsafe, size) - end - - # Returns a pointer to this static array's data. - # - # ``` - # ary = StaticArray(Int32, 3).new(42) - # ary.to_unsafe[0] # => 42 - # ``` - def to_unsafe : Pointer(T) - pointerof(@buffer) - end - - # Appends a string representation of this static array to the given `IO`. - # - # ``` - # array = StaticArray(Int32, 3).new { |i| i + 1 } - # array.to_s # => "StaticArray[1, 2, 3]" - # ``` - def to_s(io : IO) - io << "StaticArray[" - join ", ", io, &.inspect(io) - io << "]" - end - - def pretty_print(pp) - # Don't pass `self` here because we'll pass `self` by - # value and for big static arrays that seems to make - # LLVM really slow. - # TODO: investigate why, maybe report a bug to LLVM? - pp.list("StaticArray[", to_slice, "]") - end - - # Returns a new `StaticArray` where each element is cloned from elements in `self`. - def clone - array = uninitialized self - N.times do |i| - array.to_unsafe[i] = to_unsafe[i].clone - end - array - end - - # :nodoc: - def index(object, offset : Int = 0) - # Optimize for the case of looking for a byte in a byte slice - if T.is_a?(UInt8.class) && - (object.is_a?(UInt8) || (object.is_a?(Int) && 0 <= object < 256)) - return to_slice.fast_index(object, offset) - end - - super - end -end diff --git a/src/core/string.cr b/src/core/string.cr deleted file mode 100644 index c0b5107..0000000 --- a/src/core/string.cr +++ /dev/null @@ -1,4346 +0,0 @@ -# require "c/stdlib" -# require "c/stdio" -# require "c/string" -require "./lib_cr/x86_64-linux-musl/c/string" - -# A `String` represents an immutable sequence of UTF-8 characters. -# -# A `String` is typically created with a string literal, enclosing UTF-8 characters -# in double quotes: -# -# ``` -# "hello world" -# ``` -# -# A backslash can be used to denote some characters inside the string: -# -# ``` -# "\"" # double quote -# "\\" # backslash -# "\e" # escape -# "\f" # form feed -# "\n" # newline -# "\r" # carriage return -# "\t" # tab -# "\v" # vertical tab -# ``` -# -# You can use a backslash followed by an *u* and four hexadecimal characters to denote a unicode codepoint written: -# -# ``` -# "\u0041" # == "A" -# ``` -# -# Or you can use curly braces and specify up to six hexadecimal numbers (0 to 10FFFF): -# -# ``` -# "\u{41}" # == "A" -# ``` -# -# A string can span multiple lines: -# -# ``` -# "hello -# world" # same as "hello\n world" -# ``` -# -# Note that in the above example trailing and leading spaces, as well as newlines, -# end up in the resulting string. To avoid this, you can split a string into multiple lines -# by joining multiple literals with a backslash: -# -# ``` -# "hello " \ -# "world, " \ -# "no newlines" # same as "hello world, no newlines" -# ``` -# -# Alternatively, a backslash followed by a newline can be inserted inside the string literal: -# -# ``` -# "hello \ -# world, \ -# no newlines" # same as "hello world, no newlines" -# ``` -# -# In this case, leading whitespace is not included in the resulting string. -# -# If you need to write a string that has many double quotes, parentheses, or similar -# characters, you can use alternative literals: -# -# ``` -# # Supports double quotes and nested parentheses -# %(hello ("world")) # same as "hello (\"world\")" -# -# # Supports double quotes and nested brackets -# %[hello ["world"]] # same as "hello [\"world\"]" -# -# # Supports double quotes and nested curlies -# %{hello {"world"}} # same as "hello {\"world\"}" -# -# # Supports double quotes and nested angles -# %> # same as "hello <\"world\">" -# ``` -# -# To create a `String` with embedded expressions, you can use string interpolation: -# -# ``` -# a = 1 -# b = 2 -# "sum = #{a + b}" # "sum = 3" -# ``` -# -# This ends up invoking `Object#to_s(IO)` on each expression enclosed by `#{...}`. -# -# If you need to dynamically build a string, use `String#build` or `IO::Memory`. -# -# ### Non UTF-8 valid strings -# -# String might end up being conformed of bytes which are an invalid -# byte sequence according to UTF-8. This can happen if the string is created -# via one of the constructors that accept bytes, or when getting a string -# from `String.build` or `IO::Memory`. No exception will be raised, but -# invalid byte sequences, when asked as chars, will use the unicode replacement -# char (value 0xFFFD). For example: -# -# ``` -# # here 255 is not a valid byte value in the UTF-8 encoding -# string = String.new(Bytes[255, 97]) -# string.valid_encoding? # => false -# -# # The first char here is the unicode replacement char -# string.chars # => ['�', 'a'] -# ``` -# -# One can also create strings with specific byte value in them by -# using octal and hexadecimal escape sequences: -# -# ``` -# # Octal escape sequences -# "\101" # # => "A" -# "\12" # # => "\n" -# "\1" # string with one character with code point 1 -# "\377" # string with one byte with value 255 -# -# # Hexadecimal escape sequences -# "\x45" # # => "A" -# "\xFF" # string with one byte with value 255 -# ``` -# -# The reason for allowing strings that don't have a valid UTF-8 sequence -# is that the world is full of content that isn't properly encoded, -# and having a program raise an exception or stop because of this -# is not good. It's better if programs are more resilient, but -# show a replacement character when there's an error in incoming data. -class String - # :nodoc: - # TODO: Implement this! - # TYPE_ID = "".crystal_type_id - - # :nodoc: - HEADER_SIZE = sizeof({Int32, Int32, Int32}) - # TODO: Implement this! - # include Comparable(self) - # TODO: Implement this! - # macro inherited - # {{ raise "Cannot inherit from String" }} - # end - - # Creates a `String` from the given *slice*. `Bytes` will be copied from the slice. - # - # This method is always safe to call, and the resulting string will have - # the contents and size of the slice. - # - # ``` - # slice = Slice.new(4) { |i| ('a'.ord + i).to_u8 } - # String.new(slice) # => "abcd" - # ``` - # TODO: Implement this! (Bytes is alias for Slice) - # def self.new(slice : Bytes) - # new(slice.pointer(slice.size), slice.size) - # end - - # Creates a new `String` from the given *bytes*, which are encoded in the given *encoding*. - # - # The *invalid* argument can be: - # * `nil`: an exception is raised on invalid byte sequences - # * `:skip`: invalid byte sequences are ignored - # - # ``` - # slice = Slice.new(2, 0_u8) - # slice[0] = 186_u8 - # slice[1] = 195_u8 - # String.new(slice, "GB2312") # => "好" - # ``` - # TODO: Implement this! - # def self.new(bytes : Bytes, encoding : String, invalid : Symbol? = nil) : String - # String.build do |str| - # String.encode(bytes, encoding, "UTF-8", str, invalid) - # end - # end - - # Creates a `String` from a pointer. `Bytes` will be copied from the pointer. - # - # This method is **unsafe**: the pointer must point to data that eventually - # contains a zero byte that indicates the ends of the string. Otherwise, - # the result of this method is undefined and might cause a segmentation fault. - # - # This method is typically used in C bindings, where you get a `char*` from a - # library and the library guarantees that this pointer eventually has an - # ending zero byte. - # - # ``` - # ptr = Pointer.malloc(5) { |i| i == 4 ? 0_u8 : ('a'.ord + i).to_u8 } - # String.new(ptr) # => "abcd" - # ``` - # TODO: Implement this! - # def self.new(chars : UInt8*) - # new(chars, LibC.strlen(chars)) - # new(chars, LibCR.strlen(chars)) - # end - - # Creates a new `String` from a pointer, indicating its bytesize count - # and, optionally, the UTF-8 codepoints count (size). `Bytes` will be - # copied from the pointer. - # - # If the given size is zero, the amount of UTF-8 codepoints will be - # lazily computed when needed. - # - # ``` - # ptr = Pointer.malloc(4) { |i| ('a'.ord + i).to_u8 } - # String.new(ptr, 2) # => "ab" - # ``` - # TODO: Implement this! - # def self.new(chars : UInt8*, bytesize, size = 0) - # # Avoid allocating memory for the empty string - # return "" if bytesize == 0 - - # new(bytesize) do |buffer| - # buffer.copy_from(chars, bytesize) - # {bytesize, size} - # end - # end - - # Creates a new `String` by allocating a buffer (`Pointer(UInt8)`) with the given capacity, then - # yielding that buffer. The block must return a tuple with the bytesize and size - # (UTF-8 codepoints count) of the String. If the returned size is zero, the UTF-8 codepoints - # count will be lazily computed. - # - # The bytesize returned by the block must be less than or equal to the - # capacity given to this String, otherwise `ArgumentError` is raised. - # - # If you need to build a `String` where the maximum capacity is unknown, use `String#build`. - # - # ``` - # str = String.new(4) do |buffer| - # buffer[0] = 'a'.ord.to_u8 - # buffer[1] = 'b'.ord.to_u8 - # {2, 2} - # end - # str # => "ab" - # ``` - # TODO: Implement this! - # def self.new(capacity : Int) - # check_capacity_in_bounds(capacity) - - # str = GC.malloc_atomic(capacity.to_u32 + HEADER_SIZE + 1).as(UInt8*) - # buffer = str.as(String).to_unsafe - # bytesize, size = yield buffer - - # unless 0 <= bytesize <= capacity - # raise ArgumentError.new("Bytesize out of capacity bounds") - # end - - # buffer[bytesize] = 0_u8 - - # # Try to reclaim some memory if capacity is bigger than what was requested - # if bytesize < capacity - # str = str.realloc(bytesize.to_u32 + HEADER_SIZE + 1) - # end - - # str_header = str.as({Int32, Int32, Int32}*) - # str_header.value = {TYPE_ID, bytesize.to_i, size.to_i} - # str.as(String) - # end - - # Builds a `String` by creating a `String::Builder` with the given initial capacity, yielding - # it to the block and finally getting a `String` out of it. The `String::Builder` automatically - # resizes as needed. - # - # ``` - # str = String.build do |str| - # str << "hello " - # str << 1 - # end - # str # => "hello 1" - # ``` - # TODO: Implement this! - # def self.build(capacity = 64) : self - # String::Builder.build(capacity) do |builder| - # yield builder - # end - # end - - - - # Returns the number of bytes in this string. - # - # ``` - # "hello".bytesize # => 5 - # "你好".bytesize # => 6 - # ``` - def bytesize - @bytesize - end - - # Returns the result of interpreting leading characters in this string as an - # integer base *base* (between 2 and 36). - # - # If there is not a valid number at the start of this string, - # or if the resulting integer doesn't fit an `Int32`, an `ArgumentError` is raised. - # - # Options: - # * **whitespace**: if `true`, leading and trailing whitespaces are allowed - # * **underscore**: if `true`, underscores in numbers are allowed - # * **prefix**: if `true`, the prefixes `"0x"`, `"0"` and `"0b"` override the base - # * **strict**: if `true`, extraneous characters past the end of the number are disallowed - # - # ``` - # "12345".to_i # => 12345 - # "0a".to_i # raises ArgumentError - # "hello".to_i # raises ArgumentError - # "0a".to_i(16) # => 10 - # "1100101".to_i(2) # => 101 - # "1100101".to_i(8) # => 294977 - # "1100101".to_i(10) # => 1100101 - # "1100101".to_i(base: 16) # => 17826049 - # - # "12_345".to_i # raises ArgumentError - # "12_345".to_i(underscore: true) # => 12345 - # - # " 12345 ".to_i # => 12345 - # " 12345 ".to_i(whitespace: false) # raises ArgumentError - # - # "0x123abc".to_i # raises ArgumentError - # "0x123abc".to_i(prefix: true) # => 1194684 - # - # "99 red balloons".to_i # raises ArgumentError - # "99 red balloons".to_i(strict: false) # => 99 - # ``` - # TODO: Implement this! - # def to_i(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) - # to_i32(base, whitespace, underscore, prefix, strict) - # end - - # Same as `#to_i`, but returns `nil` if there is not a valid number at the start - # of this string, or if the resulting integer doesn't fit an `Int32`. - # - # ``` - # "12345".to_i? # => 12345 - # "99 red balloons".to_i? # => nil - # "0a".to_i?(strict: false) # => 0 - # "hello".to_i? # => nil - # ``` - # TODO: Implement this! - # def to_i?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) - # to_i32?(base, whitespace, underscore, prefix, strict) - # end - - # Same as `#to_i`, but returns the block's value if there is not a valid number at the start - # of this string, or if the resulting integer doesn't fit an `Int32`. - # - # ``` - # "12345".to_i { 0 } # => 12345 - # "hello".to_i { 0 } # => 0 - # ``` - # TODO: Implement this! - # def to_i(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) - # to_i32(base, whitespace, underscore, prefix, strict) { yield } - # end - - # Same as `#to_i` but returns an `Int8`. - # TODO: Implement this! - # def to_i8(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int8 - # to_i8(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid Int8: #{self}") } - # end - - # Same as `#to_i` but returns an `Int8` or `nil`. - # TODO: Implement this! - # def to_i8?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int8? - # to_i8(base, whitespace, underscore, prefix, strict) { nil } - # end - - # Same as `#to_i` but returns an `Int8` or the block's value. - # TODO: Implement this! - # def to_i8(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) - # gen_to_ i8, 127, 128 - # end - - # Same as `#to_i` but returns an `UInt8`. - # TODO: Implement this! - # def to_u8(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt8 - # to_u8(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid UInt8: #{self}") } - # end - - # Same as `#to_i` but returns an `UInt8` or `nil`. - # TODO: Implement this! - # def to_u8?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt8? - # to_u8(base, whitespace, underscore, prefix, strict) { nil } - # end - - # Same as `#to_i` but returns an `UInt8` or the block's value. - # TODO: Implement this! - # def to_u8(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) - # gen_to_ u8, 255 - # end - - # Same as `#to_i` but returns an `Int16`. - # TODO: Implement this! - # def to_i16(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int16 - # to_i16(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid Int16: #{self}") } - # end - - # Same as `#to_i` but returns an `Int16` or `nil`. - # TODO: Implement this! - # def to_i16?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int16? - # to_i16(base, whitespace, underscore, prefix, strict) { nil } - # end - - # Same as `#to_i` but returns an `Int16` or the block's value. - # TODO: Implement this! - # def to_i16(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) - # gen_to_ i16, 32767, 32768 - # end - - # Same as `#to_i` but returns an `UInt16`. - # TODO: Implement this! - # def to_u16(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt16 - # to_u16(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid UInt16: #{self}") } - # end - - # Same as `#to_i` but returns an `UInt16` or `nil`. - # TODO: Implement this! - # def to_u16?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt16? - # to_u16(base, whitespace, underscore, prefix, strict) { nil } - # end - - # Same as `#to_i` but returns an `UInt16` or the block's value. - # TODO: Implement this! - # def to_u16(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) - # gen_to_ u16, 65535 - # end - - # Same as `#to_i`. - # TODO: Implement this! - # def to_i32(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int32 - # to_i32(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid Int32: #{self}") } - # end - - # Same as `#to_i`. - # TODO: Implement this! - # def to_i32?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int32? - # to_i32(base, whitespace, underscore, prefix, strict) { nil } - # end - - # Same as `#to_i`. - # TODO: Implement this! - # def to_i32(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) - # gen_to_ i32, 2147483647, 2147483648 - # end - - # Same as `#to_i` but returns an `UInt32`. - # TODO: Implement this! - # def to_u32(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt32 - # to_u32(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid UInt32: #{self}") } - # end - - # Same as `#to_i` but returns an `UInt32` or `nil`. - # TODO: Implement this! - # def to_u32?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt32? - # to_u32(base, whitespace, underscore, prefix, strict) { nil } - # end - - # Same as `#to_i` but returns an `UInt32` or the block's value. - # TODO: Implement this! - # def to_u32(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) - # gen_to_ u32, 4294967295 - # end - - # Same as `#to_i` but returns an `Int64`. - # TODO: Implement this! - # def to_i64(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int64 - # to_i64(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid Int64: #{self}") } - # end - - # Same as `#to_i` but returns an `Int64` or `nil`. - # TODO: Implement this! - # def to_i64?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int64? - # to_i64(base, whitespace, underscore, prefix, strict) { nil } - # end - - # Same as `#to_i` but returns an `Int64` or the block's value. - # TODO: Implement this! - # def to_i64(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) - # gen_to_ i64, 9223372036854775807, 9223372036854775808 - # end - - # Same as `#to_i` but returns an `UInt64`. - # TODO: Implement this! - # def to_u64(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt64 - # to_u64(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid UInt64: #{self}") } - # end - - # Same as `#to_i` but returns an `UInt64` or `nil`. - # TODO: Implement this! - # def to_u64?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt64? - # to_u64(base, whitespace, underscore, prefix, strict) { nil } - # end - - # Same as `#to_i` but returns an `UInt64` or the block's value. - # TODO: Implement this! - # def to_u64(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block) - # gen_to_ u64 - # end - - # :nodoc: - # TODO: Implement this! - # CHAR_TO_DIGIT = begin - # table = StaticArray(Int8, 256).new(-1_i8) - # 10_i8.times do |i| - # table.to_unsafe[48 + i] = i - # end - # 26_i8.times do |i| - # table.to_unsafe[65 + i] = i + 10 - # table.to_unsafe[97 + i] = i + 10 - # end - # table - # end - - # :nodoc: - # TODO: Implement this! - # CHAR_TO_DIGIT62 = begin - # table = CHAR_TO_DIGIT.clone - # 26_i8.times do |i| - # table.to_unsafe[65 + i] = i + 36 - # end - # table - # end - - # :nodoc: - # TODO: Implement this! - # record ToU64Info, - # value : UInt64, - # negative : Bool, - # invalid : Bool - - # private macro gen_to_(method, max_positive = nil, max_negative = nil) - # info = to_u64_info(base, whitespace, underscore, prefix, strict) - # return yield if info.invalid - - # if info.negative - # {% if max_negative %} - # return yield if info.value > {{max_negative}} - # -info.value.to_{{method}} - # {% else %} - # return yield - # {% end %} - # else - # {% if max_positive %} - # return yield if info.value > {{max_positive}} - # {% end %} - # info.value.to_{{method}} - # end - # end - # TODO: Implement this! - # private def to_u64_info(base, whitespace, underscore, prefix, strict) - # raise ArgumentError.new("Invalid base #{base}") unless 2 <= base <= 36 || base == 62 - - # ptr = to_unsafe - - # # Skip leading whitespace - # if whitespace - # while ptr.value.unsafe_chr.ascii_whitespace? - # ptr += 1 - # end - # end - - # negative = false - # found_digit = false - # mul_overflow = ~0_u64 / base - - # # Check + and - - # case ptr.value.unsafe_chr - # when '+' - # ptr += 1 - # when '-' - # negative = true - # ptr += 1 - # end - - # # Check leading zero - # if ptr.value.unsafe_chr == '0' - # ptr += 1 - - # if prefix - # case ptr.value.unsafe_chr - # when 'b' - # base = 2 - # ptr += 1 - # when 'x' - # base = 16 - # ptr += 1 - # else - # base = 8 - # end - # found_digit = false - # else - # found_digit = true - # end - # end - - # value = 0_u64 - # last_is_underscore = true - # invalid = false - - # digits = (base == 62 ? CHAR_TO_DIGIT62 : CHAR_TO_DIGIT).to_unsafe - # while ptr.value != 0 - # if ptr.value.unsafe_chr == '_' && underscore - # break if last_is_underscore - # last_is_underscore = true - # ptr += 1 - # next - # end - - # last_is_underscore = false - # digit = digits[ptr.value] - # if digit == -1 || digit >= base - # break - # end - - # if value > mul_overflow - # invalid = true - # break - # end - - # value *= base - - # old = value - # value += digit - # if value < old - # invalid = true - # break - # end - - # found_digit = true - # ptr += 1 - # end - - # if found_digit - # unless ptr.value == 0 - # if whitespace - # while ptr.value.unsafe_chr.ascii_whitespace? - # ptr += 1 - # end - # end - - # if strict && ptr.value != 0 - # invalid = true - # end - # end - # else - # invalid = true - # end - - # ToU64Info.new value, negative, invalid - # end - - # Returns the result of interpreting characters in this string as a floating point number (`Float64`). - # This method raises an exception if the string is not a valid float representation. - # - # Options: - # * **whitespace**: if `true`, leading and trailing whitespaces are allowed - # * **strict**: if `true`, extraneous characters past the end of the number are disallowed - # - # ``` - # "123.45e1".to_f # => 1234.5 - # "45.67 degrees".to_f # raises ArgumentError - # "thx1138".to_f(strict: false) # raises ArgumentError - # " 1.2".to_f(whitespace: false) # raises ArgumentError - # "1.2foo".to_f(strict: false) # => 1.2 - # ``` - # TODO: Implement this! - # def to_f(whitespace = true, strict = true) - # to_f64(whitespace: whitespace, strict: strict) - # end - - # Returns the result of interpreting characters in this string as a floating point number (`Float64`). - # This method returns `nil` if the string is not a valid float representation. - # - # Options: - # * **whitespace**: if `true`, leading and trailing whitespaces are allowed - # * **strict**: if `true`, extraneous characters past the end of the number are disallowed - # - # ``` - # "123.45e1".to_f? # => 1234.5 - # "45.67 degrees".to_f? # => nil - # "thx1138".to_f? # => nil - # " 1.2".to_f?(whitespace: false) # => nil - # "1.2foo".to_f?(strict: false) # => 1.2 - # ``` - # TODO: Implement this! - # def to_f?(whitespace = true, strict = true) - # to_f64?(whitespace: whitespace, strict: strict) - # end - - # Same as `#to_f` but returns a Float32. - # TODO: Implement this! - # def to_f32(whitespace = true, strict = true) - # to_f32?(whitespace: whitespace, strict: strict) || raise ArgumentError.new("Invalid Float32: #{self}") - # end - - # Same as `#to_f?` but returns a Float32. - # TODO: Implement this! - # def to_f32?(whitespace = true, strict = true) - # to_f_impl(whitespace: whitespace, strict: strict) do - # v = LibC.strtof self, out endptr - # {v, endptr} - # end - # end - - # Same as `#to_f`. - # TODO: Implement this! - # def to_f64(whitespace = true, strict = true) - # to_f64?(whitespace: whitespace, strict: strict) || raise ArgumentError.new("Invalid Float64: #{self}") - # end - - # Same as `#to_f?`. - # TODO: Implement this! - # def to_f64?(whitespace = true, strict = true) - # to_f_impl(whitespace: whitespace, strict: strict) do - # v = LibC.strtod self, out endptr - # {v, endptr} - # end - # end - # TODO: Implement this! - # private def to_f_impl(whitespace = true, strict = true) - # return unless whitespace || '0' <= self[0] <= '9' || self[0] == '-' || self[0] == '+' - - # v, endptr = yield - # string_end = to_unsafe + bytesize - - # # blank string - # return if endptr == to_unsafe - - # if strict - # if whitespace - # while endptr < string_end && endptr.value.chr.ascii_whitespace? - # endptr += 1 - # end - # end - # # reached the end of the string - # v if endptr == string_end - # else - # ptr = to_unsafe - # if whitespace - # while ptr < string_end && ptr.value.chr.ascii_whitespace? - # ptr += 1 - # end - # end - # # consumed some bytes - # v if endptr > ptr - # end - # end - - # Returns the `Char` at the given *index*, or raises `IndexError` if out of bounds. - # - # Negative indices can be used to start counting from the end of the string. - # - # ``` - # "hello"[0] # 'h' - # "hello"[1] # 'e' - # "hello"[-1] # 'o' - # "hello"[-2] # 'l' - # "hello"[5] # raises IndexError - # ``` - # TODO: Implement this! - # def [](index : Int) - # at(index) { raise IndexError.new } - # end - - # Returns a substring by using a Range's *begin* and *end* - # as character indices. Indices can be negative to start - # counting from the end of the string. - # - # Raises `IndexError` if the range's start is not in range. - # - # ``` - # "hello"[0..2] # "hel" - # "hello"[0...2] # "he" - # "hello"[1..-1] # "ello" - # "hello"[1...-1] # "ell" - # ``` - # TODO: Implement this! - # def [](range : Range(Int, Int)) - # self[*Indexable.range_to_index_and_count(range, size)] - # end - - # Returns a substring starting from the *start* character - # of size *count*. - # - # The *start* argument can be negative to start counting - # from the end of the string. - # - # Raises `IndexError` if *start* isn't in range. - # - # Raises `ArgumentError` if *count* is negative. - # TODO: Implement this! - # def [](start : Int, count : Int) - # if ascii_only? - # return byte_slice(start, count) - # end - - # start += size if start < 0 - - # start_pos = nil - # end_pos = nil - - # reader = Char::Reader.new(self) - # i = 0 - - # reader.each do |char| - # if i == start - # start_pos = reader.pos - # elsif count >= 0 && i == start + count - # end_pos = reader.pos - # i += 1 - # break - # end - # i += 1 - # end - - # end_pos ||= reader.pos - - # if start_pos - # raise ArgumentError.new "Negative count" if count < 0 - # return "" if count == 0 - - # count = end_pos - start_pos - # return self if count == bytesize - - # String.new(count) do |buffer| - # buffer.copy_from(to_unsafe + start_pos, count) - # {count, 0} - # end - # elsif start == i - # if count >= 0 - # return "" - # else - # raise ArgumentError.new "Negative count" - # end - # else - # raise IndexError.new - # end - # end - # TODO: Implement these! - # def []?(index : Int) - # at(index) { nil } - # end - - # def []?(str : String | Char) - # includes?(str) ? str : nil - # end - - # def []?(regex : Regex) - # self[regex, 0]? - # end - - # def []?(regex : Regex, group) - # match = match(regex) - # match[group]? if match - # end - # TODO: Implement these! - # def [](str : String | Char) - # self[str]?.not_nil! - # end - - # def [](regex : Regex) - # self[regex]?.not_nil! - # end - - # def [](regex : Regex, group) - # self[regex, group]?.not_nil! - # end - # Implement these! - # def at(index : Int) - # at(index) { raise IndexError.new } - # end - - # def at(index : Int) - # if ascii_only? - # byte = byte_at?(index) - # if byte - # return byte < 0x80 ? byte.unsafe_chr : Char::REPLACEMENT - # else - # return yield - # end - # end - - # index += size if index < 0 - - # byte_index = char_index_to_byte_index(index) - # if byte_index - # reader = Char::Reader.new(self, pos: byte_index) - # return reader.current_char - # else - # yield - # end - # end - # TODO: Implement this! - # def byte_slice(start : Int, count : Int) - # start += bytesize if start < 0 - # single_byte_optimizable = ascii_only? - - # if 0 <= start < bytesize - # raise ArgumentError.new "Negative count" if count < 0 - - # count = bytesize - start if start + count > bytesize - # return "" if count == 0 - # return self if count == bytesize - - # String.new(count) do |buffer| - # buffer.copy_from(to_unsafe + start, count) - # slice_size = single_byte_optimizable ? count : 0 - # {count, slice_size} - # end - # elsif start == bytesize - # if count >= 0 - # return "" - # else - # raise ArgumentError.new "Negative count" - # end - # else - # raise IndexError.new - # end - # end - # TODO: Implement this! - # def byte_slice(start : Int) - # byte_slice start, bytesize - start - # end - # TODO: Implement these! - # def codepoint_at(index) - # char_at(index).ord - # end - - # def char_at(index) - # self[index] - # end - # Implement these! - # def byte_at(index) - # byte_at(index) { raise IndexError.new } - # end - - # def byte_at?(index) - # byte_at(index) { nil } - # end - # Implement this! - # def byte_at(index) - # index += bytesize if index < 0 - # if 0 <= index < bytesize - # to_unsafe[index] - # else - # yield - # end - # end - - def unsafe_byte_at(index) - to_unsafe[index] - end - - # Returns a new `String` with each uppercase letter replaced with its lowercase - # counterpart. - # - # ``` - # "hEllO".downcase # => "hello" - # ``` - # TODO: Implement this! - # def downcase(options = Unicode::CaseOptions::None) - # return self if empty? - - # if ascii_only? && (options.none? || options.ascii?) - # String.new(bytesize) do |buffer| - # bytesize.times do |i| - # buffer[i] = to_unsafe[i].unsafe_chr.downcase.ord.to_u8 - # end - # {@bytesize, @length} - # end - # else - # String.build(bytesize) do |io| - # each_char do |char| - # char.downcase(options) do |res| - # io << res - # end - # end - # end - # end - # end - - # Returns a new `String` with each lowercase letter replaced with its uppercase - # counterpart. - # - # ``` - # "hEllO".upcase # => "HELLO" - # ``` - # TODO: Implement this! - # def upcase(options = Unicode::CaseOptions::None) - # return self if empty? - - # if ascii_only? && (options.none? || options.ascii?) - # String.new(bytesize) do |buffer| - # bytesize.times do |i| - # buffer[i] = to_unsafe[i].unsafe_chr.upcase.ord.to_u8 - # end - # {@bytesize, @length} - # end - # else - # String.build(bytesize) do |io| - # each_char do |char| - # char.upcase(options) do |res| - # io << res - # end - # end - # end - # end - # end - - # Returns a new `String` with the first letter converted to uppercase and every - # subsequent letter converted to lowercase. - # - # ``` - # "hEllO".capitalize # => "Hello" - # ``` - # TODO: Implement this! - # def capitalize(options = Unicode::CaseOptions::None) - # return self if empty? - - # if ascii_only? && (options.none? || options.ascii?) - # String.new(bytesize) do |buffer| - # bytesize.times do |i| - # if i == 0 - # buffer[i] = to_unsafe[i].unsafe_chr.upcase.ord.to_u8 - # else - # buffer[i] = to_unsafe[i].unsafe_chr.downcase.ord.to_u8 - # end - # end - # {@bytesize, @length} - # end - # else - # String.build(bytesize) do |io| - # each_char_with_index do |char, i| - # if i == 0 - # char.upcase(options) { |c| io << c } - # else - # char.downcase(options) { |c| io << c } - # end - # end - # end - # end - # end - - # Returns a new `String` with the last carriage return removed (that is, it - # will remove \n, \r, and \r\n). - # - # ``` - # "string\r\n".chomp # => "string" - # "string\n\r".chomp # => "string\n" - # "string\n".chomp # => "string" - # "string".chomp # => "string" - # "x".chomp.chomp # => "x" - # ``` - # Implement this! - # def chomp - # return self if empty? - - # case to_unsafe[bytesize - 1] - # when '\n' - # if bytesize > 1 && to_unsafe[bytesize - 2] === '\r' - # unsafe_byte_slice_string(0, bytesize - 2) - # else - # unsafe_byte_slice_string(0, bytesize - 1) - # end - # when '\r' - # unsafe_byte_slice_string(0, bytesize - 1) - # else - # self - # end - # end - - # Returns a new `String` with *suffix* removed from the end of the string. - # If *suffix* is `'\n'` then `"\r\n"` is also removed if the string ends with it, - # - # ``` - # "hello".chomp('o') # => "hell" - # "hello".chomp('a') # => "hello" - # ``` - # TODO: Implement this! - # def chomp(suffix : Char) - # if suffix == '\n' - # chomp - # elsif ends_with?(suffix) - # unsafe_byte_slice_string(0, bytesize - suffix.bytesize) - # else - # self - # end - # end - - # Returns a new `String` with *suffix* removed from the end of the string. - # If *suffix* is `"\n"` then `"\r\n"` is also removed if the string ends with it, - # - # ``` - # "hello".chomp("llo") # => "he" - # "hello".chomp("ol") # => "hello" - # ``` - # TODO: Implement this! - # def chomp(suffix : String) - # if suffix.bytesize == 1 - # chomp(suffix.to_unsafe[0].unsafe_chr) - # elsif ends_with?(suffix) - # unsafe_byte_slice_string(0, bytesize - suffix.bytesize) - # else - # self - # end - # end - - # Returns a new `String` with the first char removed from it. - # Applying lchop to an empty string returns an empty string. - # - # ``` - # "hello".lchop # => "ello" - # "".lchop # => "" - # ``` - # TODO: Implement this! - # def lchop - # return "" if empty? - - # reader = Char::Reader.new(self) - # unsafe_byte_slice_string(reader.current_char_width, bytesize - reader.current_char_width) - # end - - # Returns a new `String` with *prefix* removed from the beginning of the string. - # - # ``` - # "hello".lchop('h') # => "ello" - # "hello".lchop('g') # => "hello" - # ``` - # TODO: Implement this! - # def lchop(prefix : Char) - # if starts_with?(prefix) - # unsafe_byte_slice_string(prefix.bytesize, bytesize - prefix.bytesize) - # else - # self - # end - # end - - # Returns a new `String` with *prefix* removed from the beginning of the string. - # - # ``` - # "hello".lchop("hel") # => "lo" - # "hello".lchop("eh") # => "hello" - # ``` - # TODO: Implement this! - # def lchop(prefix : String) - # if starts_with?(prefix) - # unsafe_byte_slice_string(prefix.bytesize, bytesize - prefix.bytesize) - # else - # self - # end - # end - - # Returns a new `String` with the last character removed. - # Applying rchop to an empty string returns an empty string. - # - # ``` - # "string\r\n".rchop # => "string\r" - # "string\n\r".rchop # => "string\n" - # "string\n".rchop # => "string" - # "string".rchop # => "strin" - # "x".rchop.rchop # => "" - # ``` - # TODO: Implement this! - # def rchop - # return "" if bytesize <= 1 - - # if to_unsafe[bytesize - 1] < 128 || ascii_only? - # return unsafe_byte_slice_string(0, bytesize - 1) - # end - - # self[0, size - 1] - # end - - # Returns a new `String` with *suffix* removed from the end of the string. - # - # ``` - # "string".rchop('g') # => "strin" - # "string".rchop('x') # => "string" - # ``` - # TODO: Implement this! - # def rchop(suffix : Char) - # return "" if empty? - - # if ends_with?(suffix) - # unsafe_byte_slice_string(0, bytesize - suffix.bytesize) - # else - # self - # end - # end - - # Returns a new `String` with *suffix* removed from the end of the string. - # - # ``` - # "string".rchop("ing") # => "str" - # "string".rchop("inx") # => "string" - # ``` - # TODO: Implement this! - # def rchop(suffix : String) - # return "" if empty? - - # if ends_with?(suffix) - # unsafe_byte_slice_string(0, bytesize - suffix.bytesize) - # else - # self - # end - # end - - # Returns a slice of bytes containing this string encoded in the given encoding. - # - # The *invalid* argument can be: - # * `nil`: an exception is raised on invalid byte sequences - # * `:skip`: invalid byte sequences are ignored - # - # ``` - # "好".encode("GB2312") # => Bytes[186, 195] - # "好".bytes # => [229, 165, 189] - # ``` - # TODO: Implement this! - # def encode(encoding : String, invalid : Symbol? = nil) : Bytes - # io = IO::Memory.new - # String.encode(to_slice, "UTF-8", encoding, io, invalid) - # io.to_slice - # end - - # :nodoc: - # TODO: Implement this! - # protected def self.encode(slice, from, to, io, invalid) - # IO::EncodingOptions.check_invalid(invalid) - - # inbuf_ptr = slice.to_unsafe - # inbytesleft = LibC::SizeT.new(slice.size) - # outbuf = uninitialized UInt8[1024] - - # Iconv.new(from, to, invalid) do |iconv| - # while inbytesleft > 0 - # outbuf_ptr = outbuf.to_unsafe - # outbytesleft = LibC::SizeT.new(outbuf.size) - # err = iconv.convert(pointerof(inbuf_ptr), pointerof(inbytesleft), pointerof(outbuf_ptr), pointerof(outbytesleft)) - # if err == -1 - # iconv.handle_invalid(pointerof(inbuf_ptr), pointerof(inbytesleft)) - # end - # io.write(outbuf.to_slice[0, outbuf.size - outbytesleft]) - # end - # end - # end - - # Interprets this string as containing a sequence of hexadecimal values - # and decodes it as a slice of bytes. Two consecutive bytes in the string - # represent a byte in the returned slice. - # - # Raises `ArgumentError` if this string does not denote an hexstring. - # - # ``` - # "0102031aff".hexbytes # => Bytes[1, 2, 3, 26, 255] - # "1".hexbytes # raises ArgumentError - # "hello world".hexbytes # raises ArgumentError - # ``` - # TODO: Implement this! - # def hexbytes : Bytes - # hexbytes? || raise(ArgumentError.new("#{self} is not a hexstring")) - # end - - # Interprets this string as containing a sequence of hexadecimal values - # and decodes it as a slice of bytes. Two consecutive bytes in the string - # represent a byte in the returned slice. - # - # Returns `nil` if this string does not denote an hexstring. - # - # ``` - # "0102031aff".hexbytes? # => Bytes[1, 2, 3, 26, 255] - # "1".hexbytes? # => nil - # "hello world".hexbytes? # => nil - # ``` - # TODO: Implement this! - # def hexbytes? : Bytes? - # return unless bytesize.divisible_by?(2) - - # bytes = Bytes.new(bytesize / 2) - - # i = 0 - # while i < bytesize - # high_nibble = to_unsafe[i].unsafe_chr.to_u8?(16) - # low_nibble = to_unsafe[i + 1].unsafe_chr.to_u8?(16) - # return unless high_nibble && low_nibble - - # bytes[i / 2] = (high_nibble << 4) | low_nibble - # i += 2 - # end - - # bytes - # end - - # Returns a new `String` that results of inserting *other* in `self` at *index*. - # Negative indices count from the end of the string, and insert **after** - # the given index. - # - # Raises `IndexError` if the index is out of bounds. - # - # ``` - # "abcd".insert(0, 'X') # => "Xabcd" - # "abcd".insert(3, 'X') # => "abcXd" - # "abcd".insert(4, 'X') # => "abcdX" - # "abcd".insert(-3, 'X') # => "abXcd" - # "abcd".insert(-1, 'X') # => "abcdX" - # - # "abcd".insert(5, 'X') # raises IndexError - # "abcd".insert(-6, 'X') # raises IndexError - # ``` - # TODO: Implement this! - # def insert(index : Int, other : Char) - # index = index.to_i - # index += size + 1 if index < 0 - - # byte_index = char_index_to_byte_index(index) - # raise IndexError.new unless byte_index - - # bytes, count = String.char_bytes_and_bytesize(other) - - # new_bytesize = bytesize + count - # new_size = (ascii_only? && other.ascii?) ? new_bytesize : 0 - - # insert_impl(byte_index, bytes.to_unsafe, count, new_bytesize, new_size) - # end - - # Returns a new `String` that results of inserting *other* in `self` at *index*. - # Negative indices count from the end of the string, and insert **after** - # the given index. - # - # Raises `IndexError` if the index is out of bounds. - # - # ``` - # "abcd".insert(0, "FOO") # => "FOOabcd" - # "abcd".insert(3, "FOO") # => "abcFOOd" - # "abcd".insert(4, "FOO") # => "abcdFOO" - # "abcd".insert(-3, "FOO") # => "abFOOcd" - # "abcd".insert(-1, "FOO") # => "abcdFOO" - # - # "abcd".insert(5, "FOO") # raises IndexError - # "abcd".insert(-6, "FOO") # raises IndexError - # ``` - # TODO: Implement this! - # def insert(index : Int, other : String) - # index = index.to_i - # index += size + 1 if index < 0 - - # byte_index = char_index_to_byte_index(index) - # raise IndexError.new unless byte_index - - # new_bytesize = bytesize + other.bytesize - # new_size = ascii_only? && other.ascii_only? ? new_bytesize : 0 - - # insert_impl(byte_index, other.to_unsafe, other.bytesize, new_bytesize, new_size) - # end - # TODO: Implement this! - # private def insert_impl(byte_index, other, other_bytesize, new_bytesize, new_size) - # String.new(new_bytesize) do |buffer| - # buffer.copy_from(to_unsafe, byte_index) - # buffer += byte_index - # buffer.copy_from(other, other_bytesize) - # buffer += other_bytesize - # buffer.copy_from(to_unsafe + byte_index, bytesize - byte_index) - # {new_bytesize, new_size} - # end - # end - - # Returns a new `String` with leading and trailing whitespace removed. - # - # ``` - # " hello ".strip # => "hello" - # "\tgoodbye\r\n".strip # => "goodbye" - # ``` - # TODO: Implement this! - # def strip - # excess_left = calc_excess_left - # if excess_left == bytesize - # return "" - # end - - # excess_right = calc_excess_right - # remove_excess(excess_left, excess_right) - # end - - # Returns a new string where leading and trailing occurrences of *char* are removed. - # - # ``` - # "aaabcdaaa".strip('a') # => "bcd" - # ``` - # TODO: Implement this! - # def strip(char : Char) - # return self if empty? - - # excess_left = calc_excess_left(char) - # if excess_left == bytesize - # return "" - # end - - # excess_right = calc_excess_right(char) - # remove_excess(excess_left, excess_right) - # end - - # Returns a new string where leading and trailing occurrences of any char - # in *chars* are removed. The *chars* argument is not a prefix or suffix; - # rather; all combinations of its values are stripped. - # - # ``` - # "abcdefcba".strip("abc") # => "def" - # ``` - # TODO: Implement this! - # def strip(chars : String) - # return self if empty? - - # case chars.size - # when 0 - # return self - # when 1 - # return strip(chars[0]) - # end - - # excess_left = calc_excess_left(chars) - # if excess_left == bytesize - # return "" - # end - - # excess_right = calc_excess_right(chars) - # remove_excess(excess_left, excess_right) - # end - - # Returns a new string where leading and trailing characters for which - # the block returns a *truthy* value are removed. - # - # ``` - # "bcadefcba".strip { |c| 'a' <= c <= 'c' } # => "def" - # ``` - # TODO: Implement this! - # def strip(&block : Char -> _) - # return self if empty? - - # excess_left = calc_excess_left { |c| yield c } - # if excess_left == bytesize - # return "" - # end - - # excess_right = calc_excess_right { |c| yield c } - # remove_excess(excess_left, excess_right) - # end - - # Returns a new `String` with trailing whitespace removed. - # - # ``` - # " hello ".rstrip # => " hello" - # "\tgoodbye\r\n".rstrip # => "\tgoodbye" - # ``` - # TODO: Implement this! - # def rstrip - # remove_excess_right(calc_excess_right) - # end - - # Returns a new string with trailing occurrences of *char* removed. - # - # ``` - # "aaabcdaaa".rstrip('a') # => "aaabcd" - # ``` - # TODO: Implement this! - # def rstrip(char : Char) - # return self if empty? - - # remove_excess_right(calc_excess_right(char)) - # end - - # Returns a new string where trailing occurrences of any char - # in *chars* are removed. The *chars* argument is not a suffix; - # rather; all combinations of its values are stripped. - # - # ``` - # "abcdefcba".rstrip("abc") # => "abcdef" - # ``` - # TODO: Implement this! - # def rstrip(chars : String) - # return self if empty? - - # case chars.size - # when 0 - # self - # when 1 - # rstrip(chars[0]) - # else - # remove_excess_right(calc_excess_right(chars)) - # end - # end - - # Returns a new string where trailing characters for which - # the block returns a *truthy* value are removed. - # - # ``` - # "bcadefcba".rstrip { |c| 'a' <= c <= 'c' } # => "bcadef" - # ``` - # TODO: Implement this! - # def rstrip(&block : Char -> _) - # return self if empty? - - # excess_right = calc_excess_right { |c| yield c } - # remove_excess_right(excess_right) - # end - - # Returns a new `String` with leading whitespace removed. - # - # ``` - # " hello ".lstrip # => "hello " - # "\tgoodbye\r\n".lstrip # => "goodbye\r\n" - # ``` - # TODO: Implement this! - # def lstrip - # remove_excess_left(calc_excess_left) - # end - - # Returns a new string with leading occurrences of *char* removed. - # - # ``` - # "aaabcdaaa".lstrip('a') # => "bcdaaa" - # ``` - # TODO: Implement this! - # def lstrip(char : Char) - # return self if empty? - - # remove_excess_left(calc_excess_left(char)) - # end - - # Returns a new string where leading occurrences of any char - # in *chars* are removed. The *chars* argument is not a suffix; - # rather; all combinations of its values are stripped. - # - # ``` - # "bcadefcba".lstrip("abc") # => "defcba" - # ``` - # TODO: Implement this! - # def lstrip(chars : String) - # return self if empty? - - # case chars.size - # when 0 - # self - # when 1 - # lstrip(chars[0]) - # else - # remove_excess_left(calc_excess_left(chars)) - # end - # end - - # Returns a new string where leading characters for which - # the block returns a *truthy* value are removed. - # - # ``` - # "bcadefcba".lstrip { |c| 'a' <= c <= 'c' } # => "defcba" - # ``` - # TODO: Implement this! - # def lstrip(&block : Char -> _) - # return self if empty? - - # excess_left = calc_excess_left { |c| yield c } - # remove_excess_left(excess_left) - # end - # TODO: Implement these! - # private def calc_excess_right - # i = bytesize - 1 - # while i >= 0 && to_unsafe[i].unsafe_chr.ascii_whitespace? - # i -= 1 - # end - # bytesize - 1 - i - # end - - # private def calc_excess_right(char : Char) - # calc_excess_right do |reader_char| - # char == reader_char - # end - # end - - # private def calc_excess_right(chars : String) - # calc_excess_right do |reader_char| - # chars.includes?(reader_char) - # end - # end - - # private def calc_excess_right(&block) - # byte_index = bytesize - # reader = Char::Reader.new(at_end: self) - # while (yield reader.current_char) - # byte_index = reader.pos - # if byte_index == 0 - # return bytesize - # else - # reader.previous_char - # end - # end - # bytesize - byte_index - # end - # TODO: Implement these! - # private def calc_excess_left - # excess_left = 0 - # # All strings end with '\0', and it's not a whitespace - # # so it's safe to access past 1 byte beyond the string data - # while to_unsafe[excess_left].unsafe_chr.ascii_whitespace? - # excess_left += 1 - # end - # excess_left - # end - - # private def calc_excess_left(char : Char) - # calc_excess_left do |reader_char| - # char == reader_char - # end - # end - - # private def calc_excess_left(chars : String) - # calc_excess_left do |reader_char| - # chars.includes?(reader_char) - # end - # end - - # private def calc_excess_left(&block) - # reader = Char::Reader.new(self) - # while (yield reader.current_char) - # reader.next_char - # return bytesize unless reader.has_next? - # end - # reader.pos - # end - # TODO: Implement these! - # private def remove_excess(excess_left, excess_right) - # if excess_right == 0 && excess_left == 0 - # self - # else - # unsafe_byte_slice_string(excess_left, bytesize - excess_right - excess_left) - # end - # end - - # private def remove_excess_right(excess_right) - # case excess_right - # when 0 - # self - # when bytesize - # "" - # else - # unsafe_byte_slice_string(0, bytesize - excess_right) - # end - # end - - # private def remove_excess_left(excess_left) - # case excess_left - # when 0 - # self - # when bytesize - # "" - # else - # unsafe_byte_slice_string(excess_left) - # end - # end - - # Returns a new string _tr_anslating characters using *from* and *to* as a - # map. If *to* is shorter than *from*, the last character in *to* is used for - # the rest. If *to* is empty, this acts like `String#delete`. - # - # ``` - # "aabbcc".tr("abc", "xyz") # => "xxyyzz" - # "aabbcc".tr("abc", "x") # => "xxxxxx" - # "aabbcc".tr("a", "xyz") # => "xxbbcc" - # ``` - # TODO: Implement this! - # def tr(from : String, to : String) - # return delete(from) if to.empty? - # multi = nil - # table = StaticArray(Int32, 256).new(-1) - # reader = Char::Reader.new(to) - # char = reader.current_char - # next_char = reader.next_char - # from.each_char do |ch| - # if ch.ord >= 256 - # multi ||= {} of Char => Char - # multi[ch] = char - # else - # table[ch.ord] = char.ord - # end - # if next_char != Char::ZERO - # char = next_char - # reader.next_char - # next_char = reader.current_char - # end - # end - - # String.build(bytesize) do |buffer| - # each_char do |ch| - # if ch.ord < 256 - # if (a = table[ch.ord]) >= 0 - # buffer << a.unsafe_chr - # else - # buffer << ch - # end - # else - # if a = multi.try &.[ch]? - # buffer << a - # else - # buffer << ch - # end - # end - # end - # end - # end - - # Returns a new `String` where the first character is yielded to the given - # block and replaced by its return value. - # - # ``` - # "hello".sub { |char| char + 1 } # => "iello" - # "hello".sub { "hi" } # => "hiello" - # ``` - # TODO: Implement this! - # def sub(&block : Char -> _) - # return self if empty? - - # String.build(bytesize) do |buffer| - # reader = Char::Reader.new(self) - # buffer << yield reader.current_char - # reader.next_char - # buffer.write unsafe_byte_slice(reader.pos) - # end - # end - - # Returns a `String` where the first occurrence of *char* is replaced by - # *replacement*. - # - # ``` - # "hello".sub('l', "lo") # => "helolo" - # "hello world".sub('o', 'a') # => "hella world" - # ``` - # TODO: Implement this! - # def sub(char : Char, replacement) - # if includes?(char) - # String.build(bytesize) do |buffer| - # reader = Char::Reader.new(self) - # while reader.has_next? - # if reader.current_char == char - # buffer << replacement - # break - # else - # buffer << reader.current_char - # end - # reader.next_char - # end - # reader.next_char - # buffer.write unsafe_byte_slice(reader.pos) - # end - # else - # self - # end - # end - - # Returns a `String` where the first occurrence of *pattern* is replaced by - # the block's return value. - # - # ``` - # "hello".sub(/./) { |s| s[0].ord.to_s + ' ' } # => "104 ello" - # ``` - # TODO: Implement this! - # def sub(pattern : Regex) - # sub_append(pattern) do |str, match, buffer| - # $~ = match - # buffer << yield str, match - # end - # end - - # Returns a `String` where the first occurrence of *pattern* is replaced by - # *replacement* - # - # ``` - # "hello".sub(/[aeiou]/, "*") # => "h*llo" - # ``` - # - # Within *replacement*, the special match variable `$~` will not refer to the - # current match. - # - # If *backreferences* is `true` (the default value), *replacement* can include backreferences: - # - # ``` - # "hello".sub(/[aeiou]/, "(\\0)") # => "h(e)llo" - # ``` - # - # When substitution is performed, any backreferences found in *replacement* - # will be replaced with the contents of the corresponding capture group in - # *pattern*. Backreferences to capture groups that were not present in - # *pattern* or that did not match will be skipped. See `Regex` for information - # about capture groups. - # - # Backreferences are expressed in the form `"\\d"`, where *d* is a group - # number, or `"\\k<name>"` where *name* is the name of a named capture group. - # A sequence of literal characters resembling a backreference can be - # expressed by placing `"\\"` before the sequence. - # - # ``` - # "foo".sub(/o/, "x\\0x") # => "fxoxo" - # "foofoo".sub(/(?oo)/, "|\\k|") # => "f|oo|foo" - # "foo".sub(/o/, "\\\\0") # => "f\\0o" - # ``` - # - # Raises `ArgumentError` if an incomplete named back-reference is present in - # *replacement*. - # - # Raises `IndexError` if a named group referenced in *replacement* is not present - # in *pattern*. - # TODO: Implement this! - # def sub(pattern : Regex, replacement, backreferences = true) - # if backreferences && replacement.is_a?(String) && replacement.has_back_references? - # sub_append(pattern) { |_, match, buffer| scan_backreferences(replacement, match, buffer) } - # else - # sub(pattern) { replacement } - # end - # end - - # Returns a `String` where the first occurrences of the given *pattern* is replaced - # with the matching entry from the *hash* of replacements. If the first match - # is not included in the *hash*, nothing is replaced. - # - # ``` - # "hello".sub(/(he|l|o)/, {"he": "ha", "l": "la"}) # => "hallo" - # "hello".sub(/(he|l|o)/, {"l": "la"}) # => "hello" - # ``` - # TODO: Implement this! - # def sub(pattern : Regex, hash : Hash(String, _) | NamedTuple) - # sub(pattern) do |match| - # if hash.has_key?(match) - # hash[match] - # else - # return self - # end - # end - # end - - # Returns a `String` where the first occurrences of the given *string* is replaced - # with the given *replacement*. - # - # ``` - # "hello yellow".sub("ll", "dd") # => "heddo yellow" - # ``` - # TODO: Implement this! - # def sub(string : String, replacement) - # sub(string) { replacement } - # end - - # Returns a `String` where the first occurrences of the given *string* is replaced - # with the block's value. - # - # ``` - # "hello yellow".sub("ll") { "dd" } # => "heddo yellow" - # ``` - # TODO: Implement this! - # def sub(string : String, &block) - # index = self.byte_index(string) - # return self unless index - - # String.build(bytesize) do |buffer| - # buffer.write unsafe_byte_slice(0, index) - # buffer << yield string - # buffer.write unsafe_byte_slice(index + string.bytesize) - # end - # end - - # Returns a `String` where the first char in the string matching a key in the - # given *hash* is replaced by the corresponding hash value. - # - # ``` - # "hello".sub({'a' => 'b', 'l' => 'd'}) # => "hedlo" - # ``` - # TODO: Implement this! - # def sub(hash : Hash(Char, _)) - # return self if empty? - - # String.build(bytesize) do |buffer| - # reader = Char::Reader.new(self) - # while reader.has_next? - # if hash.has_key?(reader.current_char) - # buffer << hash[reader.current_char] - # reader.next_char - # break - # else - # buffer << reader.current_char - # reader.next_char - # end - # end - - # buffer << reader.current_char - - # if reader.has_next? - # reader.next_char - # buffer.write unsafe_byte_slice(reader.pos) - # end - # end - # end - - # Returns a new `String` with the character at the given index - # replaced by *replacement*. - # - # ``` - # "hello".sub(1, 'a') # => "hallo" - # ``` - # TODO: Implement this! - # def sub(index : Int, replacement : Char) - # sub_index(index.to_i, replacement) do |buffer| - # replacement.each_byte do |byte| - # buffer.value = byte - # buffer += 1 - # end - # {buffer, @length} - # end - # end - - # Returns a new `String` with the character at the given index - # replaced by *replacement*. - # - # ``` - # "hello".sub(1, "eee") # => "heeello" - # ``` - # TODO: Implement this! - # def sub(index : Int, replacement : String) - # sub_index(index.to_i, replacement) do |buffer| - # buffer.copy_from(replacement.to_unsafe, replacement.bytesize) - # buffer += replacement.bytesize - # {buffer, self.size_known? && replacement.size_known? ? self.size + replacement.size - 1 : 0} - # end - # end - # TODO: Implement this! - # private def sub_index(index, replacement) - # index += size + 1 if index < 0 - - # byte_index = char_index_to_byte_index(index) - # raise IndexError.new unless byte_index - - # reader = Char::Reader.new(self, pos: byte_index) - # width = reader.current_char_width - # replacement_width = replacement.bytesize - # new_bytesize = bytesize - width + replacement_width - - # String.new(new_bytesize) do |buffer| - # buffer.copy_from(to_unsafe, byte_index) - # buffer += byte_index - # buffer, length = yield buffer - # buffer.copy_from(to_unsafe + byte_index + width, bytesize - byte_index - width) - # {new_bytesize, length} - # end - # end - - # Returns a new `String` with characters at the given range - # replaced by *replacement*. - # - # ``` - # "hello".sub(1..2, 'a') # => "halo" - # ``` - # TODO: Implement this! - # def sub(range : Range(Int, Int), replacement : Char) - # sub_range(range, replacement) do |buffer, from_index, to_index| - # replacement.each_byte do |byte| - # buffer.value = byte - # buffer += 1 - # end - # {buffer, ascii_only? ? bytesize - (to_index - from_index) + 1 : 0} - # end - # end - - # Returns a new `String` with characters at the given range - # replaced by *replacement*. - # - # ``` - # "hello".sub(1..2, "eee") # => "heeelo" - # ``` - # TODO: Implement this! - # def sub(range : Range(Int, Int), replacement : String) - # sub_range(range, replacement) do |buffer| - # buffer.copy_from(replacement.to_unsafe, replacement.bytesize) - # buffer += replacement.bytesize - # {buffer, 0} - # end - # end - # TODO: Implement this! - # private def sub_range(range, replacement) - # from, size = Indexable.range_to_index_and_count(range, self.size) - - # from_index = char_index_to_byte_index(from) - # raise IndexError.new unless from_index - - # if size == 0 - # to_index = from_index - # else - # to_index = char_index_to_byte_index(from + size) - # raise IndexError.new unless to_index - # end - - # new_bytesize = bytesize - (to_index - from_index) + replacement.bytesize - - # String.new(new_bytesize) do |buffer| - # buffer.copy_from(to_unsafe, from_index) - # buffer += from_index - # buffer, length = yield buffer, from_index, to_index - # buffer.copy_from(to_unsafe + to_index, bytesize - to_index) - # {new_bytesize, length} - # end - # end - - # This returns `true` if this string has `'\\'` in it. It might not be a back reference, - # but `'\\'` is probably used for back references, so this check is faster than parsing - # the whole thing. - # TODO: Implement this! - # def has_back_references? - # to_slice.index('\\'.ord.to_u8) - # end - # TODO: Implement this! - # private def scan_backreferences(replacement, match_data, buffer) - # # We only append to the buffer in chunks, so if we have "foo\\1", we remember that - # # the chunk starts at index 0 (first_index) and when we find a "\\" we append - # # from 0 to 3 in a single write. When we find a "\\0" or "\\k<...>", we append - # # from the first_index, process the backreference, and then reset first_index - # # to the new index. - # first_index = 0 - # index = 0 - - # while index = replacement.byte_index('\\'.ord.to_u8, index) - # index += 1 - # chr = replacement.to_unsafe[index].unsafe_chr - # case chr - # when '\\' - # buffer.write(replacement.unsafe_byte_slice(first_index, index - first_index)) - # index += 1 - # first_index = index - # when '0'..'9' - # buffer.write(replacement.unsafe_byte_slice(first_index, index - 1 - first_index)) - # buffer << match_data[chr - '0']? - # index += 1 - # first_index = index - # when 'k' - # index += 1 - # chr = replacement.to_unsafe[index].unsafe_chr - # next unless chr == '<' - - # buffer.write(replacement.unsafe_byte_slice(first_index, index - 2 - first_index)) - - # index += 1 - # start_index = index - # end_index = replacement.byte_index('>'.ord.to_u8, start_index) - # raise ArgumentError.new("Missing ending '>' for '\\\\k<...'") unless end_index - - # name = replacement.byte_slice(start_index, end_index - start_index) - # capture = match_data[name]? - # raise IndexError.new("Undefined group name reference: #{name.inspect}") unless capture - - # buffer << capture - # index = end_index + 1 - # first_index = index - # end - # end - - # if first_index != replacement.bytesize - # buffer.write(replacement.unsafe_byte_slice(first_index)) - # end - # end - - # Returns a `String` where each character yielded to the given block - # is replaced by the block's return value. - # - # ``` - # "hello".gsub { |char| char + 1 } # => "ifmmp" - # "hello".gsub { "hi" } # => "hihihihihi" - # ``` - # TODO: Implement this! - # def gsub(&block : Char -> _) - # String.build(bytesize) do |buffer| - # each_char do |my_char| - # buffer << yield my_char - # end - # end - # end - - # Returns a `String` where all occurrences of the given char are - # replaced with the given *replacement*. - # - # ``` - # "hello".gsub('l', "lo") # => "heloloo" - # "hello world".gsub('o', 'a') # => "hella warld" - # ``` - # TODO: Implement this! - # def gsub(char : Char, replacement) - # if includes?(char) - # gsub { |my_char| char == my_char ? replacement : my_char } - # else - # self - # end - # end - - # Returns a `String` where all occurrences of the given *pattern* are replaced - # by the block value's value. - # - # ``` - # "hello".gsub(/./) { |s| s[0].ord.to_s + ' ' } # => "104 101 108 108 111 " - # ``` - # TODO: Implement this! - # def gsub(pattern : Regex) - # gsub_append(pattern) do |string, match, buffer| - # $~ = match - # buffer << yield string, match - # end - # end - - # Returns a `String` where all occurrences of the given *pattern* are replaced - # with the given *replacement*. - # - # ``` - # "hello".gsub(/[aeiou]/, '*') # => "h*ll*" - # ``` - # - # Within *replacement*, the special match variable `$~` will not refer to the - # current match. - # - # If *backreferences* is `true` (the default value), *replacement* can include backreferences: - # - # ``` - # "hello".gsub(/[aeiou]/, "(\\0)") # => "h(e)ll(o)" - # ``` - # - # When substitution is performed, any backreferences found in *replacement* - # will be replaced with the contents of the corresponding capture group in - # *pattern*. Backreferences to capture groups that were not present in - # *pattern* or that did not match will be skipped. See `Regex` for information - # about capture groups. - # - # Backreferences are expressed in the form `"\\d"`, where *d* is a group - # number, or `"\\k"` where *name* is the name of a named capture group. - # A sequence of literal characters resembling a backreference can be - # expressed by placing `"\\"` before the sequence. - # - # ``` - # "foo".gsub(/o/, "x\\0x") # => "fxoxxox" - # "foofoo".gsub(/(?oo)/, "|\\k|") # => "f|oo|f|oo|" - # "foo".gsub(/o/, "\\\\0") # => "f\\0\\0" - # ``` - # - # Raises `ArgumentError` if an incomplete named back-reference is present in - # *replacement*. - # - # Raises `IndexError` if a named group referenced in *replacement* is not present - # in *pattern*. - # TODO: Implement this! - # def gsub(pattern : Regex, replacement, backreferences = true) - # if backreferences && replacement.is_a?(String) && replacement.has_back_references? - # gsub_append(pattern) { |_, match, buffer| scan_backreferences(replacement, match, buffer) } - # else - # gsub(pattern) { replacement } - # end - # end - - # Returns a `String` where all occurrences of the given *pattern* are replaced - # with a *hash* of replacements. If the *hash* contains the matched pattern, - # the corresponding value is used as a replacement. Otherwise the match is - # not included in the returned string. - # - # ``` - # # "he" and "l" are matched and replaced, - # # but "o" is not and so is not included - # "hello".gsub(/(he|l|o)/, {"he": "ha", "l": "la"}) # => "halala" - # ``` - # Implement this! - # def gsub(pattern : Regex, hash : Hash(String, _) | NamedTuple) - # gsub(pattern) do |match| - # hash[match]? - # end - # end - - # Returns a `String` where all occurrences of the given *string* are replaced - # with the given *replacement*. - # - # ``` - # "hello yellow".gsub("ll", "dd") # => "heddo yeddow" - # ``` - # TODO: Implement this! - # def gsub(string : String, replacement) - # gsub(string) { replacement } - # end - - # Returns a `String` where all occurrences of the given *string* are replaced - # with the block's value. - # - # ``` - # "hello yellow".gsub("ll") { "dd" } # => "heddo yeddow" - # ``` - # TODO: Implement this! - # def gsub(string : String, &block) - # byte_offset = 0 - # index = self.byte_index(string, byte_offset) - # return self unless index - - # last_byte_offset = 0 - - # String.build(bytesize) do |buffer| - # while index - # buffer.write unsafe_byte_slice(last_byte_offset, index - last_byte_offset) - # buffer << yield string - - # if string.bytesize == 0 - # byte_offset = index + 1 - # last_byte_offset = index - # else - # byte_offset = index + string.bytesize - # last_byte_offset = byte_offset - # end - - # index = self.byte_index(string, byte_offset) - # end - - # if last_byte_offset < bytesize - # buffer.write unsafe_byte_slice(last_byte_offset) - # end - # end - # end - - # Returns a `String` where all chars in the given hash are replaced - # by the corresponding *hash* values. - # - # ``` - # "hello".gsub({'e' => 'a', 'l' => 'd'}) # => "haddo" - # ``` - # TODO: Implement this! - # def gsub(hash : Hash(Char, _)) - # gsub do |char| - # hash[char]? || char - # end - # end - - # Returns a `String` where all chars in the given named tuple are replaced - # by the corresponding *tuple* values. - # - # ``` - # "hello".gsub({e: 'a', l: 'd'}) # => "haddo" - # ``` - # TODO: Implement this! - # def gsub(tuple : NamedTuple) - # gsub do |char| - # tuple[char.to_s]? || char - # end - # end - # TODO: Implement this! - # private def gsub_append(pattern : Regex) - # byte_offset = 0 - # match = pattern.match_at_byte_index(self, byte_offset) - # return self unless match - - # last_byte_offset = 0 - - # String.build(bytesize) do |buffer| - # while match - # index = match.byte_begin(0) - - # buffer.write unsafe_byte_slice(last_byte_offset, index - last_byte_offset) - # str = match[0] - # $~ = match - # yield str, match, buffer - - # if str.bytesize == 0 - # byte_offset = index + 1 - # last_byte_offset = index - # else - # byte_offset = index + str.bytesize - # last_byte_offset = byte_offset - # end - - # match = pattern.match_at_byte_index(self, byte_offset) - # end - - # if last_byte_offset < bytesize - # buffer.write unsafe_byte_slice(last_byte_offset) - # end - # end - # end - - # Yields each char in this string to the block, - # returns the number of times the block returned a truthy value. - # - # ``` - # "aabbcc".count { |c| ['a', 'b'].includes?(c) } # => 4 - # ``` - # TODO: Implement this! - # def count - # count = 0 - # each_char do |char| - # count += 1 if yield char - # end - # count - # end - - # Counts the occurrences of *other* char in this string. - # - # ``` - # "aabbcc".count('a') # => 2 - # ``` - # TODO: Implement this! - # def count(other : Char) - # count { |char| char == other } - # end - - # Sets should be a list of strings following the rules - # described at `Char#in_set?`. Returns the number of characters - # in this string that match the given set. - # TODO: Implement this! - # def count(*sets) - # count { |char| char.in_set?(*sets) } - # end - - # Yields each char in this string to the block. - # Returns a new `String` with all characters for which the - # block returned a truthy value removed. - # - # ``` - # "aabbcc".delete { |c| ['a', 'b'].includes?(c) } # => "cc" - # ``` - # TODO: Implement this! - # def delete - # String.build(bytesize) do |buffer| - # each_char do |char| - # buffer << char unless yield char - # end - # end - # end - - # Returns a new `String` with all occurrences of *char* removed. - # - # ``` - # "aabbcc".delete('b') # => "aacc" - # ``` - # TODO: Implement this! - # def delete(char : Char) - # delete { |my_char| my_char == char } - # end - - # Sets should be a list of strings following the rules - # described at `Char#in_set?`. Returns a new `String` with - # all characters that match the given set removed. - # - # ``` - # "aabbccdd".delete("a-c") # => "dd" - # ``` - # TODO: Implement this! - # def delete(*sets) - # delete { |char| char.in_set?(*sets) } - # end - - # Yields each char in this string to the block. - # Returns a new `String`, that has all characters removed, - # that were the same as the previous one and for which the given - # block returned a truthy value. - # - # ``` - # "aaabbbccc".squeeze { |c| ['a', 'b'].includes?(c) } # => "abccc" - # "aaabbbccc".squeeze { |c| ['a', 'c'].includes?(c) } # => "abbbc" - # ``` - # TODO: Implement this! - # def squeeze - # previous = nil - # String.build(bytesize) do |buffer| - # each_char do |char| - # buffer << char unless yield(char) && previous == char - # previous = char - # end - # end - # end - - # Returns a new `String`, with all runs of char replaced by one instance. - # - # ``` - # "a bbb".squeeze(' ') # => "a bbb" - # ``` - # TODO: Implement this! - # def squeeze(char : Char) - # squeeze { |my_char| char == my_char } - # end - - # Sets should be a list of strings following the rules - # described at `Char#in_set?`. Returns a new `String` with all - # runs of the same character replaced by one instance, if - # they match the given set. - # - # If no set is given, all characters are matched. - # - # ``` - # "aaabbbcccddd".squeeze("b-d") # => "aaabcd" - # "a bbb".squeeze # => "a b" - # ``` - # TODO: Implement this! - # def squeeze(*sets : String) - # squeeze { |char| char.in_set?(*sets) } - # end - - # Returns a new `String`, that has all characters removed, - # that were the same as the previous one. - # - # ``` - # "a bbb".squeeze # => "a b" - # ``` - # TODO: Implement this! - # def squeeze - # squeeze { true } - # end - - # Returns `true` if this is the empty string, `""`. - def empty? - bytesize == 0 - end - - # Returns `true` if this string consists exclusively of unicode whitespace. - # - # ``` - # "".blank? # => true - # " ".blank? # => true - # " a ".blank? # => false - # ``` - # TODO: Implement this! - # def blank? - # each_char do |char| - # return false unless char.whitespace? - # end - # true - # end - # TODO: Implement this! - # def ==(other : self) - # return true if same?(other) - # return false unless bytesize == other.bytesize - # to_unsafe.memcmp(other.to_unsafe, bytesize) == 0 - # end - - # Compares this string with *other*, returning `-1`, `0` or `+1` depending on whether - # this string is less, equal or greater than *other*. - # - # Comparison is done byte-per-byte: if a byte is less then the other corresponding - # byte, `-1` is returned and so on. - # - # If the strings are of different lengths, and the strings are equal when compared - # up to the shortest length, then the longer string is considered greater than - # the shorter one. - # - # ``` - # "abcdef" <=> "abcde" # => 1 - # "abcdef" <=> "abcdef" # => 0 - # "abcdef" <=> "abcdefg" # => -1 - # "abcdef" <=> "ABCDEF" # => 1 - # ``` - # TODO: Implement this! - # def <=>(other : self) - # return 0 if same?(other) - # min_bytesize = Math.min(bytesize, other.bytesize) - - # cmp = to_unsafe.memcmp(other.to_unsafe, min_bytesize) - # cmp == 0 ? (bytesize <=> other.bytesize) : cmp.sign - # end - - # Compares this string with *other*, returning `-1`, `0` or `+1` depending on whether - # this string is less, equal or greater than *other*, optionally in a *case_insensitive* - # manner. - # - # If *case_insitive* is `false`, this method delegates to `<=>`. Otherwise, - # the strings are compared char-by-char, and ASCII characters are compared in a - # case-insensitive way. - # - # ``` - # "abcdef".compare("abcde") # => 1 - # "abcdef".compare("abcdef") # => 0 - # "abcdef".compare("abcdefg") # => -1 - # "abcdef".compare("ABCDEF") # => 1 - # - # "abcdef".compare("ABCDEF", case_insensitive: true) # => 0 - # "abcdef".compare("ABCDEG", case_insensitive: true) # => -1 - # ``` - # TODO: Implement this! - # def compare(other : String, case_insensitive = false) - # return self <=> other unless case_insensitive - - # reader1 = Char::Reader.new(self) - # reader2 = Char::Reader.new(other) - # ch1 = reader1.current_char - # ch2 = reader2.current_char - - # while reader1.has_next? && reader2.has_next? - # cmp = ch1.downcase <=> ch2.downcase - # return cmp.sign if cmp != 0 - - # ch1 = reader1.next_char - # ch2 = reader2.next_char - # end - - # if reader1.has_next? - # 1 - # elsif reader2.has_next? - # -1 - # else - # 0 - # end - # end - - # Tests whether *str* matches *regex*. - # If successful, it returns the position of the first match. - # If unsuccessful, it returns `nil`. - # - # If the argument isn't a `Regex`, it returns `nil`. - # - # ``` - # "Haystack" =~ /ay/ # => 1 - # "Haystack" =~ /z/ # => nil - # - # "Haystack" =~ 45 # => nil - # ``` - # TODO: Implement this! - # def =~(regex : Regex) - # match = regex.match(self) - # $~ = match - # match.try &.begin(0) - # end - - # ditto - # TODO: Implement this! - # def =~(other) - # nil - # end - - # Concatenates *str* and *other*. - # - # ``` - # "abc" + "def" # => "abcdef" - # "abc" + 'd' # => "abcd" - # ``` - # TODO: Implement this! - # def +(other : self) - # return self if other.empty? - # return other if self.empty? - - # size = bytesize + other.bytesize - # String.new(size) do |buffer| - # buffer.copy_from(to_unsafe, bytesize) - # (buffer + bytesize).copy_from(other.to_unsafe, other.bytesize) - - # if size_known? && other.size_known? - # {size, @length + other.@length} - # else - # {size, 0} - # end - # end - # end - - # ditto - # TODO: Implement this! - # def +(char : Char) - # bytes, count = String.char_bytes_and_bytesize(char) - # size = bytesize + count - # String.new(size) do |buffer| - # buffer.copy_from(to_unsafe, bytesize) - # (buffer + bytesize).copy_from(bytes.to_unsafe, count) - - # if size_known? - # {size, @length + 1} - # else - # {size, 0} - # end - # end - # end - - # Makes a new `String` by adding *str* to itself *times* times. - # - # ``` - # "Developers! " * 4 - # # => "Developers! Developers! Developers! Developers!" - # ``` - # TODO: Implement this! - # def *(times : Int) - # raise ArgumentError.new "Negative argument" if times < 0 - - # if times == 0 || bytesize == 0 - # return "" - # elsif bytesize == 1 - # return String.new(times) do |buffer| - # Intrinsics.memset(buffer.as(Void*), to_unsafe[0], times, 0, false) - # {times, times} - # end - # end - - # total_bytesize = bytesize * times - # String.new(total_bytesize) do |buffer| - # buffer.copy_from(to_unsafe, bytesize) - # n = bytesize - - # while n <= total_bytesize / 2 - # (buffer + n).copy_from(buffer, n) - # n *= 2 - # end - - # (buffer + n).copy_from(buffer, total_bytesize - n) - # {total_bytesize, @length * times} - # end - # end - - # Prime number constant for Rabin-Karp algorithm `String#index`. - private PRIME_RK = 2097169u32 - - # Update rolling hash for Rabin-Karp algorithm `String#index`. - # TODO: Implement this! - # private macro update_hash(n) - # {% for i in 1..n %} - # {% if i != 1 %} - # byte = head_pointer.value - # {% end %} - # hash = hash * PRIME_RK + pointer.value - pow * byte - # pointer += 1 - # head_pointer += 1 - # {% end %} - # end - - # Returns the index of *search* in the string, or `nil` if the string is not present. - # If *offset* is present, it defines the position to start the search. - # - # ``` - # "Hello, World".index('o') # => 4 - # "Hello, World".index('Z') # => nil - # "Hello, World".index("o", 5) # => 8 - # "Hello, World".index("H", 2) # => nil - # "Hello, World".index(/[ ]+/) # => 6 - # "Hello, World".index(/\d+/) # => nil - # ``` - # TODO: Implement this! - # def index(search : Char, offset = 0) - # # If it's ASCII we can delegate to slice - # if search.ascii? && ascii_only? - # return to_slice.index(search.ord.to_u8, offset) - # end - - # offset += size if offset < 0 - # return nil if offset < 0 - - # each_char_with_index do |char, i| - # if i >= offset && char == search - # return i - # end - # end - - # nil - # end - # TODO: Implement this! - # ditto - # def index(search : String, offset = 0) - # offset += size if offset < 0 - # return if offset < 0 - - # return size < offset ? nil : offset if search.empty? - - # # Rabin-Karp algorithm - # # https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm - - # # calculate a rolling hash of search text (needle) - # search_hash = 0u32 - # search.each_byte do |b| - # search_hash = search_hash * PRIME_RK + b - # end - # pow = PRIME_RK ** search.bytesize - - # # Find start index with offset - # char_index = 0 - # pointer = to_unsafe - # end_pointer = pointer + bytesize - # while char_index < offset && pointer < end_pointer - # byte = pointer.value - # if byte < 0x80 - # pointer += 1 - # elsif byte < 0xe0 - # pointer += 2 - # elsif byte < 0xf0 - # pointer += 3 - # else - # pointer += 4 - # end - # char_index += 1 - # end - - # head_pointer = pointer - - # # calculate a rolling hash of this text (haystack) - # hash = 0u32 - # hash_end_pointer = pointer + search.bytesize - # return if hash_end_pointer > end_pointer - # while pointer < hash_end_pointer - # hash = hash * PRIME_RK + pointer.value - # pointer += 1 - # end - - # while true - # # check hash equality and real string equality - # if hash == search_hash && head_pointer.memcmp(search.to_unsafe, search.bytesize) == 0 - # return char_index - # end - - # return if pointer >= end_pointer - - # byte = head_pointer.value - - # # update a rolling hash of this text (heystack) - # # thanks @MaxLap for suggesting this loop reduction - # if byte < 0x80 - # update_hash 1 - # elsif byte < 0xe0 - # update_hash 2 - # elsif byte < 0xf0 - # update_hash 3 - # else - # update_hash 4 - # end - # char_index += 1 - # end - # end - - # ditto - # TODO: Implement this! - # def index(search : Regex, offset = 0) - # offset += size if offset < 0 - # return nil unless 0 <= offset <= size - - # self.match(search, offset).try &.begin - # end - - # Returns the index of the _last_ appearance of *search* in the string, - # If *offset* is present, it defines the position to _end_ the search - # (characters beyond this point are ignored). - # - # ``` - # "Hello, World".rindex('o') # => 8 - # "Hello, World".rindex('Z') # => nil - # "Hello, World".rindex("o", 5) # => 4 - # "Hello, World".rindex("W", 2) # => nil - # ``` - # TODO: Implement this! - # def rindex(search : Char, offset = size - 1) - # # If it's ASCII we can delegate to slice - # if search.ascii? && ascii_only? - # return to_slice.rindex(search.ord.to_u8, offset) - # end - - # offset += size if offset < 0 - # return nil if offset < 0 - - # if offset == size - 1 - # reader = Char::Reader.new(at_end: self) - # else - # byte_index = char_index_to_byte_index(offset) - # raise IndexError.new unless byte_index - # reader = Char::Reader.new(self, pos: byte_index) - # end - - # while true - # if reader.current_char == search - # return offset - # elsif reader.has_previous? - # reader.previous_char - # offset -= 1 - # else - # return nil - # end - # end - # end - - # ditto - # TODO: Implement this! - # def rindex(search : String, offset = size - search.size) - # offset += size if offset < 0 - # return if offset < 0 - - # # Rabin-Karp algorithm - # # https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm - - # # calculate a rolling hash of search text (needle) - # search_hash = 0u32 - # search.to_slice.reverse_each do |b| - # search_hash = search_hash * PRIME_RK + b - # end - # pow = PRIME_RK ** search.bytesize - - # hash = 0u32 - # char_index = size - - # begin_pointer = to_unsafe - # pointer = begin_pointer + bytesize - # tail_pointer = pointer - # hash_begin_pointer = pointer - search.bytesize - - # return if hash_begin_pointer < begin_pointer - - # # calculate a rolling hash of this text (haystack) - # while hash_begin_pointer < pointer - # pointer -= 1 - # byte = pointer.value - # char_index -= 1 if (byte & 0xC0) != 0x80 - - # hash = hash * PRIME_RK + byte - # end - - # while true - # # check hash equality and real string equality - # if hash == search_hash && char_index <= offset && - # pointer.memcmp(search.to_unsafe, search.bytesize) == 0 - # return char_index - # end - - # return if begin_pointer == pointer - - # pointer -= 1 - # tail_pointer -= 1 - # byte = pointer.value - # char_index -= 1 if (byte & 0xC0) != 0x80 - - # # update a rolling hash of this text (haystack) - # hash = hash * PRIME_RK + byte - pow * tail_pointer.value - # end - # end - - # ditto - # TODO: Implement this! - # def rindex(search : Regex, offset = 0) - # offset += size if offset < 0 - # return nil unless 0 <= offset <= size - - # match_result = nil - # self[0, self.size - offset].scan(search) do |match_data| - # match_result = match_data - # end - - # match_result.try &.begin(0) - # end - - # Searches separator or pattern (`Regex`) in the string, and returns - # a `Tuple` with the part before it, the match, and the part after it. - # If it is not found, returns str followed by two empty strings. - # - # ``` - # "hello".partition("l") # => {"he", "l", "lo"} - # "hello".partition("x") # => {"hello", "", ""} - # ``` - # TODO: Implement this! - # def partition(search : (Char | String)) : Tuple(String, String, String) - # pre = mid = post = "" - # search_size = search.is_a?(Char) ? 1 : search.size - # case pos = self.index(search) - # when .nil? - # pre = self - # when 0 - # mid = search.to_s - # post = self[(pos + search_size)..-1] - # else - # pre = self[0..(pos - 1)] - # mid = search.to_s - # post = self[(pos + search_size)..-1] - # end - # {pre, mid, post} - # end - - # ditto - # TODO: Implement this! - # def partition(search : Regex) : Tuple(String, String, String) - # pre = mid = post = "" - # case m = self.match(search) - # when .nil? - # pre = self - # else - # pre = m.pre_match - # mid = m[0] - # post = m.post_match - # end - # {pre, mid, post} - # end - - # Searches separator or pattern (`Regex`) in the string from the end of the string, - # and returns a `Tuple` with the part before it, the match, and the part after it. - # If it is not found, returns two empty strings and str. - # - # ``` - # "hello".rpartition("l") # => {"hel", "l", "o"} - # "hello".rpartition("x") # => {"", "", "hello"} - # "hello".rpartition(/.l/) # => {"he", "ll", "o"} - # ``` - # TODO: Implement this! - # def rpartition(search : (Char | String)) : Tuple(String, String, String) - # pos = self.rindex(search) - # search_size = search.is_a?(Char) ? 1 : search.size - - # pre = mid = post = "" - - # case pos - # when .nil? - # post = self - # when 0 - # mid = search.to_s - # post = self[(pos + search_size)..-1] - # else - # pre = self[0..(pos - 1)] - # mid = search.to_s - # post = self[(pos + search_size)..-1] - # end - # {pre, mid, post} - # end - - # ditto - # TODO: Implement this! - # def rpartition(search : Regex) : Tuple(String, String, String) - # match_result = nil - # pos = self.size - 1 - - # while pos >= 0 - # self[pos..-1].scan(search) do |m| - # match_result = m - # end - # break unless match_result.nil? - # pos -= 1 - # end - - # pre = mid = post = "" - - # case - # when match_result.nil? - # post = self - # when pos == 0 - # mid = match_result[0] - # post = self[match_result[0].size..-1] - # else - # pre = self[0..pos - 1] - # mid = match_result.not_nil![0] - # post = self[pos + match_result.not_nil![0].size..-1] - # end - # {pre, mid, post} - # end - - def byte_index(byte : Int, offset = 0) - offset.upto(bytesize - 1) do |i| - if to_unsafe[i] == byte - return i - end - end - nil - end - # TODO: Implement this! - # def byte_index(string : String, offset = 0) - # offset += bytesize if offset < 0 - # return nil if offset < 0 - - # end_pos = bytesize - string.bytesize - - # offset.upto(end_pos) do |pos| - # if (to_unsafe + pos).memcmp(string.to_unsafe, string.bytesize) == 0 - # return pos - # end - # end - - # nil - # end - - # Returns the byte index of a char index, or `nil` if out of bounds. - # - # It is valid to pass `#size` to *index*, and in this case the answer - # will be the bytesize of this string. - # - # ``` - # "hello".char_index_to_byte_index(1) # => 1 - # "hello".char_index_to_byte_index(5) # => 5 - # "こんにちは".char_index_to_byte_index(1) # => 3 - # "こんにちは".char_index_to_byte_index(5) # => 15 - # ``` - # TODO: Implement this! - # def char_index_to_byte_index(index) - # if ascii_only? - # return 0 <= index <= bytesize ? index : nil - # end - - # size = each_byte_index_and_char_index do |byte_index, char_index| - # return byte_index if index == char_index - # end - # return @bytesize if index == size - # nil - # end - - # Returns the char index of a byte index, or `nil` if out of bounds. - # - # It is valid to pass `#bytesize` to *index*, and in this case the answer - # will be the size of this string. - # TODO: Implement this! - # def byte_index_to_char_index(index) - # if ascii_only? - # return 0 <= index <= bytesize ? index : nil - # end - - # size = each_byte_index_and_char_index do |byte_index, char_index| - # return char_index if index == byte_index - # end - # return size if index == @bytesize - # nil - # end - - # Returns `true` if the string contains *search*. - # - # ``` - # "Team".includes?('i') # => false - # "Dysfunctional".includes?("fun") # => true - # ``` - # TODO: Implement this! - # def includes?(search : Char | String) - # !!index(search) - # end - - # Makes an array by splitting the string on any ASCII whitespace characters - # (and removing that whitespace). - # - # If *limit* is present, up to *limit* new strings will be created, - # with the entire remainder added to the last string. - # - # ``` - # old_pond = " - # Old pond - # a frog leaps in - # water's sound - # " - # old_pond.split # => ["Old", "pond", "a", "frog", "leaps", "in", "water's", "sound"] - # old_pond.split(3) # => ["Old", "pond", "a frog leaps in\n water's sound\n"] - # ``` - # TODO: Implement this! - # def split(limit : Int32? = nil) - # ary = Array(String).new - # split(limit) do |string| - # ary << string - # end - # ary - # end - - # Splits the string after any ASCII whitespace character and yields each part to a block. - # - # If *limit* is present, up to *limit* new strings will be created, - # with the entire remainder added to the last string. - # - # ``` - # ary = [] of String - # old_pond = " - # Old pond - # a frog leaps in - # water's sound - # " - # - # old_pond.split { |s| ary << s } - # ary # => ["Old", "pond", "a", "frog", "leaps", "in", "water's", "sound"] - # ary.clear - # - # old_pond.split(3) { |s| ary << s } - # ary # => ["Old", "pond", "a frog leaps in\n water's sound\n"] - # ``` - # TODO: Implement this! - # def split(limit : Int32? = nil, &block : String -> _) - # if limit && limit <= 1 - # yield self - # return - # end - - # yielded = 0 - # single_byte_optimizable = ascii_only? - # index = 0 - # i = 0 - # looking_for_space = false - # limit_reached = false - # while i < bytesize - # if looking_for_space - # while i < bytesize - # c = to_unsafe[i] - # i += 1 - # if c.unsafe_chr.ascii_whitespace? - # piece_bytesize = i - 1 - index - # piece_size = single_byte_optimizable ? piece_bytesize : 0 - # yield String.new(to_unsafe + index, piece_bytesize, piece_size) - # yielded += 1 - # looking_for_space = false - - # if limit && yielded + 1 == limit - # limit_reached = true - # end - - # break - # end - # end - # else - # while i < bytesize - # c = to_unsafe[i] - # i += 1 - # unless c.unsafe_chr.ascii_whitespace? - # index = i - 1 - # looking_for_space = true - # break - # end - # end - - # break if limit_reached - # end - # end - # if looking_for_space - # piece_bytesize = bytesize - index - # piece_size = single_byte_optimizable ? piece_bytesize : 0 - # yield String.new(to_unsafe + index, piece_bytesize, piece_size) - # end - # end - - # Makes an `Array` by splitting the string on the given character *separator* - # (and removing that character). - # - # If *limit* is present, up to *limit* new strings will be created, - # with the entire remainder added to the last string. - # - # ``` - # "foo,bar,baz".split(',') # => ["foo", "bar", "baz"] - # "foo,bar,baz".split(',', 2) # => ["foo", "bar,baz"] - # ``` - # TODO: Implement this! - # def split(separator : Char, limit = nil) - # ary = Array(String).new - # split(separator, limit) do |string| - # ary << string - # end - # ary - # end - - # Splits the string after each character *separator* and yields each part to a block. - # - # If *limit* is present, up to *limit* new strings will be created, - # with the entire remainder added to the last string. - # - # ``` - # ary = [] of String - # - # "foo,bar,baz".split(',') { |string| ary << string } - # ary # => ["foo", "bar", "baz"] - # ary.clear - # - # "foo,bar,baz".split(',', 2) { |string| ary << string } - # ary # => ["foo", "bar,baz"] - # ``` - # TODO: Implement this! - # def split(separator : Char, limit = nil, &block : String -> _) - # if empty? || limit && limit <= 1 - # yield self - # return - # end - - # yielded = 0 - # byte_offset = 0 - - # reader = Char::Reader.new(self) - # reader.each do |char| - # if char == separator - # piece_bytesize = reader.pos - byte_offset - # yield String.new(to_unsafe + byte_offset, piece_bytesize) - # yielded += 1 - # byte_offset = reader.pos + reader.current_char_width - # break if limit && yielded + 1 == limit - # end - # end - - # piece_bytesize = bytesize - byte_offset - # yield String.new(to_unsafe + byte_offset, piece_bytesize) - # end - - # Makes an `Array` by splitting the string on *separator* (and removing instances of *separator*). - # - # If *limit* is present, the array will be limited to *limit* items and - # the final item will contain the remainder of the string. - # - # If *separator* is an empty string (`""`), the string will be separated into one-character strings. - # - # ``` - # long_river_name = "Mississippi" - # long_river_name.split("ss") # => ["Mi", "i", "ippi"] - # long_river_name.split("i") # => ["M", "ss", "ss", "pp", ""] - # long_river_name.split("") # => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"] - # ``` - # TODO: Implement this! - # def split(separator : String, limit = nil) - # ary = Array(String).new - # split(separator, limit) do |string| - # ary << string - # end - # ary - # end - - # Splits the string after each string *separator* and yields each part to a block. - # - # If *limit* is present, the array will be limited to *limit* items and - # the final item will contain the remainder of the string. - # - # If *separator* is an empty string (`""`), the string will be separated into one-character strings. - # - # ``` - # ary = [] of String - # long_river_name = "Mississippi" - # - # long_river_name.split("ss") { |s| ary << s } - # ary # => ["Mi", "i", "ippi"] - # ary.clear - # - # long_river_name.split("i") { |s| ary << s } - # ary # => ["M", "ss", "ss", "pp", ""] - # ary.clear - # - # long_river_name.split("") { |s| ary << s } - # ary # => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"] - # ``` - # TODO: Implement this! - # def split(separator : String, limit = nil, &block : String -> _) - # if empty? || (limit && limit <= 1) - # yield self - # return - # end - - # if separator.empty? - # split_by_empty_separator(limit) do |string| - # yield string - # end - # return - # end - - # yielded = 0 - # byte_offset = 0 - # separator_bytesize = separator.bytesize - - # single_byte_optimizable = ascii_only? - - # i = 0 - # stop = bytesize - separator.bytesize + 1 - # while i < stop - # if (to_unsafe + i).memcmp(separator.to_unsafe, separator_bytesize) == 0 - # piece_bytesize = i - byte_offset - # piece_size = single_byte_optimizable ? piece_bytesize : 0 - # yield String.new(to_unsafe + byte_offset, piece_bytesize, piece_size) - # yielded += 1 - # byte_offset = i + separator_bytesize - # i += separator_bytesize - 1 - # break if limit && yielded + 1 == limit - # end - # i += 1 - # end - - # piece_bytesize = bytesize - byte_offset - # piece_size = single_byte_optimizable ? piece_bytesize : 0 - # yield String.new(to_unsafe + byte_offset, piece_bytesize, piece_size) - # end - - # Splits the string after each regex *separator* and yields each part to a block. - # - # If *limit* is present, the array will be limited to *limit* items and - # the final item will contain the remainder of the string. - # - # If *separator* is an empty regex (`//`), the string will be separated into one-character strings. - # - # ``` - # ary = [] of String - # long_river_name = "Mississippi" - # - # long_river_name.split(/s+/) { |s| ary << s } - # ary # => ["Mi", "i", "ippi"] - # ary.clear - # - # long_river_name.split(//) { |s| ary << s } - # ary # => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"] - # ``` - # TODO: Implement this! - # def split(separator : Regex, limit = nil) - # ary = Array(String).new - # split(separator, limit) do |string| - # ary << string - # end - # ary - # end - - # Makes an `Array` by splitting the string on *separator* (and removing instances of *separator*). - # - # If *limit* is present, the array will be limited to *limit* items and - # the final item will contain the remainder of the string. - # - # If *separator* is an empty regex (`//`), the string will be separated into one-character strings. - # - # ``` - # long_river_name = "Mississippi" - # long_river_name.split(/s+/) # => ["Mi", "i", "ippi"] - # long_river_name.split(//) # => ["M", "i", "s", "s", "i", "s", "s", "i", "p", "p", "i"] - # ``` - # TODO: Implement this! - # def split(separator : Regex, limit = nil, &block : String -> _) - # if empty? || (limit && limit <= 1) - # yield self - # return - # end - - # if separator.source.empty? - # split_by_empty_separator(limit) do |string| - # yield string - # end - # return - # end - - # count = 0 - # match_offset = slice_offset = 0 - - # while match = separator.match_at_byte_index(self, match_offset) - # index = match.byte_begin(0) - # match_bytesize = match[0].bytesize - # next_offset = index + match_bytesize - - # if next_offset == slice_offset - # match_offset = next_offset + char_bytesize_at(next_offset) - # else - # slice_size = index - slice_offset - - # yield byte_slice(slice_offset, slice_size) - # count += 1 - - # 1.upto(match.size) do |i| - # if group = match[i]? - # yield group - # end - # end - - # slice_offset = match_offset = next_offset - # end - - # break if limit && count + 1 == limit - # break if match_offset >= bytesize - # end - - # yield byte_slice(slice_offset) - # end - # TODO: Implement this! - # private def split_by_empty_separator(limit, &block : String -> _) - # yielded = 0 - - # each_char do |c| - # yield c.to_s - # yielded += 1 - # break if limit && yielded + 1 == limit - # end - - # if limit && yielded != size - # yield self[yielded..-1] - # yielded += 1 - # end - # end - # TODO: Implement this! - # def lines(chomp = true) - # lines = [] of String - # each_line(chomp: chomp) do |line| - # lines << line - # end - # lines - # end - - # Splits the string after each newline and yields each line to a block. - # - # ``` - # haiku = "the first cold shower - # even the monkey seems to want - # a little coat of straw" - # haiku.each_line do |stanza| - # puts stanza.upcase - # end - # # => THE FIRST COLD SHOWER - # # => EVEN THE MONKEY SEEMS TO WANT - # # => A LITTLE COAT OF STRAW - # ``` - # TODO: Implement this! - # def each_line(chomp = true) : Nil - # return if empty? - - # offset = 0 - - # while byte_index = byte_index('\n'.ord.to_u8, offset) - # count = byte_index - offset + 1 - # if chomp - # count -= 1 - # if offset + count > 0 && to_unsafe[offset + count - 1] === '\r' - # count -= 1 - # end - # end - - # yield unsafe_byte_slice_string(offset, count) - # offset = byte_index + 1 - # end - - # unless offset == bytesize - # yield unsafe_byte_slice_string(offset) - # end - # end - - # Returns an `Iterator` which yields each line of this string (see `String#each_line`). - # TODO: Implement this! - # def each_line(chomp = true) - # LineIterator.new(self, chomp) - # end - - # Converts camelcase boundaries to underscores. - # - # ``` - # "DoesWhatItSaysOnTheTin".underscore # => "does_what_it_says_on_the_tin" - # "PartyInTheUSA".underscore # => "party_in_the_usa" - # "HTTP_CLIENT".underscore # => "http_client" - # "3.14IsPi".underscore # => "3.14_is_pi" - # ``` - # TODO: Implement this! - # def underscore - # first = true - # last_is_downcase = false - # last_is_upcase = false - # last_is_digit = false - # mem = nil - - # String.build(bytesize + 10) do |str| - # each_char do |char| - # digit = '0' <= char <= '9' - # downcase = 'a' <= char <= 'z' || digit - # upcase = 'A' <= char <= 'Z' - - # if first - # str << char.downcase - # elsif last_is_downcase && upcase - # if mem - # # This is the case of A1Bcd, we need to put 'mem' (not to need to convert as downcase - # # ^ - # # because 'mem' is digit surely) before putting this char as downcase. - # str << mem - # mem = nil - # end - # # This is the case of AbcDe, we need to put an underscore before the 'D' - # # ^ - # str << '_' - # str << char.downcase - # elsif (last_is_upcase || last_is_digit) && (upcase || digit) - # # This is the case of 1) A1Bcd, 2) A1BCd or 3) A1B_cd:if the next char is upcase (case 1) we need - # # ^ ^ ^ - # # 1) we need to append this char as downcase - # # 2) we need to append an underscore and then the char as downcase, so we save this char - # # in 'mem' and decide later - # # 3) we need to append this char as downcase and then a single underscore - # if mem - # # case 2 - # str << mem.downcase - # end - # mem = char - # else - # if mem - # if char == '_' - # # case 3 - # elsif last_is_upcase && downcase - # # case 1 - # str << '_' - # end - # str << mem.downcase - # mem = nil - # end - - # str << char.downcase - # end - - # last_is_downcase = downcase - # last_is_upcase = upcase - # last_is_digit = digit - # first = false - # end - - # str << mem.downcase if mem - # end - # end - - # Converts underscores to camelcase boundaries. - # - # ``` - # "eiffel_tower".camelcase # => "EiffelTower" - # ``` - # TODO: Implement this! - # def camelcase - # return self if empty? - - # first = true - # last_is_underscore = false - - # String.build(bytesize) do |str| - # each_char do |char| - # if first - # str << char.upcase - # elsif char == '_' - # last_is_underscore = true - # elsif last_is_underscore - # str << char.upcase - # last_is_underscore = false - # else - # str << char - # end - # first = false - # end - # end - # end - - # Reverses the order of characters in the string. - # - # ``` - # "Argentina".reverse # => "anitnegrA" - # "racecar".reverse # => "racecar" - # ``` - # TODO: Implement this! - # def reverse - # return self if bytesize <= 1 - - # if ascii_only? - # String.new(bytesize) do |buffer| - # bytesize.times do |i| - # buffer[i] = self.to_unsafe[bytesize - i - 1] - # end - # {@bytesize, @length} - # end - # else - # # Iterate grpahemes to reverse the string, - # # so combining characters are placed correctly - # String.new(bytesize) do |buffer| - # buffer += bytesize - # scan(/\X/) do |match| - # grapheme = match[0] - # buffer -= grapheme.bytesize - # buffer.copy_from(grapheme.to_unsafe, grapheme.bytesize) - # end - # {@bytesize, @length} - # end - # end - # end - - # Adds instances of *char* to right of the string until it is at least size of *len*. - # - # ``` - # "Purple".ljust(8) # => "Purple " - # "Purple".ljust(8, '-') # => "Purple--" - # "Aubergine".ljust(8) # => "Aubergine" - # ``` - # TODO: Implemenet this! - # def ljust(len, char : Char = ' ') - # just len, char, true - # end - - # Adds instances of *char* to left of the string until it is at least size of *len*. - # - # ``` - # "Purple".rjust(8) # => " Purple" - # "Purple".rjust(8, '-') # => "--Purple" - # "Aubergine".rjust(8) # => "Aubergine" - # ``` - # TODO: Implement this! - # def rjust(len, char : Char = ' ') - # just len, char, false - # end - # TODO: Implement this! - # private def just(len, char, left) - # return self if size >= len - - # bytes, count = String.char_bytes_and_bytesize(char) - - # difference = len - size - # new_bytesize = bytesize + difference * count - - # String.new(new_bytesize) do |buffer| - # if left - # buffer.copy_from(to_unsafe, bytesize) - # buffer += bytesize - # end - - # if count == 1 - # Intrinsics.memset(buffer.as(Void*), char.ord.to_u8, difference.to_u32, 0_u32, false) - # buffer += difference - # else - # difference.times do - # buffer.copy_from(bytes.to_unsafe, count) - # buffer += count - # end - # end - - # unless left - # buffer.copy_from(to_unsafe, bytesize) - # end - - # {new_bytesize, len} - # end - # end - - # Returns the successor of the string. The successor is calculated - # by incrementing characters starting from the rightmost alphanumeric - # (or the rightmost character if there are no alphanumerics) in the string. - # Incrementing a digit always results in another digit, and incrementing - # a letter results in another letter of the same case. - # - # If the increment generates a "carry", the character to the left of it is - # incremented. This process repeats until there is no carry, - # adding an additional character if necessary. - # - # ``` - # "abcd".succ # => "abce" - # "THX1138".succ # => "THX1139" - # "((koala))".succ # => "((koalb))" - # "1999zzz".succ # => "2000aaa" - # "ZZZ9999".succ # => "AAAA0000" - # "***".succ # => "**+" - # ``` - # TODO: Implement this! - # def succ - # return self if empty? - - # chars = self.chars - - # carry = nil - # last_alnum = 0 - # index = size - 1 - - # while index >= 0 - # s = chars[index] - # if s.ascii_alphanumeric? - # carry = 0 - # if ('0' <= s && s < '9') || - # ('a' <= s && s < 'z') || - # ('A' <= s && s < 'Z') - # chars[index] = s.succ - # break - # elsif s == '9' - # chars[index] = '0' - # carry = '1' - # elsif s == 'z' - # chars[index] = carry = 'a' - # elsif s == 'Z' - # chars[index] = carry = 'A' - # end - - # last_alnum = index - # end - # index -= 1 - # end - - # if carry.nil? # there were no alphanumeric chars - # chars[size - 1] = chars[size - 1].succ - # end - - # if carry.is_a?(Char) && index < 0 # we still have a carry and already reached the beginning - # chars.insert(last_alnum, carry) - # end - - # String.build(chars.size) do |str| - # chars.each do |char| - # str << char - # end - # end - # end - - # Finds match of *regex*, starting at *pos*. - # TODO: Implement this! - # def match(regex : Regex, pos = 0) : Regex::MatchData? - # match = regex.match self, pos - # $~ = match - # match - # end - - # Searches the string for instances of *pattern*, - # yielding a `Regex::MatchData` for each match. - # TODO: Implement this! - # def scan(pattern : Regex) - # byte_offset = 0 - - # while match = pattern.match_at_byte_index(self, byte_offset) - # index = match.byte_begin(0) - # $~ = match - # yield match - # match_bytesize = match[0].bytesize - # match_bytesize += 1 if match_bytesize == 0 - # byte_offset = index + match_bytesize - # end - - # self - # end - - # Searches the string for instances of *pattern*, - # returning an `Array` of `Regex::MatchData` for each match. - # def scan(pattern : Regex) - # matches = [] of Regex::MatchData - # scan(pattern) do |match| - # matches << match - # end - # matches - # end - - # Searches the string for instances of *pattern*, - # yielding the matched string for each match. - # TODO: Implement this! - # def scan(pattern : String) - # return self if pattern.empty? - # index = 0 - # while index = byte_index(pattern, index) - # yield pattern - # index += pattern.bytesize - # end - # self - # end - - # Searches the string for instances of *pattern*, - # returning an array of the matched string for each match. - # TODO: Implement this! - # def scan(pattern : String) - # matches = [] of String - # scan(pattern) do |match| - # matches << match - # end - # matches - # end - - # Yields each character in the string to the block. - # - # ``` - # array = [] of Char - # "ab☃".each_char do |char| - # array << char - # end - # array # => ['a', 'b', '☃'] - # ``` - # TODO: Implement this! - # def each_char : Nil - # if ascii_only? - # each_byte do |byte| - # yield (byte < 0x80 ? byte.unsafe_chr : Char::REPLACEMENT) - # end - # else - # Char::Reader.new(self).each do |char| - # yield char - # end - # end - # end - - # Returns an `Iterator` over each character in the string. - # - # ``` - # chars = "ab☃".each_char - # chars.next # => 'a' - # chars.next # => 'b' - # chars.next # => '☃' - # ``` - # TODO: Implement this! - # def each_char - # CharIterator.new(Char::Reader.new(self)) - # end - - # Yields each character and its index in the string to the block. - # - # ``` - # array = [] of Tuple(Char, Int32) - # "ab☃".each_char_with_index do |char, index| - # array << {char, index} - # end - # array # => [{'a', 0}, {'b', 1}, {'☃', 2}] - # ``` - # TODO: Implement this! - # def each_char_with_index - # i = 0 - # each_char do |char| - # yield char, i - # i += 1 - # end - # end - - # Returns an `Array` of all characters in the string. - # - # ``` - # "ab☃".chars # => ['a', 'b', '☃'] - # ``` - # TODO: Implement this! - # def chars - # chars = Array(Char).new(@length > 0 ? @length : bytesize) - # each_char do |char| - # chars << char - # end - # chars - # end - - # Yields each codepoint to the block. - # - # ``` - # array = [] of Int32 - # "ab☃".each_codepoint do |codepoint| - # array << codepoint - # end - # array # => [97, 98, 9731] - # ``` - # - # See also: `Char#ord`. - # TODO: Implement this! - # def each_codepoint - # each_char do |char| - # yield char.ord - # end - # end - - # Returns an `Iterator` for each codepoint. - # - # ``` - # codepoints = "ab☃".each_codepoint - # codepoints.next # => 97 - # codepoints.next # => 98 - # codepoints.next # => 9731 - # ``` - # - # See also: `Char#ord`. - # TODO: Implement this! - # def each_codepoint - # each_char.map &.ord - # end - - # Returns an `Array` of the codepoints that make the string. - # - # ``` - # "ab☃".codepoints # => [97, 98, 9731] - # ``` - # - # See also: `Char#ord`. - # TODO: Implement this! - # def codepoints - # codepoints = Array(Int32).new(@length > 0 ? @length : bytesize) - # each_codepoint do |codepoint| - # codepoints << codepoint - # end - # codepoints - # end - - # Yields each byte in the string to the block. - # - # ``` - # array = [] of UInt8 - # "ab☃".each_byte do |byte| - # array << byte - # end - # array # => [97, 98, 226, 152, 131] - # ``` - def each_byte - # TODO: Implement this! - # to_slice.each do |byte| - # yield byte - # end - # nil - - size.times do |i| - yield bytes[i], i - end - end - - # Returns an `Iterator` over each byte in the string. - # - # ``` - # bytes = "ab☃".each_byte - # bytes.next # => 97 - # bytes.next # => 98 - # bytes.next # => 226 - # bytes.next # => 152 - # bytes.next # => 131 - # ``` - # TODO: Implement this! - # def each_byte - # to_slice.each - # end - - # Returns this string's bytes as an `Array(UInt8)`. - # - # ``` - # "hello".bytes # => [104, 101, 108, 108, 111] - # "你好".bytes # => [228, 189, 160, 229, 165, 189] - # ``` - def bytes - # TODO: Implement this! - # Array.new(bytesize) { |i| to_unsafe[i] } - pointerof(@c) - end - - # TODO: Implement these! - # def inspect(io) - # dump_or_inspect(io) do |char, error| - # inspect_char(char, error, io) - # end - # end - - # def pretty_print(pp) - # pp.text(inspect) - # end - - # def inspect_unquoted - # String.build do |io| - # inspect_unquoted(io) - # end - # end - - # def inspect_unquoted(io) - # dump_or_inspect_unquoted(io) do |char, error| - # inspect_char(char, error, io) - # end - # end - - # def dump - # String.build do |io| - # dump io - # end - # end - - # def dump(io) - # dump_or_inspect(io) do |char, error| - # dump_char(char, error, io) - # end - # end - - # def dump_unquoted - # String.build do |io| - # dump_unquoted(io) - # end - # end - - # def dump_unquoted(io) - # dump_or_inspect_unquoted(io) do |char, error| - # dump_char(char, error, io) - # end - # end - # TODO: Implement these! - # private def dump_or_inspect(io) - # io << "\"" - # dump_or_inspect_unquoted(io) do |char, error| - # yield char, error - # end - # io << "\"" - # end - # TODO: Implement these! - # private def dump_or_inspect_unquoted(io) - # reader = Char::Reader.new(self) - # while reader.has_next? - # current_char = reader.current_char - # case current_char - # when '"' then io << "\\\"" - # when '\\' then io << "\\\\" - # when '\b' then io << "\\b" - # when '\e' then io << "\\e" - # when '\f' then io << "\\f" - # when '\n' then io << "\\n" - # when '\r' then io << "\\r" - # when '\t' then io << "\\t" - # when '\v' then io << "\\v" - # when '#' - # current_char = reader.next_char - # if current_char == '{' - # io << "\\\#{" - # reader.next_char - # next - # else - # io << '#' - # next - # end - # else - # if reader.error - # reader.current_char_width.times do |i| - # yield '\0', to_unsafe[reader.pos + i] - # end - # else - # yield current_char, nil - # end - # end - # reader.next_char - # end - # end - - # private def inspect_char(char, error, io) - # dump_or_inspect_char char, error, io do - # char.ascii_control? - # end - # end - - # private def dump_char(char, error, io) - # dump_or_inspect_char(char, error, io) do - # char.ascii_control? || char.ord >= 0x80 - # end - # end - - # private def dump_or_inspect_char(char, error, io) - # if error - # dump_hex(error, io) - # elsif yield - # dump_unicode(char, io) - # else - # io << char - # end - # end - - # private def dump_hex(error, io) - # io << "\\x" - # io << "0" if error < 16 - # error.to_s(16, io, upcase: true) - # end - - # private def dump_unicode(char, io) - # io << "\\u" - # io << "0" if char.ord < 4096 - # io << "0" if char.ord < 256 - # io << "0" if char.ord < 16 - # char.ord.to_s(16, io) - # io << "" - # end - # TODO: Implement these! - # def starts_with?(str : String) - # return false if str.bytesize > bytesize - # to_unsafe.memcmp(str.to_unsafe, str.bytesize) == 0 - # end - - # def starts_with?(char : Char) - # each_char do |c| - # return c == char - # end - - # false - # end - # TODO: Implement these! - # def ends_with?(str : String) - # return false if str.bytesize > bytesize - # (to_unsafe + bytesize - str.bytesize).memcmp(str.to_unsafe, str.bytesize) == 0 - # end - - # def ends_with?(char : Char) - # return false unless bytesize > 0 - - # if char.ascii? || ascii_only? - # return to_unsafe[bytesize - 1] == char.ord - # end - - # bytes, count = String.char_bytes_and_bytesize(char) - # return false if bytesize < count - - # count.times do |i| - # return false unless to_unsafe[bytesize - count + i] == bytes[i] - # end - - # true - # end - - # Interpolates *other* into the string using `Kernel#sprintf`. - # - # ``` - # "Party like it's %d!!!" % 1999 # => "Party like it's 1999!!!" - # ``` - # TODO: Implement this! - # def %(other) - # sprintf self, other - # end - - # Returns a hash based on this string’s size and content. - # - # See also: `Object#hash`. - # TODO: Implement this! - # def hash - # h = 0 - # each_byte do |c| - # h = 31 * h + c - # end - # h - # end - - # Returns the number of unicode codepoints in this string. - # - # ``` - # "hello".size # => 5 - # "你好".size # => 2 - # ``` - def size - if @length > 0 || @bytesize == 0 - return @length - end - - @length = each_byte_index_and_char_index { } - end - - # Returns `true` if this String is comprised in its entirety - # by ASCII characters. - # - # ``` - # "hello".ascii_only? # => true - # "你好".ascii_only? # => false - # ``` - # TODO: Implement this! - # def ascii_only? - # @bytesize == size - # end - - # Returns `true` if this String is encoded correctly - # according to the UTF-8 encoding. - # TODO: Implement this! - # def valid_encoding? - # reader = Char::Reader.new(self) - # while reader.has_next? - # return false if reader.error - # reader.next_char - # end - # true - # end - - # Returns a String where bytes that are invalid in the - # UTF-8 encoding are replaced with *replacement*. - # TODO: Implement this! - # def scrub(replacement = Char::REPLACEMENT) : String - # # If the string is valid we have a chance of returning self - # # to avoid creating a new string - # result = nil - - # reader = Char::Reader.new(self) - # while reader.has_next? - # if reader.error - # unless result - # result = String::Builder.new(bytesize) - # result.write(to_slice[0, reader.pos]) - # end - # result << replacement - # else - # result << reader.current_char if result - # end - # reader.next_char - # end - - # result ? result.to_s : self - # end - - protected def char_bytesize_at(byte_index) - first = unsafe_byte_at(byte_index) - - if first < 0x80 - return 1 - end - - if first < 0xc2 - return 1 - end - - second = unsafe_byte_at(byte_index + 1) - if (second & 0xc0) != 0x80 - return 1 - end - - if first < 0xe0 - return 2 - end - - third = unsafe_byte_at(byte_index + 2) - if (third & 0xc0) != 0x80 - return 2 - end - - if first < 0xf0 - return 3 - end - - if first == 0xf0 && second < 0x90 - return 3 - end - - if first == 0xf4 && second >= 0x90 - return 3 - end - - return 4 - end - - protected def size_known? - @bytesize == 0 || @length > 0 - end - - protected def each_byte_index_and_char_index - byte_index = 0 - char_index = 0 - - while byte_index < bytesize - yield byte_index, char_index - byte_index += char_bytesize_at(byte_index) - char_index += 1 - end - - char_index - end - - def clone - self - end - - def dup - self - end - - def to_s - self - end - # TODO: Implement this! - # def to_s(io) - # io.write_utf8(to_slice) - # end - - # Returns the underlying bytes of this String in an **unsafe** way. - # - # The returned slice is read-only. - # TODO: Implement this! - # def to_slice : Bytes - # Slice.new(to_unsafe, bytesize, read_only: true) - # end - - # Returns a pointer to the underlying bytes of this String. - def to_unsafe : UInt8* - pointerof(@c) - end - # TODO: Implement these! - # def unsafe_byte_slice(byte_offset, count) - # Slice.new(to_unsafe + byte_offset, count, read_only: true) - # end - - # def unsafe_byte_slice(byte_offset) - # Slice.new(to_unsafe + byte_offset, bytesize - byte_offset, read_only: true) - # end - - # protected def unsafe_byte_slice_string(byte_offset) - # String.new(unsafe_byte_slice(byte_offset)) - # end - - # protected def unsafe_byte_slice_string(byte_offset, count) - # String.new(unsafe_byte_slice(byte_offset, count)) - # end - # TODO: Implement this! - # protected def self.char_bytes_and_bytesize(char : Char) - # bytes = uninitialized UInt8[4] - - # bytesize = 0 - # char.each_byte do |byte| - # bytes[bytesize] = byte - # bytesize += 1 - # end - - # {bytes, bytesize} - # end - - # Raises an `ArgumentError` if `self` has null bytes. Returns `self` otherwise. - # - # This method should sometimes be called before passing a `String` to a C function. - # TODO: Implement this! - # def check_no_null_byte - # raise ArgumentError.new("String contains null byte") if byte_index(0) - # self - # end - - # :nodoc: - # TODO: Implement this! - # def self.check_capacity_in_bounds(capacity) - # if capacity < 0 - # raise ArgumentError.new("Negative capacity") - # end - - # if capacity.to_u64 > (UInt32::MAX - HEADER_SIZE - 1) - # raise ArgumentError.new("Capacity too big") - # end - # end - # TODO: Implement this! - # private class CharIterator - # include Iterator(Char) - - # @reader : Char::Reader - # @end : Bool - - # def initialize(@reader, @end = false) - # check_empty - # end - - # def next - # return stop if @end - - # value = @reader.current_char - # @reader.next_char - # @end = true unless @reader.has_next? - - # value - # end - - # def rewind - # @reader.pos = 0 - # @end = false - # check_empty - # self - # end - - # private def check_empty - # @end = true if @reader.string.bytesize == 0 - # end - # end - # TODO: Implement this! - # private class LineIterator - # include Iterator(String) - - # def initialize(@string : String, @chomp : Bool) - # @offset = 0 - # @end = false - # end - - # def next - # return stop if @end - - # byte_index = @string.byte_index('\n'.ord.to_u8, @offset) - # if byte_index - # count = byte_index - @offset + 1 - # if @chomp - # count -= 1 - # if @offset + count > 0 && @string.to_unsafe[@offset + count - 1] === '\r' - # count -= 1 - # end - # end - - # value = @string.unsafe_byte_slice_string(@offset, count) - # @offset = byte_index + 1 - # else - # if @offset == @string.bytesize - # value = stop - # else - # value = @string.unsafe_byte_slice_string(@offset) - # end - # @end = true - # end - - # value - # end - - # def rewind - # @offset = 0 - # @end = false - # self - # end - # end -end - -# require "./string/formatter" -# require "./string/builder" diff --git a/src/core/timer.c b/src/core/timer.c new file mode 100644 index 0000000..392a8e4 --- /dev/null +++ b/src/core/timer.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include +#include + +uint32_t tick = 0; + +static void timer_callback(stack_t *stack) +{ + tick++; + + UNUSED(*stack); +} + +uint32_t timer_tick() +{ + return tick; +} + +void timer_init(int freq) +{ + register_interrupt_handler(IRQ0, timer_callback); + + int divisor = 1193180 / freq; + + port_byte_out(0x43, 0x36); + port_byte_out(0x40, divisor & 0xff); + port_byte_out(0x40, divisor >> 8); +} diff --git a/src/core/tuple.cr b/src/core/tuple.cr deleted file mode 100644 index 2afda56..0000000 --- a/src/core/tuple.cr +++ /dev/null @@ -1,511 +0,0 @@ -# A tuple is a fixed-size, immutable, stack-allocated sequence of values -# of possibly different types. -# -# You can think of a `Tuple` as an immutable `Array` whose types for each position -# are known at compile time. -# -# A tuple can be created with the usual `new` method or with a tuple literal: -# -# ``` -# tuple = {1, "hello", 'x'} # Tuple(Int32, String, Char) -# tuple[0] # => 1 -# tuple[1] # => "hello" -# tuple[2] # => 'x' -# ``` -# -# The compiler knows what types are in each position, so when indexing -# a tuple with an integer literal the compiler will return -# the value in that index and with the expected type, like in the above -# snippet. Indexing with an integer literal outside the bounds of the tuple -# will give a compile-time error. -# -# Indexing with an integer value that is only known at runtime will return -# a value whose type is the union of all the types in the tuple, and might raise -# `IndexError`. -# -# Tuples are the preferred way to return fixed-size multiple return -# values because no memory is needed to be allocated for them: -# -# ``` -# def one_and_hello -# {1, "hello"} -# end -# -# one, hello = one_and_hello -# one # => 1 -# hello # => "hello" -# ``` -# -# Good examples of the above are `Number#divmod` and `Enumerable#minmax`. -# -# Tuples can be splat with the `*` operator and passed to methods: -# -# ``` -# def multiply(string, value) -# string * value -# end -# -# tuple = {"hey", 2} -# value = multiply(*tuple) # same as multiply tuple[0], tuple[1] -# value # => "heyhey" -# ``` -# -# Finally, when using a splat argument in a method definition its type -# will be a tuple of the call arguments: -# -# ``` -# def splat_test(*args) -# args -# end -# -# tuple = splat_test 1, "hello", 'x' -# tuple.class # => Tuple(Int32, String, Char) -# tuple # => {1, "hello", 'x'} -# ``` -struct Tuple - # include Indexable(Union(*T)) - include Comparable(Tuple) - - # Creates a tuple that will contain the given arguments. - # - # This method is useful in macros and generic code because with it you can - # creates empty tuples, something that you can't do with a tuple literal. - # - # ``` - # Tuple.new(1, "hello", 'x') #=> {1, "hello", 'x'} - # Tuple.new #=> {} - # - # {} # syntax error - # ``` - def self.new(*args : *T) - args - end - - # Creates a tuple from the given array, with elements casted to the given types. - # - # ``` - # Tuple(String, Int64).from(["world", 2]) # => {"world", 2} - # Tuple(String, Int64).from(["world", 2]).class # => {String, Int64} - # ``` - # - # See also: `#from`. - def self.from(array : Array) : self - {% begin %} - Tuple.new(*{{T}}).from(array) - {% end %} - end - - # Expects to be called on a tuple of types, creates a tuple from the given array, - # with types casted appropriately. - # - # This allows you to easily pass an array as individual arguments to a method. - # - # ``` - # def speak_about(thing : String, n : Int64) - # "I see #{n} #{thing}s" - # end - # - # data = JSON.parse(%(["world", 2])).as_a - # speak_about(*{String, Int64}.from(data)) # => "I see 2 worlds" - # ``` - def from(array : Array) - if size != array.size - raise ArgumentError.new "Expected array of size #{size} but one of size #{array.size} was given." - end - - {% begin %} - Tuple.new( - {% for i in 0...@type.size %} - self[{{i}}].cast(array[{{i}}]), - {% end %} - ) - {% end %} - end - - def unsafe_at(index : Int) - self[index] - end - - # Returns the element at the given *index*. Read the type docs to understand - # the difference between indexing with a number literal or a variable. - # - # ``` - # tuple = {1, "hello", 'x'} - # tuple[0] # => 1 (Int32) - # tuple[3] # compile error: index out of bounds for tuple {Int32, String, Char} - # - # i = 0 - # tuple[i] # => 1 (Int32 | String | Char) - # - # i = 3 - # tuple[i] # raises IndexError - # ``` - def [](index : Int) - at(index) - end - - # Returns the element at the given *index* or `nil` if out of bounds. - # - # ``` - # tuple = {1, "hello", 'x'} - # tuple[0]? # => 1 - # tuple[3]? # => nil - # ``` - def []?(index : Int) - at(index) { nil } - end - - # Returns the element at the given *index* or raises IndexError if out of bounds. - # - # ``` - # tuple = {1, "hello", 'x'} - # tuple.at(0) # => 1 - # tuple.at(3) # raises IndexError - # ``` - def at(index : Int) - at(index) { raise IndexError.new } - end - - # Returns the element at the given *index* or the value returned by the block if - # out of bounds. - # - # ``` - # tuple = {1, "hello", 'x'} - # tuple.at(0) { 10 } # => 1 - # tuple.at(3) { 10 } # => 10 - # ``` - def at(index : Int) - {% for i in 0...T.size %} - return self[{{i}}] if {{i}} == index - {% end %} - yield - end - - # Yields each of the elements in this tuple. - # - # ``` - # tuple = {1, "hello", 'x'} - # tuple.each do |value| - # puts value - # end - # ``` - # - # Output: - # - # ```text - # 1 - # "hello" - # 'x' - # ``` - def each : Nil - {% for i in 0...T.size %} - yield self[{{i}}] - {% end %} - end - - # Returns `true` if this tuple has the same size as the other tuple - # and their elements are equal to each other when compared with `==`. - # - # ``` - # t1 = {1, "hello"} - # t2 = {1.0, "hello"} - # t3 = {2, "hello"} - # - # t1 == t2 # => true - # t1 == t3 # => false - # ``` - def ==(other : self) - {% for i in 0...T.size %} - return false unless self[{{i}}] == other[{{i}}] - {% end %} - true - end - - # ditto - def ==(other : Tuple) - return false unless size == other.size - - size.times do |i| - return false unless self[i] == other[i] - end - true - end - - def ==(other) - false - end - - # Returns `true` if case equality holds for the elements in `self` and *other*. - # - # ``` - # {1, 2} === {1, 2} # => true - # {1, 2} === {1, 3} # => false - # ``` - # - # See also: `Object#===`. - def ===(other : self) - {% for i in 0...T.size %} - return false unless self[{{i}}] === other[{{i}}] - {% end %} - true - end - - # Returns `true` if `self` and *other* have the same size and case equality holds - # for the elements in `self` and *other*. - # - # ``` - # {1, 2} === {1, 2, 3} # => false - # {/o+/, "bar"} === {"foo", "bar"} # => true - # ``` - # - # See also: `Object#===`. - def ===(other : Tuple) - return false unless size == other.size - - size.times do |i| - return false unless self[i] === other[i] - end - true - end - - # Implements the comparison operator. - # - # Each object in each tuple is compared (using the `<=>` operator). - # - # Tuples are compared in an "element-wise" manner; the first element of this tuple is - # compared with the first one of *other* using the `<=>` operator, then each of the second elements, - # etc. As soon as the result of any such comparison is non zero - # (i.e. the two corresponding elements are not equal), that result is returned for the whole tuple comparison. - # - # If all the elements are equal, then the result is based on a comparison of the tuple sizes. - # Thus, two tuples are "equal" according to `<=>` if, and only if, they have the same size - # and the value of each element is equal to the value of the corresponding element in the other tuple. - # - # ``` - # {"a", "a", "c"} <=> {"a", "b", "c"} # => -1 - # {1, 2, 3, 4, 5, 6} <=> {1, 2} # => +1 - # {1, 2} <=> {1, 2.0} # => 0 - # ``` - # - # See also: `Object#<=>`. - def <=>(other : self) - {% for i in 0...T.size %} - cmp = self[{{i}}] <=> other[{{i}}] - return cmp unless cmp == 0 - {% end %} - 0 - end - - # ditto - def <=>(other : Tuple) - min_size = Math.min(size, other.size) - min_size.times do |i| - cmp = self[i] <=> other[i] - return cmp unless cmp == 0 - end - size <=> other.size - end - - # Returns a hash value based on this tuple's length and contents. - # - # See also: `Object#hash`. - def hash - hash = 31 * size - {% for i in 0...T.size %} - hash = 31 * hash + self[{{i}}].hash - {% end %} - hash - end - - # Returns a tuple containing cloned elements of this tuple using the `clone` method. - def clone - {% if true %} - Tuple.new( - {% for i in 0...T.size %} - self[{{i}}].clone, - {% end %} - ) - {% end %} - end - - # Returns a tuple that contains `self`'s elements followed by *other*'s elements. - # - # ``` - # t1 = {1, 2} - # t2 = {"foo", "bar"} - # t3 = t1 + t2 - # t3 # => {1, 2, "foo", "bar"} - # typeof(t3) # => Tuple(Int32, Int32, String, String) - # ``` - def +(other : Tuple) - plus_implementation(other) - end - - private def plus_implementation(other : U) forall U - {% begin %} - Tuple.new( - {% for i in 0...@type.size %} - self[{{i}}], - {% end %} - {% for i in 0...U.size %} - other[{{i}}], - {% end %} - ) - {% end %} - end - - # Returns the number of elements in this tuple. - # - # ``` - # {'a', 'b'}.size # => 2 - # ``` - def size - {{T.size}} - end - - # Returns the types of this tuple. - # - # ``` - # tuple = {1, "hello", 'x'} - # tuple.types # => Tuple(Int32, String, Char) - # ``` - def types - T - end - - # Same as `to_s`. - def inspect - to_s - end - - # Appends a string representation of this tuple to the given `IO`. - # - # ``` - # tuple = {1, "hello"} - # tuple.to_s # => "{1, \"hello\"}" - # ``` - def to_s(io) - io << "{" - join ", ", io, &.inspect(io) - io << "}" - end - - def pretty_print(pp) : Nil - pp.list("{", self, "}") - end - - # Returns a new tuple where elements are mapped by the given block. - # - # ``` - # tuple = {1, 2.5, "a"} - # tuple.map &.to_s # => {"1", "2.5", "a"} - # ``` - def map - {% if true %} - Tuple.new( - {% for i in 0...T.size %} - (yield self[{{i}}]), - {% end %} - ) - {% end %} - end - - # Returns a new tuple where the elements are in reverse order. - # - # ``` - # tuple = {1, 2.5, "a"} - # tuple.reverse # => {"a", 2.5, 1} - # ``` - def reverse - {% if true %} - Tuple.new( - {% for i in 1..T.size %} - self[{{T.size - i}}], - {% end %} - ) - {% end %} - end - - # Yields each of the elements in this tuple in reverse order. - # - # ``` - # tuple = {1, "hello", 'x'} - # tuple.reverse_each do |value| - # puts value - # end - # ``` - # - # Output: - # - # ```text - # 'x' - # "hello" - # 1 - # ``` - def reverse_each - {% for i in 1..T.size %} - yield self[{{T.size - i}}] - {% end %} - nil - end - - # Returns the first element of this tuple. Doesn't compile - # if the tuple is empty. - # - # ``` - # tuple = {1, 2.5} - # tuple.first # => 1 - # ``` - def first - self[0] - end - - # Returns the first element of this tuple, or `nil` if this - # is the empty tuple. - # - # ``` - # tuple = {1, 2.5} - # tuple.first? # => 1 - # - # empty = Tuple.new - # empty.first? # => nil - # ``` - def first? - {% if T.size == 0 %} - nil - {% else %} - self[0] - {% end %} - end - - # Returns the last element of this tuple. Doesn't compile - # if the tuple is empty. - # - # ``` - # tuple = {1, 2.5} - # tuple.last # => 2.5 - # ``` - def last - {% if true %} - self[{{T.size - 1}}] - {% end %} - end - - # Returns the last element of this tuple, or `nil` if this - # is the empty tuple. - # - # ``` - # tuple = {1, 2.5} - # tuple.last? # => 2.5 - # - # empty = Tuple.new - # empty.last? # => nil - # ``` - def last? - {% if T.size == 0 %} - nil - {% else %} - self[{{T.size - 1}}] - {% end %} - end -end diff --git a/src/core_test/comparable_test.cr b/src/core_test/comparable_test.cr deleted file mode 100644 index 7cf521e..0000000 --- a/src/core_test/comparable_test.cr +++ /dev/null @@ -1,18 +0,0 @@ -private class ComparableTestClass - include Comparable(Int) - - def initialize(@value : Int32) - end - - def <=>(other : Int) - @value <=> other - end -end - -# TODO: include Comparable in Number -# def comparable_test -# obj = ComparableTestClass.new(4) -# puts "It can compare against Int" unless obj == 3 -# puts "It can compare against Int" unless obj < 3 -# puts "It can compare against Int" if obj > 3 -# end diff --git a/src/core_test/pointer_test.cr b/src/core_test/pointer_test.cr deleted file mode 100644 index 5105548..0000000 --- a/src/core_test/pointer_test.cr +++ /dev/null @@ -1,39 +0,0 @@ -def pointer_test - puts "----------------" - puts "Test for Pointer" - puts "----------------" - - # #null? - a = 1 - puts "pointerof(1) returns false" unless pointerof(a).null? - - b = Pointer(Int32).new(0) - puts "Pointer(Int32).new(0) returns true" if b.null? - - # #+ - ptr1 = Pointer(Int32).new(1234) - puts "ptr1: address is 1234" if ptr1.address == 1234 - - # An Int32 occupies four bytes - puts "ptr1 + 1 (Int32, 4 bytes)" - ptr2 = ptr1 + 1 - puts "ptr2: address is 1238" if ptr2.address == 1238 - - - puts "ptr2 - 1 (Int32, 4 bytes)" - ptr3 = ptr2 - 1 - puts "ptr3: address is 1234" if ptr3.address == 1234 - - # self.null - ptr = Pointer(Int32).null - puts "address of self.null returns 0" if ptr.address == 0 - - # #clone - ptr = Pointer(Int32).new(123) - puts "pointer can be cloned" if ptr.clone.address == 123 -end - -private def reset(p1, p2) - p1.value = 10 - p2.value = 20 -end diff --git a/src/core_test/prelude.cr b/src/core_test/prelude.cr deleted file mode 100644 index 5baf775..0000000 --- a/src/core_test/prelude.cr +++ /dev/null @@ -1,3 +0,0 @@ -require "./string_test" -require "./pointer_test" -require "./comparable_test" diff --git a/src/core_test/string_test.cr b/src/core_test/string_test.cr deleted file mode 100644 index 2d167ff..0000000 --- a/src/core_test/string_test.cr +++ /dev/null @@ -1,67 +0,0 @@ -def string_test - puts "---------------" - puts "Test for String" - puts "---------------" - - # #bytesize - # "hello".bytesize # => 5 - "你好".bytesize # => 6 - "你好".bytesize.times do - print "你好" # It's garbled, but print 6 times - end - puts - - # #unsafe_byte_at - "hello".unsafe_byte_at(0) #=> 104 - - # #empty? - puts "Not empty" unless "hello".empty? - - # #byte_index - "hello".byte_index('l'.ord) #=> 3 - "foo bar booz".byte_index('o'.ord, 3) #=> 9 - - # #each_byte - "ab".each_byte do |byte| #=> 'a' is 97, 'b' is 98 - (byte - 96).times do - print "each_byte" #=> So, print "each_byte" 3 times - end - end - puts - - # "你好".size #=> 2 - "你好".size.times do - print "你好" # It's garbled, but print 2 times - end - puts - - # #dup - dup = "foo".dup - puts dup #=> puts "foo" - - # #clone - clone = "foo".clone - puts clone #=> puts "foo" - - # Testing for Int - # #upto - sum = 0 - 1.upto(3) do |n| - sum += n - end - sum.times do - print "upto" #=> print "upto" 6 times - end - puts - - # #downto - sum = 0 - 3.downto(1) do |n| - sum += n - end - sum.times do - print "downto" #=> print "downto" 6 times - end - puts - -end diff --git a/src/core_test/test.cr b/src/core_test/test.cr deleted file mode 100644 index fd9ab13..0000000 --- a/src/core_test/test.cr +++ /dev/null @@ -1,7 +0,0 @@ -require "./prelude" - -def core_test - string_test - pointer_test - # comparable_test -end diff --git a/src/drivers/keyboard.c b/src/drivers/keyboard.c new file mode 100644 index 0000000..c9f04c1 --- /dev/null +++ b/src/drivers/keyboard.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include +#include + +#define SCANCODE_MAX 57 + +static void keyboard_callback(stack_t *stack) +{ + uint8_t scancode = port_byte_in(KEYBOARD_DATA_PORT); + + if (scancode > SCANCODE_MAX) { + return; + } + + printfk("Received scancode: %d\n", scancode); + + UNUSED(*stack); +} + +void keyboard_init() +{ + register_interrupt_handler(IRQ1, keyboard_callback); +} diff --git a/src/drivers/screen.c b/src/drivers/screen.c new file mode 100644 index 0000000..6bdc5cf --- /dev/null +++ b/src/drivers/screen.c @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include + +#define FB_COMMAND_PORT 0x3D4 +#define FB_DATA_PORT 0x3D5 +#define FB_HIGH_BYTE_COMMAND 14 +#define FB_LOW_BYTE_COMMAND 15 + +// private +void screen_write_at(char c, uint8_t scheme, int x, int y); +uint8_t color_scheme(uint8_t fg, uint8_t bg); +void move_cursor(uint16_t pos); + +uint8_t screen_scheme; +char* framebuffer; +int screen_col; +int screen_row; + +void screen_init() +{ + framebuffer = (char*) VIDEO_ADDRESS; + screen_scheme = color_scheme(COLOR_LIGHT_GREY, COLOR_BLACK); + screen_col = 0; + screen_row = 0; +} + +void screen_clear() +{ + for (int y = 0; y < SCREEN_HEIGHT; y++) { + for (int x = 0; x < SCREEN_WIDTH; x++) { + screen_write_at(' ', screen_scheme, x, y); + } + } +} + +void screen_print(const char* str) +{ + for (unsigned int i = 0; i < strlen(str); i++) { + screen_write(str[i]); + } +} + +uint8_t color_scheme(uint8_t fg, uint8_t bg) +{ + return fg | bg << 4; +} + +void screen_write_at(char c, uint8_t scheme, int x, int y) +{ + const int offset = 2 * (y * SCREEN_WIDTH + x); + + framebuffer[offset] = c; + framebuffer[offset + 1] = scheme; + + // scrolling + if (offset > SCREEN_HEIGHT * SCREEN_WIDTH * 2) { + for (int i = 1; i < SCREEN_HEIGHT; i++) { + // NOTE: Something's wrong when using musl libc's memcpy + // So using an alternative function + memcpyk( + ((char *) VIDEO_ADDRESS + (2 * i * SCREEN_WIDTH)), + ((char *) VIDEO_ADDRESS + (2 * (i - 1) * SCREEN_WIDTH)), + 2 * SCREEN_WIDTH + ); + } + + char* last_line = (char *)(2 * (SCREEN_HEIGHT) * SCREEN_WIDTH + VIDEO_ADDRESS); + for (int i = 0; i < SCREEN_WIDTH * 2; i++) { + last_line[i] = 0; + } + + screen_row--; + } +} + +void screen_write(char c) +{ + if (c == '\n') { + screen_col = 0; + screen_row++; + } else if (c == '\b' && screen_col > 0) { + screen_col--; + screen_write_at(0x0, screen_scheme, screen_col, screen_row); + } else if (c == '\t') { + screen_col = screen_col + 8 - (screen_col % 8); + } else if (c == '\r') { + screen_col = 0; + } else { + screen_write_at(c, screen_scheme, screen_col, screen_row); + screen_col++; + + if (screen_col == SCREEN_WIDTH) { + screen_col = 0; + screen_row++; + } + } + + move_cursor((screen_row * SCREEN_WIDTH) + screen_col); +} + +void move_cursor(uint16_t pos) +{ + port_byte_out(FB_COMMAND_PORT, FB_HIGH_BYTE_COMMAND); + port_byte_out(FB_DATA_PORT, ((pos >> 8) & 0x00FF)); + port_byte_out(FB_COMMAND_PORT, FB_LOW_BYTE_COMMAND); + port_byte_out(FB_DATA_PORT, pos & 0x00FF); +} diff --git a/src/drivers/serial.c b/src/drivers/serial.c new file mode 100644 index 0000000..a58443c --- /dev/null +++ b/src/drivers/serial.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// private functions +bool serial_received(uint16_t com); +bool serial_is_transmit_fifo_empty(uint16_t com); +// could be exposed later +char serial_read(uint16_t com); + +void serial_init(uint16_t com, uint16_t divisor) +{ + port_byte_out(SERIAL_LINE_COMMAND_PORT(com), SERIAL_LINE_ENABLE_DLAB); + port_byte_out(SERIAL_DATA_PORT(com), (divisor >> 8) & 0x00FF); + port_byte_out(SERIAL_DATA_PORT(com), divisor & 0x00FF); + + // https://littleosbook.github.io/#configuring-the-serial-port + // + // Bit: | 7 | 6 | 5 4 3 | 2 | 1 0 | + // Content: | d | b | prty | s | dl | + // Value: | 0 | 0 | 0 0 0 | 0 | 1 1 | = 0x03 + port_byte_out(SERIAL_LINE_COMMAND_PORT(com), 0x03); + + // Enable FIFO, clear them, with 14b threshold + port_byte_out(SERIAL_FIFO_COMMAND_PORT(com), 0xC7); + + // IRQs enabled, RTS/DSR set + port_byte_out(SERIAL_MODEM_COMMAND_PORT(com), 0x0B); +} + +void serial_print(uint16_t com, const char* str) +{ + for (unsigned int i = 0; i < strlen(str); i++) { + serial_write(com, str[i]); + } +} + +void serial_printf(uint16_t com, const char* format, ...) +{ + va_list arg; + va_start(arg, format); + vprintfk(com, format, arg); + va_end(arg); +} + +bool serial_is_transmit_fifo_empty(uint16_t com) +{ + /* 0x20 = 0010 0000 */ + return (port_byte_in(SERIAL_LINE_STATUS_PORT(com)) & 0x20); +} + +bool serial_received(uint16_t com) +{ + return (port_byte_in(SERIAL_LINE_STATUS_PORT(com)) & 1); +} + +char serial_read(uint16_t com) +{ + while (serial_received(com) == 0) ; + + return port_byte_in(com); +} + +void serial_write(uint16_t com, char c) { + while (serial_is_transmit_fifo_empty(com) == 0) ; + + port_byte_out(com, c); +} diff --git a/src/include/core/boot.h b/src/include/core/boot.h new file mode 100644 index 0000000..96677d3 --- /dev/null +++ b/src/include/core/boot.h @@ -0,0 +1,135 @@ +#ifndef CORE_BOOT_H +#define CORE_BOOT_H + +#include +#include + +// https://www.uclibc.org/docs/elf-64-gen.pdf + +/* The magic field should contain this. */ +#define MULTIBOOT2_MAGIC_NUMBER 0xe85250d6 + +/* This should be in %eax. */ +#define MULTIBOOT2_MAGIC_VALUE 0x36d76289 + +/* Flags set in the 'flags' member of the multiboot header. */ +#define MULTIBOOT_TAG_TYPE_END 0 +#define MULTIBOOT_TAG_TYPE_CMDLINE 1 +#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2 +#define MULTIBOOT_TAG_TYPE_MODULE 3 +#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4 +#define MULTIBOOT_TAG_TYPE_BOOTDEV 5 +#define MULTIBOOT_TAG_TYPE_MMAP 6 +#define MULTIBOOT_TAG_TYPE_VBE 7 +#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8 +#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9 +#define MULTIBOOT_TAG_TYPE_APM 10 +#define MULTIBOOT_TAG_TYPE_EFI32 11 +#define MULTIBOOT_TAG_TYPE_EFI64 12 +#define MULTIBOOT_TAG_TYPE_SMBIOS 13 +#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14 +#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15 +#define MULTIBOOT_TAG_TYPE_NETWORK 16 +#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17 +#define MULTIBOOT_TAG_TYPE_EFI_BS 18 +#define MULTIBOOT_TAG_TYPE_EFI32_IH 19 +#define MULTIBOOT_TAG_TYPE_EFI64_IH 20 +#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21 + +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 + +#define MULTIBOOT_ELF_SECTION_TYPE_NULL 0 + +typedef struct multiboot_tag { + uint32_t type; + uint32_t size; + // other fieds +} __attribute__((packed)) multiboot_tag_t; + +typedef struct multiboot_info +{ + uint32_t size; + uint32_t reserved; + multiboot_tag_t tags[]; + // end tags +} __attribute__((packed)) multiboot_info_t; + +typedef struct multiboot_tag_string { + uint32_t type; + uint32_t size; + char string[]; +} __attribute__((packed)) multiboot_tag_string_t; + +typedef struct multiboot_tag_basic_meminfo { + uint32_t type; + uint32_t size; + uint32_t mem_lower; + uint32_t mem_upper; +} __attribute__((packed)) multiboot_tag_basic_meminfo_t; + +typedef struct multiboot_tag_bootdev { + uint32_t type; + uint32_t size; + uint32_t biosdev; + uint32_t slice; + uint32_t part; +} __attribute__((packed)) multiboot_tag_bootdev_t; + +typedef struct multiboot_tag_module { + uint32_t type; + uint32_t size; + uint32_t mod_start; + uint32_t mod_end; + char cmdline[]; +} __attribute__((packed)) multiboot_tag_module_t; + +typedef struct multiboot_mmap_entry { + uint64_t addr; + uint64_t len; + uint32_t type; + uint32_t zero; +} __attribute__((packed)) multiboot_mmap_entry_t; + +typedef struct multiboot_tag_mmap { + uint32_t type; + uint32_t size; + uint32_t entry_size; + uint32_t entry_version; + multiboot_mmap_entry_t entries[]; +} __attribute__((packed)) multiboot_tag_mmap_t; + +typedef struct multiboot_elf_sections_entry { + uint32_t name; + uint32_t type; + uint64_t flags; + uint64_t addr; + uint64_t offset; + uint64_t size; + uint32_t link; + uint32_t info; + uint64_t alignment; + uint64_t entry_size; +} __attribute__((packed)) multiboot_elf_sections_entry_t; + +typedef struct multiboot_tag_elf_sections { + uint32_t type; + uint32_t size; + uint32_t num; + uint32_t section_size; + uint32_t shndx; + multiboot_elf_sections_entry_t sections[]; +} __attribute__((packed)) multiboot_tag_elf_sections_t; + +typedef struct reserved_areas { + uint64_t kernel_start; + uint64_t kernel_end; + uint64_t multiboot_start; + uint64_t multiboot_end; +} reserved_areas_t; + +bool multiboot_is_valid(unsigned long magic, unsigned long addr); +void* find_multiboot_tag(multiboot_tag_t *tags, uint16_t type); +reserved_areas_t read_multiboot_info(multiboot_info_t *mbi); + +#endif diff --git a/src/include/core/check.h b/src/include/core/check.h new file mode 100644 index 0000000..2d0464c --- /dev/null +++ b/src/include/core/check.h @@ -0,0 +1,6 @@ +#ifndef CORE_CHECK_H +#define CORE_CHECK_H + +void check_interrupts(); + +#endif diff --git a/src/include/core/cmos.h b/src/include/core/cmos.h new file mode 100644 index 0000000..876a9d3 --- /dev/null +++ b/src/include/core/cmos.h @@ -0,0 +1,34 @@ +#ifndef CORE_CMOS_H +#define CORE_CMOS_H + +#include + +#define CMOS_COMMAND_PORT 0x70 +#define CMOS_DATA_PORT 0x71 + +// http://wiki.osdev.org/CMOS +#define CMOS_REG_SECONDS 0x00 +#define CMOS_REG_MINUTES 0x02 +#define CMOS_REG_HOURS 0x04 +#define CMOS_REG_WEEKDAYS 0x06 +#define CMOS_REG_DAY 0x07 +#define CMOS_REG_MONTH 0x08 +#define CMOS_REG_YEAR 0x09 +#define CMOS_REG_CENTURY 0x32 +#define CMOS_REG_STATUS_A 0x0A +#define CMOS_REG_STATUS_B 0x0B + +typedef struct cmos_rtc { + uint8_t seconds; + uint8_t minutes; + uint8_t hours; + uint8_t weekdays; + uint8_t day; + uint8_t month; + uint16_t year; + uint8_t century; +} cmos_rtc_t; + +cmos_rtc_t cmos_read_rtc(); + +#endif diff --git a/src/include/core/debug.h b/src/include/core/debug.h new file mode 100644 index 0000000..5862301 --- /dev/null +++ b/src/include/core/debug.h @@ -0,0 +1,14 @@ +#ifndef CORE_DEBUG_H +#define CORE_DEBUG_H + +#include + +#ifdef ENABLE_KERNEL_DEBUG +#define DEBUG(format, ...) serial_printf(SERIAL_COM1, \ + "DEBUG: %s:%d:%s(): " format "\n", \ + __FILE__, __LINE__, __func__, __VA_ARGS__) +#else +#define DEBUG(format, ...) +#endif + +#endif diff --git a/src/include/core/idt.h b/src/include/core/idt.h new file mode 100644 index 0000000..48718d1 --- /dev/null +++ b/src/include/core/idt.h @@ -0,0 +1,38 @@ +#ifndef CORE_IDT_H +#define CORE_IDT_H + +#include + +#define IDT_ENTRIES 256 + +typedef struct opts { + uint8_t stack_OK : 3; + uint8_t ZEROS : 5; + uint8_t gate_type : 1; + uint8_t ONES : 3; + uint8_t ZERO : 1; + uint8_t DPL : 2; + uint8_t present : 1; +} __attribute__((packed)) opts_t; + +typedef struct idt_gate { + uint16_t ptr_low; + uint16_t selector; + opts_t opts; + uint16_t ptr_mid; + uint32_t ptr_high; + + uint8_t _1_reserved : 8; + uint8_t _type : 5; + uint32_t _2_reserved : 19; +} __attribute__((packed)) idt_gate_t; + +typedef struct idt_register { + uint16_t length; + uint64_t base; +} __attribute__((packed)) idt_register_t; + +void set_idt_gate(uint16_t n, uint64_t handler); +void set_idt(); + +#endif diff --git a/src/include/core/isr.h b/src/include/core/isr.h new file mode 100644 index 0000000..3d8ab61 --- /dev/null +++ b/src/include/core/isr.h @@ -0,0 +1,93 @@ +#ifndef CORE_ISR_H +#define CORE_ISR_H + +#include +#include + +#define PIC1 0x20 // Master PIC +#define PIC2 0xA0 // Slave PIC +#define PIC1_DATA (PIC1 + 1) +#define PIC2_DATA (PIC2 + 1) +#define PIC_EOI 0x20 // end of interrupt +#define IRQ_BASE 0x20 + +// exceptions, http://wiki.osdev.org/Exceptions +#define EXCEPTION_DE 0 +#define EXCEPTION_DB 1 +#define EXCEPTION_BP 3 +#define EXCEPTION_OF 4 +#define EXCEPTION_BR 5 +#define EXCEPTION_UD 6 +#define EXCEPTION_NM 7 +#define EXCEPTION_DF 8 +#define EXCEPTION_TS 10 +#define EXCEPTION_NP 11 +#define EXCEPTION_SS 12 +#define EXCEPTION_GP 13 +#define EXCEPTION_PF 14 +// ... + +#define IRQ0 32 +#define IRQ1 33 +#define IRQ2 34 +#define IRQ3 35 +#define IRQ4 36 + +// These functions are declared in interrupt.asm file +extern void isr0(); +extern void isr1(); +extern void isr2(); +extern void isr3(); +extern void isr4(); +extern void isr5(); +extern void isr6(); +extern void isr7(); +extern void isr8(); +extern void isr9(); +extern void isr10(); +extern void isr11(); +extern void isr12(); +extern void isr13(); +extern void isr14(); +extern void isr15(); +extern void isr16(); +extern void isr17(); +extern void isr18(); +extern void isr19(); +extern void isr20(); +extern void isr21(); +extern void isr22(); +extern void isr23(); +extern void isr24(); +extern void isr25(); +extern void isr26(); +extern void isr27(); +extern void isr28(); +extern void isr29(); +extern void isr30(); +extern void isr31(); + +extern void irq0(); +extern void irq1(); +extern void irq2(); +extern void irq3(); +extern void irq4(); + +typedef struct stack { + uint64_t instruction_pointer; + uint64_t code_segment; + uint64_t cpu_flags; + uint64_t stack_pointer; + uint64_t stack_segment; +} __attribute__((packed)) stack_t; + +typedef void (*isr_t) (stack_t *stack); + +void isr_init(); +void irq_init(); +void irq_disable(); +void isr_handler(uint64_t id, uint64_t stack) __asm__("isr_handler"); +void irq_handler(uint64_t id, uint64_t stack) __asm__("irq_handler"); +void register_interrupt_handler(uint64_t id, isr_t handler); + +#endif diff --git a/src/include/core/ports.h b/src/include/core/ports.h new file mode 100644 index 0000000..c75e4a8 --- /dev/null +++ b/src/include/core/ports.h @@ -0,0 +1,11 @@ +#ifndef CORE_PORT_H +#define CORE_PORT_H + +#include + +uint8_t port_byte_in(uint16_t port); +void port_byte_out(uint16_t port, uint8_t data); +uint16_t port_word_in(uint16_t port); +void port_word_out(uint16_t port, uint16_t data); + +#endif diff --git a/src/include/core/timer.h b/src/include/core/timer.h new file mode 100644 index 0000000..ce322ae --- /dev/null +++ b/src/include/core/timer.h @@ -0,0 +1,9 @@ +#ifndef CORE_TIMER_H +#define CORE_TIMER_H + +#include + +void timer_init(int freq); +uint32_t timer_tick(); + +#endif diff --git a/src/include/drivers/keyboard.h b/src/include/drivers/keyboard.h new file mode 100644 index 0000000..a83c0cd --- /dev/null +++ b/src/include/drivers/keyboard.h @@ -0,0 +1,8 @@ +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#define KEYBOARD_DATA_PORT 0x60 + +void keyboard_init(); + +#endif diff --git a/src/include/drivers/screen.h b/src/include/drivers/screen.h new file mode 100644 index 0000000..33b6fe2 --- /dev/null +++ b/src/include/drivers/screen.h @@ -0,0 +1,32 @@ +#ifndef SCREEN_H +#define SCREEN_H + +#include + +#define VIDEO_ADDRESS 0xB8000 +#define SCREEN_WIDTH 80 +#define SCREEN_HEIGHT 24 + +static const uint8_t COLOR_BLACK = 0; +static const uint8_t COLOR_BLUE = 1; +static const uint8_t COLOR_GREEN = 2; +static const uint8_t COLOR_CYAN = 3; +static const uint8_t COLOR_RED = 4; +static const uint8_t COLOR_MAGENTA = 5; +static const uint8_t COLOR_BROWN = 6; +static const uint8_t COLOR_LIGHT_GREY = 7; +static const uint8_t COLOR_DARK_GREY = 8; +static const uint8_t COLOR_LIGHT_BLUE = 9; +static const uint8_t COLOR_LIGHT_GREEN = 10; +static const uint8_t COLOR_LIGHT_CYAN = 11; +static const uint8_t COLOR_LIGHT_RED = 12; +static const uint8_t COLOR_LIGHT_MAGENTA = 13; +static const uint8_t COLOR_LIGHT_BROWN = 14; +static const uint8_t COLOR_WHITE = 15; + +void screen_init(); +void screen_clear(); +void screen_write(char c); +void screen_print(const char*); + +#endif diff --git a/src/include/drivers/serial.h b/src/include/drivers/serial.h new file mode 100644 index 0000000..77df44b --- /dev/null +++ b/src/include/drivers/serial.h @@ -0,0 +1,31 @@ +#ifndef SERIAL_H +#define SERIAL_H + +#include + +// All the I/O ports are calculated relative to the data port. This is because +// all serial ports (COM1, COM2, COM3, COM4) have their ports in the same +// order, but they start at different values. + +#define SERIAL_COM1 0x3F8 + +#define SERIAL_DATA_PORT(base) (base) +#define SERIAL_FIFO_COMMAND_PORT(base) (base + 2) +#define SERIAL_LINE_COMMAND_PORT(base) (base + 3) +#define SERIAL_MODEM_COMMAND_PORT(base) (base + 4) +#define SERIAL_LINE_STATUS_PORT(base) (base + 5) + +// Tells the serial port to expect first the highest 8 bits on the data port, +// then the lowest 8 bits will follow. +#define SERIAL_LINE_ENABLE_DLAB 0x80 + +#define SERIAL_SPEED_115200 1 +#define SERIAL_SPEED_57600 2 +#define SERIAL_SPEED_38400 3 + +void serial_init(uint16_t com, uint16_t divisor); +void serial_print(uint16_t com, const char* str); +void serial_write(uint16_t com, char c); +void serial_printf(uint16_t com, const char* format, ...); + +#endif diff --git a/src/include/kernel/kmain.h b/src/include/kernel/kmain.h new file mode 100644 index 0000000..8d2b849 --- /dev/null +++ b/src/include/kernel/kmain.h @@ -0,0 +1,25 @@ +#ifndef KMAIN_H +#define KMAIN_H + +#include + +#define KERNEL_ASCII " _____ _____ _____ _____ _____ \n" \ + "| | |_ _| __| __ | |\n" \ + "| | | | | | __| -| | |\n" \ + "|_____| |_| |_____|__|__|_____|" + + +#define KERNEL_NAME "utero" + +#ifdef ENABLE_KERNEL_DEBUG +#define KERNEL_VERSION "DEBUG MODE" +#else +#define KERNEL_VERSION "0.5.0" +#endif + +#define KERNEL_DATE __DATE__ +#define KERNEL_TIME __TIME__ + +void kmain(unsigned long magic, unsigned long addr) __asm__("kmain"); + +#endif diff --git a/src/include/kernel/panic.h b/src/include/kernel/panic.h new file mode 100644 index 0000000..a7d8e7c --- /dev/null +++ b/src/include/kernel/panic.h @@ -0,0 +1,11 @@ +#ifndef CORE_PANIC_H +#define CORE_PANIC_H + +#define __PANIC(format, ...) kernel_panic("\nPANIC in %s:%d:%s(): " \ + format "\n" "%s", \ + __FILE__, __LINE__, __func__, __VA_ARGS__); +#define PANIC(...) __PANIC(__VA_ARGS__, "") + +void kernel_panic(const char* format, ...); + +#endif diff --git a/src/include/libk/ctype.h b/src/include/libk/ctype.h new file mode 100644 index 0000000..d826b0b --- /dev/null +++ b/src/include/libk/ctype.h @@ -0,0 +1,8 @@ +#ifndef CTYPE_H +#define CTYPE_H + +#include + +bool isdigitk(int c); + +#endif diff --git a/src/include/libk/itoa.h b/src/include/libk/itoa.h new file mode 100644 index 0000000..93092e2 --- /dev/null +++ b/src/include/libk/itoa.h @@ -0,0 +1,8 @@ +#ifndef ITOA_H +#define ITOA_H + +#include + +void itoa(int n, char* str, int base); + +#endif diff --git a/src/include/libk/mem.h b/src/include/libk/mem.h new file mode 100644 index 0000000..e8e25b1 --- /dev/null +++ b/src/include/libk/mem.h @@ -0,0 +1,9 @@ +#ifndef MEM_H +#define MEM_H + +#include +#include + +void memcpyk(void *src, void *dest, size_t bytes); + +#endif diff --git a/src/include/libk/printfk.h b/src/include/libk/printfk.h new file mode 100644 index 0000000..f7f774c --- /dev/null +++ b/src/include/libk/printfk.h @@ -0,0 +1,12 @@ +#ifndef STDIO_H +#define STDIO_H + +#include +#include + +#define DEVICE_SCREEN 0 + +void printfk(const char* format, ...); +void vprintfk(int device, const char* format, va_list arg); + +#endif diff --git a/src/include/libk/strrev.h b/src/include/libk/strrev.h new file mode 100644 index 0000000..50ab51e --- /dev/null +++ b/src/include/libk/strrev.h @@ -0,0 +1,8 @@ +#ifndef STRREV_H +#define STRREV_H + +#include + +void strrev(char* s); + +#endif diff --git a/src/include/libk/ulltoa.h b/src/include/libk/ulltoa.h new file mode 100644 index 0000000..d3bdf35 --- /dev/null +++ b/src/include/libk/ulltoa.h @@ -0,0 +1,8 @@ +#ifndef ULLTOA_H +#define ULLTOA_H + +#include + +void ulltoa(uint64_t n, char* str, int base); + +#endif diff --git a/src/include/mmu/mmap.h b/src/include/mmu/mmap.h new file mode 100644 index 0000000..10641cf --- /dev/null +++ b/src/include/mmu/mmap.h @@ -0,0 +1,19 @@ +#ifndef MMU_MMAP_H +#define MMU_MMAP_H + +#include + +#define MMAP_GET_NUM 0 +#define MMAP_GET_ADDR 1 + +typedef uint64_t physical_address_t; +typedef uint32_t frame_t; + +void mmap_init(multiboot_tag_mmap_t *mmap, reserved_areas_t reserved); +frame_t mmap_allocate_frame(); +physical_address_t mmap_read(uint32_t request, uint8_t mode); +frame_t frame_containing_address(uint64_t addr); +uint64_t frame_starting_address(frame_t frame); +void mmap_deallocate_frame(frame_t frame); + +#endif diff --git a/src/include/mmu/mmu.h b/src/include/mmu/mmu.h new file mode 100644 index 0000000..8fcbaa8 --- /dev/null +++ b/src/include/mmu/mmu.h @@ -0,0 +1,9 @@ +#ifndef MMU_MMU_H +#define MMU_MMU_H + +#include +#include + +void mmu_init(multiboot_tag_mmap_t *mmap, reserved_areas_t reserved); + +#endif diff --git a/src/include/mmu/paging.h b/src/include/mmu/paging.h new file mode 100644 index 0000000..fb952c8 --- /dev/null +++ b/src/include/mmu/paging.h @@ -0,0 +1,59 @@ +#ifndef MMU_PAGING_H +#define MMU_PAGING_H + +#include +#include + +#define PAGE_SIZE 4096 +#define PAGE_ENTRIES 512 +#define P4_TABLE 0xfffffffffffff000 + +typedef uint64_t virtual_address_t; +typedef uint64_t page_t; + +// http://os.phil-opp.com/modifying-page-tables.html#page-table-entries +// https://github.com/tmathmeyer/sos +typedef union page_entry { + uint64_t packed; + struct { + uint8_t present : 1; + uint8_t writable : 1; + uint8_t user_accessable : 1; + uint8_t write_thru_cache : 1; + uint8_t disable_cache : 1; + uint8_t accessed : 1; + uint8_t dirty : 1; + uint8_t huge_page : 1; + uint8_t global : 1; + uint8_t OS_1 : 1; + uint8_t OS_2 : 1; + uint8_t OS_3 : 1; + uint64_t _addr_mask : 40; + uint8_t OS_4 : 1; + uint8_t OS_5 : 1; + uint8_t OS_6 : 1; + uint8_t OS_7 : 1; + uint8_t OS_8 : 1; + uint8_t OS_9 : 1; + uint8_t OS_A : 1; + uint8_t OS_B : 1; + uint8_t OS_C : 1; + uint8_t OS_D : 1; + uint8_t OS_E : 1; + uint8_t no_execute : 1; + }; +} __attribute__((packed)) page_entry_t; + +typedef page_entry_t* page_table_t; + +void paging_init(); +page_t page_containing_address(uint64_t addr); +uint64_t page_starting_address(page_t page); +physical_address_t translate(virtual_address_t addr); +frame_t translate_page(page_t page); +void map_page_to_frame(page_t page, frame_t frame, uint8_t flags); +void identity_map(frame_t frame, uint8_t flags); +void map(page_t page, uint8_t flags); +void unmap(page_t page); + +#endif diff --git a/src/include/unused.h b/src/include/unused.h new file mode 100644 index 0000000..8d66004 --- /dev/null +++ b/src/include/unused.h @@ -0,0 +1,6 @@ +#ifndef UNUSED_H +#define UNUSED_H + +#define UNUSED(x) (void)(x) + +#endif diff --git a/src/kernel/kmain.c b/src/kernel/kmain.c new file mode 100644 index 0000000..7d7442c --- /dev/null +++ b/src/kernel/kmain.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void print_welcome_messge() +{ + printfk("%s\n", KERNEL_ASCII); + printfk("%s %s / Built on: %s at %s\n\n", KERNEL_NAME, KERNEL_VERSION, KERNEL_DATE, KERNEL_TIME); +} + +void kmain(unsigned long magic, unsigned long addr) +{ + screen_init(); + screen_clear(); + + if (multiboot_is_valid(magic, addr) == false) { + PANIC("invalid multiboot"); + } + + multiboot_info_t *mbi = (multiboot_info_t*) addr; + reserved_areas_t reserved = read_multiboot_info(mbi); + + print_welcome_messge(); + printfk("- multiboot_start = 0x%X, multiboot_end = 0x%X\n", reserved.multiboot_start, reserved.multiboot_end); + printfk("- kernel_start = 0x%X, kernel_end = 0x%X\n", reserved.kernel_start, reserved.kernel_end); + + isr_init(); + irq_init(); + printfk("- interruptions enabled\n"); + + // enable scheduler + timer_init(50); // 50hz + printfk("- clock (timer) enabled\n"); + + // self-checks + check_interrupts(); + + // enable serial port + serial_init(SERIAL_COM1, SERIAL_SPEED_115200); + DEBUG("%s has started", KERNEL_NAME); + + keyboard_init(); + printfk("- keyboard routine enabled\n"); + + // memory + multiboot_tag_mmap_t *mmap = find_multiboot_tag(mbi->tags, MULTIBOOT_TAG_TYPE_MMAP); + mmu_init(mmap, reserved); + printfk("- frame allocator and paging enabled\n"); + + // cmos + cmos_rtc_t rtc = cmos_read_rtc(); + printfk( + "\nHello Human, today's date and time is %2d/%2d/%2d %2d:%2d:%2d UTC\n", + rtc.year, rtc.month, rtc.day, + rtc.hours, rtc.minutes, rtc.seconds + ); + DEBUG( + "\nHello Human, today's date and time is %2d/%2d/%2d %2d:%2d:%2d UTC\n", + rtc.year, rtc.month, rtc.day, + rtc.hours, rtc.minutes, rtc.seconds + ); + // prompt :p + printfk("$ "); + + // Test for PANIC + // PANIC("This is a test!"); + + while (1) { + __asm__("hlt"); + } +} diff --git a/src/kernel/lib_u.cr b/src/kernel/lib_u.cr deleted file mode 100644 index cd64d84..0000000 --- a/src/kernel/lib_u.cr +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -# This was taken from crystal/src/lib_c.cr -# This is a copy of src/core/lib_cr.cr - -lib LibU - alias Char = UInt8 - alias UChar = Char - alias SChar = Int8 - alias Short = Int16 - alias UShort = UInt16 - alias Int = Int32 - alias UInt = UInt32 - - {% if flag?(:x86_64) || flag?(:aarch64) %} - alias Long = Int64 - alias ULong = UInt64 - {% elsif flag?(:i686) || flag?(:arm) %} - alias Long = Int32 - alias ULong = UInt32 - {% end %} - - alias LongLong = Int64 - alias ULongLong = UInt64 - alias Float = Float32 - alias Double = Float64 - - # typedef unsigned int tid_t; - alias Tid_t = Int - alias UInt8_t = UChar - - $environ : Char** -end diff --git a/src/kernel/lib_u/x86_64-linux-musl/c/dummy_exception.cr b/src/kernel/lib_u/x86_64-linux-musl/c/dummy_exception.cr deleted file mode 100644 index fb6d693..0000000 --- a/src/kernel/lib_u/x86_64-linux-musl/c/dummy_exception.cr +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. -require "../../../lib_u" - -# dummy_exception.c -lib LibU - fun dummy_exception : LibU::Char* -end diff --git a/src/kernel/lib_u/x86_64-linux-musl/c/hello.cr b/src/kernel/lib_u/x86_64-linux-musl/c/hello.cr deleted file mode 100644 index d3da277..0000000 --- a/src/kernel/lib_u/x86_64-linux-musl/c/hello.cr +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. -require "../../../lib_u" - -# hello.c -lib LibU - fun hello_from_c : LibU::Char* -end diff --git a/src/kernel/lib_u/x86_64-linux-musl/c/idt.cr b/src/kernel/lib_u/x86_64-linux-musl/c/idt.cr deleted file mode 100644 index 99da984..0000000 --- a/src/kernel/lib_u/x86_64-linux-musl/c/idt.cr +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. -require "../../../lib_u" - -# idt.c -lib LibU - fun idt_install : Void -end diff --git a/src/kernel/lib_u/x86_64-linux-musl/c/isrs.cr b/src/kernel/lib_u/x86_64-linux-musl/c/isrs.cr deleted file mode 100644 index 3c115e6..0000000 --- a/src/kernel/lib_u/x86_64-linux-musl/c/isrs.cr +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. -require "../../../lib_u" - -# isrs.c -lib LibU - fun isrs_install : Void -end diff --git a/src/kernel/lib_u/x86_64-linux-musl/c/make_string.cr b/src/kernel/lib_u/x86_64-linux-musl/c/make_string.cr deleted file mode 100644 index d5a7224..0000000 --- a/src/kernel/lib_u/x86_64-linux-musl/c/make_string.cr +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. -require "../../../lib_u" - -# make_string.c -lib LibU - fun make_string(x1 : LibU::Char*, ...) : LibU::Char* -end diff --git a/src/kernel/main.cr b/src/kernel/main.cr index 04d79ed..c6650ad 100644 --- a/src/kernel/main.cr +++ b/src/kernel/main.cr @@ -1,38 +1,5 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. +lib LibUtero + fun kmain : Void +end -require "./utero_init" - -print "24: This line uses print method + \\n\n" - -set_color(VGAColor::WHITE.value, VGAColor::BLACK.value) -puts "25: " -puts "26: Hello World from Crystal!!!" -puts "27: " -puts "28: Welcome to Utero!!!" -puts "29: " -puts "30: \t\t\t\t\t\t\t\t\t\t\ttab * 11 = 80 cols + 8 cols(in the next line)" -reset_color -puts "31: Reset Color" -# print 80 characters and insert a blank into the end of line -print "32: 5678901234567890123456789012345678901234567890123456789012345678901234567890" -print "\b"; puts -puts "33: Current directory is #{__DIR__}" -print "34: "; cprint(LibU.hello_from_c) -cprint(LibU.dummy_exception) -# For testing: Division By Zero -# 42 / 0 -# clear -# This would fail... -# LibU.create_foo - -# Calling core test method -# Uncomment these lines when you test -# clear -# core_test +LibUtero.kmain diff --git a/src/kernel/panic.c b/src/kernel/panic.c new file mode 100644 index 0000000..a8c4450 --- /dev/null +++ b/src/kernel/panic.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +void kernel_panic(const char* format, ...) +{ + va_list arg; + va_start(arg, format); + vprintfk(DEVICE_SCREEN, format, arg); + va_end(arg); + + printfk("\nSystem halted!\n"); + + // completely stop + irq_disable(); + while (1) ; +} diff --git a/src/kernel/prelude.cr b/src/kernel/prelude.cr deleted file mode 100644 index 40115e3..0000000 --- a/src/kernel/prelude.cr +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -# Core libraries -require "../core/prelude" - -# Order-dependent list -require "./lib_u" -require "./lib_u/**" - -# Alpha-sorted list -require "./tasks" diff --git a/src/kernel/scrn.cr b/src/kernel/scrn.cr deleted file mode 100644 index 1faa9d5..0000000 --- a/src/kernel/scrn.cr +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. -require "./scrn_h" - -struct Scrn - # Constants - private TAB_SIZE = 8 - private VGA_WIDTH = 80 - private VGA_HEIGHT = 25 - private VGA_SIZE = VGA_WIDTH * VGA_HEIGHT - private VGA_MEMORY = Pointer(UInt16).new(0xb8000_u64) - private VGA_FG_COLOR = VGAColor::LIGHT_GREEN.value - private VGA_BG_COLOR = VGAColor::BLACK.value - - # To avoid a bug(regression) - # https://github.com/crystal-lang/crystal/issues/4054 - # https://groups.google.com/d/topic/crystal-lang/wy9hfzAe0_U/discussion - @cur_x : Int32 - @cur_y : Int32 - - def initialize - @vmem = VGA_MEMORY - @cur_x = 0 - @cur_y = 0 - @vga_color = set_default_color.as(UInt8) - - VGA_WIDTH.times do |cur_x| - VGA_HEIGHT.times do |cur_y| - @vmem[cur_y * VGA_WIDTH + cur_x] = 0_u16 - end - end - clear - end - - private def scroll - # Move the current text chunk that makes up the screen - # back in the buffer by a line - VGA_WIDTH.times do |cur_x| - VGA_HEIGHT.times do |cur_y| - @vmem[cur_y * VGA_WIDTH + cur_x] = @vmem[(cur_y + 1) * VGA_WIDTH + cur_x] - end - end - - # Insert VGA_WIDTH blank character to the last line - VGA_WIDTH.times do |cur_x| - @vmem[VGA_SIZE - VGA_WIDTH + cur_x] = (@vga_color.to_u16 << 8 | ' '.ord).as(UInt16) - end - - # The cursor should be on the last line - @cur_y = VGA_HEIGHT - 1 - end - - # Comments are taken from http://www.osdever.net/bkerndev/Docs/printing.htm - # Updates the hardware cursor: the little blinking line - # on the screen under the last character pressed! - private def update_cur - # The equation for finding the index in a linear - # chunk of memory can be represented by: - # Index = [(y * width) + x] - temp = @cur_y * VGA_WIDTH + @cur_x - - # This sends a command to indicies 14 and 15 in the - # CRT Control Register of the VGA controller. These - # are the high and low bytes of the index that show - # where the hardware cursor is to be 'blinking'. To - # learn more, you should look up some VGA specific - # programming documents. A great start to graphics: - # http://www.brackeen.com/home/vga - outb(0x3d4_u16, 15_u8) - outb(0x3d5_u16, temp.to_u8) - outb(0x3d4_u16, 14_u8) - outb(0x3d5_u16, (temp >> 8).to_u8) - end - - private def linebreak - @cur_x = 0 - @cur_y += 1 - scroll if @cur_y == VGA_HEIGHT - update_cur - end - - private def put_byte(byte) - if byte == '\n'.ord - linebreak - return - end - if byte == '\r'.ord - @cur_x = 0 - return - end - if byte == '\t'.ord - # Handles a tab by incrementing the cursor's x, but only - # to a point that will make it divisible by TAB_SIZE - @cur_x = (@cur_x + TAB_SIZE) / TAB_SIZE * TAB_SIZE - linebreak if @cur_x >= VGA_WIDTH - return - end - # Backspace - if byte == '\b'.ord - if @cur_x == 0 - @cur_y = @cur_y > 0 ? @cur_y - 1 : 0 - @cur_x = VGA_WIDTH - 1 - else - @cur_x -= 1 - end - @vmem[@cur_y * VGA_WIDTH + @cur_x] = (@vga_color.to_u16 << 8 | ' '.ord).as(UInt16) - return - end - - @vmem[@cur_y * VGA_WIDTH + @cur_x] = (@vga_color.to_u16 << 8 | byte).as(UInt16) - @cur_x += 1 - linebreak if @cur_x == VGA_WIDTH - update_cur - end - - def clear - attr = 0x0f.to_u16 << 8 | ' '.ord - VGA_SIZE.times { |i| @vmem[i] = attr } - - @cur_x = 0 - @cur_y = 0 - update_cur - end - - def set_default_color - @vga_color = VGA_BG_COLOR << 4 | VGA_FG_COLOR - end - - def set_color(fc : VGAColor_T, bc : VGAColor_T) : VGAColor_T - @vga_color = bc << 4 | fc - end - - def print(str) - str.each_byte { |byte| put_byte(byte) } - end - - def cprint(buf) - len = LibCR.strlen(buf) - len.times do |i| - put_byte(buf[i]) - end - end - - def puts(str) - print(str) - linebreak - end -end - -SCRN = Scrn.new - -def print(str) - SCRN.print(str) -end - -def puts(str) - SCRN.puts(str) -end - -def puts - print "\n" -end - -def cprint(str : UInt8*) - SCRN.cprint(str) -end - -# Displays error messages from C with linebreak -fun eputs(str : UInt8*) - SCRN.cprint(str) - puts -end - -# Displays error messages from C -fun eprint(str : UInt8*) - SCRN.cprint(str) -end - -def clear - SCRN.clear -end - -def set_color(fc : VGAColor_T, bc : VGAColor_T) - SCRN.set_color(fc, bc) -end - -def reset_color - SCRN.set_default_color -end diff --git a/src/kernel/scrn_h.cr b/src/kernel/scrn_h.cr deleted file mode 100644 index 1ea4d6b..0000000 --- a/src/kernel/scrn_h.cr +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. - -enum VGAColor : UInt8 - BLACK # => 0 - BLUE # => 1 - GREEN # => 2 - CYAN # => 3 - RED # => 4 - MAGENTA # => 5 - BROWN # => 6 - LIGHT_GREY # => 7 - DARK_GREY # => 8 - LIGHT_BLUE # => 9 - LIGHT_GREEN # => 10 - LIGHT_CYAN # => 11 - LIGHT_RED # => 12 - LIGHT_MAGENTA # => 13 - LIGHT_BROWN # => 14 - WHITE # => 15 -end - -alias VGAColor_T = UInt8 diff --git a/src/kernel/tasks.cr b/src/kernel/tasks.cr deleted file mode 100644 index bcd4a74..0000000 --- a/src/kernel/tasks.cr +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2016-2017 Utero OS Developers. See the COPYRIGHT -# file at the top-level directory of this distribution. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. -require "./lib_u" - -# tasks.c -lib LibU - fun multitasking_init : LibU::Int - fun reschedule : Void - fun create_kernel_task(id : LibU::Tid_t*, ep : Void* -> LibU::Int, args : Void*, prio : LibU::UInt8_t) : Int -end diff --git a/src/kernel/utero_init.cr b/src/kernel/utero_init.cr deleted file mode 100644 index b26d737..0000000 --- a/src/kernel/utero_init.cr +++ /dev/null @@ -1,24 +0,0 @@ -require "./prelude" -require "./scrn" -require "../core_test/test" - -lib LibU - fun make_kernel_info : LibU::Char* - fun create_foo : LibU::Int -end - -# A same named function in C, and call that from assembly before call main -# fun early_info(info : LibBootstrap::EarlyInfo) -# puts "Call early_info" -# puts "Info here" if info -# cprint(LibU.make_string("kernel_start: %p\nkernel_end: %p\n", info.kernel_start, info.kernel_end)) -# cprint(LibU.make_string("Dummy Exception (%d) at 0x%llx:0x%llx, error code 0x%llx, rflags 0x%llx\n", -# 7, 0x8000000000000000, 0x8000000000000000, 0x8000000000000000, 0x8000000000000000)) -# end - -# On C code, it makes string to display addresses of kernel_start, kernel_end, etc... -# and print it on Crystal (it's so complecated, you know...) -cprint LibU.make_kernel_info -LibU.idt_install -LibU.isrs_install -LibU.multitasking_init diff --git a/src/libk/ctype.c b/src/libk/ctype.c new file mode 100644 index 0000000..526bd0c --- /dev/null +++ b/src/libk/ctype.c @@ -0,0 +1,6 @@ +#include + +bool isdigitk(int c) +{ + return (c >= 0 && c <= 9); +} diff --git a/src/libk/itoa.c b/src/libk/itoa.c new file mode 100644 index 0000000..0be5b82 --- /dev/null +++ b/src/libk/itoa.c @@ -0,0 +1,31 @@ +#include +#include + +void itoa(int n, char* str, int base) +{ + int i = 0; + int sign; + + if (n == 0) { + str[i++] = '0'; + str[i] = '\0'; + + return; + } + + if ((sign = n) < 0 && base == 10) { + n = -n; + } + + do { + int r = n % base; + str[i++] = (r < 10) ? r + '0' : r + 'a' - 10; + } while ((n /= base) > 0); + + if (sign < 0 && base == 10) { + str[i++] = '-'; + } + + str[i] = '\0'; + strrev(str); +} diff --git a/src/libk/mem.c b/src/libk/mem.c new file mode 100644 index 0000000..995b72c --- /dev/null +++ b/src/libk/mem.c @@ -0,0 +1,11 @@ +#include + +void memcpyk(void *src, void *dest, size_t bytes) +{ + char *s = (char *) src; + char *d = (char *) dest; + + for(uint64_t i = 0; i < bytes; i++) { + d[i] = s[i]; + } +} diff --git a/src/libk/printfk.c b/src/libk/printfk.c new file mode 100644 index 0000000..71b5177 --- /dev/null +++ b/src/libk/printfk.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +void putchark(int device, char c); +void putsk(int device, char *s); + +void printfk(const char* format, ...) +{ + va_list arg; + va_start(arg, format); + vprintfk(DEVICE_SCREEN, format, arg); + va_end(arg); +} + +void vprintfk(int device, const char* format, va_list arg) +{ + int32_t i_val; + uint32_t u_val; + uint64_t l_val; + char s_val[20]; + + for (unsigned int i = 0; i < strlen(format); i++) { + char c = format[i]; + + if (c == '%') { + uint8_t leftpad = format[i + 1] - '0'; + + if (isdigitk(leftpad)) { + i++; + } else { + leftpad = 0; + } + + switch (format[i + 1]) { + case '%': + putchark(device, '%'); + break; + + case 'c': + putchark(device, va_arg(arg, int)); + break; + + case 'd': + i_val = va_arg(arg, int32_t); + itoa(i_val, s_val, 10); + + while ((leftpad - (uint8_t) strlen(s_val)) > 0) { + putchark(device, '0'); + leftpad--; + } + + putsk(device, s_val); + break; + + case 'u': + case 'x': + u_val = va_arg(arg, uint32_t); + itoa(u_val, s_val, format[i + 1] == 'x' ? 16 : 10); + putsk(device, s_val); + break; + + case 'L': + case 'X': + l_val = va_arg(arg, uint64_t); + ulltoa(l_val, s_val, format[i + 1] == 'X' ? 16 : 10); + putsk(device, s_val); + break; + + case 's': + putsk(device, va_arg(arg, char *)); + break; + } + + i++; + } else { + putchark(device, c); + } + } +} + +void putchark(int device, char c) +{ + if (device > DEVICE_SCREEN) { + serial_write(device, c); + } else { + screen_write(c); + } +} + +void putsk(int device, char *s) +{ + if (device > DEVICE_SCREEN) { + serial_print(device, s); + } else { + screen_print(s); + } +} diff --git a/src/libk/strrev.c b/src/libk/strrev.c new file mode 100644 index 0000000..3c45e85 --- /dev/null +++ b/src/libk/strrev.c @@ -0,0 +1,12 @@ +#include +#include + +void strrev(char* s) +{ + char c; + for (size_t i = 0, j = strlen(s) - 1; i < j; i++, j--) { + c = s[i]; + s[i] = s[j]; + s[j] = c; + } +} diff --git a/src/libk/ulltoa.c b/src/libk/ulltoa.c new file mode 100644 index 0000000..cdd1add --- /dev/null +++ b/src/libk/ulltoa.c @@ -0,0 +1,31 @@ +#include +#include + +void ulltoa(uint64_t n, char* str, int base) +{ + int i = 0; + int sign; + + if (n == 0) { + str[i++] = '0'; + str[i] = '\0'; + + return; + } + + if ((sign = n) < 0 && base == 10) { + n = -n; + } + + do { + int r = n % base; + str[i++] = (r < 10) ? r + '0' : r + 'a' - 10; + } while ((n /= base) > 0); + + if (sign < 0 && base == 10) { + str[i++] = '-'; + } + + str[i] = '\0'; + strrev(str); +} diff --git a/src/mmu/mmap.c b/src/mmu/mmap.c new file mode 100644 index 0000000..af9fb7f --- /dev/null +++ b/src/mmu/mmap.c @@ -0,0 +1,126 @@ +#include +#include +#include + +multiboot_tag_mmap_t *memory_area; +physical_address_t kernel_start; +physical_address_t kernel_end; +physical_address_t multiboot_start; +physical_address_t multiboot_end; +frame_t next_free_frame; + +void mmap_init(multiboot_tag_mmap_t *mmap, reserved_areas_t reserved) { + memory_area = mmap; + kernel_start = reserved.kernel_start; + kernel_end = reserved.kernel_end; + multiboot_start = reserved.multiboot_start; + multiboot_end = reserved.multiboot_end; + next_free_frame = 1; + + DEBUG( + "Initialized MMAP with memory_area = 0x%x, multiboot_start = 0x%x, " + "multiboot_end = 0x%x, next_free_frame = %d", + memory_area, + multiboot_start, + multiboot_end, + next_free_frame + ); +} + +physical_address_t mmap_read(frame_t request, uint8_t mode) +{ + // If the user specifies an invalid mode, also skip the request + if (mode != MMAP_GET_NUM && mode != MMAP_GET_ADDR) { + return 0; + } + + DEBUG("request = %d, mode = %d", request, mode); + + frame_t cur_num = 0; + multiboot_mmap_entry_t *entry; + for ( + entry = ((multiboot_tag_mmap_t *) memory_area)->entries; + (uint8_t *) entry < (uint8_t *) memory_area + memory_area->size; + entry = (multiboot_mmap_entry_t *) ((unsigned long) entry + ((multiboot_tag_mmap_t *) memory_area)->entry_size) + ) { + physical_address_t i; + physical_address_t entry_end = entry->addr + entry->len; + for (i = entry->addr; i + PAGE_SIZE < entry_end; i += PAGE_SIZE) { + if (mode == MMAP_GET_NUM && request >= i && request <= i + PAGE_SIZE) { + // If we're looking for a frame number from an address and we + // found it return the frame number + return cur_num + 1; + } + + // If the requested chunk is in reserved space, skip it + if (entry->type == MULTIBOOT_MEMORY_RESERVED) { + if (mode == MMAP_GET_ADDR && cur_num == request) { + // The address of a chunk in reserved space was requested + // Increment the request until it is no longer reserved + request++; + } + // Skip to the next chunk until it's no longer reserved + cur_num++; + continue; + } else if (mode == MMAP_GET_ADDR && cur_num == request) { + // If we're looking for a frame starting address and we found + // it return the starting address + return i; + } + cur_num++; + } + } + + return 0; +} + +frame_t mmap_allocate_frame() +{ + DEBUG("start (next_free_frame = %d)", next_free_frame); + + // Get the address for the next free frame + physical_address_t current_addr = mmap_read(next_free_frame, MMAP_GET_ADDR); + + DEBUG("current_addr = 0x%x", current_addr); + + // Verify that the frame is not in the reserved areas. If it is, increment + // the next free frame number and recursively call back. + if ( + (current_addr >= multiboot_start && current_addr <= multiboot_end) || + (current_addr >= kernel_start && current_addr <= kernel_end) + ) { + next_free_frame++; + + return mmap_allocate_frame(); + } + + // Call mmap_read again to get the frame number for our address + frame_t current_frame_num = mmap_read(current_addr, MMAP_GET_NUM); + + // Update next_free_frame to the next unallocated frame number + next_free_frame = current_frame_num + 1; + + DEBUG("return current_frame_num = %d (next_free_frame = %d)", current_frame_num, next_free_frame); + + // Finally, return the newly allocated frame num + return current_frame_num; +} + +frame_t frame_containing_address(uint64_t addr) +{ + return addr / PAGE_SIZE; +} + +uint64_t frame_starting_address(frame_t frame) +{ + return frame * PAGE_SIZE; +} + +void mmap_deallocate_frame(frame_t frame) +{ + uint64_t addr = frame_starting_address(frame); + + for (int i = 0; i < PAGE_SIZE; i++) { + ((uint64_t *)addr)[i] = 0; + } +} diff --git a/src/mmu/mmu.c b/src/mmu/mmu.c new file mode 100644 index 0000000..15d29d2 --- /dev/null +++ b/src/mmu/mmu.c @@ -0,0 +1,7 @@ +#include + +void mmu_init(multiboot_tag_mmap_t *mmap, reserved_areas_t reserved) +{ + mmap_init(mmap, reserved); + paging_init(); +} diff --git a/src/mmu/paging.c b/src/mmu/paging.c new file mode 100644 index 0000000..eb32755 --- /dev/null +++ b/src/mmu/paging.c @@ -0,0 +1,182 @@ +#include +#include +#include + +uint64_t p4_index(frame_t frame); +uint64_t p3_index(frame_t frame); +uint64_t p2_index(frame_t frame); +uint64_t p1_index(frame_t frame); +frame_t pointed_frame(page_entry_t entry); +void set_addr_mask(page_entry_t *entry, uint64_t addr); +page_table_t next_table_create(page_table_t in, uint64_t index); +page_table_t next_table_address(page_table_t table, uint32_t index); + +void paging_init() +{ +} + +page_t page_containing_address(uint64_t addr) +{ + return addr / PAGE_SIZE; +} + +physical_address_t translate(virtual_address_t addr) +{ + page_t virt = page_containing_address(addr); + uint64_t offset = virt % PAGE_SIZE; + + return (physical_address_t) (page_starting_address((uint64_t) translate_page(virt)) + offset); +} + +frame_t translate_page(page_t page) +{ + page_table_t p3 = next_table_address((page_table_t)P4_TABLE, p4_index(page)); + if (!p3) { + return 0; + } + + // TODO: huge page + + page_table_t p2 = next_table_address(p3, p3_index(page)); + if (!p2) { + return 0; + } + + page_table_t p1 = next_table_address(p2, p2_index(page)); + if (!p1) { + return 0; + } + + page_entry_t entry = p1[p1_index(page)]; + + return pointed_frame(entry); +} + +void map_page_to_frame(page_t page, frame_t frame, uint8_t flags) +{ + page_table_t p3 = next_table_create((page_table_t)P4_TABLE, p4_index(page)); + DEBUG("p3 = 0x%X", p3); + + page_table_t p2 = next_table_create((page_table_t)p3, p3_index(page)); + DEBUG("p2 = 0x%X", p2); + + page_table_t p1 = next_table_create((page_table_t)p2, p2_index(page)); + DEBUG("p1 = 0x%X", p1); + + set_addr_mask(&p1[p1_index(page)], frame); + p1[p1_index(page)].packed = flags; + p1[p1_index(page)].present = 1; +} + +void identity_map(frame_t frame, uint8_t flags) +{ + page_t page = page_containing_address(frame_starting_address(frame)); + map_page_to_frame(page, frame, flags); +} + +void map(page_t page, uint8_t flags) +{ + frame_t frame = mmap_allocate_frame(); + map_page_to_frame(page, frame, flags); +} + +void unmap(page_t page) +{ + page_table_t p3 = next_table_address((page_table_t)P4_TABLE, p4_index(page)); + page_table_t p2 = next_table_address(p3, p3_index(page)); + page_table_t p1 = next_table_address(p2, p2_index(page)); + + // TODO: free p(1,2,3) table if empty + + page_entry_t entry = p1[p1_index(page)]; + + if (!entry.present) { + DEBUG("%s", "ERROR"); + return; + } + + p1[p1_index(page)].present = 0; + mmap_deallocate_frame(pointed_frame(entry)); + + // flush the translation lookaside buffer + // http://os.phil-opp.com/modifying-page-tables.html#unmap + __asm__("invlpg (%0)" ::"r" (page_starting_address(page)) : "memory"); +} + +void set_addr_mask(page_entry_t *entry, uint64_t addr) +{ + uint64_t mask = 0xfff0000000000fff; + entry->packed &= mask; + entry->packed |= (addr * PAGE_SIZE); +} + +page_table_t next_table_create(page_table_t table, uint64_t index) +{ + page_entry_t location = table[index]; + page_table_t res; + + DEBUG("index = %L - start", index); + + if (!location.present) { + frame_t frame = mmap_allocate_frame(); + physical_address_t addr = mmap_read(frame, MMAP_GET_ADDR); + + DEBUG("index = %L, frame = %d, addr = 0x%X", index, frame, addr); + + set_addr_mask(&table[index], addr); + table[index].present = 1; + table[index].writable = 1; + } + + res = next_table_address(table, index); + + return res; +} + +uint64_t p4_index(frame_t frame) +{ + return (frame >> 27) & 0777; +} + +uint64_t p3_index(frame_t frame) +{ + return (frame >> 18) & 0777; +} + +uint64_t p2_index(frame_t frame) +{ + return (frame >> 9) & 0777; +} + +uint64_t p1_index(frame_t frame) +{ + return (frame >> 0) & 0777; +} + +frame_t pointed_frame(page_entry_t entry) +{ + if (entry.present) { + uint64_t e = entry.packed; + e &= 0x000ffffffffff000; + + return frame_containing_address(e); + } + + return 0; +} + +uint64_t page_starting_address(page_t page) +{ + return page * PAGE_SIZE; +} + +page_table_t next_table_address(page_table_t table, uint32_t index) +{ + if (table[index].present && !table[index].huge_page) { + uint64_t table_address = (uint64_t) table; + + return (page_table_t) ((table_address << 9) | (index << 12)); + } + + return 0; +} diff --git a/src/musl b/src/musl deleted file mode 160000 index fc85fb3..0000000 --- a/src/musl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fc85fb38605a8bf341c367b8ab0d36edab2bdbfc