Espresso 0.0.0e
This commit is contained in:
@ -44,7 +44,6 @@ int32_t fat32_init(int32_t drive)
|
||||
|
||||
memcpy(&bpb, sector, sizeof(bpb));
|
||||
|
||||
|
||||
fat_start_lba = bpb.reserved_sector_count;
|
||||
cluster_heap_lba = fat_start_lba + bpb.num_fats * bpb.fat_size_32;
|
||||
current_directory_cluster = bpb.root_cluster;
|
||||
|
@ -1,34 +1,52 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <gdt.h>
|
||||
#include <drivers/gdt_ec.h>
|
||||
|
||||
#define GDT_ENTRIES 5
|
||||
uint64_t gdt[GDT_ENTRIES];
|
||||
|
||||
#define GDT_ENTRIES 3
|
||||
struct {
|
||||
uint16_t limit;
|
||||
uint32_t base;
|
||||
} __attribute__((packed)) gp;
|
||||
|
||||
struct gdt_entry gdt[GDT_ENTRIES];
|
||||
struct gdt_ptr gp;
|
||||
|
||||
extern void gdt_flush(uint32_t);
|
||||
|
||||
void gdt_install()
|
||||
void gdt_install(bool prnt_gdt)
|
||||
{
|
||||
gp.limit = (sizeof(struct gdt_entry) * GDT_ENTRIES) - 1;
|
||||
gp.base = (uint32_t)&gdt;
|
||||
create_descriptor(0, 0, 0, 0, prnt_gdt); // Null
|
||||
create_descriptor(1, 0, 0x000FFFFF, GDT_CODE_PL0, prnt_gdt); // Kernel code
|
||||
create_descriptor(2, 0, 0x000FFFFF, GDT_DATA_PL0, prnt_gdt); // Kernel data
|
||||
create_descriptor(3, 0, 0x000FFFFF, GDT_CODE_PL3, prnt_gdt); // User code
|
||||
create_descriptor(4, 0, 0x000FFFFF, GDT_DATA_PL3, prnt_gdt); // User data
|
||||
|
||||
gdt_set_entry(0, 0, 0, 0, 0); /* Null segment */
|
||||
gdt_set_entry(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); /* Code segment */
|
||||
gdt_set_entry(2, 0, 0xFFFFFFFF, 0x92, 0xCF); /* Data segment */
|
||||
gp.limit = sizeof(gdt) - 1;
|
||||
gp.base = (uint32_t)&gdt;
|
||||
|
||||
gdt_flush((uint32_t)&gp);
|
||||
}
|
||||
|
||||
void gdt_set_entry(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran)
|
||||
void create_descriptor(int index, uint32_t base, uint32_t limit, uint16_t flag, bool prnt_gdt)
|
||||
{
|
||||
gdt[num].base_low = (base & 0xFFFF);
|
||||
gdt[num].base_middle = (base >> 16) & 0xFF;
|
||||
gdt[num].base_high = (base >> 24) & 0xFF;
|
||||
uint64_t descriptor;
|
||||
|
||||
gdt[num].limit_low = (limit & 0xFFFF);
|
||||
gdt[num].granularity = (limit >> 16) & 0x0F;
|
||||
descriptor = limit & 0x000F0000; // limit bits 19:16
|
||||
descriptor |= (flag << 8) & 0x00F0FF00; // flags and access
|
||||
descriptor |= (base >> 16) & 0x000000FF; // base bits 23:16
|
||||
descriptor |= base & 0xFF000000; // base bits 31:24
|
||||
|
||||
gdt[num].granularity |= (gran & 0xF0);
|
||||
gdt[num].access = access;
|
||||
descriptor <<= 32;
|
||||
|
||||
descriptor |= ((uint64_t)base << 16); // base bits 15:0
|
||||
descriptor |= (limit & 0x0000FFFF); // limit bits 15:0
|
||||
|
||||
gdt[index] = descriptor;
|
||||
|
||||
if (prnt_gdt)
|
||||
{
|
||||
printf("GDT[%d] = 0x%llX\n", index, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
|
0
drivers/gdt_ec.c
Normal file
0
drivers/gdt_ec.c
Normal file
228
drivers/idt.c
228
drivers/idt.c
@ -1,131 +1,141 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <port_io.h>
|
||||
|
||||
#include <idt.h>
|
||||
#include <drivers/idt.h>
|
||||
|
||||
#define IDT_ENTRIES 256
|
||||
|
||||
struct idt_entry idt[IDT_ENTRIES];
|
||||
struct idt_ptr idtp;
|
||||
#define IDT_MAX_DESCRIPTORS 256
|
||||
|
||||
extern void idt_load(uint32_t);
|
||||
#define PIC1_COMMAND 0x20
|
||||
#define PIC1_DATA 0x21
|
||||
#define PIC2_COMMAND 0xA0
|
||||
#define PIC2_DATA 0xA1
|
||||
|
||||
void idt_install(void)
|
||||
/*
|
||||
Most of the code is this file (and idt.h) are taken from the osdev wiki: https://wiki.osdev.org/Interrupts_Tutorial, though not all of it.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
typedef struct {
|
||||
uint16_t limit;
|
||||
uint32_t base;
|
||||
} __attribute__((packed)) idtr_t;
|
||||
|
||||
__attribute__((aligned(0x10)))
|
||||
|
||||
static idt_entry_t idt[256]; // Create an array of IDT entries; aligned for performance
|
||||
|
||||
static idtr_t idtr;
|
||||
|
||||
static bool vectors[IDT_MAX_DESCRIPTORS];
|
||||
|
||||
extern void* isr_stub_table[];
|
||||
|
||||
|
||||
__attribute__((noreturn))
|
||||
void exception_dispatcher(uint32_t int_no, uint32_t err_code)
|
||||
{
|
||||
idtp.limit = sizeof(struct idt_entry) * IDT_ENTRIES - 1;
|
||||
idtp.base = (uint32_t)&idt;
|
||||
switch (int_no)
|
||||
{
|
||||
case 0:
|
||||
printf("Divide by zero exception\n");
|
||||
break;
|
||||
case 13:
|
||||
printf("General Protection Fault: err=0x%x\n", err_code);
|
||||
break;
|
||||
case 14:
|
||||
{
|
||||
uint32_t cr2;
|
||||
__asm__ volatile ("mov %%cr2, %0" : "=r"(cr2));
|
||||
printf("Page Fault at address: 0x%x, err=0x%x\n", cr2, err_code);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("Unhandled exception #%u, err=0x%x\n", int_no, err_code);
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&idt, 0, sizeof(struct idt_entry) * IDT_ENTRIES);
|
||||
uint16_t cs, ds, es, ss;
|
||||
asm volatile ("mov %%cs, %0" : "=r"(cs));
|
||||
asm volatile ("mov %%ds, %0" : "=r"(ds));
|
||||
asm volatile ("mov %%es, %0" : "=r"(es));
|
||||
asm volatile ("mov %%ss, %0" : "=r"(ss));
|
||||
|
||||
/* Set entries for IRQs/ISRs here using `idt_set_entry(...)` */
|
||||
/* Example: idt_set_entry(0, (uint32_t)isr0, 0x08, 0x8E); */
|
||||
printf("CS=0x%04x DS=0x%04x ES=0x%04x SS=0x%04x\n", cs, ds, es, ss);
|
||||
|
||||
idt_load((uint32_t)&idtp);
|
||||
__asm__ volatile ("cli; hlt");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void idt_set_descriptor(uint8_t vector, void* isr, uint8_t flags) {
|
||||
idt_entry_t* descriptor = &idt[vector];
|
||||
|
||||
descriptor->isr_low = (uint32_t)isr & 0xFFFF;
|
||||
descriptor->kernel_cs = 0x08;
|
||||
descriptor->attributes = flags;
|
||||
descriptor->isr_high = (uint32_t)isr >> 16;
|
||||
descriptor->reserved = 0;
|
||||
}
|
||||
|
||||
void pic_remap(void)
|
||||
{
|
||||
outb(0x20, 0x11); /* Start init for master PIC */
|
||||
outb(0xA0, 0x11); /* Start init for slave PIC */
|
||||
outb(0x21, 0x20); /* Master PIC vector offset */
|
||||
outb(0xA1, 0x28); /* Slave PIC vector offset */
|
||||
outb(0x21, 0x04); /* Tell Master PIC about Slave PIC at IRQ2 */
|
||||
outb(0xA1, 0x02); /* Tell Slave PIC its cascade identity */
|
||||
outb(0x21, 0x01); /* 8086/88 mode */
|
||||
outb(0xA1, 0x01);
|
||||
outb(0x21, 0x0); /* Unmask master */
|
||||
outb(0xA1, 0x0); /* Unmask slave */
|
||||
uint8_t a1, a2;
|
||||
|
||||
// Save masks
|
||||
a1 = inb(PIC1_DATA);
|
||||
a2 = inb(PIC2_DATA);
|
||||
|
||||
// Start initialization sequence (in cascade mode)
|
||||
outb(PIC1_COMMAND, 0x11);
|
||||
outb(PIC2_COMMAND, 0x11);
|
||||
|
||||
// Set vector offset
|
||||
outb(PIC1_DATA, 0x20); // IRQs 0-7 mapped to IDT entries 0x20-0x27 (32–39)
|
||||
outb(PIC2_DATA, 0x28); // IRQs 8-15 mapped to IDT entries 0x28-0x2F (40–47)
|
||||
|
||||
// Tell Master PIC about Slave PIC at IRQ2 (0000 0100)
|
||||
outb(PIC1_DATA, 0x04);
|
||||
|
||||
// Tell Slave PIC its cascade identity (0000 0010)
|
||||
outb(PIC2_DATA, 0x02);
|
||||
|
||||
// Set 8086/88 mode
|
||||
outb(PIC1_DATA, 0x01);
|
||||
outb(PIC2_DATA, 0x01);
|
||||
|
||||
// Restore saved masks
|
||||
outb(PIC1_DATA, a1);
|
||||
outb(PIC2_DATA, a2);
|
||||
}
|
||||
|
||||
void idt_set_entry(int num, uint32_t base, uint16_t sel, uint8_t flags)
|
||||
void idt_init(void)
|
||||
{
|
||||
idt[num].base_low = base & 0xFFFF;
|
||||
idt[num].base_high = (base >> 16) & 0xFFFF;
|
||||
idtr.base = (uintptr_t)&idt[0];
|
||||
idtr.limit = (uint16_t)sizeof(idt_entry_t) * IDT_MAX_DESCRIPTORS - 1;
|
||||
|
||||
idt[num].sel = sel;
|
||||
idt[num].always0 = 0;
|
||||
idt[num].flags = flags;
|
||||
}
|
||||
|
||||
extern void IRQ0_handler(void);
|
||||
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);
|
||||
|
||||
extern void isr33(void);
|
||||
|
||||
extern void isr128(void);
|
||||
|
||||
|
||||
void idt_install_isrs(void)
|
||||
{
|
||||
idt_set_entry(0, (uint32_t)isr0, 0x08, 0x8E);
|
||||
idt_set_entry(1, (uint32_t)isr1, 0x08, 0x8E);
|
||||
idt_set_entry(2, (uint32_t)isr2, 0x08, 0x8E);
|
||||
idt_set_entry(3, (uint32_t)isr3, 0x08, 0x8E);
|
||||
idt_set_entry(4, (uint32_t)isr4, 0x08, 0x8E);
|
||||
idt_set_entry(5, (uint32_t)isr5, 0x08, 0x8E);
|
||||
idt_set_entry(6, (uint32_t)isr6, 0x08, 0x8E);
|
||||
idt_set_entry(7, (uint32_t)isr7, 0x08, 0x8E);
|
||||
idt_set_entry(8, (uint32_t)isr8, 0x08, 0x8E);
|
||||
idt_set_entry(9, (uint32_t)isr9, 0x08, 0x8E);
|
||||
idt_set_entry(10, (uint32_t)isr10, 0x08, 0x8E);
|
||||
idt_set_entry(11, (uint32_t)isr11, 0x08, 0x8E);
|
||||
idt_set_entry(12, (uint32_t)isr12, 0x08, 0x8E);
|
||||
idt_set_entry(13, (uint32_t)isr13, 0x08, 0x8E);
|
||||
idt_set_entry(14, (uint32_t)isr14, 0x08, 0x8E);
|
||||
idt_set_entry(15, (uint32_t)isr15, 0x08, 0x8E);
|
||||
idt_set_entry(16, (uint32_t)isr16, 0x08, 0x8E);
|
||||
idt_set_entry(17, (uint32_t)isr17, 0x08, 0x8E);
|
||||
idt_set_entry(18, (uint32_t)isr18, 0x08, 0x8E);
|
||||
idt_set_entry(19, (uint32_t)isr19, 0x08, 0x8E);
|
||||
idt_set_entry(20, (uint32_t)isr20, 0x08, 0x8E);
|
||||
idt_set_entry(21, (uint32_t)isr21, 0x08, 0x8E);
|
||||
idt_set_entry(22, (uint32_t)isr22, 0x08, 0x8E);
|
||||
idt_set_entry(23, (uint32_t)isr23, 0x08, 0x8E);
|
||||
idt_set_entry(24, (uint32_t)isr24, 0x08, 0x8E);
|
||||
idt_set_entry(25, (uint32_t)isr25, 0x08, 0x8E);
|
||||
idt_set_entry(26, (uint32_t)isr26, 0x08, 0x8E);
|
||||
idt_set_entry(27, (uint32_t)isr27, 0x08, 0x8E);
|
||||
idt_set_entry(28, (uint32_t)isr28, 0x08, 0x8E);
|
||||
idt_set_entry(29, (uint32_t)isr29, 0x08, 0x8E);
|
||||
idt_set_entry(30, (uint32_t)isr30, 0x08, 0x8E);
|
||||
idt_set_entry(31, (uint32_t)isr31, 0x08, 0x8E);
|
||||
for (uint8_t vector = 0; vector < 32; vector++)
|
||||
{
|
||||
idt_set_descriptor(vector, isr_stub_table[vector], 0x8E);
|
||||
vectors[vector] = true;
|
||||
}
|
||||
|
||||
idt_set_entry(33, (uint32_t)isr33, 0x08, 0x8E);
|
||||
extern void* irq_stub_table[];
|
||||
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
{
|
||||
idt_set_descriptor(32 + i, irq_stub_table[i], 0x8E);
|
||||
}
|
||||
|
||||
|
||||
__asm__ volatile ("lidt %0" : : "m"(idtr)); /* load the new IDT */
|
||||
__asm__ volatile ("sti"); /* set the interrupt flag */
|
||||
}
|
||||
|
||||
void idt_install_syscall(void)
|
||||
{
|
||||
idt_set_entry(128, (uint32_t)isr128, 0x08, 0xEE);
|
||||
}
|
||||
|
9
drivers/irq.c
Normal file
9
drivers/irq.c
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
#include <drivers/irq.h>
|
||||
|
||||
|
||||
void irq_handler(uint8_t irq_number)
|
||||
{
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
#include <port_io.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <isr.h>
|
||||
|
||||
|
||||
void (*irq_handlers[256])(isr_stack_t *r) = { 0 };
|
||||
|
||||
void register_interrupt_handler(uint8_t n, void (*handler)(isr_stack_t *))
|
||||
{
|
||||
irq_handlers[n] = handler;
|
||||
}
|
||||
|
||||
void isr_handler(isr_stack_t *r)
|
||||
{
|
||||
if (irq_handlers[r->int_no]) {
|
||||
irq_handlers[r->int_no](r);
|
||||
} else {
|
||||
printf("Unhandled interrupt: %d\n", r->int_no);
|
||||
}
|
||||
|
||||
|
||||
/* Send EOI to PIC */
|
||||
if (r->int_no >= 40)
|
||||
{
|
||||
outb(0xA0, 0x20);
|
||||
}
|
||||
if (r->int_no >= 32)
|
||||
{
|
||||
outb(0x20, 0x20);
|
||||
}
|
||||
}
|
||||
|
||||
void syscall_handler(regs_t *r)
|
||||
{
|
||||
switch (r->eax) {
|
||||
case 0: printf("Syscall: Hello world\n"); break;
|
||||
case 1: printf("Syscall: value = %d\n", r->ebx); break;
|
||||
default: printf("Unknown syscall %d\n", r->eax);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*#include <stdint.h>
|
||||
#include <port_io.h>
|
||||
#include <drivers/irq.h>
|
||||
|
||||
#define PIT_CHANNEL0 0x40
|
||||
#define PIT_COMMAND 0x43
|
||||
#define PIT_FREQUENCY 1193182
|
||||
|
||||
static uint32_t tick = 0;
|
||||
|
||||
void pit_callback(struct regs* r)
|
||||
{
|
||||
(void)r;
|
||||
tick++;
|
||||
}
|
||||
|
||||
void pit_init(uint32_t frequency)
|
||||
{
|
||||
uint32_t divisor = PIT_FREQUENCY / frequency;
|
||||
|
||||
outb(PIT_COMMAND, 0x36);
|
||||
outb(PIT_CHANNEL0, (uint8_t)(divisor & 0xFF));
|
||||
outb(PIT_CHANNEL0, (uint8_t)((divisor >> 8) & 0xFF));
|
||||
|
||||
register_interrupt_handler(32, pit_callback);
|
||||
}
|
||||
|
||||
void sleep(uint32_t milliseconds)
|
||||
{
|
||||
uint32_t target = tick + milliseconds;
|
||||
while (tick < target);
|
||||
}*/
|
||||
|
@ -5,94 +5,18 @@
|
||||
|
||||
|
||||
/*
|
||||
PS/2 Controller IO Ports
|
||||
PS/2 Controller IO Ports
|
||||
|
||||
The PS/2 Controller itself uses 2 IO ports (IO ports 0x60 and 0x64). Like many IO ports, reads and writes may access different internal registers.
|
||||
The PS/2 Controller itself uses 2 IO ports (IO ports 0x60 and 0x64). Like many IO ports, reads and writes may access different internal registers.
|
||||
|
||||
Historical note: The PC-XT PPI had used port 0x61 to reset the keyboard interrupt request signal (among other unrelated functions). Port 0x61 has no keyboard related functions on AT and PS/2 compatibles.
|
||||
IO Port Access Type Purpose
|
||||
0x60 Read/Write Data Port
|
||||
0x64 Read Status Register
|
||||
0x64 Write Command Register
|
||||
Historical note: The PC-XT PPI had used port 0x61 to reset the keyboard interrupt request signal (among other unrelated functions). Port 0x61 has no keyboard related functions on AT and PS/2 compatibles.
|
||||
IO Port Access Type Purpose
|
||||
0x60 Read/Write Data Port
|
||||
0x64 Read Status Register
|
||||
0x64 Write Command Register
|
||||
*/
|
||||
|
||||
#define PS2_DATA_PORT 0x60
|
||||
#define PS2_STATUS_PORT 0x64
|
||||
|
||||
#define KEYBOARD_IRQ 33
|
||||
|
||||
/*extern void register_interrupt_handler(int irq, void (*handler)(void));*/
|
||||
|
||||
static const char scancode_map[128] = {
|
||||
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=','\b', /* Backspace */
|
||||
'\t', /* Tab */
|
||||
'q','w','e','r','t','y','u','i','o','p','[',']','\n', /* Enter */
|
||||
0, /* Control */
|
||||
'a','s','d','f','g','h','j','k','l',';','\'','`',
|
||||
0, /* Left shift */
|
||||
'\\','z','x','c','v','b','n','m',',','.','/',
|
||||
0, /* Right shift */
|
||||
'*',
|
||||
0, /* Alt */
|
||||
' ', /* Spacebar */
|
||||
0, /* Caps lock */
|
||||
/* The rest are function and control keys */
|
||||
};
|
||||
|
||||
static bool shift_pressed = false;
|
||||
|
||||
void keyboard_handler(isr_stack_t* regs)
|
||||
{
|
||||
(void)regs; /* Only here to dismiss the "Unused parameter" warnings */
|
||||
printd("PS/2 Keyboard handler fired!\n");
|
||||
|
||||
uint8_t scancode = inb(PS2_DATA_PORT);
|
||||
|
||||
/* Handle shift press/release */
|
||||
if (scancode == 0x2A || scancode == 0x36)
|
||||
{
|
||||
shift_pressed = true;
|
||||
return;
|
||||
}
|
||||
else if (scancode == 0xAA || scancode == 0xB6)
|
||||
{
|
||||
shift_pressed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (scancode & 0x80)
|
||||
{
|
||||
/* Key release event, ignore for now */
|
||||
}
|
||||
else
|
||||
{
|
||||
char c = scancode_map[scancode];
|
||||
if (shift_pressed && c >= 'a' && c <= 'z')
|
||||
{
|
||||
c -= 32; /* Convert to uppercase */
|
||||
}
|
||||
|
||||
if (c)
|
||||
{
|
||||
/* Do something with the keypress (e.g., print or buffer) */
|
||||
printf("Char: %c", c);
|
||||
}
|
||||
}
|
||||
|
||||
outb(0x20, 0x20); /* Acknowledge PIC */
|
||||
}
|
||||
|
||||
void test_handler(isr_stack_t* r) {
|
||||
printf("IRQ1 fired!\n");
|
||||
}
|
||||
|
||||
|
||||
void keyboard_init(void)
|
||||
{
|
||||
outb(0x21, inb(0x21) & ~0x02);
|
||||
|
||||
register_interrupt_handler(KEYBOARD_IRQ, test_handler);
|
||||
|
||||
/* Enable keyboard (controller-specific, often optional if already working) */
|
||||
//outb(0x64, 0xAE);
|
||||
}
|
||||
|
21
drivers/timer.c
Normal file
21
drivers/timer.c
Normal file
@ -0,0 +1,21 @@
|
||||
/*#include <drivers/isr.h>
|
||||
#include <drivers/irq.h>
|
||||
|
||||
#include <drivers/timer.h>
|
||||
|
||||
volatile uint32_t tick_count = 0;
|
||||
|
||||
void irq0_handler(regs_t* regs)
|
||||
{
|
||||
tick_count++;
|
||||
}
|
||||
|
||||
void timer_sleep(uint32_t ms)
|
||||
{
|
||||
uint32_t target = tick_count + ms;
|
||||
while (tick_count < target)
|
||||
{
|
||||
asm volatile("hlt");
|
||||
}
|
||||
}
|
||||
*/
|
@ -151,7 +151,10 @@ void terminal_clearl(size_t num_lines)
|
||||
|
||||
while (terminal_row < num_lines)
|
||||
{
|
||||
terminal_writechar_r(' ');
|
||||
for (int32_t k = 0; k < (int32_t)VGA_WIDTH; k++)
|
||||
{
|
||||
terminal_putentryat((unsigned char)' ', terminal_getcolor(), (size_t)terminal_row, (size_t)k);
|
||||
}
|
||||
terminal_row -= 1;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user