Initial commit (not working)
This commit is contained in:
commit
9caf9dcd62
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
.cproject
|
||||
.project
|
||||
*.o
|
||||
*.bin
|
||||
*.iso
|
45
Makefile
Normal file
45
Makefile
Normal file
|
@ -0,0 +1,45 @@
|
|||
PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
|
||||
ifeq ($(BUILD_MODE),debug)
|
||||
CFLAGS += -g
|
||||
endif
|
||||
|
||||
TARGET = i686-elf
|
||||
CC = $(TARGET)-gcc
|
||||
STRIP = $(TARGET)-strip
|
||||
NASM = nasm
|
||||
QEMU = qemu-system-i386
|
||||
|
||||
CFLAGS += -std=gnu17 -ffreestanding -O2 -Wall -Wextra
|
||||
LDFLAGS += -ffreestanding -O2 -nostdlib -lgcc
|
||||
NASMFLAGS += -felf32
|
||||
|
||||
BIN = isodir/boot/hhhos.bin
|
||||
ISO = HhhOS.iso
|
||||
|
||||
OBJS = boot.o kernel.o string.o terminal.o
|
||||
|
||||
.PHONY: all qemu clean
|
||||
|
||||
all: qemu
|
||||
|
||||
qemu: $(ISO)
|
||||
$(QEMU) -cdrom $(ISO)
|
||||
|
||||
$(ISO): $(BIN)
|
||||
mkdir -p isodir/boot/grub
|
||||
cp $(PROJECT_ROOT)grub.cfg isodir/boot/grub/grub.cfg
|
||||
grub-mkrescue -o $(ISO) isodir
|
||||
|
||||
$(BIN): $(OBJS)
|
||||
mkdir -p isodir/boot
|
||||
$(CC) -T $(PROJECT_ROOT)linker.ld $(LDFLAGS) -o $@ $^
|
||||
|
||||
%.o: $(PROJECT_ROOT)%.c
|
||||
$(CC) -c $(CFLAGS) -I$(PROJECT_ROOT)include -o $@ $<
|
||||
|
||||
%.o: $(PROJECT_ROOT)%.asm
|
||||
$(NASM) $(NASMFLAGS) -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -fr HhhOS $(OBJS)
|
113
boot.asm
Normal file
113
boot.asm
Normal file
|
@ -0,0 +1,113 @@
|
|||
; Declare constants for the multiboot header.
|
||||
MBALIGN equ 1 << 0 ; align loaded modules on page boundaries
|
||||
MEMINFO equ 1 << 1 ; provide memory map
|
||||
FLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field
|
||||
MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
|
||||
CHECKSUM equ -(MAGIC + FLAGS) ; checksum of above, to prove we are multiboot
|
||||
|
||||
; Declare a multiboot header that marks the program as a kernel. These are magic
|
||||
; values that are documented in the multiboot standard. The bootloader will
|
||||
; search for this signature in the first 8 KiB of the kernel file, aligned at a
|
||||
; 32-bit boundary. The signature is in its own section so the header can be
|
||||
; forced to be within the first 8 KiB of the kernel file.
|
||||
section .multiboot
|
||||
align 4
|
||||
dd MAGIC
|
||||
dd FLAGS
|
||||
dd CHECKSUM
|
||||
|
||||
; The multiboot standard does not define the value of the stack pointer register
|
||||
; (esp) and it is up to the kernel to provide a stack. This allocates room for a
|
||||
; small stack by creating a symbol at the bottom of it, then allocating 16384
|
||||
; bytes for it, and finally creating a symbol at the top. The stack grows
|
||||
; downwards on x86. The stack is in its own section so it can be marked nobits,
|
||||
; which means the kernel file is smaller because it does not contain an
|
||||
; uninitialized stack. The stack on x86 must be 16-byte aligned according to the
|
||||
; System V ABI standard and de-facto extensions. The compiler will assume the
|
||||
; stack is properly aligned and failure to align the stack will result in
|
||||
; undefined behavior.
|
||||
section .bss
|
||||
align 16
|
||||
stack_bottom:
|
||||
resb 16384 ; 16 KiB
|
||||
stack_top:
|
||||
|
||||
section .data
|
||||
gdt:
|
||||
gdt_null:
|
||||
dq 0
|
||||
gdt_code:
|
||||
dw 0FFFFh
|
||||
dw 0
|
||||
db 0
|
||||
db 10011010b
|
||||
db 11001111b
|
||||
db 0
|
||||
gdt_data:
|
||||
dw 0FFFFh
|
||||
dw 0
|
||||
db 0
|
||||
db 10010010b
|
||||
db 11001111b
|
||||
db 0
|
||||
gdt_size: equ $ - gdt
|
||||
gdt_desc:
|
||||
dw gdt_size - 1
|
||||
dd gdt
|
||||
|
||||
; The linker script specifies _start as the entry point to the kernel and the
|
||||
; bootloader will jump to this position once the kernel has been loaded. It
|
||||
; doesn't make sense to return from this function as the bootloader is gone.
|
||||
; Declare _start as a function symbol with the given symbol size.
|
||||
section .text
|
||||
global _start:function (_start.end - _start)
|
||||
_start:
|
||||
; The bootloader has loaded us into 32-bit protected mode on a x86
|
||||
; machine. Interrupts are disabled. Paging is disabled. The processor
|
||||
; state is as defined in the multiboot standard. The kernel has full
|
||||
; control of the CPU. The kernel can only make use of hardware features
|
||||
; and any code it provides as part of itself. There's no printf
|
||||
; function, unless the kernel provides its own <stdio.h> header and a
|
||||
; printf implementation. There are no security restrictions, no
|
||||
; safeguards, no debugging mechanisms, only what the kernel provides
|
||||
; itself. It has absolute and complete power over the machine.
|
||||
|
||||
; To set up a stack, we set the esp register to point to the top of our
|
||||
; stack (as it grows downwards on x86 systems). This is necessarily done
|
||||
; in assembly as languages such as C cannot function without a stack.
|
||||
mov esp, stack_top
|
||||
|
||||
; This is a good place to initialize crucial processor state before the
|
||||
; high-level kernel is entered. It's best to minimize the early
|
||||
; environment where crucial features are offline. Note that the
|
||||
; processor is not fully initialized yet: Features such as floating
|
||||
; point instructions and instruction set extensions are not initialized
|
||||
; yet. The GDT should be loaded here. Paging should be enabled here.
|
||||
; C++ features such as global constructors and exceptions will require
|
||||
; runtime support to work as well.
|
||||
lgdt [gdt_desc]
|
||||
|
||||
; Enter the high-level kernel. The ABI requires the stack is 16-byte
|
||||
; aligned at the time of the call instruction (which afterwards pushes
|
||||
; the return pointer of size 4 bytes). The stack was originally 16-byte
|
||||
; aligned above and we've since pushed a multiple of 16 bytes to the
|
||||
; stack since (pushed 0 bytes so far) and the alignment is thus
|
||||
; preserved and the call is well defined.
|
||||
; note, that if you are building on Windows, C functions may have "_" prefix in assembly: _kernel_main
|
||||
extern kernel_main
|
||||
call kernel_main
|
||||
|
||||
; If the system has nothing more to do, put the computer into an
|
||||
; infinite loop. To do that:
|
||||
; 1) Disable interrupts with cli (clear interrupt enable in eflags).
|
||||
; They are already disabled by the bootloader, so this is not needed.
|
||||
; Mind that you might later enable interrupts and return from
|
||||
; kernel_main (which is sort of nonsensical to do).
|
||||
; 2) Wait for the next interrupt to arrive with hlt (halt instruction).
|
||||
; Since they are disabled, this will lock up the computer.
|
||||
; 3) Jump to the hlt instruction if it ever wakes up due to a
|
||||
; non-maskable interrupt occurring or due to system management mode.
|
||||
cli
|
||||
.hang: hlt
|
||||
jmp .hang
|
||||
.end:
|
3
build/default/isodir/boot/grub/grub.cfg
Normal file
3
build/default/isodir/boot/grub/grub.cfg
Normal file
|
@ -0,0 +1,3 @@
|
|||
menuentry "HhhOS" {
|
||||
multiboot /boot/hhhos.bin
|
||||
}
|
48
gdt.c
Normal file
48
gdt.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
const struct gdt_entry {
|
||||
size_t limit_low : 16;
|
||||
size_t base_low : 24;
|
||||
// attribute byte split into bitfields
|
||||
size_t accessed : 1;
|
||||
size_t read_write : 1; // readable for code, writable for data
|
||||
size_t conforming_expand_down : 1; // conforming for code, expand down for data
|
||||
size_t code : 1; // 1 for code, 0 for data
|
||||
size_t always_1 : 1; // should be 1 for everything but TSS and LDT
|
||||
size_t DPL : 2; // privilege level
|
||||
size_t present : 1;
|
||||
// and now into granularity
|
||||
size_t limit_high : 4;
|
||||
size_t available : 1;
|
||||
size_t always_0 : 1; // should always be 0
|
||||
size_t big : 1; // 32bit opcodes for code, uint32_t stack for data
|
||||
size_t gran : 1; // 1 to use 4k page addressing, 0 for byte addressing
|
||||
size_t base_high : 8;
|
||||
} __attribute__((packed));
|
||||
|
||||
void initGDT() {
|
||||
struct gdt_entry *code;
|
||||
code->limit_low=0xFFFF;
|
||||
code->present = 1;
|
||||
code->DPL = 0;
|
||||
code->always_1 = 1;
|
||||
code->code = 1;
|
||||
code->conforming_expand_down = 0;
|
||||
code->read_write = 1;
|
||||
code->accessed = 0;
|
||||
|
||||
code->base_high = 0;
|
||||
code->gran = 1;
|
||||
code->big = 0;
|
||||
code->always_0 = 0;
|
||||
code->available = 1;
|
||||
code->limit_high = 0xF;
|
||||
|
||||
struct gdt_entry *data = *code;
|
||||
data->code = 0;
|
||||
|
||||
struct gdt_entry *tss = 0;
|
||||
|
||||
|
||||
}
|
60
include/inline.h
Normal file
60
include/inline.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
#ifndef INLINE_H_
|
||||
#define INLINE_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline void outb(uint16_t const port, uint8_t const val) {
|
||||
asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
|
||||
}
|
||||
|
||||
static inline void outw(uint16_t const port, uint16_t const val) {
|
||||
asm volatile ( "outw %0, %1" : : "a"(val), "Nd"(port) );
|
||||
}
|
||||
|
||||
static inline void outl(uint16_t const port, uint32_t const val) {
|
||||
asm volatile ( "outl %0, %1" : : "a"(val), "Nd"(port) );
|
||||
}
|
||||
|
||||
static inline uint8_t inb(uint16_t const port) {
|
||||
uint8_t ret;
|
||||
asm volatile ( "inb %1, %0" : "=a"(ret) : "Nd"(port) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline uint16_t inw(uint16_t const port) {
|
||||
uint16_t ret;
|
||||
asm volatile ( "inw %1, %0" : "=a"(ret) : "Nd"(port) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline uint32_t inl(uint16_t const port) {
|
||||
uint32_t ret;
|
||||
asm volatile ( "inl %1, %0" : "=a"(ret) : "Nd"(port) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void io_wait(void) {
|
||||
/* TODO: This is probably fragile. */
|
||||
asm volatile ( "jmp 1f\n\t"
|
||||
"1:jmp 2f\n\t"
|
||||
"2:" );
|
||||
}
|
||||
|
||||
static inline bool are_interrupts_enabled() {
|
||||
unsigned long flags;
|
||||
asm volatile ( "pushf\n\t"
|
||||
"pop %0"
|
||||
: "=g"(flags) );
|
||||
return flags & (1 << 9);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INLINE_H_ */
|
22
include/string.h
Normal file
22
include/string.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef _STRING_H
|
||||
#define _STRING_H 1
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int memcmp(const void* aptr, const void* bptr, size_t size);
|
||||
void* memcpy(void* __restrict dstptr, const void* __restrict srcptr, size_t size);
|
||||
void* memmove(void* dstptr, const void* srcptr, size_t size);
|
||||
void* memset(void* bufptr, int value, size_t size);
|
||||
size_t strlen(const char* str);
|
||||
char* strcpy(char* dst, const char* src);
|
||||
char* strcat(char* dst, const char* src);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _STRING_H */
|
74
include/terminal.h
Normal file
74
include/terminal.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
#ifndef _TERMINAL_H
|
||||
#define _TERMINAL_H 1
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <inline.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 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,
|
||||
};
|
||||
|
||||
static inline uint8_t vga_entry_color(enum vga_color const fg, enum vga_color const bg) {
|
||||
return fg | bg << 4;
|
||||
}
|
||||
|
||||
static inline uint16_t vga_entry(unsigned char const uc, uint8_t const color) {
|
||||
return (uint16_t) uc | (uint16_t) color << 8;
|
||||
}
|
||||
|
||||
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 terminal_clear(void);
|
||||
void terminal_initialize(void);
|
||||
void terminal_setcolor(uint8_t color);
|
||||
void terminal_clearline(size_t line);
|
||||
void terminal_clearlines(size_t from, size_t to);
|
||||
void terminal_updatecursor(void);
|
||||
void terminal_scrollup(size_t lines);
|
||||
void terminal_checknewline(void);
|
||||
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);
|
||||
|
||||
static inline void terminal_writestring(const char* data) {
|
||||
terminal_write(data, strlen(data));
|
||||
}
|
||||
|
||||
static inline void terminal_writeline(const char* data) {
|
||||
terminal_writestring(data);
|
||||
terminal_putchar('\n');
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TERMINAL_H */
|
33
kernel.c
Normal file
33
kernel.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <terminal.h>
|
||||
|
||||
/* Check if the compiler thinks you are targeting the wrong operating system. */
|
||||
#if defined(__linux__)
|
||||
#error "You are not using a cross-compiler, you will most certainly run into trouble"
|
||||
#endif
|
||||
|
||||
/* This tutorial will only work for the 32-bit ix86 targets. */
|
||||
#if !defined(__i386__)
|
||||
#error "This tutorial needs to be compiled with a ix86-elf compiler"
|
||||
#endif
|
||||
|
||||
static size_t rand_next = 1;
|
||||
|
||||
int rand(void) {
|
||||
rand_next = rand_next * 1103515245 + 12345;
|
||||
return (size_t)(rand_next/65536) % 32768;
|
||||
}
|
||||
|
||||
void srand(size_t seed) {
|
||||
rand_next = seed;
|
||||
}
|
||||
|
||||
void kernel_main() {
|
||||
terminal_initialize();
|
||||
|
||||
while(true)
|
||||
terminal_writestring("gnhijsrg");
|
||||
}
|
43
linker.ld
Normal file
43
linker.ld
Normal 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. */
|
||||
}
|
57
string.c
Normal file
57
string.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include <string.h>
|
||||
|
||||
int memcmp(const void* aptr, const void* bptr, size_t size) {
|
||||
const unsigned char* a = (const unsigned char*) aptr;
|
||||
const unsigned char* b = (const unsigned char*) bptr;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (a[i] < b[i])
|
||||
return -1;
|
||||
else if (b[i] < a[i])
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* memcpy(void* restrict dstptr, const void* restrict srcptr, size_t size) {
|
||||
unsigned char* dst = (unsigned char*) dstptr;
|
||||
const unsigned char* src = (const unsigned char*) srcptr;
|
||||
for (size_t i = 0; i < size; i++)
|
||||
dst[i] = src[i];
|
||||
return dstptr;
|
||||
}
|
||||
|
||||
void* memmove(void* dstptr, const void* srcptr, size_t size) {
|
||||
unsigned char* dst = (unsigned char*) dstptr;
|
||||
const unsigned char* src = (const unsigned char*) srcptr;
|
||||
if (dst < src) {
|
||||
for (size_t i = 0; i < size; i++)
|
||||
dst[i] = src[i];
|
||||
} else {
|
||||
for (size_t i = size; i != 0; i--)
|
||||
dst[i-1] = src[i-1];
|
||||
}
|
||||
return dstptr;
|
||||
}
|
||||
|
||||
void* memset(void* bufptr, int value, size_t size) {
|
||||
unsigned char* buf = (unsigned char*) bufptr;
|
||||
for (size_t i = 0; i < size; i++)
|
||||
buf[i] = (unsigned char) value;
|
||||
return bufptr;
|
||||
}
|
||||
|
||||
size_t strlen(const char* str) {
|
||||
size_t len = 0;
|
||||
while (str[len])
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
char* strcpy(char* dst, const char* src) {
|
||||
return memcpy(dst, src, strlen(src) + 1);
|
||||
}
|
||||
|
||||
char* strcat(char* dst, const char* src) {
|
||||
strcpy(dst + strlen(dst), src);
|
||||
return dst;
|
||||
}
|
99
terminal.c
Normal file
99
terminal.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
#include <terminal.h>
|
||||
|
||||
void terminal_clear(void) {
|
||||
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_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;
|
||||
terminal_clear();
|
||||
}
|
||||
|
||||
void terminal_setcolor(uint8_t color) {
|
||||
terminal_color = color;
|
||||
}
|
||||
|
||||
void terminal_clearline(size_t line) {
|
||||
for (size_t x = 0; x < VGA_WIDTH; x++) {
|
||||
const size_t index = line * VGA_WIDTH + x;
|
||||
terminal_buffer[index] = vga_entry(' ', terminal_color);
|
||||
}
|
||||
}
|
||||
|
||||
void terminal_clearlines(size_t from, size_t to) {
|
||||
for (size_t y = from; y <= to; 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_updatecursor(void) {
|
||||
size_t temp = terminal_row * VGA_WIDTH + terminal_column;
|
||||
outb(0x3D4, 14);
|
||||
outb(0x3D5, temp >> 8);
|
||||
outb(0x3D4, 15);
|
||||
outb(0x3D5, temp);
|
||||
}
|
||||
|
||||
void terminal_scrollup(size_t lines) {
|
||||
terminal_clearlines(0, lines - 1);
|
||||
for (size_t index = 0; index < VGA_WIDTH * (VGA_HEIGHT - 1) * 2; index++) {
|
||||
terminal_buffer[index] = terminal_buffer[index + VGA_WIDTH * 2 * lines];
|
||||
}
|
||||
terminal_clearlines(VGA_HEIGHT - 1 - lines, VGA_HEIGHT - 1);
|
||||
if ((terminal_row - lines) < 0) {
|
||||
terminal_row = 0;
|
||||
terminal_column = 0;
|
||||
} else {
|
||||
terminal_row -= lines;
|
||||
}
|
||||
terminal_updatecursor();
|
||||
}
|
||||
|
||||
void terminal_checknewline(void) {
|
||||
if (terminal_row >= VGA_HEIGHT - 1) {
|
||||
terminal_scrollup(1);
|
||||
}
|
||||
}
|
||||
|
||||
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) {
|
||||
terminal_buffer[y * VGA_WIDTH + x] = vga_entry(c, color);
|
||||
}
|
||||
|
||||
void terminal_putchar(char c) {
|
||||
switch (c) {
|
||||
case '\r':
|
||||
terminal_column = 0;
|
||||
break;
|
||||
case '\n':
|
||||
terminal_column = 0;
|
||||
terminal_row++;
|
||||
break;
|
||||
default:
|
||||
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
|
||||
terminal_column++;
|
||||
}
|
||||
|
||||
if (terminal_column >= VGA_WIDTH) {
|
||||
terminal_column = 0;
|
||||
terminal_row++;
|
||||
}
|
||||
|
||||
terminal_updatecursor();
|
||||
terminal_checknewline();
|
||||
}
|
||||
|
||||
void terminal_write(const char* data, size_t size) {
|
||||
for (size_t i = 0; i < size; i++)
|
||||
terminal_putchar(data[i]);
|
||||
}
|
Loading…
Reference in a new issue