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:
parent
26dd32345f
commit
d6465ade55
8 changed files with 109 additions and 92 deletions
18
include/io.h
18
include/io.h
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,24 @@
|
||||||
#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,
|
||||||
|
|
@ -20,7 +27,8 @@ void generic_isr_handler(StateSnapshot_t* cpu_state) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
34
src/io.c
34
src/io.c
|
|
@ -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)
|
||||||
|
|
|
||||||
45
src/main.c
45
src/main.c
|
|
@ -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;
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
printf(outWriter, "Entry eax:%X\n", multiboot_magic);
|
//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(&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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
33
src/vga.c
33
src/vga.c
|
|
@ -1,21 +1,28 @@
|
||||||
#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() {
|
||||||
|
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 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
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue