Compare commits

...

2 commits

Author SHA1 Message Date
53198515c8 Temporary "default output" global, makefile changes.
Default output variable holds a generic writer, and any output that
isn't really sure where to output to can just output there. Set by the
system early in boot.

Also made some changes to the makefile to support arguments in the "make
run" or "make debug" subset of calls. For now, it's just VGA=1/0 or
SERIAL=1/0 to enable/disable VGA/serial. Defaults are VGA=1, SERIAL=0.
2025-07-04 02:20:31 -04:00
62ab718e4d Serial output works! 2025-07-04 01:47:44 -04:00
7 changed files with 71 additions and 44 deletions

View file

@ -25,6 +25,19 @@ OBJECTS := $(OBJECTS:.s=.o)
OUT_ELF := $(BIN_DIR)/ukern.elf
OUT_ISO := $(BIN_DIR)/uOS.iso
###QEMU CONFIG
QEMU_FLAGS :=
VGA ?= 1
SERIAL ?= 0
ifeq ($(VGA), 0)
QEMU_FLAGS += -display none
endif
ifeq ($(SERIAL), 1)
QEMU_FLAGS += -serial stdio
endif
###RULES
.PHONY: all
@ -61,16 +74,16 @@ iso: $(OUT_ISO)
.PHONY: run
run: $(OUT_ELF)
$(QEMU) -kernel $(OUT_ELF)
$(QEMU) $(QEMU_FLAGS) -kernel $(OUT_ELF)
.PHONY: debug
debug:
$(QEMU) -s -S -kernel $(OUT_ELF)
$(QEMU) $(QEMU_FLAGS) -s -S -kernel $(OUT_ELF)
.PHONY: run_iso
run_iso:
$(QEMU) -cdrom $(OUT_ISO)
$(QEMU) $(QEMU_FLAGS) -cdrom $(OUT_ISO)
.PHONY: debug_iso
debug_iso:
$(QEMU) -s -S -cdrom $(OUT_ISO)
$(QEMU) $(QEMU_FLAGS) -s -S -cdrom $(OUT_ISO)

View file

@ -14,6 +14,8 @@ struct char_writer_s {
void* ctx;
};
extern char_writer_t* default_output;
int putc(char_writer_t*, char);
int print(char_writer_t*, const char*);
int println(char_writer_t*, const char*);

View file

@ -9,9 +9,9 @@
#include "io.h"
#include <stdint.h>
typedef struct SerialState_s {
typedef struct serial_ctx_s {
uint16_t port;
} SerialState_t;
} serial_ctx_t;
extern char_writer_t* default_COM;
@ -20,7 +20,7 @@ extern char_writer_t* default_COM;
* @param port The serial port number to initialize
* @return Status code indicating success or failure
*/
int serial_init(uint16_t port);
int serial_init(serial_ctx_t* ctx);
/**
* @brief Check if there is pending data to receive on the serial port
@ -32,19 +32,19 @@ int serial_recv_pending();
* @brief Receive a single byte from the serial port
* @return The received byte as an 8-bit unsigned integer
*/
uint8_t serial_recv8(uint16_t port);
uint8_t serial_recv8(serial_ctx_t* ctx);
/**
* @brief Check if the serial port is ready to send data
* @return Non-zero if ready to send, 0 otherwise
*/
int serial_send_pending(uint16_t port);
int serial_send_pending(serial_ctx_t* ctx);
/**
* @brief Send a single byte through the serial port
* @param data The 8-bit data byte to send
*/
int serial_send8(uint16_t port, char data);
int serial_send8(void* ctx, char data);
#endif

View file

@ -4,21 +4,13 @@
#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:
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",
if(default_output){
printf(default_output, "INTERRUPT TRIGGERED! Info below.\n");
printf(default_output, "Int_ID|ERR: %X|%X\n", cpu_state->interrupt_id, cpu_state->error_code);
printf(default_output, "EFLAGS|CS|EIP: %X|%X|%X\n", cpu_state->eflags, cpu_state->cs,cpu_state->eip);
printf(default_output, "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,
@ -27,7 +19,7 @@ void generic_isr_handler(StateSnapshot_t* cpu_state) {
cpu_state->ebp,
cpu_state->esi,
cpu_state->edi);
printf(out, "HALT!");
printf(default_output, "HALT!");
while(1);
}
}

View file

