Initial commit

This commit is contained in:
g1n 2022-02-02 10:22:28 +02:00
commit 1079061353
Signed by: g1n
GPG Key ID: 8D352193D65D4E2C
23 changed files with 970 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*~
src/build

10
LICENSE Normal file
View File

@ -0,0 +1,10 @@
MIT License
Copyright (c) 2021-2022 GRU
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.

14
README.org Normal file
View File

@ -0,0 +1,14 @@
#+TITLE: Orion OS
Orion is simple OS on C (and a bit assembler).
Some code can be taken from OSDev wiki and James Molloy tutorial (I am trying to use it as less as possible)
This is rewrite of old version of Orion that is not maintained now to better understand how it works.
** Build
To build your need crosscompiler for i686 (check OSDev wiki for instructions)
- First you need to ~cd src~
- Then ~make~
- And run it: if you want to run without grub - ~make run~, if with grub - ~make run-grub~

64
src/Makefile Normal file
View File

@ -0,0 +1,64 @@
CC= i686-elf-gcc
ASMFILES = arch/i386/boot.asm
CFILES = kernel.c vga.c
INCLUDEFLAGS=-Iinclude -Ilibk/include
DEBUGFLAGS=
CFLAGS=-std=c99 -ffreestanding -fstack-protector-all $(INCLUDEFLAGS) $(DEBUGFLAGS) -Wall -Wextra
CRTI_OBJ = build/crti.o
CRTBEGIN_OBJ:=$(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o)
CRTEND_OBJ:=$(shell $(CC) $(CFLAGS) -print-file-name=crtend.o)
CRTN_OBJ = build/crtn.o
LIBKOBJFILES = build/libk/string.o build/libk/stdio.o build/libk/stdlib.o build/libk/stack_protector.o build/libk/asm.o
OBJFILES = \
$(CRTI_OBJ) \
$(CRTBEGIN_OBJ) \
build/boot.o \
$(LIBKOBJFILES) \
build/early_kernel.o \
build/kernel.o \
build/vga.o \
build/idt.o \
$(CRTEND_OBJ) $(CRTN_OBJ)
KERNELFILE = build/orion.bin
all:
mkdir -p build
mkdir -p build/libk
i686-elf-gcc -c libk/string.c -o build/libk/string.o $(CFLAGS)
i686-elf-gcc -c libk/stdio.c -o build/libk/stdio.o $(CFLAGS)
i686-elf-gcc -c libk/stdlib.c -o build/libk/stdlib.o $(CFLAGS)
i686-elf-gcc -c libk/stack_protector.c -o build/libk/stack_protector.o $(CFLAGS)
i686-elf-gcc -c libk/asm.c -o build/libk/asm.o $(CFLAGS)
nasm arch/i386/boot.asm -f elf32 -o build/boot.o
nasm arch/i386/crti.asm -f elf32 -o build/crti.o
nasm arch/i386/crtn.asm -f elf32 -o build/crtn.o
i686-elf-gcc -c vga.c -o build/vga.o $(CFLAGS)
i686-elf-gcc -c idt.c -o build/idt.o $(CFLAGS)
i686-elf-gcc -c kernel.c -o build/kernel.o $(CFLAGS)
i686-elf-gcc -c early_kernel.c -o build/early_kernel.o $(CFLAGS)
i686-elf-gcc -T linker.ld -o $(KERNELFILE) $(CFLAGS) -nostdlib $(OBJFILES) -lgcc
run:
qemu-system-i386 -kernel $(KERNELFILE) -serial stdio -S -s
run-grub:
mkdir -p build/isodir
mkdir -p build/isodir/boot
mkdir -p build/isodir/boot/grub
cp build/orion.bin build/isodir/boot/orion.kernel
echo 'menuentry "orion" { ' > build/isodir/boot/grub/grub.cfg
echo ' multiboot /boot/orion.kernel' >> build/isodir/boot/grub/grub.cfg
echo '}' >> build/isodir/boot/grub/grub.cfg
grub-mkrescue -o build/orion.iso build/isodir
qemu-system-i386 -cdrom build/orion.iso -serial stdio -S -s

