Have getc, halting, timer, graphics, mode switching

This commit is contained in:
Андреев Григорий 2026-04-06 16:08:47 +03:00
parent f601c99d39
commit 4a446c2d51
11 changed files with 242 additions and 26 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ cmake-build-debug
CMakeLists.txt
__pycache__
res.txt
vibing.txt

View File

@ -106,7 +106,7 @@ debug-nox: image.bin
-ex "break _start" \
-ex "continue"
USERPROGS=./user/false ./user/greet ./user/div0 ./user/shout ./user/badputs ./user/bss
USERPROGS=./user/false ./user/greet ./user/div0 ./user/shout ./user/badputs ./user/bss ./user/snake
fs.img: ./kernel.bin ./tools/mkfs $(USERPROGS)
./tools/mkfs $@ $< $(USERPROGS)

134
cpu/idt.c
View File

@ -3,7 +3,10 @@
#include "memlayout.h"
#include "../syscall.h"
#include "../proc.h"
#include "../drivers/keyboard.h"
#include "../drivers/port.h"
#include "../drivers/pit.h"
#include "../drivers/vga.h"
#include "../console.h"
enum {
@ -147,7 +150,31 @@ bool is_userspace_ptr_mapped(uint32_t ptr) {
return 1;
}
static void* get_userspace_cstr(uint32_t ptr) {
static bool is_userspace_range_mapped(uint32_t ptr, uint32_t size) {
if (size == 0) {
return 1;
}
if (ptr >= KERNBASE) {
return 0;
}
uint32_t end = ptr + size - 1;
if (end < ptr || end >= KERNBASE) {
return 0;
}
uint32_t last_page = PGROUNDDOWN(end);
for (uint32_t addr = ptr;; addr = PGROUNDDOWN(addr) + PGSIZE) {
if (!is_userspace_ptr_mapped(addr)) {
return 0;
}
if (PGROUNDDOWN(addr) == last_page) {
return 1;
}
}
}
static bool is_userspace_cstr(uint32_t ptr) {
for (uint32_t addr = ptr;; addr++) {
if (addr == 0 || !is_userspace_ptr_mapped(addr)) {
return 0;
@ -158,16 +185,75 @@ static void* get_userspace_cstr(uint32_t ptr) {
}
}
static int handle_puts(const char* s) {
if (!s) {
static _Noreturn void userspace_panic(const char* msg) {
if (!vga_is_text_mode()) {
switch_to_text_mode();
vga_clear_screen();
}
printk(msg);
killproc();
}
static void handle_puts(uintptr_t s) {
if (!is_userspace_cstr(s)) {
userspace_panic("SYS_puts panic: page fault\n");
}
printk((const char*)s);
}
static void require_text_mode_for_userspace_text_syscall() {
if (!vga_is_text_mode()) {
userspace_panic("Userspace panic: text syscall in graphics mode\n");
}
}
static void handle_swap_frame(uintptr_t frame) {
enum {
VGA_GRAPHICS_FRAME_SIZE = VGA_GRAPHICS_WIDTH * VGA_GRAPHICS_HEIGHT,
};
if (vga_is_text_mode()) {
userspace_panic("Userspace panic: frame swap in text mode\n");
}
if (!is_userspace_range_mapped(frame, VGA_GRAPHICS_FRAME_SIZE)) {
userspace_panic("SYS_swap_frame panic: page fault\n");
}
uint8_t *video = (uint8_t*)(KERNBASE + 0xA0000);
uint8_t *user = (uint8_t*)frame;
for (uint32_t i = 0; i < VGA_GRAPHICS_FRAME_SIZE; i++) {
video[i] = user[i];
}
}
static void refill_keyboard_copy_buffer(void) {
size_t count = kbd_state_shrd.len;
if (count == 0) {
return;
}
cli();
size_t rem = KEYBOARD_INTERRUPT_BUF_CAP - kbd_state_shrd.copy_len;
size_t copying = rem < count ? rem : count;
memcpy(kbd_state_shrd.copy_buf, kbd_state_shrd.buf, copying);
kbd_state_shrd.len -= copying;
kbd_state_shrd.copy_len += copying;
sti();
}
static int handle_getc(void) {
if (kbd_can_take_from_copy_buffer()) {
return kbd_take_from_copy_buffer();
}
refill_keyboard_copy_buffer();
if (!kbd_can_take_from_copy_buffer()) {
return -1;
}
printk(s);
return 0;
return kbd_take_from_copy_buffer();
}
static void handle_syscall(registers_t* r) {
int ret;
switch (r->eax) {
case SYS_exit:
if (r->ebx == 0) {
@ -177,24 +263,44 @@ static void handle_syscall(registers_t* r) {
}
killproc();
case SYS_greet:
require_text_mode_for_userspace_text_syscall();
printk("Hello world!\n");
r->eax = 0;
break;
case SYS_putc:
printk((const char[]){r->ebx, '\0'});
require_text_mode_for_userspace_text_syscall();
printk((const char[]){(char)r->ebx, '\0'});
r->eax = 0;
break;
case SYS_puts:
ret = handle_puts(get_userspace_cstr(r->ebx));
if (ret < 0) {
printk("SYS_puts panic: page fault\n");
killproc();
}
require_text_mode_for_userspace_text_syscall();
handle_puts(r->ebx);
r->eax = 0;
break;
case SYS_switch_to_text:
switch_to_text_mode();
r->eax = 0;
break;
case SYS_switch_to_graphics:
switch_to_graphics_mode();
r->eax = 0;
break;
case SYS_swap_frame:
handle_swap_frame(r->ebx);
r->eax = 0;
break;
case SYS_time_ms:
r->eax = get_uptime_ms();
break;
case SYS_halt:
asm volatile("hlt");
r->eax = 0;
break;
case SYS_getc:
r->eax = handle_getc();
break;
default:
printk("Userspace panic: Unknown syscall\n");
killproc();
userspace_panic("Userspace panic: Unknown syscall\n");
}
}

View File

@ -4,10 +4,13 @@
borrowed bits from http://files.osdev.org/mirrors/geezer/osd/graphics/modes.c
*/
#include "vga.h"
#include "cpu/x86.h"
#include "cpu/memlayout.h"
#include "port.h"
static vga_display_mode_t current_display_mode = VGA_DISPLAY_MODE_TEXT;
static inline char inb(int port) {
return port_byte_in(port);
}
@ -410,6 +413,7 @@ void vgaMode13() {
outb(0x3C0, 0x20);
setdefaultVGApalette();
current_display_mode = VGA_DISPLAY_MODE_GRAPHICS;
}
void vgaMode3() {
@ -518,4 +522,21 @@ void vgaMode3() {
inb(VGA+0x1A);
outb(0x3C0, 0x20);
current_display_mode = VGA_DISPLAY_MODE_TEXT;
}
void switch_to_graphics_mode() {
vgaMode13();
}
void switch_to_text_mode() {
vgaMode3();
}
vga_display_mode_t vga_get_display_mode() {
return current_display_mode;
}
bool vga_is_text_mode() {
return current_display_mode == VGA_DISPLAY_MODE_TEXT;
}

View File

@ -25,8 +25,11 @@ enum {
PIT_MODES_HW_TRIGGERRED_STROBE = 0x5,
};
// Wtf is that O_o
static timer_callback callbacks[100];
static int registered_callbacks = 0;
static volatile uint32_t uptime_ms = 0;
void add_timer_callback(timer_callback tc) {
callbacks[registered_callbacks++] = tc;
@ -45,7 +48,7 @@ struct pit_command_t {
unsigned char select_channel : 2;
} __attribute__((packed));
static void dec_sleep_counter(void);
static void timer_int_callback(void);
void init_pit() {
struct pit_command_t cmd = {
@ -59,15 +62,20 @@ void init_pit() {
port_byte_out(0x40, (PIT_PROGRAM_REG & 0xff00) >> 8);
register_interrupt_handler(IRQ0, timer_interrupt_handler);
add_timer_callback(dec_sleep_counter);
add_timer_callback(timer_int_callback);
}
static volatile int sleep_counter = 0;
static void dec_sleep_counter(void) {
static void timer_int_callback(void) {
uptime_ms += 1000 / CLOCK_PRECISION_HZ;
sleep_counter--;
}
uint32_t get_uptime_ms(void) {
return uptime_ms;
}
void msleep(int ms) {
sleep_counter = ms / 10;
while (sleep_counter > 0) {

View File

@ -1,8 +1,11 @@
#pragma once
#include <stdint.h>
typedef void (*timer_callback)(void);
void init_pit(void);
void add_timer_callback(timer_callback tc);
uint32_t get_uptime_ms(void);
void msleep(int ms);

View File

@ -1,5 +1,6 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
enum {
@ -23,5 +24,17 @@ void vga_clear_screen();
void vga_set_char(unsigned offset, char c);
void vga_print_string(const char* s);
#define VGA_GRAPHICS_WIDTH 320
#define VGA_GRAPHICS_HEIGHT 200
typedef enum {
VGA_DISPLAY_MODE_TEXT = 0,
VGA_DISPLAY_MODE_GRAPHICS = 1,
} vga_display_mode_t;
void vgaMode13();
void vgaMode3();
void switch_to_graphics_mode();
void switch_to_text_mode();
vga_display_mode_t vga_get_display_mode();
bool vga_is_text_mode();

View File

@ -19,14 +19,14 @@ void vga_set_pixel(int x, int y, int color) {
}
void graphtest() {
vgaMode13();
switch_to_graphics_mode();
for (int i = 0; i < 320; ++i) {
for (int j = 0; j < 200; ++j) {
vga_set_pixel(i, j, (i+j)/2);
}
}
msleep(5000);
vgaMode3();
switch_to_text_mode();
vga_clear_screen();
}

11
proc.c
View File

@ -1,4 +1,5 @@
#include "proc.h"
#include "drivers/vga.h"
struct context {
// matches the behavior of swtch()
@ -28,9 +29,9 @@ void trapret();
void swtch(void** oldstack, void* newstack);
pde_t *get_user_proc_page_directory() {
// if (!vm.user_task) {
// return 0;
// }
if (!vm.user_task) {
return 0;
}
return vm.user_task->pgdir;
}
@ -87,6 +88,10 @@ void run_elf(const char* name) {
_Noreturn void killproc() {
void* task_stack;
if (!vga_is_text_mode()) {
switch_to_text_mode();
vga_clear_screen();
}
switchkvm();
freevm(vm.user_task->pgdir);
sti();

View File

@ -8,6 +8,12 @@ enum {
SYS_greet = 1,
SYS_putc = 2,
SYS_puts = 3,
SYS_switch_to_text = 4,
SYS_switch_to_graphics = 5,
SYS_swap_frame = 6,
SYS_time_ms = 7,
SYS_halt = 8,
SYS_getc = 9,
};
int syscall(int call, uintptr_t arg);

View File

@ -1,8 +1,61 @@
#include "../syscall.h"
#include "../drivers/vga.h"
#include <stdint.h>
#include <stdbool.h>
int main() {
return 0;
enum {
FRAME_SIZE = VGA_GRAPHICS_WIDTH * VGA_GRAPHICS_HEIGHT,
BLOCK_WIDTH = 270,
BLOCK_HEIGHT = 100,
BLOCK_X = 10,
BLOCK_Y = 0,
};
// Non-zero initializer keeps the framebuffer in the program image, which this
// loader maps more reliably than a large BSS object.
static uint8_t frame[FRAME_SIZE] = { 1 };
static uint32_t time_ms(void) {
return (uint32_t)syscall(SYS_time_ms, 0);
}
static void clear_frame(uint8_t color) {
for (uint32_t i = 0; i < FRAME_SIZE; i++) {
frame[i] = color;
}
}
static void put_pixel(int x, int y, uint8_t color) {
if (x < 0 || x >= VGA_GRAPHICS_WIDTH || y < 0 || y >= VGA_GRAPHICS_HEIGHT) {
return;
}
frame[y * VGA_GRAPHICS_WIDTH + x] = color;
}
static void draw_demo(void) {
clear_frame(0x01);
for (int y = 0; y < BLOCK_HEIGHT; y++) {
for (int x = 0; x < BLOCK_WIDTH; x++) {
int screen_x = BLOCK_X + x;
int screen_y = BLOCK_Y + y;
uint8_t color = (uint8_t)((x + 2 * y) & 0x3f);
if (x < 3 || y < 3 || x >= BLOCK_WIDTH - 3 || y >= BLOCK_HEIGHT - 3) {
color = 0x3f;
}
put_pixel(screen_x, screen_y, color);
}
}
}
int main(void) {
syscall(SYS_switch_to_graphics, 0);
draw_demo();
syscall(SYS_swap_frame, (uintptr_t)frame);
uint32_t start = time_ms();
while ((uint32_t)(time_ms() - start) < 2000) {
}
return 0;
}