Changed I/O to be more generic.

instead of stuffing all of the i/o stuff into the "kio" library, i've
decided to rework i/o to be a generic handler that takes in a function
pointer for putc, and does the rest of the logic onto that pointer.
That way, you can pass any function that can take in characters and move
them to buffers.
I'll do a writeup on this at some point to document it.
This commit is contained in:
lordtet 2025-06-29 01:13:43 -04:00
parent 55d5823bde
commit 4899877cec
6 changed files with 135 additions and 90 deletions

18
include/io.h Normal file
View file

@ -0,0 +1,18 @@
/* io.h
* In/Out
*
* Common library for handling user i/o. Aims to be mechanic agnostic, and instead
* focus on implementing generic functions to format and manipulate data.
*/
#ifndef IO_H
#define IO_H
#include <stdint.h>
int putc(int (*)(char), char);
int print(int (*)(char), const char*);
int println(int (*)(char), const char*);
int printhex(int (*)(char), uint32_t);
int prindec(int (*)(char), uint32_t out);
int printf(int (*)(char), const char* fmt, ...);
#endif

View file

@ -1,10 +1,10 @@
/* kio.h
* Kernel I/O
/* vga.h
* VGA
*
* Responsible for kernel related in/output, such as VGA and serial
* Responsible for VGA buffer control.
*/
#ifndef KIO_H
#define KIO_H
#ifndef VGA_H
#define VGA_H
#define VGA_GRID_COLS 80
#define VGA_GRID_ROWS 25
#include <stdint.h>
@ -31,12 +31,10 @@ extern uint16_t vga_attributes; // Black background, White foreground
//Clear the VGA buffer
void vga_clear();
//Put a character in the VGA buffer and move the cursor to the right by one
void vga_putc(char c);
void vga_set_attributes(uint8_t attributes);
void vga_print(const char* out);
void vga_println(const char* out);
void vga_printhex(uint32_t out);
void vga_prindec(uint32_t out);
void vga_printf(const char* fmt, ...);
//Write character c to cursor
//Implements IO generic ASCII output
int vga_out(char c);
#endif

View file

@ -1,21 +1,26 @@
#include "interrupt_handlers.h"
#include "kio.h"
#include "vga.h"
#include "io.h"
void generic_isr_handler(StateSnapshot_t* cpu_state) {
//We made it to C for our interrupt! For now, let's just print our interrupt to the screen.
vga_clear();
vga_printf("INTERRUPT TRIGGERED! Info below.\n");
vga_printf("Int_ID|ERR: %X|%X\n", cpu_state->interrupt_id, cpu_state->error_code);
vga_printf("EFLAGS|CS|EIP: %X|%X|%X\n", cpu_state->eflags, cpu_state->cs,cpu_state->eip);
vga_printf("GP Registers:\neax:%X\nebx:%X\necx:%X\nedx:%X\nesp:%X\nebp:%X\nesi:%X\nedi:%X\n",
cpu_state->eax,
cpu_state->ebx,
cpu_state->ecx,
cpu_state->edx,
cpu_state->esp,
cpu_state->ebp,
cpu_state->esi,
cpu_state->edi);
vga_printf("HALT!");
while(1);
switch(cpu_state->interrupt_id) {
default:
vga_clear();
printf(vga_out, "INTERRUPT TRIGGERED! Info below.\n");
printf(vga_out, "Int_ID|ERR: %X|%X\n", cpu_state->interrupt_id, cpu_state->error_code);
printf(vga_out, "EFLAGS|CS|EIP: %X|%X|%X\n", cpu_state->eflags, cpu_state->cs,cpu_state->eip);
printf(vga_out, "GP Registers:\neax:%X\nebx:%X\necx:%X\nedx:%X\nesp:%X\nebp:%X\nesi:%X\nedi:%X\n",
cpu_state->eax,
cpu_state->ebx,
cpu_state->ecx,
cpu_state->edx,
cpu_state->esp,
cpu_state->ebp,
cpu_state->esi,
cpu_state->edi);
printf(vga_out, "HALT!");
while(1);
}
}

75
src/io.c Normal file
View file