228
src/arch/i386/boot.asm Normal file
View File

@ -0,0 +1,228 @@
MBALIGN equ 1 << 0
MEMINFO equ 1 << 1
FLAGS equ MBALIGN | MEMINFO
MAGIC equ 0x1BADB002
CHECKSUM equ -(MAGIC + FLAGS)
section .multiboot
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
section .text
global boot
boot:
cli
lgdt [gdt_pointer]
mov eax, cr0
or eax,0x1 ; set protected mode bit
mov cr0, eax
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp CODE_SEG:_start
gdt_start:
dq 0x0
gdt_code:
dw 0xFFFF
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
gdt_data:
dw 0xFFFF
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
gdt_pointer:
dw gdt_end - gdt_start
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
times 510 - ($-$$) db 0
dw 0xaa55
section .text
global _start
_start:
mov esp, stack_top
extern early_kernel_main
call early_kernel_main
extern _init
call _init
extern kernel_main
call kernel_main
cli
hang: hlt
jmp hang
global idt_flush
idt_flush:
lidt [eax]
ret
times 1024-($-$$) db 0
%macro isr_err_stub 1
isr_stub_%+%1:
cli
push %1
jmp isr_wrapper
%endmacro
%macro isr_no_err_stub 1
isr_stub_%+%1:
cli
push 0x0
push %1
jmp isr_wrapper
%endmacro
%macro irq 2
global irq%1
irq%1:
push 0x0
push %2
jmp irq_wrapper
%endmacro
extern isr_handler
isr_no_err_stub 0
isr_no_err_stub 1
isr_no_err_stub 2
isr_no_err_stub 3
isr_no_err_stub 4
isr_no_err_stub 5
isr_no_err_stub 6
isr_no_err_stub 7
isr_err_stub 8
isr_no_err_stub 9
isr_err_stub 10
isr_err_stub 11
isr_err_stub 12
isr_err_stub 13
isr_err_stub 14
isr_no_err_stub 15
isr_no_err_stub 16
isr_err_stub 17
isr_no_err_stub 18
isr_no_err_stub 19
isr_no_err_stub 20
isr_no_err_stub 21
isr_no_err_stub 22
isr_no_err_stub 23
isr_no_err_stub 24
isr_no_err_stub 25
isr_no_err_stub 26
isr_no_err_stub 27
isr_no_err_stub 28
isr_no_err_stub 29
isr_err_stub 30
isr_no_err_stub 31
global isr_stub_table
isr_stub_table:
%assign i 0
%rep 32
dd isr_stub_%+i ; use DQ instead if targeting 64-bit
%assign i i+1
%endrep
extern irq_handler
irq 0, 32
irq 1, 33
irq 2, 34
irq 3, 35
irq 4, 36
irq 5, 37
irq 6, 38
irq 7, 39
irq 8, 40
irq 9, 41
irq 10, 42
irq 11, 43
irq 12, 44
irq 13, 45
irq 14, 46
irq 15, 47
global irq_stub_table
irq_stub_table:
%assign i 0
%rep 15
dd irq%+i ; use DQ instead if targeting 64-bit
%assign i i+1
%endrep
global isr_wrapper
isr_wrapper:
pusha
mov ax, ds
push eax
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call isr_handler
pop eax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 0x8
sti
iret
global irq_wrapper
irq_wrapper:
pusha
mov ax, ds
push eax
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call irq_handler
pop ebx
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa
add esp, 0x8
iret
section .bss
align 16
stack_bottom:
resb 16384 ; 16 KiB
stack_top:

11
src/arch/i386/crti.asm Normal file
View File

@ -0,0 +1,11 @@
section .init
global _init
_init:
push ebp
mov ebp, esp
section .fini
global _fini
_fini:
push ebp
mov ebp, esp

7
src/arch/i386/crtn.asm Normal file
View File

@ -0,0 +1,7 @@
section .init
pop ebp
ret
section .fini
pop ebp
ret

9
src/early_kernel.c Normal file
View File

