Reworked I/O methodology

Instead of passing function pointers, we're passing structs with context
now. The idea is the same, I/O functions require some struct with a
function pointer and some generic memory that only the output device
cares about. I/O just calls it.

VGA has been reworked to accomodate this change, as well as default
outputs being created for low-overhead use (such as interrupt handlers).
This commit is contained in:
lordtet 2025-07-03 01:36:47 -04:00
parent 26dd32345f
commit d6465ade55
8 changed files with 109 additions and 92 deletions

View file

@ -8,11 +8,17 @@
#define IO_H #define IO_H
#include <stdint.h> #include <stdint.h>
int putc(int (*)(char), char); typedef struct char_writer_s char_writer_t;
int print(int (*)(char), const char*); struct char_writer_s {
int println(int (*)(char), const char*); int (*putChar)(void* ctx, char out);
int printhex(int (*)(char), uint32_t); void* ctx;
int prindec(int (*)(char), uint32_t out); };
int printf(int (*)(char), const char* fmt, ...);
int putc(char_writer_t*, char);
int print(char_writer_t*, const char*);
int println(char_writer_t*, const char*);
int printhex(char_writer_t*, uint32_t);
int prindec(char_writer_t*, uint32_t out);
int printf(char_writer_t*, const char* fmt, ...);
#endif #endif

View file

@ -6,12 +6,15 @@
#ifndef SERIAL_H #ifndef SERIAL_H
#define SERIAL_H #define SERIAL_H
#include "asm.h" #include "asm.h"
#include "io.h"
#include <stdint.h> #include <stdint.h>
typedef struct SerialState_s { typedef struct SerialState_s {
uint16_t port; uint16_t port;
} SerialState_t; } SerialState_t;
extern char_writer_t* default_COM;
/** /**
* @brief Initialize a serial port for communication * @brief Initialize a serial port for communication
* @param port The serial port number to initialize * @param port The serial port number to initialize

View file

@ -7,34 +7,37 @@
#define VGA_H #define VGA_H
#define VGA_GRID_COLS 80 #define VGA_GRID_COLS 80
#define VGA_GRID_ROWS 25 #define VGA_GRID_ROWS 25
#define DEFAULT_ATTRIBUTES 0x0F00
#include <stdint.h> #include <stdint.h>
#include <stdarg.h> #include <stdarg.h>
#include "io.h"
/* /*
* CONSTANTS AND VARIABLES * CONSTANTS AND VARIABLES
*/ */
//Information on the VGA buffer //Information on the VGA buffer
extern volatile uint16_t* const vga_buffer; extern volatile uint16_t* const vga_buffer;
//grid is top left origin. This is our cursor! typedef struct vga_ctx_s {
extern int cursor_col; int cursor_col;
extern int cursor_row; int cursor_row;
extern uint16_t vga_attributes; // Black background, White foreground uint16_t attributes;
} vga_ctx_t;
//grid is top left origin. This is our cursor!
extern uint16_t vga_attributes; // Black background, White foreground
extern char_writer_t* default_vga;
/* /*
* Functions * Functions
*/ */
//Clear the VGA buffer //Clear the VGA buffer, context optional
void vga_clear_ctx(vga_ctx_t* ctx);
void vga_clear(); void vga_clear();
//Put a character in the VGA buffer and move the cursor to the right by one
void vga_set_attributes(uint8_t attributes);
//Write character c to cursor //Write character c to cursor
//Implements IO generic ASCII output //Implements IO generic ASCII output
int vga_out(char c); int vga_out(void* ctx, char c);
#endif #endif

View file

@ -1,26 +1,34 @@
#include "interrupt_handlers.h" #include "interrupt_handlers.h"
#include "vga.h" #include "vga.h"
#include "io.h" #include "io.h"
#include "serial.h"
void generic_isr_handler(StateSnapshot_t* cpu_state) { 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. //We made it to C for our interrupt! For now, let's just print our interrupt to the screen.
//What's cookin for outputs?
char_writer_t* out = 0;
if(default_COM) {
out = default_COM;
} else if(default_vga) {
out = default_vga;
vga_clear_ctx(default_vga->ctx);
}
switch(cpu_state->interrupt_id) { switch(cpu_state->interrupt_id) {
default: default:
vga_clear(); if(out){
printf(vga_out, "INTERRUPT TRIGGERED! Info below.\n"); printf(out, "INTERRUPT TRIGGERED! Info below.\n");
printf(vga_out, "Int_ID|ERR: %X|%X\n", cpu_state->interrupt_id, cpu_state->error_code); printf(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(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", printf(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->eax,
cpu_state->ebx, cpu_state->ebx,
cpu_state->ecx, cpu_state->ecx,
cpu_state->edx, cpu_state->edx,
cpu_state->esp, cpu_state->esp,
cpu_state->ebp, cpu_state->ebp,
cpu_state->esi, cpu_state->esi,
cpu_state->edi); cpu_state->edi);
printf(vga_out, "HALT!"); printf(out, "HALT!");
while(1); while(1);
}
} }
} }

