From d6465ade55f0357cde59e858bceb5a9c9c4dc1d6 Mon Sep 17 00:00:00 2001 From: lordtet Date: Thu, 3 Jul 2025 01:36:47 -0400 Subject: [PATCH] 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). --- include/io.h | 18 ++++++++++------ include/serial.h | 3 +++ include/vga.h | 23 +++++++++++--------- src/interrupt_handlers.c | 42 ++++++++++++++++++++++--------------- src/io.c | 34 +++++++++++++++--------------- src/main.c | 45 ++++++++++++++++------------------------ src/serial.c | 1 + src/vga.c | 35 +++++++++++++++++-------------- 8 files changed, 109 insertions(+), 92 deletions(-) diff --git a/include/io.h b/include/io.h index a58a26e..d6890f3 100644 --- a/include/io.h +++ b/include/io.h @@ -8,11 +8,17 @@ #define IO_H #include -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, ...); +typedef struct char_writer_s char_writer_t; +struct char_writer_s { + int (*putChar)(void* ctx, char out); + void* ctx; +}; + +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 diff --git a/include/serial.h b/include/serial.h index bc3665e..6b044ad 100644 --- a/include/serial.h +++ b/include/serial.h @@ -6,12 +6,15 @@ #ifndef SERIAL_H #define SERIAL_H #include "asm.h" +#include "io.h" #include typedef struct SerialState_s { uint16_t port; } SerialState_t; +extern char_writer_t* default_COM; + /** * @brief Initialize a serial port for communication * @param port The serial port number to initialize diff --git a/include/vga.h b/include/vga.h index 198a226..139ec66 100644 --- a/include/vga.h +++ b/include/vga.h @@ -7,34 +7,37 @@ #define VGA_H #define VGA_GRID_COLS 80 #define VGA_GRID_ROWS 25 +#define DEFAULT_ATTRIBUTES 0x0F00 #include #include - +#include "io.h" /* * CONSTANTS AND VARIABLES */ //Information on the VGA buffer - extern volatile uint16_t* const vga_buffer; -//grid is top left origin. This is our cursor! -extern int cursor_col; -extern int cursor_row; -extern uint16_t vga_attributes; // Black background, White foreground +typedef struct vga_ctx_s { + int cursor_col; + int cursor_row; + 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 */ -//Clear the VGA buffer +//Clear the VGA buffer, context optional +void vga_clear_ctx(vga_ctx_t* ctx); 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 //Implements IO generic ASCII output -int vga_out(char c); +int vga_out(void* ctx, char c); #endif diff --git a/src/interrupt_handlers.c b/src/interrupt_handlers.c index c928384..67a45a5 100644 --- a/src/interrupt_handlers.c +++ b/src/interrupt_handlers.c @@ -1,26 +1,34 @@ #include "interrupt_handlers.h" #include "vga.h" #include "io.h" +#include "serial.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. + //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) { - - 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); + if(out){ + printf(out, "INTERRUPT TRIGGERED! Info below.\n"); + printf(out, "Int_ID|ERR: %X|%X\n", cpu_state->interrupt_id, cpu_state->error_code); + printf(out, "EFLAGS|CS|EIP: %X|%X|%X\n", cpu_state->eflags, cpu_state->cs,cpu_state->eip); + 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->ebx, + cpu_state->ecx, + cpu_state->edx, + cpu_state->esp, + cpu_state->ebp, + cpu_state->esi, + cpu_state->edi); + printf(out, "HALT!"); + while(1); + } } } diff --git a/src/io.c b/src/io.c index 8cb32cd..acd07ed 100644 --- a/src/io.c +++ b/src/io.c @@ -2,39 +2,39 @@ #include "kttools.h" #include -int printhex(int (*sendDataFnc)(char), uint32_t out) { +int printhex(char_writer_t* writer, uint32_t out) { char buff[9]; 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]; i_to_str(out, buff, 11, 10); - return print(sendDataFnc, buff); + return print(writer, buff); } -int putc(int (*sendDataFnc)(char), char out) { - return sendDataFnc(out); +int putc(char_writer_t* writer, char 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; for (int i = 0; out[i] != '\0' && !ret; i++) - ret = putc(sendDataFnc, out[i]); + ret = putc(writer, out[i]); return ret; } -int println(int (*sendDataFnc)(char), const char* out) { - int ret = print(sendDataFnc, out); +int println(char_writer_t* writer, const char* out) { + int ret = print(writer, out); if(ret) return ret; - ret = print(sendDataFnc, "\n"); + ret = print(writer, "\n"); 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. * @@ -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_start(args, fmt); int ret = 0; @@ -54,17 +54,17 @@ int printf(int (*sendDataFnc)(char), const char* fmt, ...) { fmt++; switch(*fmt) { case 'X': - ret = print(sendDataFnc, "0x"); + ret = print(writer, "0x"); case 'x': - ret = printhex(sendDataFnc, va_arg(args,uint32_t)); + ret = printhex(writer, va_arg(args,uint32_t)); break; case 'd': - ret = printdec(sendDataFnc, va_arg(args,uint32_t)); + ret = printdec(writer, va_arg(args,uint32_t)); break; } } else { - sendDataFnc(*fmt); + ret = putc(writer,*fmt); } fmt++; if(ret) diff --git a/src/main.c b/src/main.c index 3a63404..7ca8b88 100644 --- a/src/main.c +++ b/src/main.c @@ -1,8 +1,4 @@ //Our own code, at this point... -//Output table: -//0: VGA -//1: COM1 -#define OUTPUT_TYPE 1 #include #include "vga.h" #include "io.h" @@ -16,35 +12,30 @@ void kern_main(uint32_t multiboot_magic, mb_info_t* multiboot_info) //First interrupts. setup_idt(); - //Let's pick an output schema. - int (*outWriter)(char) = 0; - int comstatus = 1234; - switch(OUTPUT_TYPE) { - case 1: - comstatus = serial_init(COM1); - if(comstatus) { - //Fail... - break; - } - outWriter = serial_send8; - break; - } - //VGA is selected or the selected option failed, falling back - if(outWriter == 0){ - outWriter = vga_out; - vga_clear(); - } + //Let's get some output schemas ready. + + //VGA prep + vga_ctx_t vga_ctx; + vga_ctx.attributes = DEFAULT_ATTRIBUTES; + vga_ctx.cursor_row = 0; + vga_ctx.cursor_col = 0; + char_writer_t vga_writer; + vga_writer.ctx = &vga_ctx; + vga_writer.putChar = vga_out; + default_vga = &vga_writer; + vga_clear_ctx(&vga_ctx); - printf(outWriter, "Entry eax:%X\n", multiboot_magic); + + printf(&vga_writer, "Entry eax:%X\n", multiboot_magic); 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; } 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(outWriter, writerState"MEM_UPPER:%X\n", multiboot_info->mem_upper); + printf(&vga_writer, "MEM_LOWER:%X\n", multiboot_info->mem_lower); + printf(&vga_writer, "MEM_UPPER:%X\n", multiboot_info->mem_upper); } diff --git a/src/serial.c b/src/serial.c index b5a5969..3d21ee1 100644 --- a/src/serial.c +++ b/src/serial.c @@ -1,6 +1,7 @@ #include "serial.h" #include +char_writer_t* default_COM = 0; int serial_init(uint16_t port) { //disable interrupts when messing with it diff --git a/src/vga.c b/src/vga.c index 22cf66c..247a872 100644 --- a/src/vga.c +++ b/src/vga.c @@ -1,22 +1,29 @@ #include "vga.h" #include "kttools.h" #include - /* * VARS */ -int cursor_col = 0; -int cursor_row = 0; volatile uint16_t* const vga_buffer = (uint16_t*)0xB8000; -uint16_t vga_attributes = 0x0F00; +char_writer_t* default_vga = 0; /* * FUNCTIONS */ -void vga_clear() { - for (int col = 0; col < VGA_GRID_COLS; col ++) { +void cga_clear() { + 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 ++) { //works out to iterating every cell 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 if(c == '\n') { - cursor_col = 0; + vga_ctx->cursor_col = 0; //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; } //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[index] = vga_attributes | c; - cursor_col++; + vga_buffer[index] = vga_ctx->attributes | c; + vga_ctx->cursor_col++; return 0; } -void vga_set_attributes(uint8_t attributes) { - vga_attributes = ((uint16_t)attributes) << 8; -}