@ -0,0 +1,9 @@
#include <idt.h>
#include <vga.h>
#include <stdio.h>
#include <stdlib.h>
void early_kernel_main() {
terminal_initialize();
idt_init();
}

79
src/idt.c Normal file
View File

@ -0,0 +1,79 @@
#include <idt.h>
#include <asm.h>
#include <stdio.h>
static idt_entry_t idt[256]; // Create an array of IDT entries; aligned for performance
static idtr_t idtr;
isr_t interrupt_handlers[256];
void isr_handler(registers_t regs) {
if (interrupt_handlers[regs.int_no] != 0) {
isr_t handler = interrupt_handlers[regs.int_no];
handler(regs);
} else {
kprintf("Unhandled interrupt: %d\n", regs.int_no);
}
}
void irq_handler(registers_t regs) {
if (regs.int_no >= 40) {
outb(0xA0, 0x20);
}
outb(0x20, 0x20);
if (interrupt_handlers[regs.int_no] != 0) {
isr_t handler = interrupt_handlers[regs.int_no];
handler(regs);
} else {
kprintf("Unhandled IRQ interrupt: %d\n", regs.int_no);
}
}
void register_interrupt_handler(uint8_t n, isr_t handler) {
interrupt_handlers[n] = handler;
}
void idt_set_gate(uint8_t num, uint32_t base, uint8_t flags) {
idt[num].isr_low = base & 0xFFFF;
idt[num].isr_high = (base >> 16) & 0xFFFF;
idt[num].kernel_cs = 0x08;
idt[num].reserved = 0;
idt[num].attributes = flags;
}
extern void* isr_stub_table[];
extern void* irq_stub_table[];
void idt_init() {
idtr.base = (uintptr_t)&idt;
idtr.limit = (uint16_t)sizeof(idt_entry_t) * 256 - 1;
for (uint8_t vector = 0; vector < 32; vector++) {
idt_set_gate(vector, (uint32_t)isr_stub_table[vector], 0x8E);
}
extern void idt_flush(uint32_t);
idt_flush((uint32_t)&idtr);
// Remap the irq table.
outb(0x20, 0x11);
outb(0xA0, 0x11);
outb(0x21, 0x20);
outb(0xA1, 0x28);
outb(0x21, 0x04);
outb(0xA1, 0x02);
outb(0x21, 0x01);
outb(0xA1, 0x01);
outb(0x21, 0x0);
outb(0xA1, 0x0);
for (uint8_t vector = 32; vector < 48; vector++) {
idt_set_gate(vector, (uint32_t)irq_stub_table[vector], 0x8E);
}
outb(0x21,0xfc);
outb(0xa1,0xff);
__asm__("sti");
}

50
src/include/idt.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef IDT_H
#define IDT_H
#include <stdint.h>
#define IRQ0 32
#define IRQ1 33
#define IRQ2 34
#define IRQ3 35
#define IRQ4 36
#define IRQ5 37
#define IRQ6 38
#define IRQ7 39
#define IRQ8 40
#define IRQ9 41
#define IRQ10 42
#define IRQ11 43
#define IRQ12 44
#define IRQ13 45
#define IRQ14 46
#define IRQ15 47
typedef struct {
uint16_t isr_low; // The lower 16 bits of the ISR's address
uint16_t kernel_cs; // The GDT segment selector that the CPU will load into CS before calling the ISR
uint8_t reserved; // Set to zero
uint8_t attributes; // Type and attributes; see the IDT page
uint16_t isr_high; // The higher 16 bits of the ISR's address
} __attribute__((packed)) idt_entry_t;
__attribute__((aligned(0x10)))
typedef struct {
uint16_t limit;
uint32_t base;
} __attribute__((packed)) idtr_t;
typedef struct registers {
uint32_t ds; // Data segment selector
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha.
uint32_t int_no, err_code; // Interrupt number and error code (if applicable)
uint32_t eip, cs, eflags, useresp, ss; // Pushed by the processor automatically.
} registers_t;
typedef void (*isr_t)(registers_t);
void register_interrupt_handler(uint8_t n, isr_t handler);
void idt_init();
#endif