View file

@ -2,39 +2,39 @@
#include "kttools.h" #include "kttools.h"
#include <stdarg.h> #include <stdarg.h>
int printhex(int (*sendDataFnc)(char), uint32_t out) { int printhex(char_writer_t* writer, uint32_t out) {
char buff[9]; char buff[9];
i_to_str(out, buff, 9, 0x10); i_to_str(out, buff, 9, 0x10);
return print(sendDataFnc, buff); return print(writer, buff);
} }
int printdec(int (*sendDataFnc)(char), uint32_t out) { int printdec(char_writer_t* writer, uint32_t out) {
char buff[11]; char buff[11];
i_to_str(out, buff, 11, 10); i_to_str(out, buff, 11, 10);
return print(sendDataFnc, buff); return print(writer, buff);
} }
int putc(int (*sendDataFnc)(char), char out) { int putc(char_writer_t* writer, char out) {
return sendDataFnc(out); return writer->putChar(writer->ctx, out);
} }
int print(int (*sendDataFnc)(char), const char* out) { int print(char_writer_t* writer, const char* out) {
int ret = 0; int ret = 0;
for (int i = 0; out[i] != '\0' && !ret; i++) for (int i = 0; out[i] != '\0' && !ret; i++)
ret = putc(sendDataFnc, out[i]); ret = putc(writer, out[i]);
return ret; return ret;
} }
int println(int (*sendDataFnc)(char), const char* out) { int println(char_writer_t* writer, const char* out) {
int ret = print(sendDataFnc, out); int ret = print(writer, out);
if(ret) if(ret)
return ret; return ret;
ret = print(sendDataFnc, "\n"); ret = print(writer, "\n");
return ret; return ret;
} }
/** /**
* @brief Print a format string to the outstream function. * @brief Print a format string to the outwriter function.
* *
* Print some format string to the output given in sendDataFnc. Supports a few format strings in C format. * Print some format string to the output given in sendDataFnc. Supports a few format strings in C format.
* *
@ -45,7 +45,7 @@ int println(int (*sendDataFnc)(char), const char* out) {
* *
*/ */
int printf(int (*sendDataFnc)(char), const char* fmt, ...) { int printf(char_writer_t* writer, const char* fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
int ret = 0; int ret = 0;
@ -54,17 +54,17 @@ int printf(int (*sendDataFnc)(char), const char* fmt, ...) {
fmt++; fmt++;
switch(*fmt) { switch(*fmt) {
case 'X': case 'X':
ret = print(sendDataFnc, "0x"); ret = print(writer, "0x");
case 'x': case 'x':
ret = printhex(sendDataFnc, va_arg(args,uint32_t)); ret = printhex(writer, va_arg(args,uint32_t));
break; break;
case 'd': case 'd':
ret = printdec(sendDataFnc, va_arg(args,uint32_t)); ret = printdec(writer, va_arg(args,uint32_t));
break; break;
} }
} else { } else {
sendDataFnc(*fmt); ret = putc(writer,*fmt);
} }
fmt++; fmt++;
if(ret) if(ret)

View file

@ -1,8 +1,4 @@
//Our own code, at this point... //Our own code, at this point...
//Output table:
//0: VGA
//1: COM1
#define OUTPUT_TYPE 1
#include <stdint.h> #include <stdint.h>
#include "vga.h" #include "vga.h"
#include "io.h" #include "io.h"
@ -16,35 +12,30 @@ void kern_main(uint32_t multiboot_magic, mb_info_t* multiboot_info)
//First interrupts. //First interrupts.
setup_idt(); setup_idt();
//Let's pick an output schema. //Let's get some output schemas ready.
int (*outWriter)(char) = 0;
int comstatus = 1234; //VGA prep
switch(OUTPUT_TYPE) { vga_ctx_t vga_ctx;
case 1: vga_ctx.attributes = DEFAULT_ATTRIBUTES;
comstatus = serial_init(COM1); vga_ctx.cursor_row = 0;
if(comstatus) { vga_ctx.cursor_col = 0;
//Fail... char_writer_t vga_writer;
break; vga_writer.ctx = &vga_ctx;
} vga_writer.putChar = vga_out;
outWriter = serial_send8; default_vga = &vga_writer;
break; vga_clear_ctx(&vga_ctx);
}
//VGA is selected or the selected option failed, falling back
if(outWriter == 0){
outWriter = vga_out;
vga_clear();
}
printf(outWriter, "Entry eax:%X\n", multiboot_magic);
printf(&vga_writer, "Entry eax:%X\n", multiboot_magic);
if(multiboot_magic != 0x2BADB002) { if(multiboot_magic != 0x2BADB002) {
println(outWriter, writerState, "Bootloader not multiboot1 compliant! Needed for mmap, etc. Can't work without it, kthxbye!"); println(&vga_writer, "Bootloader not multiboot1 compliant! Needed for mmap, etc. Can't work without it, kthxbye!");
return; return;
} else { } else {
println(outWriter, writerState, "Multiboot detected! Continuing..."); println(&vga_writer, "Multiboot detected! Continuing...");
} }
printf(outWriter, writerState, "MEM_LOWER:%X\n", multiboot_info->mem_lower); printf(&vga_writer, "MEM_LOWER:%X\n", multiboot_info->mem_lower);
printf(outWriter, writerState"MEM_UPPER:%X\n", multiboot_info->mem_upper); printf(&vga_writer, "MEM_UPPER:%X\n", multiboot_info->mem_upper);
} }

