Browse Source

Attempts at fixing broken VGA driver + initial work on interrupts

master
Mattia Giambirtone 2 months ago
parent
commit
bdfb10e3a6
  1. 6
      .vscode/settings.json
  2. 14
      Makefile
  3. 95
      include/cpu/idt.h
  4. 139
      include/cpu/isr.h
  5. 11
      include/kernel/drivers/vga/screen.h
  6. 4
      include/kernel/types.h
  7. 35
      include/kernel/util.h
  8. 2
      src/boot/mbr.s
  9. 7
      src/boot/util/enablea20.s
  10. 80
      src/cpu/isr.c
  11. 8
      src/kernel/drivers/ports/ports.c
  12. 136
      src/kernel/drivers/vga/screen.c
  13. 6
      src/kernel/main.c
  14. 54
      src/kernel/util.c

6
.vscode/settings.json

@ -1,6 +0,0 @@
{
"files.associations": {
"ktypes.h": "c",
"ports.h": "c"
}
}

14
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
debug: $(BUILDDIR)/kernel.elf image
qemu-system-i386 -drive format=raw,file=$(DISTDIR)/os.img,index=0,media=disk -gdb tcp:localhost:8080 -S

95
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

139
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

11
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

4
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

35
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

2
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

7
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

80
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(")");
}

8
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));
}

136
src/kernel/drivers/vga/screen.c

@ -19,67 +19,81 @@ limitations under the License.
#include "kernel/types.h"
#include "kernel/util.h"
#include <stdarg.h>
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

6
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
}

54
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';
}
}
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';
}

Loading…
Cancel
Save