38
src/include/vga.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef VGA_H
#define VGA_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/* Hardware text mode color constants. */
enum vga_color {
VGA_COLOR_BLACK = 0,
VGA_COLOR_BLUE = 1,
VGA_COLOR_GREEN = 2,
VGA_COLOR_CYAN = 3,
VGA_COLOR_RED = 4,
VGA_COLOR_MAGENTA = 5,
VGA_COLOR_BROWN = 6,
VGA_COLOR_LIGHT_GREY = 7,
VGA_COLOR_DARK_GREY = 8,
VGA_COLOR_LIGHT_BLUE = 9,
VGA_COLOR_LIGHT_GREEN = 10,
VGA_COLOR_LIGHT_CYAN = 11,
VGA_COLOR_LIGHT_RED = 12,
VGA_COLOR_LIGHT_MAGENTA = 13,
VGA_COLOR_LIGHT_BROWN = 14,
VGA_COLOR_WHITE = 15,
};
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
void terminal_initialize(void);
void terminal_setcolor(uint8_t color);
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y);
void terminal_putchar(char c);
void terminal_write(const char* data, size_t size);
void terminal_writestring(const char* data);
#endif

7
src/kernel.c Normal file
View File

@ -0,0 +1,7 @@
#include <vga.h>
#include <stdio.h>
#include <stdlib.h>
void kernel_main() {
kprintf("Hello, World!\n");
}

13
src/libk/asm.c Normal file
View File

@ -0,0 +1,13 @@
#include <asm.h>
inline void outb(uint16_t port, uint8_t val) {
__asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port));
}
inline int inb(int port) {
uint8_t ret;
__asm__ volatile ( "inb %1, %0"
: "=a"(ret)
: "Nd"(port) );
return ret;
}

9
src/libk/include/asm.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef _ASM_H
#define _ASM_H
#include <stdint.h>
void outb(uint16_t port, uint8_t val);
int inb(int port);
#endif

17
src/libk/include/stdio.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef _STDIO_H
#define _STDIO_H
#include <vga.h>
#include <stdarg.h>
int kputchar(int c);
int kputs(const char *s);
int kvsnprintf(char *restrict s, size_t n, const char *restrict format, va_list ap);
int ksnprintf(char *restrict s, size_t n, const char *restrict format, ...);
int kvsprintf(char *restrict s, const char *restrict format, va_list ap);
int ksprintf(char *restrict s, const char *restrict format, ...);
int kvprintf(const char *restrict format, va_list ap);
int kprintf(const char *restrict format, ...);
#endif

View File

@ -0,0 +1,6 @@
#ifndef _STDLIB_H
#define _STDLIB_H
void abort(void);
#endif

12
src/libk/include/string.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef _STRING_H
#define _STRING_H
#include <stddef.h>
size_t strlen(const char *s);
void *memmove(void *dest, const void *src, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);
void *memset(void *s, int c, size_t n);
void *memcpy(void *restrict dest, const void *restrict src, size_t n);
#endif

View File

@ -0,0 +1,20 @@
#include <stdint.h>
#include <stdlib.h>
#if UINT32_MAX == UINTPTR_MAX
#define STACK_CHK_GUARD 0xe2dee396
#else
#define STACK_CHK_GUARD 0x595e9fbd94fda766
#endif
uintptr_t __stack_chk_guard = STACK_CHK_GUARD;
// __attribute__((noreturn)) // FIXME
void __stack_chk_fail(void)
{
#if __STDC_HOSTED__
abort();
#elif __is_myos_kernel
panic("Stack smashing detected");
#endif
}

156
src/libk/stdio.c Normal file
View File