View file

@ -1,6 +1,7 @@
#include "serial.h" #include "serial.h"
#include <stdint.h> #include <stdint.h>
char_writer_t* default_COM = 0;
int serial_init(uint16_t port) { int serial_init(uint16_t port) {
//disable interrupts when messing with it //disable interrupts when messing with it

View file

@ -1,22 +1,29 @@
#include "vga.h" #include "vga.h"
#include "kttools.h" #include "kttools.h"
#include <stddef.h> #include <stddef.h>
/* /*
* VARS * VARS
*/ */
int cursor_col = 0;
int cursor_row = 0;
volatile uint16_t* const vga_buffer = (uint16_t*)0xB8000; volatile uint16_t* const vga_buffer = (uint16_t*)0xB8000;
uint16_t vga_attributes = 0x0F00; char_writer_t* default_vga = 0;
/* /*
* FUNCTIONS * FUNCTIONS
*/ */
void vga_clear() { void cga_clear() {
for (int col = 0; col < VGA_GRID_COLS; col ++) { vga_clear_ctx(0);
}
void vga_clear_ctx(vga_ctx_t* ctx) {
uint16_t vga_attributes = DEFAULT_ATTRIBUTES;
if(ctx) {
ctx->cursor_col = 0;
ctx->cursor_row = 0;
vga_attributes = ctx->attributes;
}
for (int col = 0; col < VGA_GRID_COLS; col ++) {
for (int row = 0; row < VGA_GRID_ROWS; row ++) { for (int row = 0; row < VGA_GRID_ROWS; row ++) {
//works out to iterating every cell //works out to iterating every cell
const size_t index = (VGA_GRID_COLS * row) + col; const size_t index = (VGA_GRID_COLS * row) + col;
@ -30,26 +37,24 @@ void vga_clear() {
} }
} }
int vga_out(char c) int vga_out(void* ctx, char c)
{ {
vga_ctx_t* vga_ctx = (vga_ctx_t*) ctx;
//Check for some freaky escape character first //Check for some freaky escape character first
if(c == '\n') { if(c == '\n') {
cursor_col = 0; vga_ctx->cursor_col = 0;
//mod implements wraparound //mod implements wraparound
cursor_row = (cursor_row + 1) % (VGA_GRID_ROWS-1); vga_ctx->cursor_row = (vga_ctx->cursor_row + 1) % (VGA_GRID_ROWS-1);
return 0; return 0;
} }
//Calculate where in the vga buffer to put the character //Calculate where in the vga buffer to put the character
const size_t index = (VGA_GRID_COLS * cursor_row) + cursor_col; const size_t index = (VGA_GRID_COLS * vga_ctx->cursor_row) + vga_ctx->cursor_col;
//VGA buffer cell consists of the first half attributes, second half character //VGA buffer cell consists of the first half attributes, second half character
vga_buffer[index] = vga_attributes | c; vga_buffer[index] = vga_ctx->attributes | c;
cursor_col++; vga_ctx->cursor_col++;
return 0; return 0;
} }
void vga_set_attributes(uint8_t attributes) {
vga_attributes = ((uint16_t)attributes) << 8;
}