@ -2,6 +2,8 @@
#include "kttools.h"
#include <stdarg.h>
char_writer_t* default_output = 0;
int printhex(char_writer_t* writer, uint32_t out) {
char buff[9];
i_to_str(out, buff, 9, 0x10);

View file

@ -25,17 +25,27 @@ void kern_main(uint32_t multiboot_magic, mb_info_t* multiboot_info)
default_vga = &vga_writer;
vga_clear_ctx(&vga_ctx);
//Serial prep
serial_ctx_t serial_ctx;
serial_ctx.port = COM1;
char_writer_t serial_writer;
serial_writer.ctx = &serial_ctx;
serial_writer.putChar = serial_send8;
default_COM = &serial_writer;
serial_init(&serial_ctx);
printf(&vga_writer, "Entry eax:%X\n", multiboot_magic);
//AND OUR DEFAULT OUTPUT IS:
default_output = default_vga;
printf(default_output, "Entry eax:%X\n", multiboot_magic);
if(multiboot_magic != 0x2BADB002) {
println(&vga_writer, "Bootloader not multiboot1 compliant! Needed for mmap, etc. Can't work without it, kthxbye!");
println(default_output, "Bootloader not multiboot1 compliant! Needed for mmap, etc. Can't work without it, kthxbye!");
return;
} else {
println(&vga_writer, "Multiboot detected! Continuing...");
}
printf(&vga_writer, "MEM_LOWER:%X\n", multiboot_info->mem_lower);
printf(&vga_writer, "MEM_UPPER:%X\n", multiboot_info->mem_upper);
println(default_output, "Multiboot detected! Continuing...");
}
printf(default_output, "MEM_LOWER:%X\n", multiboot_info->mem_lower);
printf(default_output, "MEM_UPPER:%X\n", multiboot_info->mem_upper);
}

View file

@ -3,11 +3,12 @@
char_writer_t* default_COM = 0;
int serial_init(uint16_t port) {
int serial_init(serial_ctx_t* ctx) {
//disable interrupts when messing with it
outb(port + 1, 0x00);
outb(ctx->port + 1, 0x00);
//set the dlab latch in order to make the baud rate accessible. In binary for clarity.
outb(port + 3, 0b10000000);
outb(ctx->port + 3, 0b10000000);
//Sets the baud rate. Quick except on baud rate:
//internal clock for the serial controller is 115200 ticks/sec. Or 115200hz.
@ -18,44 +19,51 @@ int serial_init(uint16_t port) {
//(Source:https://wiki.osdev.org/Serial_Ports#Baud_Rate)
//Baud lo byte
outb(port, 0x03);
outb(ctx->port, 0x03);
//Baud hi byte
outb(port+1, 0x00);
outb(ctx->port+1, 0x00);
//Again from OSWiki we're going to use their defaults for now.
//DLAB off
//Parity=0(no Parity)
//One stop bit (set to 0)
//Transmit in units of 8 bits
outb(port+3, 0b00000011);
outb(ctx->port+3, 0b00000011);
//FIFO Control register
//14 bit threshold for interrupt trigger
//flush and enable FIFOs.
outb(port+2, 0b11000111);
outb(ctx->port+2, 0b11000111);
//Enable IRQs + DTR + RTS
//Nicely explained here: https://stackoverflow.com/questions/957337/what-is-the-difference-between-dtr-dsr-and-rts-cts-flow-control
//In the future, this will be referred to 0x0B.
outb(port+4, 0b00001011);
outb(ctx->port+4, 0b00001011);
//done! Let's test our chip. put it in loopback mode.
outb(port+4, 0x1E);
outb(ctx->port+4, 0x1E);
//Lets try sending C4 (boom!) to test it
outb(port, 0xC4);
outb(ctx->port, 0xC4);
if(inb(port) != 0xC4) {
if(inb(ctx->port) != 0xC4) {
//Boo womp
return 1;
}
//we're good! All set up. Lets put it back into normal operational mode and gtfo.
outb(port+4, 0x0B);
outb(ctx->port+4, 0x0B);
return 0;
}
int serial_send_pending(uint16_t port) {
int serial_send_pending(serial_ctx_t* ctx) {
//Bit 5 of the line status register has our output queue, essentially
return !(inb(port + 5) & 0x20);
return !(inb(ctx->port + 5) & 0x20);
}
int serial_send8(void* ctx, char out) {
serial_ctx_t* serial_ctx = (serial_ctx_t*) ctx;
while(serial_send_pending(serial_ctx));
outb(serial_ctx->port, out);
return 0;
}