@ -0,0 +1,75 @@
#include "io.h"
#include "kttools.h"
#include <stdarg.h>
int printhex(int (*sendDataFnc)(char), uint32_t out) {
char buff[9];
i_to_str(out, buff, 9, 0x10);
return print(sendDataFnc, buff);
}
int printdec(int (*sendDataFnc)(char), uint32_t out) {
char buff[11];
i_to_str(out, buff, 11, 10);
return print(sendDataFnc, buff);
}
int putc(int (*sendDataFnc)(char), char out) {
return sendDataFnc(out);
}
int print(int (*sendDataFnc)(char), const char* out) {
int ret = 0;
for (int i = 0; out[i] != '\0' && !ret; i++)
ret = putc(sendDataFnc, out[i]);
return ret;
}
int println(int (*sendDataFnc)(char), const char* out) {
int ret = print(sendDataFnc, out);
if(ret)
return ret;
ret = print(sendDataFnc, "\n");
return ret;
}
/**
* @brief Print a format string to the outstream function.
*
* Print some format string to the output given in sendDataFnc. Supports a few format strings in C format.
*
* @param fmt: The format string to print
* @param ...: Values for the format specifiers
*
* @return No return value
*
*/
int printf(int (*sendDataFnc)(char), const char* fmt, ...) {
va_list args;
va_start(args, fmt);
int ret = 0;
while(*fmt) {
if(*fmt == '%') {
fmt++;
switch(*fmt) {
case 'X':
ret = print(sendDataFnc, "0x");
case 'x':
ret = printhex(sendDataFnc, va_arg(args,uint32_t));
break;
case 'd':
ret = printdec(sendDataFnc, va_arg(args,uint32_t));
break;
}
} else {
sendDataFnc(*fmt);
}
fmt++;
if(ret)
return ret;
}
return 0;
}

View file

@ -1,7 +1,8 @@
//Our own code, at this point...
//#include <stddef.h>
#include <stdint.h>
#include "kio.h"
#include "vga.h"
#include "io.h"
#include "kttools.h"
#include "kmultiboot.h"
#include "idt.h"
@ -12,22 +13,21 @@ void kern_main(uint32_t multiboot_magic, mb_info_t* multiboot_info)
//First interrupts.
setup_idt();
//wipe the screen
vga_clear();
//We're going to use this buffer as our 8char hex representation for reading mem
vga_printf("Entry eax:%X\n", multiboot_magic);
printf(vga_out, "Entry eax:%X\n", multiboot_magic);
if(multiboot_magic != 0x2BADB002) {
vga_println("Bootloader not multiboot1 compliant! Needed for mmap, etc. Can't work without it, kthxbye!");
println(vga_out, "Bootloader not multiboot1 compliant! Needed for mmap, etc. Can't work without it, kthxbye!");
return;
} else {
vga_println("Multiboot detected! Continuing...");
println(vga_out, "Multiboot detected! Continuing...");
}
vga_printf("MEM_LOWER:%X\n", multiboot_info->mem_lower);
vga_printf("MEM_UPPER:%X\n", multiboot_info->mem_upper);
printf(vga_out, "MEM_LOWER:%X\n", multiboot_info->mem_lower);
printf(vga_out, "MEM_UPPER:%X\n", multiboot_info->mem_upper);
}

View file

@ -1,4 +1,4 @@
#include "kio.h"
#include "vga.h"
#include "kttools.h"
#include <stddef.h>
@ -30,20 +30,21 @@ void vga_clear() {
}
}
void vga_putc(char c)
int vga_out(char c)
{
//Check for some freaky escape character first
if(c == '\n') {
cursor_col = 0;
//mod implements wraparound
cursor_row = (cursor_row + 1) % (VGA_GRID_ROWS-1);
return;
return 0;
}
//Calculate where in the vga buffer to put the character
const size_t index = (VGA_GRID_COLS * cursor_row) + cursor_col;
//VGA buffer cell consists of the first half attributes, second half character
vga_buffer[index] = vga_attributes | c;
cursor_col++;
return 0;
}
void vga_set_attributes(uint8_t attributes) {
@ -51,61 +52,9 @@ void vga_set_attributes(uint8_t attributes) {
}
void vga_print(const char* out) {
for (int i = 0; out[i] != '\0'; i++)
vga_putc(out[i]);
}
void vga_println(const char* out) {
vga_print(out);
vga_print("\n");
}
void vga_printhex(uint32_t out) {
char buff[9];
i_to_str(out, buff, 9, 0x10);
vga_print(buff);
}
void vga_printdec(uint32_t out) {
char buff[11];
i_to_str(out, buff, 11, 10);
vga_print(buff);
}
/**
* @brief Print a format string to the VGA output.
*
* Print some format string to the vga output cleanly. Supports a few format strings in C format.
*
* @param fmt: The format string to print
* @param ...: Values for the format specifiers
*
* @return No return value
*
*/
void vga_printf(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
while(*fmt) {
if(*fmt == '%') {
fmt++;
switch(*fmt) {
case 'X':
vga_print("0x");
case 'x':
vga_printhex(va_arg(args,uint32_t));
break;
case 'd':
vga_printdec(va_arg(args,uint32_t));
break;
}
} else {
vga_putc(*fmt);
}
fmt++;
}
}