From bdfb10e3a685de44737f07258ac76e19cce8e216 Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Sun, 20 Nov 2022 14:13:52 +0100 Subject: [PATCH] Attempts at fixing broken VGA driver + initial work on interrupts --- .vscode/settings.json | 6 -- Makefile | 14 ++- include/cpu/idt.h | 95 +++++++++++++++++++ include/cpu/isr.h | 139 ++++++++++++++++++++++++++++ include/kernel/drivers/vga/screen.h | 11 +-- include/kernel/types.h | 4 +- include/kernel/util.h | 35 ++++++- src/boot/mbr.s | 2 +- src/boot/util/enablea20.s | 7 +- src/cpu/isr.c | 80 ++++++++++++++++ src/kernel/drivers/ports/ports.c | 8 +- src/kernel/drivers/vga/screen.c | 136 ++++++++++++++------------- src/kernel/main.c | 6 +- src/kernel/util.c | 54 +++++++++-- 14 files changed, 492 insertions(+), 105 deletions(-) delete mode 100644 .vscode/settings.json create mode 100644 include/cpu/idt.h create mode 100644 include/cpu/isr.h create mode 100644 src/cpu/isr.c diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index feb886d..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "files.associations": { - "ktypes.h": "c", - "ports.h": "c" - } -} \ No newline at end of file diff --git a/Makefile b/Makefile index 15c286e..58137c4 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,8 @@ SRCDIR := src BUILDDIR := build OBJDIR := obj DISTDIR := dist -CFLAGS := -g -I $(IDIR) -Wall -pedantic -Wno-builtin-declaration-mismatch -ffreestanding +GDB := gdb +CFLAGS := -o0 -ggdb -I $(IDIR) -Wall -pedantic -Wno-builtin-declaration-mismatch -ffreestanding KERNEL_SOURCES := $(shell find src/kernel -name '*.c' -type f) KERNEL_OBJS := $(KERNEL_SOURCES:.c=.o) KERNEL_OBJS := $(subst src/,$(OBJDIR)/,$(KERNEL_OBJS)) @@ -72,6 +73,10 @@ drivers: $(DRIVERS_OBJS) $(BUILDDIR)/kernel.bin: $(OBJDIR)/entrypoint.o $(KERNEL_OBJS) $(DRIVERS_OBJS) $(LD) -o $@ -Ttext 0x1000 $^ --oformat binary +# Compile and link the kernel with debug symbols +$(BUILDDIR)/kernel.elf: $(OBJDIR)/entrypoint.o $(KERNEL_OBJS) $(DRIVERS_OBJS) + $(LD) -o $@ -Ttext 0x1000 $^ + kernel: $(BUILDDIR)/kernel.bin @@ -89,7 +94,8 @@ image: $(BUILDDIR)/mbr.bin $(BUILDDIR)/kernel.bin run: image - qemu-system-x86_64 -drive format=raw,file=$(DISTDIR)/os.img,index=0,media=disk + qemu-system-i386 -drive format=raw,file=$(DISTDIR)/os.img,index=0,media=disk -debug: image - qemu-system-x86_64 -drive format=raw,file=$(DISTDIR)/os.img,index=0,media=disk -monitor stdio \ No newline at end of file + +debug: $(BUILDDIR)/kernel.elf image + qemu-system-i386 -drive format=raw,file=$(DISTDIR)/os.img,index=0,media=disk -gdb tcp:localhost:8080 -S diff --git a/include/cpu/idt.h b/include/cpu/idt.h new file mode 100644 index 0000000..7ef0db9 --- /dev/null +++ b/include/cpu/idt.h @@ -0,0 +1,95 @@ +/* +Copyright 2022 Mattia Giambirtone & Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef TSOS_CPU_IDT_H +#define TSOS_CPU_IDT_H + +#include "kernel/types.h" + +#define KERNEL_CODE_SEGMENT_SELECTOR 0x8 + + + +typedef struct { + /* + An entry in the Interrupt + Descriptor Table of the CPU + */ + + u16 handlerOffsetLow; /* Low 16 bits of handler's address */ + u16 segmentSelector; /* Kernel segment selector */ + u8 _unused; /* This is always zero! (padding?) */ + u8 flags; /* Flags of this handler */ + u16 handlerOffsetHigh; /* High 16 bits of handler's address */ + /* + The flags byte is structured as follows: + - Bit 7 : "Interrupt is present". Serves to make the whole structure valid + - Bits 6-5: Privilege level ("ring") of the caller. Ring zero is the kernel, + ring one is usually reserved for driver code, ring two is unused and ring 3 + is userland code + - Bits 4-0: The gate's type and attributes. They are structured as follows: + - Bit 4 : Set to zero for interrupts and one for traps (traps are usually used for exceptions) + - Bit 3-0: The type of the gate (whether it's 16- or 32-bit, etc.) + The only valid values for bits 4-0 are five (taken from the OSDev wiki): + - 0b0101 or 0x5: Task Gate. Note that in this case, the offset value is unused and should be set to zero. + - 0b0110 or 0x6: 16-bit Interrupt Gate + - 0b0111 or 0x7: 16-bit Trap Gate + - 0b1110 or 0xE: 32-bit Interrupt Gate + - 0b1111 or 0xF: 32-bit Trap Gate + */ +} __attribute__((packed)) GateDescriptor; +// The packed attribute tells gcc not to align +// or reorder the struct's fields in memory. +// Doing so in normal code is fine and speeds up +// memory accessing, but the CPU expects the structure +// of a GDT entry to be exactly in this order, with no +// padding (besides, each entry is exactly 8 bytes long +// already, so the performance hit is probably neglibible) + + +typedef struct { + /* + The Interrupt Descriptor Table + Register. Stores the location + of the IDT in memory + */ + + u16 size; /* Size of the IDT. Always one less than its true size*/ + u32 loc; +} __attribute__((packed)) IDTRegister; + + +/* +Note: While the IDT can contain up to 2 ** 16 entries, only the first +256 are considered and the rest is ignored; However, if there's less than +256 entries in the table, when the CPU then tries to fetch an interrupt gate +and doesn't find one, a GP (General Protection) fault is triggered. This may +cause the CPU to triple fault if the GP handler itself hasn't been set, as it +is one of the interrupt handlers the CPU expects to be present +*/ + + +#define IDT_SIZE 256 + +/* These are the IDT and IDTR (We'll access these from asm) */ +GateDescriptor idt[IDT_SIZE]; +IDTRegister idtReg; + +/* Prototypes */ +void setIDTGate(i32 n, u32 handler); +void setIDT(void); + +#endif \ No newline at end of file diff --git a/include/cpu/isr.h b/include/cpu/isr.h new file mode 100644 index 0000000..2b17e5b --- /dev/null +++ b/include/cpu/isr.h @@ -0,0 +1,139 @@ +/* +Copyright 2022 Mattia Giambirtone & Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef TSOS_CPU_ISR_H +#define TSOS_CPU_ISR_H + +#include "kernel/types.h" + +/* +The first 32 ISRs are reserved for CPU exceptions, so we +manually forward-declare them. They will be implemented in +assembly (hence why "extern" is explicit here, despite it +being the default for C functions) +*/ + +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); + + +typedef struct { + /* + A wrapper around the metadata + that we pass to ISRs (from asm) + when they're called + */ + + u32 ds; /* The DS register */ + /* General Purpose registers saved by pusha */ + u32 edi; + u32 esi; + u32 ebp; + u32 esp; + u32 ebx; + u32 edx; + u32 ecx; + u32 eax; + u32 kind; /* The interrupt number */ + u32 err; /* The interrupt's error code (optional, may be zero) */ + /* + These are pushed by the processor automatically when an interrupt + is triggered + */ + u32 eip; + u32 cs; + u32 eflags; + u32 useresp; + u32 ss; +} ISRMetadata; + + +void installDefaultHandlers(void); +void interruptHandler(ISRMetadata data); + +// Maps each builtin ISR number to an error message +char* errorMessages[] = { + "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" +}; + +#define interruptMessage(x) errorMessages[x] + + +#endif \ No newline at end of file diff --git a/include/kernel/drivers/vga/screen.h b/include/kernel/drivers/vga/screen.h index 5200000..513150b 100644 --- a/include/kernel/drivers/vga/screen.h +++ b/include/kernel/drivers/vga/screen.h @@ -15,8 +15,8 @@ limitations under the License. */ -#ifndef TSOS_DRV_VGA_SCREEN -#define TSOS_DRV_VGA_SCREEN +#ifndef TSOS_DRV_VGA_SCREEN_H +#define TSOS_DRV_VGA_SCREEN_H #include "kernel/types.h" #include "kernel/drivers/ports/ports.h" @@ -39,9 +39,8 @@ limitations under the License. // Public API void clearScreen(void); -void kprintAt(char* message, i32 col, i32 row); -void kprint(char* message); -void kprintln(char* message); - +void kprint(const char* message); +void kprintln(const char* message); +void kprintf(const char *fmt, ...); #endif \ No newline at end of file diff --git a/include/kernel/types.h b/include/kernel/types.h index 890fd58..f02561e 100644 --- a/include/kernel/types.h +++ b/include/kernel/types.h @@ -16,8 +16,8 @@ limitations under the License. // Shorthand definitions of various types // used in the kernel -#ifndef TSOS_KTYPES -#define TSOS_KTYPES +#ifndef TSOS_KTYPES_H +#define TSOS_KTYPES_H #define true 1 #define false 0 diff --git a/include/kernel/util.h b/include/kernel/util.h index 0654017..edf35aa 100644 --- a/include/kernel/util.h +++ b/include/kernel/util.h @@ -19,8 +19,41 @@ limitations under the License. #include "kernel/types.h" +// Some handy macros + +/* +These bitwise tricks *seem* like black +magic, but they're quite simple: shifting +the offset by 8 bits moves the high bits 8 +positions down, so we lose the low bits and +the high bits now fit into a single byte. To +get the high bits, we do a bitwise and with 0xff +(11111111 in decimal) leaving us with only the low +bits: the reason this works is because 0xff (which +is smaller than our offset), is zero-extended from +the beginning with zeroes, so when we perform the +operation the high bits are cancelled out. Neat huh? +*/ + +#define LOW8(x) (u8)(x & 0xff) +#define HIGH8(x) (u8)(x >> 8) + + + +/* +Here we do the same thing, except it's to get the +low/high 16 bits of a 32-bit value instead +*/ + +#define LOW16(x) (u16)(x & 0xffff) +#define HIGH16(x) (u16)(LOW16(x >> 16)) + + + + void copystr(const char* source, char* dest, i32 n); -void memset(u8* dest, u8 val, u32 len); +void memset(byte* dest, byte val, u32 len); void itoa(const i32 i, char* a); +void utoa(const u32 i, char* a); #endif \ No newline at end of file diff --git a/src/boot/mbr.s b/src/boot/mbr.s index f68caa5..ec1585e 100644 --- a/src/boot/mbr.s +++ b/src/boot/mbr.s @@ -120,7 +120,7 @@ load_kernel: ; Loads the kernel into memory mov bx, kernel_offset mov cl, reserved_sectors + 1 - mov dh, 4 + mov dh, 5 mov dl, [boot_drive] call load_disk mov si, kernel_loaded_msg diff --git a/src/boot/util/enablea20.s b/src/boot/util/enablea20.s index 50166f5..b646fe4 100644 --- a/src/boot/util/enablea20.s +++ b/src/boot/util/enablea20.s @@ -183,15 +183,16 @@ enableA20: ;; the fact that we had to use the nonstandard alternate enabling ;; method .success: - ;sti ; Note: When this is uncommented, shit breaks. I have a few theories as to why, - ; but none of them make sense, so ¯\_(ツ)_/¯ + ;sti ; Note: When this is uncommented, shit breaks. Probably because the CPU + ; throws a fault or a trap without the IDT being loaded, so it triple + ; faults and resets. Or maybe not, who knows popa xor eax, eax ret .fail: - sti + ; sti popa mov eax, -1 ret diff --git a/src/cpu/isr.c b/src/cpu/isr.c new file mode 100644 index 0000000..2798e83 --- /dev/null +++ b/src/cpu/isr.c @@ -0,0 +1,80 @@ +/* +Copyright 2022 Mattia Giambirtone & Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Low-level handling of interrupts + +#include "kernel/types.h" +#include "kernel/util.h" +#include "cpu/isr.h" +#include "cpu/idt.h" + + +void installDefaultHandlers(void) { + /* + Installs the first 32 ISRs that are + reserved for the CPU + */ + + // If only C functions were first-class + // objects :( + setIDTGate(0, (u32)isr0); + setIDTGate(1, (u32)isr1); + setIDTGate(2, (u32)isr2); + setIDTGate(3, (u32)isr3); + setIDTGate(4, (u32)isr4); + setIDTGate(5, (u32)isr5); + setIDTGate(6, (u32)isr6); + setIDTGate(7, (u32)isr7); + setIDTGate(8, (u32)isr8); + setIDTGate(9, (u32)isr9); + setIDTGate(10, (u32)isr10); + setIDTGate(11, (u32)isr11); + setIDTGate(12, (u32)isr12); + setIDTGate(13, (u32)isr13); + setIDTGate(14, (u32)isr14); + setIDTGate(15, (u32)isr15); + setIDTGate(16, (u32)isr16); + setIDTGate(17, (u32)isr17); + setIDTGate(18, (u32)isr18); + setIDTGate(19, (u32)isr19); + setIDTGate(20, (u32)isr20); + setIDTGate(21, (u32)isr21); + setIDTGate(22, (u32)isr22); + setIDTGate(23, (u32)isr23); + setIDTGate(24, (u32)isr24); + setIDTGate(25, (u32)isr25); + setIDTGate(26, (u32)isr26); + setIDTGate(27, (u32)isr27); + setIDTGate(28, (u32)isr28); + setIDTGate(29, (u32)isr29); + setIDTGate(30, (u32)isr30); + setIDTGate(31, (u32)isr31); + setIDT(); // Here we're actually calling assembly code! +} + + +void interruptHandler(ISRMetadata data) { + /* + Handles CPU interrupts + */ + char v[3]; + itoa(data.kind, v); + kprint("TSKL: Received interrupt "); + kprint(v); + kprint("("); + kprint(interruptMessage(data.err)); + kprintln(")"); +} \ No newline at end of file diff --git a/src/kernel/drivers/ports/ports.c b/src/kernel/drivers/ports/ports.c index b6ccaaf..de15db2 100644 --- a/src/kernel/drivers/ports/ports.c +++ b/src/kernel/drivers/ports/ports.c @@ -28,26 +28,26 @@ limitations under the License. byte readByte(u16 port) { // Reads a byte from the specified I/O port volatile byte result; - __asm__ volatile ("in %%dx, %%al" : "=a" (result) : "d" (port)); + asm volatile ("in %%dx, %%al" : "=a" (result) : "d" (port)); return result; } void writeByte(u16 port, byte data) { // Writes a byte to the specified I/O port - __asm__ volatile ("out %%al, %%dx" : : "a" (data), "d" (port)); + asm volatile ("out %%al, %%dx" : : "a" (data), "d" (port)); } u16 readWord(u16 port) { // Reads a word (16 bits) from the specified I/O port volatile u16 result; - __asm__ volatile ("in %%dx, %%ax" : "=a" (result) : "d" (port)); + asm volatile ("in %%dx, %%ax" : "=a" (result) : "d" (port)); return result; } void writeWord(u16 port, u16 data) { // Writes a word (16 bits) to the specified I/O port - __asm__ volatile ("out %%ax, %%dx" : : "a" (data), "d" (port)); + asm volatile ("out %%ax, %%dx" : : "a" (data), "d" (port)); } \ No newline at end of file diff --git a/src/kernel/drivers/vga/screen.c b/src/kernel/drivers/vga/screen.c index 10e6613..b1aef88 100644 --- a/src/kernel/drivers/vga/screen.c +++ b/src/kernel/drivers/vga/screen.c @@ -19,67 +19,81 @@ limitations under the License. #include "kernel/types.h" #include "kernel/util.h" +#include + + +u16 getCursorOffset(void); +void setCursorOffset(u16 offset); +u16 putchar(char ch, u8 row, u8 col, char attr); +u16 getOffset(u8 row, u8 col); +u8 getRow(u16 offset); +u8 getColumn(u16 offset); +void printc(char c); -i32 getCursorOffset(void); -void setCursorOffset(i32 offset); -i32 putchar(byte ch, i32 col, i32 row, byte attr); -i32 getOffset(i32 col, i32 row); -i32 getRow(i32 offset); -i32 getColumn(i32 offset); // Public API below -void kprintAt(char* message, i32 col, i32 row) { +void kprint(const char* message) { /* Prints a null-terminated string to the VGA - text buffer at the specified row and column. - The cursor's position is not updated. If both - col and row are negative, the current cursor - location is used + text buffer */ - i32 offset; - if (col >= 0 && row >= 0) { - offset = getOffset(col, row); - } - else { - offset = getCursorOffset(); - row = getRow(offset); - col = getColumn(offset); - } - for (i32 i = 0; message[i]; i++) { - offset = putchar(message[i], col, row, 0); + i32 offset = getCursorOffset(); + i32 row = getRow(offset), col = getColumn(offset); + while (*message) { + offset = putchar(*(message++), row, col, 0); // We compute the new row and column for the // next iteration. Hopefully the compiler inlines // these two calls row = getRow(offset); col = getColumn(offset); } - + setCursorOffset(getOffset(row, col)); } -void inline kprint(char* message) { + +void kprintf(const char *fmt, ...) { /* - Prints a null-terminated string to the - VGA text buffer + A simple reimplementation of printf in + kernel space. The following basic format + specifiers can be used: + - %s -> string + - %d -> signed number + - %u -> unsigned number + - %c -> character */ - kprintAt(message, -1, -1); + va_list args; + va_start(args, fmt); + char* sbuf; /* Used to print strings */ + char ibuf[12]; /* Used as input to itoa. 12 is the number of + digits of 2 ** 32 + 2 (for the sign and null byte)*/ + u32 offset = getCursorOffset(); + u32 row = getRow(offset), col = getColumn(offset); + memset(ibuf, '0', 12); + while (*fmt) { + offset = putchar(*(fmt++), row, col, 0); + row = getRow(offset); + col = getColumn(offset); + } + va_end(args); } -void inline kprintln(char* message) { +void inline kprintln(const char* message) { /* Identical to kprint, but calls kprint("\n") afterwards */ - kprint(message); - kprint("\n"); + kprint(message); + kprint("\n"); } + // Private API below -i32 putchar(byte ch, i32 col, i32 row, byte attr) { +u16 putchar(char ch, u8 row, u8 col, char attr) { /* Writes a single character to the VGA text buffer at the given row and column and returns the new @@ -108,16 +122,16 @@ i32 putchar(byte ch, i32 col, i32 row, byte attr) { if (row >= MAX_ROWS) { return -2; } - i32 offset = (col >= 0 && row >= 0)? getOffset(col, row): getCursorOffset(); + u16 offset = (col >= 0 && row >= 0)? getOffset(row, col): getCursorOffset(); switch (ch) { // Note the difference between a carriage return (which // only brings the write head back at 0 without changing // the row) and the newline (which increases the row as well) case '\r': - offset = getOffset(0, getRow(offset)); + offset = getOffset(getRow(offset), 0); break; case '\n': - offset = getOffset(0, getRow(offset) + 1); + offset = getOffset(getRow(offset) + 1, 0); break; case '\t': for (i32 i = 0; i < VGA_TABSIZE; i++) { @@ -135,29 +149,28 @@ i32 putchar(byte ch, i32 col, i32 row, byte attr) { // We check if we reached the end of the screen, in which // case we scroll if (offset >= SCREEN_SIZE * 2) { - for (i32 i = 1; i < MAX_ROWS; i ++) { + for (u8 i = 1; i < MAX_ROWS; i++) { // This loop will take the bytes of row i and copy // them to row i - 1, effectively erasing the first // one and causing the text on the screen to scroll! - copystr((char *)(getOffset(0, i) + VMEM_ADDRESS), - (char*)(getOffset(0, i - 1) + VMEM_ADDRESS), + copystr((char*)(getOffset(i, 0) + VMEM_ADDRESS), + (char*)(getOffset(i - 1, 0) + VMEM_ADDRESS), MAX_COLS * 2); } // We empty the last line - char* last = (char*)(getOffset(0, MAX_ROWS - 1) + VMEM_ADDRESS); - for (i32 i = 0; i < MAX_COLS * 2; i++) { + char* last = (char*)(getOffset(MAX_ROWS - 1, 0) + VMEM_ADDRESS); + for (u8 i = 0; i < MAX_COLS * 2; i++) { last[i] = 0; } offset -= 2 * MAX_COLS; } - setCursorOffset(offset); return offset; } -i32 getCursorOffset(void) { +u16 getCursorOffset(void) { /* Returns the current offset of the VGA text cursor @@ -170,7 +183,7 @@ i32 getCursorOffset(void) { // Memory-mapped I/O is great, isn't it? writeByte(REG_SCREEN_CTRL, 14); // And we read it - i32 offset = readByte(REG_SCREEN_DATA); + u16 offset = readByte(REG_SCREEN_DATA); // Since we requested the high byte // (i.e. the 8 most significant bits) // we now want to move them by 8 places. @@ -185,7 +198,7 @@ i32 getCursorOffset(void) { } -void setCursorOffset(i32 offset) { +void setCursorOffset(u16 offset) { /* Sets the offset of the VGA text cursor to the desired value. Note @@ -193,11 +206,11 @@ void setCursorOffset(i32 offset) { around I/O ports, no bounds checking is performed: the caller should make sure that the offset fits within the - 80x25 grid of the VGA screen! + 80x25 grid of the VGA screen */ // We now divide by 2 because while we count - // in bytes, the VGA controller counts in cells! + // in bytes, the VGA controller counts in cells offset /= 2; // This function is almost identical to getCursorOffset, // except we're now writing on the bus instead of reading @@ -205,23 +218,11 @@ void setCursorOffset(i32 offset) { // We write the high bits first writeByte(REG_SCREEN_CTRL, 14); - writeByte(REG_SCREEN_DATA, (byte)(offset >> 8)); + writeByte(REG_SCREEN_DATA, HIGH8(offset)); // Then the low bits writeByte(REG_SCREEN_CTRL, 15); - writeByte(REG_SCREEN_DATA, (byte)(offset & 0xff)); - // These bitwise tricks *seem* like black - // magic, but they're quite simple: shifting - // the offset by 8 bits moves the high bits 8 - // positions down (so we lose the low bits and - // the high bits now fit into a single byte, which - // is what writeByte wants). Then we get rid of the - // high bits (0xff is 11110000) with a bitwise and, - // leaving us with only the low bits set which we - // then feed to the I/O port again; The reason this - // works is because 0xff (which is smaller than our - // offset), is zero-extended from the beginning with - // zeroes, so when we perform the operation the high - // bits are cancelled out + writeByte(REG_SCREEN_DATA, LOW8(offset)); + } @@ -230,17 +231,16 @@ void clearScreen(void) { Clears the screen and resets the cursor position to 0, 0 */ - for (i32 i = 0; i < SCREEN_SIZE; i++) { - VMEM_BUF[i * 2] = ' '; // The screen is actually never "empty", just filled with spaces! + VMEM_BUF[i * 2] = ' '; // The screen is actually never "empty", just filled with spaces VMEM_BUF[i * 2 + 1] = LIGHT_GREY_ON_BLACK; } - setCursorOffset(getOffset(0, 0)); + } -i32 inline getOffset(i32 col, i32 row) { +u16 inline getOffset(u8 row, u8 col) { /* Converts a column, row pair into an absolute offset into the VGA @@ -253,17 +253,21 @@ i32 inline getOffset(i32 col, i32 row) { } -i32 inline getRow(i32 offset) { +u8 inline getRow(u16 offset) { /* Converts an absolute offset into the VGA text buffer into a row */ + + // Conversely, we divide by two + // when converting back from the + // raw offset to columns/rows return offset / (2 * MAX_COLS); } -i32 inline getColumn(i32 offset) { +u8 inline getColumn(u16 offset) { /* Converts an absolute offset into the VGA text buffer into a column diff --git a/src/kernel/main.c b/src/kernel/main.c index b0ebc0c..bff8754 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -26,9 +26,9 @@ i32 kmain(void) { */ - // TODO: Set VGA cursor position from assembly. Skipping - // log messages like this is just awful - kprintln("\n\n\nTSKL - INFO: Kernel booted successfully"); + clearScreen(); + kprint("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + kprint("X"); // TODO... return 0x202172; // :D } \ No newline at end of file diff --git a/src/kernel/util.c b/src/kernel/util.c index 27d802b..c372130 100644 --- a/src/kernel/util.c +++ b/src/kernel/util.c @@ -35,9 +35,10 @@ void copystr(const char* restrict source, char* restrict dest, i32 n) { } -void memset(u8* dest, u8 val, u32 len) { +void memset(byte* dest, byte val, u32 len) { /* - Implementation for memset + Fills the first len bytes of the memory area pointed to by dest + with the constant byte val */ u8* temp = (u8*)dest; while (len) { @@ -47,7 +48,22 @@ void memset(u8* dest, u8 val, u32 len) { } -i32 countDigits(i32 n) { +u8 countDigits(i32 n) { + /* + Returns the number of digits + of n + */ + i8 result = 0; + while (n) { + n /= 10; + result++; + } + return result; + +} + + +i32 uCountDigits(u32 n) { /* Returns the number of digits of n @@ -73,13 +89,33 @@ void itoa(i32 n, char* a) { buffer is not large enough, the behavior is undefined) */ - int i, sign; - if ((sign = n) < 0) n = -n; - i = countDigits(n); + + bool sign = n >= 0; + if (!sign) n = -n; + i32 i = countDigits(n); do { a[--i] = n % 10 + '0'; - } while ((n /= 10) > 0); + } while (n /= 10); - if (sign < 0) a[--i] = '-'; + if (!sign) a[--i] = '-'; a[--i] = '\0'; -} \ No newline at end of file +} + + +void utoa(u32 n, char* a) { + /* + Converts the unsigned integer n to + a string. The result is written + to a, which is assumed to be large + enough to accomodate the number + itself and a null byte at the end + (if the buffer is not large enough, + the behavior is undefined) + */ + + i32 i = uCountDigits(n); + do { + a[--i] = n % 10 + '0'; + } while (n /= 10); + a[--i] = '\0'; +}