@ -0,0 +1,156 @@
#include <stdio.h>
#include <string.h>
int kputchar(int c) {
terminal_putchar(c);
return (unsigned char)c;
}
int kputs(const char *s) {
int i = 0;
while(s[i] != '\0') {
kputchar(s[i]);
i++;
}
kputchar('\n');
return 1; // FIXME
}
int kvsnprintf(char *restrict s, size_t n, const char *restrict format, va_list ap) {
char *str;
int value;
size_t size = 0;
for (size_t i = 0; i < strlen(format) && size < n-1; i++) {
if (format[i] == '%') {
i++;
switch(format[i]) {
case 'c':
s[size] = (char) va_arg(ap, int);
size++;
break;
case 's':
str = va_arg(ap, char *);
for (size_t j = 0; j < strlen(str); j++) {
s[size] = str[j];
size++;
}
break;
case 'd':
value = va_arg(ap, int);
if (value == 0) {
s[size] = '0';
size++;
} if (value < 0) {
s[size] = '-';
value *= (-1);
size++;
}
long long power = 1;
while (value > power)
power *= 10;
if (power >= 10)
power /= 10;
while (value != 0) {
int digit = (int)(value / power);
s[size] = '0' + digit;
size++;
if (digit != 0)
value = value - digit * power;
if (power != 1)
power /= 10;
}
break;
case 'x':
value = va_arg(ap, int);
if (value == 0) {
s[size] = '0';
size++;
}
power = 1;
while (value > power)
power *= 16;
if (power >= 16)
power /= 16;
while (value != 0) {
int digit = (int)(value / power);
if (digit < 10)
s[size] = '0' + digit;
else if (digit == 10)
s[size] = 'a';
else if (digit == 11)
s[size] = 'b';
else if (digit == 12)
s[size] = 'c';
else if (digit == 13)
s[size] = 'd';
else if (digit == 14)
s[size] = 'e';
else if (digit == 15)
s[size] = 'f';
size++;
if (digit != 0)
value = value - digit * power;
if (power != 1)
power /= 16;
}
break;
}
} else {
s[size] = format[i];
size++;
}
}
s[size] = '\0';
return size;
}
int ksnprintf(char *restrict s, size_t n, const char *restrict format, ...) {
int size;
va_list ap;
va_start(ap, format);
size = kvsnprintf(s, n, format, ap);
va_end(ap);
return size;
}
int kvsprintf(char *restrict s, const char *restrict format, va_list ap) {
size_t n = strlen(s);
return kvsnprintf(s, n, format, ap);
}
int ksprintf(char *restrict s, const char *restrict format, ...) {
int size;
va_list ap;
va_start(ap, format);
size = kvsprintf(s, format, ap);
va_end(ap);
return size;
}
int kvprintf(const char *restrict format, va_list ap) {
char s[1024] = ""; // FIXME
int size = kvsprintf(s, format, ap);
int i = 0;
while (i < size) {
kputchar(s[i]);
i++;
}
return size;
}
int kprintf(const char *restrict format, ...) {
int size;
va_list ap;
va_start(ap, format);
size = kvprintf(format, ap);
va_end(ap);
return size;
}

15
src/libk/stdlib.c Normal file
View File

@ -0,0 +1,15 @@
#include <stdio.h>
#include <stdlib.h>
__attribute__((__noreturn__))
void abort(void) {
#if defined(__is_libk)
// TODO: Add proper kernel panic.
kprintf("kernel: panic: abort()\n");
#else
// TODO: Abnormally terminate the process as if by SIGABRT.
kprintf("abort()\n");
#endif
while (1) { }
__builtin_unreachable();
}

64
src/libk/string.c Normal file
View File

