diff options
author | the lemons <citrons@mondecitronne.com> | 2021-03-31 00:35:03 -0500 |
---|---|---|
committer | the lemons <citrons@mondecitronne.com> | 2021-03-31 00:35:03 -0500 |
commit | e03bf1f1ec1230117ca358a75624f2c50c01f045 (patch) | |
tree | 113c0d0360abec753998ced5fc59f7552e603524 |
initial files
-rw-r--r-- | Makefile | 54 | ||||
-rw-r--r-- | asm_obj/boot.o | bin | 0 -> 18332 bytes | |||
-rw-r--r-- | asm_obj/interrupt_stub.o | bin | 0 -> 1620 bytes | |||
-rw-r--r-- | grub.cfg | 6 | ||||
-rw-r--r-- | isodir/boot/grub/grub.cfg | 6 | ||||
-rwxr-xr-x | isodir/boot/kernel.bin | bin | 0 -> 86632 bytes | |||
-rwxr-xr-x | kernel.bin | bin | 0 -> 86632 bytes | |||
-rw-r--r-- | linker.ld | 26 | ||||
-rw-r--r-- | obj/bees.o | bin | 0 -> 4076 bytes | |||
-rw-r--r-- | obj/interrupt.o | bin | 0 -> 8668 bytes | |||
-rw-r--r-- | obj/keyboard.o | bin | 0 -> 6180 bytes | |||
-rw-r--r-- | obj/main.o | bin | 0 -> 6720 bytes | |||
-rw-r--r-- | obj/port.o | bin | 0 -> 3168 bytes | |||
-rw-r--r-- | obj/printf.o | bin | 0 -> 32208 bytes | |||
-rw-r--r-- | obj/string.o | bin | 0 -> 5652 bytes | |||
-rw-r--r-- | obj/vga.o | bin | 0 -> 7656 bytes | |||
-rw-r--r-- | os.iso | bin | 0 -> 19890176 bytes | |||
-rw-r--r-- | src/.tags | 119 | ||||
-rw-r--r-- | src/bees.c | 22 | ||||
-rw-r--r-- | src/bees.h | 6 | ||||
-rw-r--r-- | src/boot.s | 80 | ||||
-rw-r--r-- | src/interrupt.c | 92 | ||||
-rw-r--r-- | src/interrupt.h | 46 | ||||
-rw-r--r-- | src/interrupt_stub.s | 54 | ||||
-rw-r--r-- | src/keyboard.c | 98 | ||||
-rw-r--r-- | src/keyboard.h | 15 | ||||
-rw-r--r-- | src/main.c | 45 | ||||
-rw-r--r-- | src/port.c | 38 | ||||
-rw-r--r-- | src/port.h | 19 | ||||
-rw-r--r-- | src/printf.c | 914 | ||||
-rw-r--r-- | src/printf.h | 117 | ||||
-rw-r--r-- | src/string.c | 64 | ||||
-rw-r--r-- | src/string.h | 15 | ||||
-rw-r--r-- | src/vga.c | 103 | ||||
-rw-r--r-- | src/vga.h | 50 |
35 files changed, 1989 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3d52bf6 --- /dev/null +++ b/Makefile @@ -0,0 +1,54 @@ +NAME = os + +CC = i686-elf-gcc +ASM = i686-elf-as +CFLAGS = -std=gnu99 -ffreestanding -O2 -Wall -Wextra -g +LFLAGS = -ffreestanding -O2 -nostdlib -lgcc -g + +SDIR = src +ODIR = obj +SRC = $(wildcard $(SDIR)/*.c) +OBJ = $(patsubst $(SDIR)/%.c,$(ODIR)/%.o,$(SRC)) + +ASMODIR = asm_obj +ASMSRC = $(wildcard $(SDIR)/*.s) +ASMOBJ = $(patsubst $(SDIR)/%.s,$(ASMODIR)/%.o,$(ASMSRC)) + +default: kernel.bin + +kernel.bin: $(OBJ) $(ASMOBJ) + $(CC) -T linker.ld -o $@ $^ $(LFLAGS) + +$(ODIR)/%.o: $(SDIR)/%.c $(ODIR) + $(CC) $(CFLAGS) -c -o $@ $< + +$(ASMODIR)/%.o: $(SDIR)/%.s $(ASMODIR) + $(ASM) -g -c -o $@ $< + +$(NAME).iso: kernel.bin + mkdir -p isodir/boot/grub + cp $< isodir/boot/ + cp grub.cfg isodir/boot/grub/grub.cfg + grub-mkrescue -o $@ isodir + +.PHONY: test debug +test: os.iso + qemu-system-i386 -cdrom os.iso + +debug: kernel.bin + qemu-system-i386 -s -S -kernel kernel.bin + +$(ODIR): + mkdir -p $@ + +$(ASMODIR): + mkdir -p $@ + +.PHONY: clean + +clean: + rm -rf $(ODIR) + rm -rf $(ASMODIR) + rm -f boot.o + rm -rf isodir + rm -f $(NAME).iso diff --git a/asm_obj/boot.o b/asm_obj/boot.o Binary files differnew file mode 100644 index 0000000..884c973 --- /dev/null +++ b/asm_obj/boot.o diff --git a/asm_obj/interrupt_stub.o b/asm_obj/interrupt_stub.o Binary files differnew file mode 100644 index 0000000..669b218 --- /dev/null +++ b/asm_obj/interrupt_stub.o diff --git a/grub.cfg b/grub.cfg new file mode 100644 index 0000000..84f9dfd --- /dev/null +++ b/grub.cfg @@ -0,0 +1,6 @@ +set timeout=0 +set default=0 + +menuentry "citrons os (good)" { + multiboot /boot/kernel.bin +} diff --git a/isodir/boot/grub/grub.cfg b/isodir/boot/grub/grub.cfg new file mode 100644 index 0000000..84f9dfd --- /dev/null +++ b/isodir/boot/grub/grub.cfg @@ -0,0 +1,6 @@ +set timeout=0 +set default=0 + +menuentry "citrons os (good)" { + multiboot /boot/kernel.bin +} diff --git a/isodir/boot/kernel.bin b/isodir/boot/kernel.bin Binary files differnew file mode 100755 index 0000000..c0362ec --- /dev/null +++ b/isodir/boot/kernel.bin diff --git a/kernel.bin b/kernel.bin Binary files differnew file mode 100755 index 0000000..c0362ec --- /dev/null +++ b/kernel.bin diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..cd22338 --- /dev/null +++ b/linker.ld @@ -0,0 +1,26 @@ + +ENTRY(_start) + +SECTIONS +{ + /* Begin at 1 MiB */ + . = 1M; + + /* Multiboot header */ + .text BLOCK(4K) : ALIGN(4K) + { + *(.multiboot) + *(.text) + } + + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } +} diff --git a/obj/bees.o b/obj/bees.o Binary files differnew file mode 100644 index 0000000..5014992 --- /dev/null +++ b/obj/bees.o diff --git a/obj/interrupt.o b/obj/interrupt.o Binary files differnew file mode 100644 index 0000000..62fa47b --- /dev/null +++ b/obj/interrupt.o diff --git a/obj/keyboard.o b/obj/keyboard.o Binary files differnew file mode 100644 index 0000000..f8457ab --- /dev/null +++ b/obj/keyboard.o diff --git a/obj/main.o b/obj/main.o Binary files differnew file mode 100644 index 0000000..4e314ac --- /dev/null +++ b/obj/main.o diff --git a/obj/port.o b/obj/port.o Binary files differnew file mode 100644 index 0000000..3643494 --- /dev/null +++ b/obj/port.o diff --git a/obj/printf.o b/obj/printf.o Binary files differnew file mode 100644 index 0000000..fea2cec --- /dev/null +++ b/obj/printf.o diff --git a/obj/string.o b/obj/string.o Binary files differnew file mode 100644 index 0000000..4afe28a --- /dev/null +++ b/obj/string.o diff --git a/obj/vga.o b/obj/vga.o Binary files differBinary files differnew file mode 100644 index 0000000..44460d2 --- /dev/null +++ b/obj/vga.o diff --git a/src/.tags b/src/.tags new file mode 100644 index 0000000..d249904 --- /dev/null +++ b/src/.tags @@ -0,0 +1,119 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.9~svn20110310 // +GDT_H gdt.h 3;" d +INTERRUPT_H interrupt.h 3;" d +KERNEL main.c 2;" d file: +PORT_H port.h 3;" d +STA_R gdt.h 10;" d +STA_W gdt.h 9;" d +STA_X gdt.h 8;" d +STRING_H string.h 3;" d +STS_IG32 gdt.h 13;" d +STS_T32A gdt.h 12;" d +STS_TG32 gdt.h 14;" d +VGA_H vga.h 3;" d +VGA_HEIGHT vga.h /^static const size_t VGA_HEIGHT = 25;$/;" v +VGA_WIDTH vga.h /^static const size_t VGA_WIDTH = 80;$/;" v +_start boot.s /^_start:$/;" l +access interrupt.h /^ uint8_t access; \/\/ Access rights$/;" m struct:gate_desc +bandwidth port.h /^ port16bit, port32bit} bandwidth;$/;" m struct:port typeref:enum:port::__anon1 +base interrupt.h /^ uint32_t base;$/;" m struct:idt_pointer +base_15_0 gdt.h /^ unsigned int base_15_0 : 16; \/\/ Low bits of segment base address$/;" m struct:gdt_seg +base_23_16 gdt.h /^ unsigned int base_23_16 : 8; \/\/ Middle bits of segment base address$/;" m struct:gdt_seg +base_31_24 gdt.h /^ unsigned int base_31_24 : 8; \/\/ High bits of segment base address$/;" m struct:gdt_seg +code gdt.h /^ struct gdt_seg code;$/;" m struct:kernel_gdt typeref:struct:kernel_gdt::gdt_seg +data gdt.h /^ struct gdt_seg data;$/;" m struct:kernel_gdt typeref:struct:kernel_gdt::gdt_seg +db gdt.h /^ unsigned int db : 1; \/\/ 0 = 16-bit segment, 1 = 32-bit segment$/;" m struct:gdt_seg +g gdt.h /^ unsigned int g : 1; \/\/ Granularity: limit scaled by 4K when set$/;" m struct:gdt_seg +gate_desc interrupt.h /^struct gate_desc$/;" s +gdt_code_seg interrupt.h /^ uint16_t gdt_code_seg; \/\/ The address of the code segment descriptor$/;" m struct:gate_desc +gdt_seg gdt.h /^struct gdt_seg$/;" s +handle_int interrupt.c /^uint32_t handle_int(uint8_t int_number, uint32_t esp)$/;" f +handle_irq interrupt_stub.s /^handle_irq 0x00$/;" l +handle_irq interrupt_stub.s /^handle_irq 0x01$/;" l +handler_high interrupt.h /^ uint16_t handler_high; \/\/ The high bits of the address of the handler$/;" m struct:gate_desc +handler_low interrupt.h /^ uint16_t handler_low; \/\/ The low bits of the address of the handler$/;" m struct:gate_desc +hex string.c /^char *hex(uint32_t n, char *buf)$/;" f +idt interrupt.h /^struct gate_desc idt[256];$/;" v typeref:struct:gate_desc +idt_pointer interrupt.h /^struct idt_pointer$/;" s +init_gdt_seg gdt.c /^struct gdt_seg *init_gdt_seg(struct gdt_seg *g, uint32_t base, uint32_t limit, uint8_t flags)$/;" f +init_kernel_gdt gdt.c /^struct kernel_gdt *init_kernel_gdt()$/;" f +int_bottom interrupt_stub.s /^int_bottom:$/;" l +int_ignore interrupt_stub.s /^int_ignore:$/;" l +int_number interrupt_stub.s /^ int_number: .byte 0$/;" l +interrupt_init interrupt.c /^void interrupt_init(const struct kernel_gdt *g)$/;" f +kernel_gdt gdt.h /^struct kernel_gdt$/;" s +kernel_main main.c /^void kernel_main(void)$/;" f +kgdt gdt.c /^struct kernel_gdt kgdt;$/;" v typeref:struct:kernel_gdt +lim_15_0 gdt.h /^ unsigned int lim_15_0 : 16; \/\/ Low bits of segment limit$/;" m struct:gdt_seg +lim_19_16 gdt.h /^ unsigned int lim_19_16 : 4; \/\/ High bits of segment limit$/;" m struct:gdt_seg +load_kernel_gdt gdt.c /^void __attribute__((optimize("O0"))) load_kernel_gdt(struct kernel_gdt *g)$/;" f +memcmp string.c /^int memcmp(void *s1, const void *s2, size_t n)$/;" f +memcpy string.c /^void *memcpy(void *dest, const void *src, size_t n)$/;" f +memmove string.c /^void *memmove(void *dest, const void *src, size_t n)$/;" f +memset string.c /^void *memset(void *s, int c, size_t n) $/;" f +null gdt.h /^ struct gdt_seg null;$/;" m struct:kernel_gdt typeref:struct:kernel_gdt::gdt_seg +pic_master_command interrupt.c /^struct port pic_master_command = {0x20, port8bit_slow};$/;" v typeref:struct:port +pic_master_command interrupt.h /^struct port pic_master_command;$/;" v typeref:struct:port +pic_master_data interrupt.c /^struct port pic_master_data = {0x21, port8bit_slow};$/;" v typeref:struct:port +pic_master_data interrupt.h /^struct port pic_master_data;$/;" v typeref:struct:port +pic_slave_command interrupt.c /^struct port pic_slave_command = {0xA0, port8bit_slow};$/;" v typeref:struct:port +pic_slave_command interrupt.h /^struct port pic_slave_command;$/;" v typeref:struct:port +pic_slave_data interrupt.c /^struct port pic_slave_data = {0xA1, port8bit_slow}; $/;" v typeref:struct:port +pic_slave_data interrupt.h /^struct port pic_slave_data; $/;" v typeref:struct:port +port port.h /^struct port$/;" s +port16bit port.h /^ port16bit, port32bit} bandwidth;$/;" e enum:port::__anon1 +port32bit port.h /^ port16bit, port32bit} bandwidth;$/;" e enum:port::__anon1 +port8bit port.h /^ enum { port8bit, port8bit_slow,$/;" e enum:port::__anon1 +port8bit_slow port.h /^ enum { port8bit, port8bit_slow,$/;" e enum:port::__anon1 +port_number port.h /^ uint16_t port_number;$/;" m struct:port +port_read port.c /^uint32_t port_read(struct port p)$/;" f +port_write port.c /^void port_write(struct port p, uint32_t data)$/;" f +present gdt.h /^ unsigned int present : 1; \/\/ Present$/;" m struct:gdt_seg +priv gdt.h /^ unsigned int priv : 2; \/\/ Descriptor Privilege Level$/;" m struct:gdt_seg +reserved gdt.h /^ unsigned int reserved : 1; \/\/ Reserved$/;" m struct:gdt_seg +reserved interrupt.h /^ uint8_t reserved; \/\/ We don't use this apparently$/;" m struct:gate_desc +s gdt.h /^ unsigned int s : 1; \/\/ 0 = system, 1 = application$/;" m struct:gdt_seg +set_int_desc_entry interrupt.c /^void set_int_desc_entry(uint8_t int_number, uint16_t gdt_code_seg,$/;" f +size interrupt.h /^ uint16_t size;$/;" m struct:idt_pointer +spin main.c /^void spin()$/;" f +stack_bottom boot.s /^stack_bottom:$/;" l +stack_top boot.s /^stack_top:$/;" l +start_interrupts interrupt.c /^void start_interrupts()$/;" f +strlen string.c /^size_t strlen(const char *s)$/;" f +terminal_buffer vga.h /^uint16_t* terminal_buffer;$/;" v +terminal_color vga.h /^uint8_t terminal_color;$/;" v +terminal_column vga.h /^size_t terminal_column;$/;" v +terminal_row vga.h /^size_t terminal_row;$/;" v +type gdt.h /^ unsigned int type : 4; \/\/ Segment type $/;" m struct:gdt_seg +unused gdt.h /^ unsigned int unused : 1; \/\/ Unused (available for software use)$/;" m struct:gdt_seg +vga_black vga.h /^ vga_black,$/;" e enum:vga_color +vga_blue vga.h /^ vga_blue,$/;" e enum:vga_color +vga_brown vga.h /^ vga_brown,$/;" e enum:vga_color +vga_color vga.c /^uint8_t vga_color(enum vga_color fg, enum vga_color bg)$/;" f +vga_color vga.h /^enum vga_color {$/;" g +vga_cyan vga.h /^ vga_cyan,$/;" e enum:vga_color +vga_dgray vga.h /^ vga_dgray,$/;" e enum:vga_color +vga_entry vga.c /^uint16_t vga_entry(unsigned char uc, uint8_t color)$/;" f +vga_green vga.h /^ vga_green,$/;" e enum:vga_color +vga_lblue vga.h /^ vga_lblue,$/;" e enum:vga_color +vga_lbrown vga.h /^ vga_lbrown,$/;" e enum:vga_color +vga_lcyan vga.h /^ vga_lcyan,$/;" e enum:vga_color +vga_lgray vga.h /^ vga_lgray,$/;" e enum:vga_color +vga_lgreen vga.h /^ vga_lgreen,$/;" e enum:vga_color +vga_lmagenta vga.h /^ vga_lmagenta,$/;" e enum:vga_color +vga_lred vga.h /^ vga_lred,$/;" e enum:vga_color +vga_magenta vga.h /^ vga_magenta,$/;" e enum:vga_color +vga_red vga.h /^ vga_red,$/;" e enum:vga_color +vga_white vga.h /^ vga_white$/;" e enum:vga_color +vinit vga.c /^void vinit(void)$/;" f +vlocate vga.c /^void vlocate(size_t column, size_t row)$/;" f +vprint vga.c /^void vprint(const char* str)$/;" f +vputchar vga.c /^void vputchar(char c)$/;" f +vscroll vga.c /^void vscroll()$/;" f +vsetchar vga.c /^void vsetchar(char c, uint8_t color, size_t x, size_t y)$/;" f +vsetcolor vga.c /^void vsetcolor(uint8_t color)$/;" f diff --git a/src/bees.c b/src/bees.c new file mode 100644 index 0000000..7c2fbcb --- /dev/null +++ b/src/bees.c @@ -0,0 +1,22 @@ + +#include "vga.h" +#include "printf.h" + +const char *bees_msg = +"\n\n\n" +" / \\ the number of bees generated by citrons os has exceeded the" "\n" +" / \\ maximum amount your computer is capable of handling." "\n" +" \\ -- /" "\n" +" _====_ your computer will now halt." "\n" +" / |--| \\" "\n" +" / /==\\ \\ error: %s" "\n" +" / /----\\ \\" "\n" +" / /======\\ \\" "\n" +" \\_/ ---- \\_/" "\n"; + +void bees(const char *error) { + vsetcolor(vga_color(vga_white, vga_blue)); + vclear(); + printf(bees_msg, error); + while (1) asm("cli;hlt"); +} diff --git a/src/bees.h b/src/bees.h new file mode 100644 index 0000000..0e5b1d2 --- /dev/null +++ b/src/bees.h @@ -0,0 +1,6 @@ +#ifndef __BUGCHECK_H +#define __BUGCHECK_H + +void bees(const char *error); + +#endif diff --git a/src/boot.s b/src/boot.s new file mode 100644 index 0000000..71017ff --- /dev/null +++ b/src/boot.s @@ -0,0 +1,80 @@ +/* Parameters for the multiboot header */ +.set ALIGN, 1<<0 +.set MEMINFO, 1<<1 +.set FLAGS, ALIGN | MEMINFO +.set MAGIC, 0x1BADB002 +.set CHECKSUM, -(MAGIC + FLAGS) + +.section .multiboot +.align 4 +.long MAGIC +.long FLAGS +.long CHECKSUM + + +/* Allocate room for stack */ +.section bss +.align 16 +stack_bottom: +.skip 16384 /* 16 KiB */ +stack_top: + +.section .data + +.intel_syntax noprefix +gdt: + null_gdt: /* null descriptor */ + .quad 0x00 + + code_gdt: + .word 0xFFFF /* limit */ + .word 0x0000 /* base */ + .byte 0x00 /* base */ + .byte 0x9A /* type flag (access) */ + .byte 0b11001111 /* limit */ + .byte 0x00 /* base */ + + data_gdt: + .word 0xFFFF /* limit */ + .word 0x0000 /* base */ + .byte 0x00 /* base */ + .byte 0x92 /* type flag (access) */ + .byte 0b11001111 /* limit */ + .byte 0x00 /* base */ + + end_gdt: + + gdt_desc: + .word end_gdt - gdt - 1 + .int gdt + +.att_syntax + +.section .text +.global _start +.type _start, @function +_start: + mov $stack_top, %esp + + cli + +.intel_syntax noprefix + lgdt [gdt_desc] /* load GDT */ + mov ax, 0x10 + mov ds, ax + mov ss, ax + mov es, ax + mov fs, ax + mov gs, ax + jmp 0x08:continue +.att_syntax + +continue: + call kernel_main + + cli +1: hlt + jmp 1b + +/* The size of the _start symbol is the current location '.' minus its start. */ +.size _start, . - _start diff --git a/src/interrupt.c b/src/interrupt.c new file mode 100644 index 0000000..e588353 --- /dev/null +++ b/src/interrupt.c @@ -0,0 +1,92 @@ + +#include "interrupt.h" +#include "string.h" +#include "vga.h" +#include "bees.h" +#include "printf.h" +#include "keyboard.h" + +struct port pic_master_command = {0x20, port8bit_slow}; +struct port pic_master_data = {0x21, port8bit_slow}; +struct port pic_slave_command = {0xA0, port8bit_slow}; +struct port pic_slave_data = {0xA1, port8bit_slow}; + +struct port ps2_port = {0x60, port8bit}; + +struct gate_desc idt[256] = {0}; + +#define PIC_EOI 0x20 + +void irq_end(uint8_t int_number) { + if (int_number >= 0x28) + port_write(pic_slave_command, PIC_EOI); + port_write(pic_master_command, PIC_EOI); +} + +uint32_t handle_int(uint8_t int_number, uint32_t idk, uint32_t esp) { + switch (int_number) { + case 0x0D: + // bye + bees("general protection fault"); + break; + case 0x21: + process_scancode(port_read(ps2_port)); + irq_end(int_number); + break; + } + + return esp; +} + +void set_int_desc_entry(uint8_t int_number, uint16_t gdt_code_seg, + void (*handler)(), uint8_t privilege_level, + uint8_t desc_type) { + const uint8_t IDT_DESC_PRESENT = 0x80; + + idt[int_number].handler_low = ((uint32_t) handler) & 0xFFFF; + idt[int_number].handler_high = ((uint32_t) handler >> 16) & 0xFFFF; + idt[int_number].gdt_code_seg = gdt_code_seg; + idt[int_number].access = IDT_DESC_PRESENT | desc_type | ((privilege_level&3) << 5); + idt[int_number].reserved = 0; +} + +void interrupt_init() { + uint16_t code_seg = 0x08; + const uint8_t IDT_INTERRUPT_GATE = 0xE; + + for (uint16_t i = 0; i < 256; i++) + set_int_desc_entry(i, code_seg, &int_ignore, 0, IDT_INTERRUPT_GATE); + + // Put the interrupt handlers in the IDT + set_int_desc_entry(0x20, code_seg, &handle_irq0x00, 0, IDT_INTERRUPT_GATE); + set_int_desc_entry(0x21, code_seg, &handle_irq0x01, 0, IDT_INTERRUPT_GATE); + set_int_desc_entry(0x0D, code_seg, &handle_irq0x0D, 0, IDT_INTERRUPT_GATE); + + port_write(pic_master_command, 0x11); + port_write(pic_slave_command, 0x11); + + // (I actually don't understand how this works) + // Remap the PIC + port_write(pic_master_data, 0x20); // Interrupt vector offsets + port_write(pic_slave_data, 0x28); + + port_write(pic_master_data, 0x04); // There is a slave PIC at IRQ2 + port_write(pic_slave_data, 0x02); // Slave PIC cascade identity + + port_write(pic_master_data, 0x01); + port_write(pic_slave_data, 0x01); + + port_write(pic_master_data, 0xFD); + port_write(pic_slave_data, 0xFF); + + // Load the IDT + struct idt_pointer i; + i.size = 256 * sizeof(struct gate_desc) - 1; + i.base = (uint32_t) idt; + + asm volatile("lidt %0" : : "m" (i)); +} + +void start_interrupts() { + asm("sti"); +} diff --git a/src/interrupt.h b/src/interrupt.h new file mode 100644 index 0000000..5e0112b --- /dev/null +++ b/src/interrupt.h @@ -0,0 +1,46 @@ + +#ifndef __INTERRUPT_H +#define __INTERRUPT_H + +#include <stdint.h> +#include <stddef.h> +#include "vga.h" +#include "port.h" + +struct gate_desc +{ + uint16_t handler_low; // the low bits of the address of the handler + uint16_t gdt_code_seg; // the address of the code segment descriptor + uint8_t reserved; // we don't use this apparently + uint8_t access; // access rights + uint16_t handler_high; // the high bits of the address of the handler +} __attribute__((packed)); + +struct gate_desc idt[256]; + +struct idt_pointer +{ + uint16_t size; + uint32_t base; +} __attribute__((packed)); + +void set_int_desc_entry(uint8_t int_number, uint16_t gdt_code_seg, + void (*handler)(), uint8_t privlege_level, + uint8_t desc_type); + +void interrupt_init(); + +void start_interrupts(); + +void handle_irq0x00(); +void handle_irq0x01(); +void handle_irq0x0D(); + +void int_ignore(); + +struct port pic_master_command; +struct port pic_master_data; +struct port pic_slave_command; +struct port pic_slave_data; + +#endif diff --git a/src/interrupt_stub.s b/src/interrupt_stub.s new file mode 100644 index 0000000..618c982 --- /dev/null +++ b/src/interrupt_stub.s @@ -0,0 +1,54 @@ + +.set IRQ_BASE, 0x20 + +.section .text + +.extern handle_int +.global int_ignore + + +.macro handle_exception num +.global handle_irq\num +handle_irq\num: + movb $\num, (int_number) + jmp int_bottom +.endm + +.macro handle_irq num +.global handle_irq\num +handle_irq\num: + movb $\num + IRQ_BASE, (int_number) + jmp int_bottom +.endm + +handle_irq 0x00 +handle_irq 0x01 +handle_exception 0x0D + +int_bottom: + pop %eax + push int_bottom + pusha + pushl %ds + pushl %es + pushl %fs + pushl %gs + + pushl %esp + pushl %eax + push (int_number) + call handle_int + movl %eax, %esp // Set the stack pointer to the return value of handle_int + + popl %gs + popl %fs + popl %es + popl %ds + popa + +int_ignore: + + iret + +.data + int_number: .byte 0 diff --git a/src/keyboard.c b/src/keyboard.c new file mode 100644 index 0000000..30dc5b7 --- /dev/null +++ b/src/keyboard.c @@ -0,0 +1,98 @@ + +#include <stdint.h> +#include "vga.h" + +enum special_key { + K_ESCAPE = 0x1b, + K_BACKSPACE = 0x08, + K_F1 = 0x0E, + K_F2 = 0x0F, + K_F3 = 0x10, + K_F4 = 0x11, + K_F5 = 0x12, + K_F6 = 0x13, + K_F7 = 0x14, + K_F8 = 0x15, + K_F9 = 0x16, + K_F10 = 0x17, + K_F11 = 0x18, + K_F12 = 0x19 +}; + +enum mod_scancodes { + SC_LSHIFT = 0x2A, + SC_RSHIFT = 0x36, + SC_LCTRL = 0x1D, + SC_LALT = 0x38, + SC_META = 0x3A +}; + +char keymap_unshift[] = { + 0,K_ESCAPE,'1','2','3','4','5','6','7','8','9','0','-','=',K_BACKSPACE, + '\t','q','w','e','r','t','y','u','i','o','p','[',']','\n',0, + 'a','s','d','f','g','h','j','k','l',';','\'','`',0,'\\', + 'z','x','c','v','b','n','m',',','.','/',0,'*',0,' ',0, + K_F1,K_F2,K_F3,K_F4,K_F5,K_F6,K_F7,K_F8,K_F9,K_F10,0,0, + '7','8','9','-','4','5','6','1','2','3','0','.',0,0,0,K_F11,K_F12 +}; +char keymap_shift[] = { + 0,K_ESCAPE,'!','@','#','$','%','^','&','*','(',')','_','+',K_BACKSPACE, + '\t','Q','W','E','R','T','Y','U','I','O','P','{','}','\n',0, + 'A','S','D','F','G','H','J','K','L',':','"','~',0,'|', + 'Z','X','C','V','B','N','M','<','>','?',0,'*',0,' ',0, + K_F1,K_F2,K_F3,K_F4,K_F5,K_F6,K_F7,K_F8,K_F9,K_F10,0,0, + '7','8','9','-','4','5','6','1','2','3','0','.',0,0,0,K_F11,K_F12 +}; + +char *keymap = keymap_unshift; + +struct { + int shift; + int ctrl; + int alt; + int meta; +} mod_keys = {0}; + +void keydown(char c) { + vputchar(c); +} + +void keyup(char c) { + +} + +void process_scancode(uint8_t c) { + uint8_t code; + int release; + if (c > 0x80 && c <= 0xD8) { + release = 1; + code = c - 0x80; + } else { + release = 0; + code = c; + } + switch (code) { + default: + if (code <= 0x58 && keymap[code] != 0) { + if (!release) + keydown(keymap[code]); + else + keyup(keymap[code]); + } + break; + case SC_LSHIFT: + case SC_RSHIFT: + mod_keys.shift = !release; + keymap = release ? keymap_unshift : keymap_shift; + break; + case SC_LCTRL: + mod_keys.ctrl = !release; + break; + case SC_LALT: + mod_keys.alt = !release; + break; + case SC_META: + mod_keys.meta = !release; + break; + } +} diff --git a/src/keyboard.h b/src/keyboard.h new file mode 100644 index 0000000..c867e07 --- /dev/null +++ b/src/keyboard.h @@ -0,0 +1,15 @@ +#ifndef __KEYBOARD_H +#define __KEYBOARD_H + +#include <stdint.h> + +void process_scancode(uint8_t c); + +struct { + int shift; + int ctrl; + int alt; + int meta; +} mod_keys; + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..725f438 --- /dev/null +++ b/src/main.c @@ -0,0 +1,45 @@ + +#define KERNEL + +#include "vga.h" +#include "interrupt.h" +#include "string.h" + +extern void *gdt; + +const char *pope = +"The pope, also known as the supreme pontiff (Pontifex maximus) or the Roman \n" +"pontiff (Romanus Pontifex), is the bishop of Rome, chief pastor of the \n" +"worldwide Catholic Church,[4] and head of state or sovereign of the Vatican \n" +"City State.[5] The primacy of the bishop of Rome is largely derived from his \n" +"role as the apostolic successor to Saint Peter, to whom primacy was conferred \n" +"by Jesus, giving him the Keys of Heaven and the powers of \"binding and \n" +"loosing\", naming him as the \"rock\" upon which the church would be built. \n" +"Since 1929, the pope has official residence in the Apostolic Palace in the \n" +"Vatican City, a city-state enclaved within Rome, Italy.[6] The current pope is \n" +"Francis, who was elected on 13 March 2013, succeeding Benedict XVI.[7] \n" +"While his office is called the papacy, the jurisdiction of the episcopal see is\n" +"called the Holy See.[8] It is the Holy See that is the sovereign entity by \n" +"international law headquartered in the distinctively independent Vatican City \n" +"State, established by the Lateran Treaty in 1929 between Italy and the Holy See\n" +"to ensure its temporal, diplomatic, and spiritual independence. The Holy See is\n" +"recognized by its adherence at various levels to international organization and\n" +"by means of its diplomatic relations and political accords with many \n" +"independent states.\n"; + +void kernel_main(void) { + vsetcolor(vga_color(vga_black, vga_white)); + vclear(); + interrupt_init(); + start_interrupts(); + + vprint(" _ _ \n"); + vprint(" ___(_) |_ _ __ ___ _ __ ___ ___ ___ \n"); + vprint(" / __| | __| '__/ _ \\| '_ \\/ __| / _ \\/ __|\n"); + vprint("| (__| | |_| | | (_) | | | \\__ \\ | (_) \\__ \n"); + vprint(" \\___|_|\\__|_| \\___/|_| |_|___/ \\___/|___/\n"); + + vprint(pope); + + while (1) asm("hlt"); +} diff --git a/src/port.c b/src/port.c new file mode 100644 index 0000000..9af4a49 --- /dev/null +++ b/src/port.c @@ -0,0 +1,38 @@ + +#include "port.h" + +void port_write(struct port p, uint32_t data) { + switch (p.bandwidth) { + case port8bit: + asm volatile("outb %0, %1" : : "a" ((uint8_t) data), "Nd" (p.port_number)); + break; + case port8bit_slow: + asm volatile("outb %0, %1\njmp 1f\n1: jmp 1f\n1:" : + : "a" ((uint8_t) data), "Nd" (p.port_number)); + break; + case port16bit: + asm volatile("outw %0, %1" : : "a" ((uint16_t) data), "Nd" (p.port_number)); + case port32bit: + asm volatile("outl %0, %1" : : "a" (data), "Nd" (p.port_number)); + break; + } +} + +uint32_t port_read(struct port p) { + uint32_t result = 0; + switch (p.bandwidth) { + case port8bit: + asm volatile("inb %1, %0" : "=a" (result) : "Nd" (p.port_number)); + break; + case port8bit_slow: + asm volatile("inb %1, %0\njmp 1f\n1: jmp 1f\n1:" : "=a" (result) : "Nd" (p.port_number)); + break; + case port16bit: + asm volatile("inw %1, %0" : "=a" (result) : "Nd" (p.port_number)); + break; + case port32bit: + asm volatile("inl %1, %0" : "=a" (result) : "Nd" (p.port_number)); + } + + return result; +} diff --git a/src/port.h b/src/port.h new file mode 100644 index 0000000..c7ed457 --- /dev/null +++ b/src/port.h @@ -0,0 +1,19 @@ + +#ifndef __PORT_H +#define __PORT_H + +#include <stddef.h> +#include <stdint.h> + +struct port +{ + uint16_t port_number; + enum { port8bit, port8bit_slow, + port16bit, port32bit} bandwidth; +}; + +void port_write(struct port p, uint32_t data); +uint32_t port_read(struct port p); + + +#endif diff --git a/src/printf.c b/src/printf.c new file mode 100644 index 0000000..8a700ad --- /dev/null +++ b/src/printf.c @@ -0,0 +1,914 @@ +///////////////////////////////////////////////////////////////////////////////
+// \author (c) Marco Paland (info@paland.com)
+// 2014-2019, PALANDesign Hannover, Germany
+//
+// \license The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
+// embedded systems with a very limited resources. These routines are thread
+// safe and reentrant!
+// Use this instead of the bloated standard/newlib printf cause these use
+// malloc for printf (and may not be thread safe).
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "printf.h"
+
+
+// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
+// printf_config.h header file
+// default: undefined
+#ifdef PRINTF_INCLUDE_CONFIG_H
+#include "printf_config.h"
+#endif
+
+
+// 'ntoa' conversion buffer size, this must be big enough to hold one converted
+// numeric number including padded zeros (dynamically created on stack)
+// default: 32 byte
+#ifndef PRINTF_NTOA_BUFFER_SIZE
+#define PRINTF_NTOA_BUFFER_SIZE 32U
+#endif
+
+// 'ftoa' conversion buffer size, this must be big enough to hold one converted
+// float number including padded zeros (dynamically created on stack)
+// default: 32 byte
+#ifndef PRINTF_FTOA_BUFFER_SIZE
+#define PRINTF_FTOA_BUFFER_SIZE 32U
+#endif
+
+// support for the floating point type (%f)
+// default: activated
+#ifndef PRINTF_DISABLE_SUPPORT_FLOAT
+#define PRINTF_SUPPORT_FLOAT
+#endif
+
+// support for exponential floating point notation (%e/%g)
+// default: activated
+#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
+#define PRINTF_SUPPORT_EXPONENTIAL
+#endif
+
+// define the default floating point precision
+// default: 6 digits
+#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
+#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
+#endif
+
+// define the largest float suitable to print with %f
+// default: 1e9
+#ifndef PRINTF_MAX_FLOAT
+#define PRINTF_MAX_FLOAT 1e9
+#endif
+
+// support for the long long types (%llu or %p)
+// default: activated
+#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
+#define PRINTF_SUPPORT_LONG_LONG
+#endif
+
+// support for the ptrdiff_t type (%t)
+// ptrdiff_t is normally defined in <stddef.h> as long or long long type
+// default: activated
+#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
+#define PRINTF_SUPPORT_PTRDIFF_T
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+// internal flag definitions
+#define FLAGS_ZEROPAD (1U << 0U)
+#define FLAGS_LEFT (1U << 1U)
+#define FLAGS_PLUS (1U << 2U)
+#define FLAGS_SPACE (1U << 3U)
+#define FLAGS_HASH (1U << 4U)
+#define FLAGS_UPPERCASE (1U << 5U)
+#define FLAGS_CHAR (1U << 6U)
+#define FLAGS_SHORT (1U << 7U)
+#define FLAGS_LONG (1U << 8U)
+#define FLAGS_LONG_LONG (1U << 9U)
+#define FLAGS_PRECISION (1U << 10U)
+#define FLAGS_ADAPT_EXP (1U << 11U)
+
+
+// import float.h for DBL_MAX
+#if defined(PRINTF_SUPPORT_FLOAT)
+#include <float.h>
+#endif
+
+
+// output function type
+typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
+
+
+// wrapper (used as buffer) for output function type
+typedef struct {
+ void (*fct)(char character, void* arg);
+ void* arg;
+} out_fct_wrap_type;
+
+
+// internal buffer output
+static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen)
+{
+ if (idx < maxlen) {
+ ((char*)buffer)[idx] = character;
+ }
+}
+
+
+// internal null output
+static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen)
+{
+ (void)character; (void)buffer; (void)idx; (void)maxlen;
+}
+
+
+// internal _putchar wrapper
+static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen)
+{
+ (void)buffer; (void)idx; (void)maxlen;
+ if (character) {
+ _putchar(character);
+ }
+}
+
+
+// internal output function wrapper
+static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen)
+{
+ (void)idx; (void)maxlen;
+ if (character) {
+ // buffer is the output fct pointer
+ ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
+ }
+}
+
+
+// internal secure strlen
+// \return The length of the string (excluding the terminating 0) limited by 'maxsize'
+static inline unsigned int _strnlen_s(const char* str, size_t maxsize)
+{
+ const char* s;
+ for (s = str; *s && maxsize--; ++s);
+ return (unsigned int)(s - str);
+}
+
+
+// internal test if char is a digit (0-9)
+// \return true if char is a digit
+static inline bool _is_digit(char ch)
+{
+ return (ch >= '0') && (ch <= '9');
+}
+
+
+// internal ASCII string to unsigned int conversion
+static unsigned int _atoi(const char** str)
+{
+ unsigned int i = 0U;
+ while (_is_digit(**str)) {
+ i = i * 10U + (unsigned int)(*((*str)++) - '0');
+ }
+ return i;
+}
+
+
+// output the specified string in reverse, taking care of any zero-padding
+static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags)
+{
+ const size_t start_idx = idx;
+
+ // pad spaces up to given width
+ if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
+ for (size_t i = len; i < width; i++) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+
+ // reverse string
+ while (len) {
+ out(buf[--len], buffer, idx++, maxlen);
+ }
+
+ // append pad spaces up to given width
+ if (flags & FLAGS_LEFT) {
+ while (idx - start_idx < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+
+ return idx;
+}
+
+
+// internal itoa format
+static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ // pad leading zeros
+ if (!(flags & FLAGS_LEFT)) {
+ if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
+ width--;
+ }
+ while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = '0';
+ }
+ while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = '0';
+ }
+ }
+
+ // handle hash
+ if (flags & FLAGS_HASH) {
+ if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
+ len--;
+ if (len && (base == 16U)) {
+ len--;
+ }
+ }
+ if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = 'x';
+ }
+ else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = 'X';
+ }
+ else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = 'b';
+ }
+ if (len < PRINTF_NTOA_BUFFER_SIZE) {
+ buf[len++] = '0';
+ }
+ }
+
+ if (len < PRINTF_NTOA_BUFFER_SIZE) {
+ if (negative) {
+ buf[len++] = '-';
+ }
+ else if (flags & FLAGS_PLUS) {
+ buf[len++] = '+'; // ignore the space if the '+' exists
+ }
+ else if (flags & FLAGS_SPACE) {
+ buf[len++] = ' ';
+ }
+ }
+
+ return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
+}
+
+
+// internal itoa for 'long' type
+static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ char buf[PRINTF_NTOA_BUFFER_SIZE];
+ size_t len = 0U;
+
+ // no hash for 0 values
+ if (!value) {
+ flags &= ~FLAGS_HASH;
+ }
+
+ // write if precision != 0 and value is != 0
+ if (!(flags & FLAGS_PRECISION) || value) {
+ do {
+ const char digit = (char)(value % base);
+ buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
+ value /= base;
+ } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
+ }
+
+ return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
+}
+
+
+// internal itoa for 'long long' type
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ char buf[PRINTF_NTOA_BUFFER_SIZE];
+ size_t len = 0U;
+
+ // no hash for 0 values
+ if (!value) {
+ flags &= ~FLAGS_HASH;
+ }
+
+ // write if precision != 0 and value is != 0
+ if (!(flags & FLAGS_PRECISION) || value) {
+ do {
+ const char digit = (char)(value % base);
+ buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
+ value /= base;
+ } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
+ }
+
+ return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
+}
+#endif // PRINTF_SUPPORT_LONG_LONG
+
+
+#if defined(PRINTF_SUPPORT_FLOAT)
+
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
+static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags);
+#endif
+
+
+// internal ftoa for fixed decimal floating point
+static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ char buf[PRINTF_FTOA_BUFFER_SIZE];
+ size_t len = 0U;
+ double diff = 0.0;
+
+ // powers of 10
+ static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
+
+ // test for special values
+ if (value != value)
+ return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
+ if (value < -DBL_MAX)
+ return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
+ if (value > DBL_MAX)
+ return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
+
+ // test for very large values
+ // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
+ if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+ return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
+#else
+ return 0U;
+#endif
+ }
+
+ // test for negative
+ bool negative = false;
+ if (value < 0) {
+ negative = true;
+ value = 0 - value;
+ }
+
+ // set default precision, if not set explicitly
+ if (!(flags & FLAGS_PRECISION)) {
+ prec = PRINTF_DEFAULT_FLOAT_PRECISION;
+ }
+ // limit precision to 9, cause a prec >= 10 can lead to overflow errors
+ while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
+ buf[len++] = '0';
+ prec--;
+ }
+
+ int whole = (int)value;
+ double tmp = (value - whole) * pow10[prec];
+ unsigned long frac = (unsigned long)tmp;
+ diff = tmp - frac;
+
+ if (diff > 0.5) {
+ ++frac;
+ // handle rollover, e.g. case 0.99 with prec 1 is 1.0
+ if (frac >= pow10[prec]) {
+ frac = 0;
+ ++whole;
+ }
+ }
+ else if (diff < 0.5) {
+ }
+ else if ((frac == 0U) || (frac & 1U)) {
+ // if halfway, round up if odd OR if last digit is 0
+ ++frac;
+ }
+
+ if (prec == 0U) {
+ diff = value - (double)whole;
+ if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
+ // exactly 0.5 and ODD, then round up
+ // 1.5 -> 2, but 2.5 -> 2
+ ++whole;
+ }
+ }
+ else {
+ unsigned int count = prec;
+ // now do fractional part, as an unsigned number
+ while (len < PRINTF_FTOA_BUFFER_SIZE) {
+ --count;
+ buf[len++] = (char)(48U + (frac % 10U));
+ if (!(frac /= 10U)) {
+ break;
+ }
+ }
+ // add extra 0s
+ while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
+ buf[len++] = '0';
+ }
+ if (len < PRINTF_FTOA_BUFFER_SIZE) {
+ // add decimal
+ buf[len++] = '.';
+ }
+ }
+
+ // do whole part, number is reversed
+ while (len < PRINTF_FTOA_BUFFER_SIZE) {
+ buf[len++] = (char)(48 + (whole % 10));
+ if (!(whole /= 10)) {
+ break;
+ }
+ }
+
+ // pad leading zeros
+ if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
+ if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
+ width--;
+ }
+ while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
+ buf[len++] = '0';
+ }
+ }
+
+ if (len < PRINTF_FTOA_BUFFER_SIZE) {
+ if (negative) {
+ buf[len++] = '-';
+ }
+ else if (flags & FLAGS_PLUS) {
+ buf[len++] = '+'; // ignore the space if the '+' exists
+ }
+ else if (flags & FLAGS_SPACE) {
+ buf[len++] = ' ';
+ }
+ }
+
+ return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
+}
+
+
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
+static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ // check for NaN and special values
+ if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
+ return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
+ }
+
+ // determine the sign
+ const bool negative = value < 0;
+ if (negative) {
+ value = -value;
+ }
+
+ // default precision
+ if (!(flags & FLAGS_PRECISION)) {
+ prec = PRINTF_DEFAULT_FLOAT_PRECISION;
+ }
+
+ // determine the decimal exponent
+ // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
+ union {
+ uint64_t U;
+ double F;
+ } conv;
+
+ conv.F = value;
+ int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
+ conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
+ // now approximate log10 from the log2 integer part and an expansion of ln around 1.5
+ int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
+ // now we want to compute 10^expval but we want to be sure it won't overflow
+ exp2 = (int)(expval * 3.321928094887362 + 0.5);
+ const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
+ const double z2 = z * z;
+ conv.U = (uint64_t)(exp2 + 1023) << 52U;
+ // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
+ conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
+ // correct for rounding errors
+ if (value < conv.F) {
+ expval--;
+ conv.F /= 10;
+ }
+
+ // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
+ unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
+
+ // in "%g" mode, "prec" is the number of *significant figures* not decimals
+ if (flags & FLAGS_ADAPT_EXP) {
+ // do we want to fall-back to "%f" mode?
+ if ((value >= 1e-4) && (value < 1e6)) {
+ if ((int)prec > expval) {
+ prec = (unsigned)((int)prec - expval - 1);
+ }
+ else {
+ prec = 0;
+ }
+ flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
+ // no characters in exponent
+ minwidth = 0U;
+ expval = 0;
+ }
+ else {
+ // we use one sigfig for the whole part
+ if ((prec > 0) && (flags & FLAGS_PRECISION)) {
+ --prec;
+ }
+ }
+ }
+
+ // will everything fit?
+ unsigned int fwidth = width;
+ if (width > minwidth) {
+ // we didn't fall-back so subtract the characters required for the exponent
+ fwidth -= minwidth;
+ } else {
+ // not enough characters, so go back to default sizing
+ fwidth = 0U;
+ }
+ if ((flags & FLAGS_LEFT) && minwidth) {
+ // if we're padding on the right, DON'T pad the floating part
+ fwidth = 0U;
+ }
+
+ // rescale the float value
+ if (expval) {
+ value /= conv.F;
+ }
+
+ // output the floating part
+ const size_t start_idx = idx;
+ idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
+
+ // output the exponent part
+ if (minwidth) {
+ // output the exponential symbol
+ out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
+ // output the exponent value
+ idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
+ // might need to right-pad spaces
+ if (flags & FLAGS_LEFT) {
+ while (idx - start_idx < width) out(' ', buffer, idx++, maxlen);
+ }
+ }
+ return idx;
+}
+#endif // PRINTF_SUPPORT_EXPONENTIAL
+#endif // PRINTF_SUPPORT_FLOAT
+
+
+// internal vsnprintf
+static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va)
+{
+ unsigned int flags, width, precision, n;
+ size_t idx = 0U;
+
+ if (!buffer) {
+ // use null output function
+ out = _out_null;
+ }
+
+ while (*format)
+ {
+ // format specifier? %[flags][width][.precision][length]
+ if (*format != '%') {
+ // no
+ out(*format, buffer, idx++, maxlen);
+ format++;
+ continue;
+ }
+ else {
+ // yes, evaluate it
+ format++;
+ }
+
+ // evaluate flags
+ flags = 0U;
+ do {
+ switch (*format) {
+ case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break;
+ case '-': flags |= FLAGS_LEFT; format++; n = 1U; break;
+ case '+': flags |= FLAGS_PLUS; format++; n = 1U; break;
+ case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break;
+ case '#': flags |= FLAGS_HASH; format++; n = 1U; break;
+ default : n = 0U; break;
+ }
+ } while (n);
+
+ // evaluate width field
+ width = 0U;
+ if (_is_digit(*format)) {
+ width = _atoi(&format);
+ }
+ else if (*format == '*') {
+ const int w = va_arg(va, int);
+ if (w < 0) {
+ flags |= FLAGS_LEFT; // reverse padding
+ width = (unsigned int)-w;
+ }
+ else {
+ width = (unsigned int)w;
+ }
+ format++;
+ }
+
+ // evaluate precision field
+ precision = 0U;
+ if (*format == '.') {
+ flags |= FLAGS_PRECISION;
+ format++;
+ if (_is_digit(*format)) {
+ precision = _atoi(&format);
+ }
+ else if (*format == '*') {
+ const int prec = (int)va_arg(va, int);
+ precision = prec > 0 ? (unsigned int)prec : 0U;
+ format++;
+ }
+ }
+
+ // evaluate length field
+ switch (*format) {
+ case 'l' :
+ flags |= FLAGS_LONG;
+ format++;
+ if (*format == 'l') {
+ flags |= FLAGS_LONG_LONG;
+ format++;
+ }
+ break;
+ case 'h' :
+ flags |= FLAGS_SHORT;
+ format++;
+ if (*format == 'h') {
+ flags |= FLAGS_CHAR;
+ format++;
+ }
+ break;
+#if defined(PRINTF_SUPPORT_PTRDIFF_T)
+ case 't' :
+ flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+ format++;
+ break;
+#endif
+ case 'j' :
+ flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+ format++;
+ break;
+ case 'z' :
+ flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+ format++;
+ break;
+ default :
+ break;
+ }
+
+ // evaluate specifier
+ switch (*format) {
+ case 'd' :
+ case 'i' :
+ case 'u' :
+ case 'x' :
+ case 'X' :
+ case 'o' :
+ case 'b' : {
+ // set the base
+ unsigned int base;
+ if (*format == 'x' || *format == 'X') {
+ base = 16U;
+ }
+ else if (*format == 'o') {
+ base = 8U;
+ }
+ else if (*format == 'b') {
+ base = 2U;
+ }
+ else {
+ base = 10U;
+ flags &= ~FLAGS_HASH; // no hash for dec format
+ }
+ // uppercase
+ if (*format == 'X') {
+ flags |= FLAGS_UPPERCASE;
+ }
+
+ // no plus or space flag for u, x, X, o, b
+ if ((*format != 'i') && (*format != 'd')) {
+ flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
+ }
+
+ // ignore '0' flag when precision is given
+ if (flags & FLAGS_PRECISION) {
+ flags &= ~FLAGS_ZEROPAD;
+ }
+
+ // convert the integer
+ if ((*format == 'i') || (*format == 'd')) {
+ // signed
+ if (flags & FLAGS_LONG_LONG) {
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ const long long value = va_arg(va, long long);
+ idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
+#endif
+ }
+ else if (flags & FLAGS_LONG) {
+ const long value = va_arg(va, long);
+ idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
+ }
+ else {
+ const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
+ idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
+ }
+ }
+ else {
+ // unsigned
+ if (flags & FLAGS_LONG_LONG) {
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
+#endif
+ }
+ else if (flags & FLAGS_LONG) {
+ idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
+ }
+ else {
+ const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
+ idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
+ }
+ }
+ format++;
+ break;
+ }
+#if defined(PRINTF_SUPPORT_FLOAT)
+ case 'f' :
+ case 'F' :
+ if (*format == 'F') flags |= FLAGS_UPPERCASE;
+ idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
+ format++;
+ break;
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP;
+ if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE;
+ idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
+ format++;
+ break;
+#endif // PRINTF_SUPPORT_EXPONENTIAL
+#endif // PRINTF_SUPPORT_FLOAT
+ case 'c' : {
+ unsigned int l = 1U;
+ // pre padding
+ if (!(flags & FLAGS_LEFT)) {
+ while (l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ // char output
+ out((char)va_arg(va, int), buffer, idx++, maxlen);
+ // post padding
+ if (flags & FLAGS_LEFT) {
+ while (l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ format++;
+ break;
+ }
+
+ case 's' : {
+ const char* p = va_arg(va, char*);
+ unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
+ // pre padding
+ if (flags & FLAGS_PRECISION) {
+ l = (l < precision ? l : precision);
+ }
+ if (!(flags & FLAGS_LEFT)) {
+ while (l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ // string output
+ while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
+ out(*(p++), buffer, idx++, maxlen);
+ }
+ // post padding
+ if (flags & FLAGS_LEFT) {
+ while (l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ format++;
+ break;
+ }
+
+ case 'p' : {
+ width = sizeof(void*) * 2U;
+ flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
+ if (is_ll) {
+ idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags);
+ }
+ else {
+#endif
+ idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags);
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ }
+#endif
+ format++;
+ break;
+ }
+
+ case '%' :
+ out('%', buffer, idx++, maxlen);
+ format++;
+ break;
+
+ default :
+ out(*format, buffer, idx++, maxlen);
+ format++;
+ break;
+ }
+ }
+
+ // termination
+ out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
+
+ // return written chars without terminating \0
+ return (int)idx;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+int printf_(const char* format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ char buffer[1];
+ const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
+ va_end(va);
+ return ret;
+}
+
+
+int sprintf_(char* buffer, const char* format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
+ va_end(va);
+ return ret;
+}
+
+
+int snprintf_(char* buffer, size_t count, const char* format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
+ va_end(va);
+ return ret;
+}
+
+
+int vprintf_(const char* format, va_list va)
+{
+ char buffer[1];
+ return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
+}
+
+
+int vsnprintf_(char* buffer, size_t count, const char* format, va_list va)
+{
+ return _vsnprintf(_out_buffer, buffer, count, format, va);
+}
+
+
+int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...)
+{
+ va_list va;
+ va_start(va, format);
+ const out_fct_wrap_type out_fct_wrap = { out, arg };
+ const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);
+ va_end(va);
+ return ret;
+}
diff --git a/src/printf.h b/src/printf.h new file mode 100644 index 0000000..6104ccf --- /dev/null +++ b/src/printf.h @@ -0,0 +1,117 @@ +///////////////////////////////////////////////////////////////////////////////
+// \author (c) Marco Paland (info@paland.com)
+// 2014-2019, PALANDesign Hannover, Germany
+//
+// \license The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
+// embedded systems with a very limited resources.
+// Use this instead of bloated standard/newlib printf.
+// These routines are thread safe and reentrant.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _PRINTF_H_
+#define _PRINTF_H_
+
+#include <stdarg.h>
+#include <stddef.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Output a character to a custom device like UART, used by the printf() function
+ * This function is declared here only. You have to write your custom implementation somewhere
+ * \param character Character to output
+ */
+void _putchar(char character);
+
+
+/**
+ * Tiny printf implementation
+ * You have to implement _putchar if you use printf()
+ * To avoid conflicts with the regular printf() API it is overridden by macro defines
+ * and internal underscore-appended functions like printf_() are used
+ * \param format A string that specifies the format of the output
+ * \return The number of characters that are written into the array, not counting the terminating null character
+ */
+#define printf printf_
+int printf_(const char* format, ...);
+
+
+/**
+ * Tiny sprintf implementation
+ * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
+ * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
+ * \param format A string that specifies the format of the output
+ * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
+ */
+#define sprintf sprintf_
+int sprintf_(char* buffer, const char* format, ...);
+
+
+/**
+ * Tiny snprintf/vsnprintf implementation
+ * \param buffer A pointer to the buffer where to store the formatted string
+ * \param count The maximum number of characters to store in the buffer, including a terminating null character
+ * \param format A string that specifies the format of the output
+ * \param va A value identifying a variable arguments list
+ * \return The number of characters that COULD have been written into the buffer, not counting the terminating
+ * null character. A value equal or larger than count indicates truncation. Only when the returned value
+ * is non-negative and less than count, the string has been completely written.
+ */
+#define snprintf snprintf_
+#define vsnprintf vsnprintf_
+int snprintf_(char* buffer, size_t count, const char* format, ...);
+int vsnprintf_(char* buffer, size_t count, const char* format, va_list va);
+
+
+/**
+ * Tiny vprintf implementation
+ * \param format A string that specifies the format of the output
+ * \param va A value identifying a variable arguments list
+ * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
+ */
+#define vprintf vprintf_
+int vprintf_(const char* format, va_list va);
+
+
+/**
+ * printf with output function
+ * You may use this as dynamic alternative to printf() with its fixed _putchar() output
+ * \param out An output function which takes one character and an argument pointer
+ * \param arg An argument pointer for user data passed to output function
+ * \param format A string that specifies the format of the output
+ * \return The number of characters that are sent to the output function, not counting the terminating null character
+ */
+int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // _PRINTF_H_
diff --git a/src/string.c b/src/string.c new file mode 100644 index 0000000..f1876f2 --- /dev/null +++ b/src/string.c @@ -0,0 +1,64 @@ + + +#include <stddef.h> +#include <stdint.h> + + +int memcmp(void *s1, const void *s2, size_t n) +{ + unsigned char *a = (unsigned char *) s1; + unsigned char *b = (unsigned char *) s2; + for (size_t i=0;i<n;i++) + if (a[i] != b[i]) return a[i] - b[i]; + return 0; +} + +void *memcpy(void *dest, const void *src, size_t n) +{ + unsigned char *d = (unsigned char *) dest; + unsigned char *s = (unsigned char *) src; + for (size_t i=0;i<n;i++) d[i] = s[i]; + return dest; +} + +void *memmove(void *dest, const void *src, size_t n) +{ + unsigned char *d = (unsigned char *) dest; + unsigned char *s = (unsigned char *) src; + if (d < s) + for (size_t i=0;i<n;i++) + d[i] = s[i]; + else + for (size_t i=n;i!=0;i--) + d[i-1] = s[i-1]; + return dest; +} + +void *memset(void *s, int c, size_t n) +{ + unsigned char *str = (unsigned char *) s; + for (size_t i=0;i<n;i++) str[i] = c; + return s; +} + +size_t strlen(const char *s) +{ + size_t len = 0; + while (s[++len]); + return len; +} + + +char *hex(uint32_t n, char *buf) +{ + buf[0] = '0'; buf[1] = 'x'; + for (int i=0;i<8;i++) + { + int digit = (n >> (i * 4)) & 0xF; + if (digit < 10) + buf[9 - i] = '0' + digit; + else + buf[9 - i] = 'A' + (digit - 10); + } + return buf; +} diff --git a/src/string.h b/src/string.h new file mode 100644 index 0000000..0d2c8d4 --- /dev/null +++ b/src/string.h @@ -0,0 +1,15 @@ + +#ifndef STRING_H +#define STRING_H + +#include <stddef.h> +#include <stdint.h> + +int memcmp(const void *s1, const void *s2, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +size_t strlen(const char *s); +char *hex(uint32_t n, char *buf); + +#endif diff --git a/src/vga.c b/src/vga.c new file mode 100644 index 0000000..3ff8342 --- /dev/null +++ b/src/vga.c @@ -0,0 +1,103 @@ + +#include "vga.h" +#include "string.h" +#include "port.h" +#include <stdint.h> +#include <stddef.h> + +uint16_t* terminal_buffer = (uint16_t*) 0xB8000;; + +uint8_t vga_color(enum vga_color fg, enum vga_color bg) { + return fg | bg << 4; +} + +uint16_t vga_entry(unsigned char uc, uint8_t color) { + return (uint16_t) uc | (uint16_t) color << 8; +} + +void vclear(void) { + terminal_row = 0; + terminal_column = 0; + for (size_t y = 0; y < VGA_HEIGHT; y++) + { + for (size_t x = 0; x < VGA_WIDTH; x++) + { + const size_t index = y * VGA_WIDTH + x; + terminal_buffer[index] = vga_entry(' ', terminal_color); + } + } +} + +void vsetcolor(uint8_t color) { + terminal_color = color; +} + +void vsetchar(char c, uint8_t color, size_t x, size_t y) { + const size_t index = y * VGA_WIDTH + x; + terminal_buffer[index] = vga_entry(c, color); +} + +void vscroll() { + for (unsigned int row=1; row < VGA_HEIGHT; row++) + { + size_t row_bytesize = VGA_WIDTH * 2; // A VGA entry is 2 bytes + // Copy each line one line up + memcpy(&terminal_buffer[(row - 1) * VGA_WIDTH], + &terminal_buffer[row * VGA_WIDTH], row_bytesize); + } + // Clear last line + for (unsigned int col=0; col < VGA_WIDTH; col++) + { + terminal_buffer[VGA_WIDTH * (VGA_HEIGHT - 1) + col] = vga_entry(' ', terminal_color); + } + terminal_row--; +} + +void vputchar(char c) { + void newline() { + terminal_column = 0; + if (++terminal_row >= VGA_HEIGHT) + vscroll(); + } + switch (c) { + default: + vsetchar(c, terminal_color, terminal_column, terminal_row); + terminal_column++; + break; + case '\n': + newline(); + break; + case '\t': + // tab width of 4 + terminal_column = (terminal_column / 4 + 1) * 4; + break; + case '\b': + if (terminal_column > 0) terminal_column--; + break; + case '\r': + terminal_column = 0; + break; + case '\f': + vclear(); + break; + case '\a': + break; + } + if (terminal_column >= VGA_WIDTH) + newline(); +} + +void _putchar(char c) { + vputchar(c); +} + +void vprint(const char* str) { + for (size_t i = 0; i < strlen(str); i++) + vputchar(str[i]); +} + +void vlocate(size_t column, size_t row) { + terminal_column = column % VGA_WIDTH; + terminal_row = row % VGA_HEIGHT; +} + diff --git a/src/vga.h b/src/vga.h new file mode 100644 index 0000000..50cf1e6 --- /dev/null +++ b/src/vga.h @@ -0,0 +1,50 @@ + +#ifndef __VGA_H +#define __VGA_H + +#include <stddef.h> +#include <stdint.h> + +enum vga_color { + vga_black, + vga_blue, + vga_green, + vga_cyan, + vga_red, + vga_magenta, + vga_brown, + vga_lgray, + vga_dgray, + vga_lblue, + vga_lgreen, + vga_lcyan, + vga_lred, + vga_lmagenta, + vga_lbrown, + vga_white +}; + +uint8_t vga_color(enum vga_color fg, enum vga_color bg); +uint16_t vga_entry(unsigned char uc, uint8_t color); + +static const size_t VGA_WIDTH = 80; +static const size_t VGA_HEIGHT = 25; + +size_t terminal_row; +size_t terminal_column; +uint8_t terminal_color; +uint16_t* terminal_buffer; + +void vclear(void); + +void vsetcolor(uint8_t color); + +void vsetchar(char c, uint8_t color, size_t x, size_t y); + +void vputchar(char c); + +void vprint(const char* str); + +void vlocate(size_t column, size_t row); + +#endif |