@ -0,0 +1,64 @@
#include <string.h>
size_t strlen(const char *s) {
int i = 0;
while(s[i] != '\0') {
i++;
}
return i;
}
void *memmove(void *dest, const void *src, size_t n) {
unsigned char* chardest = dest;
const unsigned char* charsrc = src;
if (chardest == charsrc) {
return (void*)chardest;
} else if (chardest > charsrc) {
for (size_t i = n; i > sizeof(charsrc); i--)
chardest[i] = charsrc[i];
} else {
for (int i = 0; i < chardest - charsrc; i++)
chardest[i] = charsrc[i];
}
memcpy(chardest, charsrc, n);
return (void*)chardest;
}
int memcmp(const void *s1, const void *s2, size_t n) {
int status = sizeof(s1) - sizeof(s2);
if (status > 0 || status < 0) {
return status;
} else {
const unsigned char* first = s1;
const unsigned char* second = s2;
for (size_t i = 0; i < n; i++) {
if (first[i] != second[i])
return first[i] - second[i];
}
return 0;
}
}
void *memset(void *s, int c, size_t n) {
unsigned char uc = c;
unsigned char* ss = s;
for (size_t i = 0; i < n; i++)
ss[i] = uc;
return (void*)ss;
}
void *memcpy(void *restrict dest, const void *restrict src, size_t n) {
size_t i;
unsigned char* chardest = dest;
const unsigned char* charsrc = src;
for (i = 0; i < n && charsrc[i] != '\0'; i++) {
chardest[i] = charsrc[i];
}
for (; i < n; i++) {
chardest[i] = '\0';
}
return (void*)chardest;
}

43
src/linker.ld Normal file
View File

@ -0,0 +1,43 @@
/* The bootloader will look at this image and start execution at the symbol
designated as the entry point. */
ENTRY(_start)
/* Tell where the various sections of the object files will be put in the final
kernel image. */
SECTIONS
{
/* Begin putting sections at 1 MiB, a conventional place for kernels to be
loaded at by the bootloader. */
. = 1M;
/* First put the multiboot header, as it is required to be put very early
early in the image or the bootloader won't recognize the file format.
Next we'll put the .text section. */
.text BLOCK(4K) : ALIGN(4K)
{
*(.multiboot)
*(.text)
}
/* Read-only data. */
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
/* Read-write data (initialized) */
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
/* Read-write data (uninitialized) and stack */
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
/* The compiler may produce other sections, by default it will put them in
a segment with the same name. Simply add stuff here as needed. */
}

96
src/vga.c Normal file
View File

@ -0,0 +1,96 @@
/*
From OSDev Wiki:
IMPORTANT NOTE: the VGA text mode (as well as the BIOS) is deprecated
on newer machines, and UEFI only supports pixel buffers. For forward
compatibility you might want to start with that. Ask GRUB to set up a
framebuffer using appropriate Multiboot flags or call VESA VBE yourself. Unlike
VGA text mode, a framebuffer has pixels, so you have to draw each glyph
yourself.
*/
#include <vga.h>
#include <string.h>
size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t* terminal_buffer;
static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) {
return fg | bg << 4;
}
static inline uint16_t vga_entry(unsigned char uc, int color) {
return (uint16_t) uc | (uint16_t) color << 8;
}
void terminal_initialize(void) {
terminal_row = 0;
terminal_column = 0;
terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
terminal_buffer = (uint16_t*) 0xB8000;
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 terminal_setcolor(uint8_t color) {
terminal_color = color;
}
void terminal_putentryat(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 terminal_scroll() {
if (terminal_row >= 25) {
int i;
for (i = 0; i < 24*80; i++)
terminal_buffer[i] = terminal_buffer[i+80];
for (i = 24*80; i < 25*80; i++)
terminal_buffer[i] = ' ';
terminal_row = 24;
}
}
void terminal_delete_last_line() {
int x, *ptr;
for(x = 0; x < VGA_WIDTH * 2; x++) {
ptr = (int*)0xB8000 + (VGA_WIDTH * 2) * (VGA_HEIGHT - 1) + x;
*ptr = 0;
}
}
void terminal_putchar(char c) {
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
if (c != '\n') {
if (++terminal_column == VGA_WIDTH) {
terminal_column = 0;
if (++terminal_row == VGA_HEIGHT) {
for(int line = 1; line <= VGA_HEIGHT - 1; line++)
terminal_scroll(line);
terminal_delete_last_line();
terminal_row = VGA_HEIGHT - 1;
}
}
} else {
terminal_row += 1;
terminal_column = 0;
}
terminal_scroll();
}
void terminal_write(const char* data, size_t size) {
for (size_t i = 0; i < size; i++)
terminal_putchar(data[i]);
}
void terminal_writestring(const char* data) {
terminal_write(data, strlen(data));
}